diff --git a/package.json b/package.json index 02b63e6d..c87ecffa 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,10 @@ "format": "prettier --write --ignore-unknown .", "format:check": "prettier --check .", "test": "npm run format:check && npm run lint && npm run flow && npm run test:unit", + "test:unit": "vitest run --coverage", + "test:watch": "vitest", "webpack": "babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack -- --progress", "test:unit:watch": "vitest --coverage", - "test:unit": "vitest run --coverage", "prepublishOnly": "npm run babel", "postpublish": "rm -rf ./server && git checkout ./server", "validate-codecov": "curl --data-binary @.github/codecov.yml https://codecov.io/validate", diff --git a/server/meta.jsx b/server/meta.jsx index 0e4ed4e4..8b86061e 100644 --- a/server/meta.jsx +++ b/server/meta.jsx @@ -5,7 +5,6 @@ import urlLib from "url"; import { - ENV, SDK_PATH, SDK_QUERY_KEYS, SDK_SETTINGS, @@ -167,8 +166,8 @@ function isLocalUrl(host: string): boolean { HOST.LOCALTUNNEL, ]; - // eslint-disable-next-line no-process-env return ( + // eslint-disable-next-line no-process-env process.env.NODE_ENV === "development" && localUrls.some((url) => host.includes(url)) ); @@ -177,7 +176,7 @@ function isLocalUrl(host: string): boolean { function validateHostAndPath( hostname: string | null, pathname: string | null -): { hostname: string, pathname: string } { +): {| hostname: string, pathname: string |} { if (!pathname || !hostname) { throw new Error(`Expected host and pathname to be passed for sdk url`); } @@ -206,6 +205,7 @@ function validateSDKUrl(sdkUrl: string) { ); } + // eslint-disable-next-line no-useless-escape const hostnameMatchResults = hostname.match(/[a-z0-9\.\-]+/); if (!hostnameMatchResults || hostnameMatchResults[0] !== hostname) { @@ -360,6 +360,27 @@ export function unpackSDKMeta(sdkMeta?: string): SDKMeta { version = ''; } + // if patch version is less than 132, we want to + // set the version to 132. If for some reason we can't + // parse out a patch version, set version as latest + // if neither cases are true, leave version alone because + // it can be more than a semver version number ("min" for example) + // + // NOTE ABOUT REGEX + // The . in the regex technically need to be escaped but that breaks the + // regex in real browsers. Because we are writing JavaScript in a string, we + // need a double escape (\\.) which breaks the browser but works when using eval() + // a single escape works in the browser but breaks in the tests with eval() + if (/4.0.\\d{1,3}/.test(version)) { + var patchString = version?.split('.')?.pop() + + if (!patchString) { + version = '' + } else if (parseInt(patchString, 10) < 132) { + version = '4.0.132' + } + } + var url = '${baseURL}checkout' + (version ? ('.' + version) : '') + '.js'; var attributes = '${DATA_ATTRIBUTES.PAYPAL_CHECKOUT} ${DATA_ATTRIBUTES.NO_BRIDGE}'; diff --git a/server/meta.test.js b/server/meta.test.js index 2e32da53..18a87e5e 100644 --- a/server/meta.test.js +++ b/server/meta.test.js @@ -2,7 +2,7 @@ /* eslint max-lines: off */ import cheerio from "cheerio"; -import { test, afterEach } from "vitest"; +import { test, afterEach, describe, expect, vi } from "vitest"; import { unpackSDKMeta } from "."; @@ -11,363 +11,211 @@ afterEach(() => { process.env.NODE_ENV = "test"; }); -test("should construct a valid script url", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a valid script url with data-popups-disabled attribute", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { "data-popups-disabled": "true" }, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const dataPopUsDisabled = $("script").attr("data-popups-disabled"); - - if (dataPopUsDisabled !== "true") { - throw new Error( - `Expected dataPopUsDisabled to be true - got ${dataPopUsDisabled}` - ); - } -}); - -test("should construct a valid script url with paypalobjects", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a valid script url with url encoded sdkMeta and trailing ? in checkout.js", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.js?"; - - const { getSDKLoader } = unpackSDKMeta( - encodeURIComponent( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ) - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== "https://www.paypalobjects.com/api/checkout.js") { - throw new Error(`unexpected script url ${src}`); - } -}); - -test("should construct a valid script url with checkout.js using the qa cdn", () => { - const sdkUrl = - "https://uideploy--staticcontent--7482d416a81b5--ghe.preview.dev.paypalinc.com/api/checkout.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a valid script url with checkout.js on localhost", () => { - const sdkUrl = "http://localhost.paypal.com:8000/api/checkout.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a script url with checkout.js on localhost without a paypal.com domain", () => { - // eslint-disable-next-line no-process-env - process.env.NODE_ENV = "development"; - - const sdkUrl = "http://localhost:8000/api/checkout.js"; - - let error; - - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } - - if (error) { - throw new Error(`Should construct script with localhost url`); - } -}); - -test("should not construct a script url with checkout.js for non-supported local urls", () => { - // eslint-disable-next-line no-process-env - process.env.NODE_ENV = "development"; - - const sdkUrl = "http://not.a.supported.url:8000/api/checkout.js"; - - let error; - - try { - unpackSDKMeta( +describe.only("valid checkout.js loading scenarios", () => { + test.each([ + { url: "https://www.paypalobjects.com/api/checkout.js" }, + { url: "http://www.paypalobjects.com/api/checkout.js" }, + { url: "https://www.paypalobjects.com/api/checkout.min.js" }, + { url: "http://www.paypalobjects.com/api/checkout.min.js" }, + { url: "https://www.objects.paypal.cn/api/checkout.js" }, + { url: "http://www.objects.paypal.cn/api/checkout.js" }, + { + url: "https://www.paypalobjects.com/api/checkout.js?", + expected: "https://www.paypalobjects.com/api/checkout.js", + }, + { + url: "https://uideploy--staticcontent--7482d416a81b5--ghe.preview.dev.paypalinc.com/api/checkout.js", + }, + { url: "http://localhost.paypal.com:8000/api/checkout.js" }, + { url: "https://www.paypalobjects.com/api/checkout.min.js" }, + { + url: "https://www.sandbox.paypal.com/cgi-bin/webscr/checkout.js?cmd=_flow&CONTEXT=wtgSziM4oze46J3pBRQ", + expected: "https://www.sandbox.paypal.com/cgi-bin/webscr/checkout.js", + }, + { url: "https://www.paypalobjects.com/api/checkout.4.0.125.js" }, + { url: "https://www.paypalobjects.com/api/checkout.4.0.125.min.js" }, + ])("$url is valid and loads", ({ url, expected }) => { + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ - url: sdkUrl, + url, }) ).toString("base64") ); - } catch (err) { - error = err; - } - - if (!error) { - throw new Error( - `Should construct script with supported local urls: (localhost, loca.lt)` - ); - } -}); - -test("should construct a valid minified script url with paypalobjects", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.min.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); -test("should prevent query string parameters with checkout.js", () => { - const sdkUrl = - "https://www.sandbox.paypal.com/cgi-bin/webscr/checkout.js?cmd=_flow&CONTEXT=wtgSziM4oze46J3pBRQ"; + const $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - // eslint-disable-next-line compat/compat - const urlObject = new URL(sdkUrl); - // we expect the query string params to be stripped out for v4 - urlObject.search = ""; - const expectedUrl = urlObject.toString(); - - if (src !== expectedUrl) { - throw new Error(`Expected script url to be ${expectedUrl} - got ${src}`); - } -}); - -test("should construct a valid versioned script url with paypalobjects", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.125.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + expect(src).toEqual(expected ? expected : url); + }); - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); + test.each([ + { + windowName: "xcomponent__ppcheckout__latest__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.js", + }, + { + windowName: "xcomponent__ppcheckout__min__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.min.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_435__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.435.js", + }, + { + windowName: "xcomponent__ppcheckout__4__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_1__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.132.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_65__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.132.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_131__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.132.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_132__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.132.js", + }, + { + windowName: "xcomponent__ppcheckout__4_0_133__abc12345", + expected: "https://www.paypalobjects.com/api/checkout.4.0.133.js", + }, + ])( + "constructing url from window.name with $windowName is valid and loads", + ({ windowName, expected }) => { + const { getSDKLoader } = unpackSDKMeta(); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); -test("should construct a valid versioned minified script url with paypalobjects", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.125.min.js"; + let scriptTag; - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + // eslint-disable-next-line no-unused-vars + const window = { + name: windowName, + }; - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); -test("should construct a valid localhost script url", () => { - const sdkUrl = "http://localhost.paypal.com:8000/sdk/js?client-id=foo"; + const $$ = cheerio.load(scriptTag); + const scriptz = $$("script[data-paypal-checkout]"); + const src = scriptz.attr("src"); - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") + expect(src).toEqual(expected); + } ); - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + test("should construct a script url with checkout.js on localhost without a paypal.com domain", () => { + // eslint-disable-next-line no-process-env + process.env.NODE_ENV = "development"; -test("should unpack a valid sdk meta bundle with a component", () => { - const sdkUrl = - "https://www.paypal.com/sdk/js?client-id=foo&components=buttons"; + const sdkUrl = "http://localhost:8000/api/checkout.js"; - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + let error; - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } + expect(error).toEqual(undefined); + }); }); -test("should unpack a valid sdk meta bundle with multiple components", () => { - const sdkUrl = - "https://www.paypal.com/sdk/js?client-id=foo&components=buttons,hosted-fields"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); +describe("invalid checkout.js loading scenarios", () => { + test.each([ + { + url: "data://www.paypalobjects.com/api/checkout.js", + expected: + "Expected protocol for sdk url to be http: or https: for host: www.paypalobjects.com - got data:", + }, + { + url: "\uFEFFhttp://www.paypalobjects.com/api/checkout.js", + expected: + "Expected protocol for sdk url to be http: or https: for host: www.paypalobjects.com - got http:", + }, + { + url: "https://www.paypalobjects.com/**/checkout.js", + expected: "Invalid path for legacy sdk url: /**/checkout.js", + }, + ])("$url is not valid and does not load", ({ url, expected }) => { + let error; - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + expect(error).toEqual(new Error(expected)); + }); -test("should unpack a valid sdk meta bundle with multiple merchant-id email addresses", () => { - const emails = [ - "test@gmail.com", - "foo@bar.com", - "test@test.org.uk", - "test-test@test.com", - "test.test@test.com", - "test@test@test.com", - ]; - - const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${emails - .map((anEmail) => encodeURIComponent(anEmail)) - .join(",")}`; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") + test.each([ + "", + "ppcheckout__4_0_435__abc12345", + "ppcheckout__4_0_435__abc12345", + "xcomponent__ppcheckout__4_*_435__abc12345", + "xcomponent__ppcheckout__4_!_435__abc12345", + ])( + "constructing url from window.name with %s is not valid and does not load", + (windowName) => { + const { getSDKLoader } = unpackSDKMeta(); + const writeMock = vi.fn(); + + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); + + // eslint-disable-next-line no-unused-vars + const window = { + name: windowName, + }; + + // eslint-disable-next-line no-unused-vars + const document = { + write: writeMock, + }; + + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); + + expect(writeMock).not.toHaveBeenCalled(); + } ); - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + test("should not construct a script url with checkout.js for non-supported local urls", () => { + // eslint-disable-next-line no-process-env + process.env.NODE_ENV = "development"; -test("should error out from invalid merchant-id email addresses", () => { - const emails = ["@", "@io", "@test.com", "name@"]; + const sdkUrl = "http://not.a.supported.url:8000/api/checkout.js"; - emails.forEach((email) => { - const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${email}`; let error; try { @@ -383,1019 +231,945 @@ test("should error out from invalid merchant-id email addresses", () => { } if (!error) { - throw new Error(`Expected error to be thrown for ${sdkUrl}`); + throw new Error( + `Should construct script with supported local urls: (localhost, loca.lt)` + ); } }); }); -test("should error from very long merchant-id email addresses", () => { - const longEmail = `${"a-very-long-email".repeat(20)}@a-very-long-domain.com`; - const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${longEmail}`; - let error; +describe("loading /sdk/js", () => { + test("should construct a valid script url", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - - if (!error) { - throw new Error(`Expected error to be thrown for ${sdkUrl}`); - } -}); - -test("should construct a valid script url with multiple merchant ids", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - const merchantId = "abcd1234, abcd5678"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-merchant-id": merchantId, - }, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - const dataMerchantId = $("script").attr("data-merchant-id"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - - if (dataMerchantId !== merchantId) { - throw new Error( - `Expected data-merchant-id to be ${merchantId} - got ${dataMerchantId}` - ); - } -}); - -test("should construct a valid script url with a single merchant id in the url", () => { - const merchantId = "UYEGJNV75RAJQ"; - const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${merchantId}`; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a valid script url without invalid attributes", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-dummy-id": "abcd", - }, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - const result = $("script").attr("data-dummy-id"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); - if (result !== undefined) { - throw new Error( - `Expected invalid attribute to be undefined - got ${result}` - ); - } -}); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); -test("should error out with an unsecure protocol", () => { - const sdkUrl = "http://www.paypal.com/sdk/js?client-id=foo&"; - let error; + test("should construct a valid script url with data-popups-disabled attribute", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, + attrs: { "data-popups-disabled": "true" }, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); - -test("should error out with an invalid protocol", () => { - const sdkUrl = "meep://www.paypal.com/sdk/js?client-id=foo"; - let error; - - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + const $ = cheerio.load(getSDKLoader()); + const dataPopUsDisabled = $("script").attr("data-popups-disabled"); - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + if (dataPopUsDisabled !== "true") { + throw new Error( + `Expected dataPopUsDisabled to be true - got ${dataPopUsDisabled}` + ); + } + }); -test("should error out with an invalid protocol in localhost", () => { - const sdkUrl = "meep://localhost.paypal.com:8000/sdk/js?client-id=foo"; - let error; + test("should construct a valid localhost script url", () => { + const sdkUrl = "http://localhost.paypal.com:8000/sdk/js?client-id=foo"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); -test("should error out with an invalid host", () => { - const sdkUrl = "https://?client-id=foo"; - let error; + test("should unpack a valid sdk meta bundle with a component", () => { + const sdkUrl = + "https://www.paypal.com/sdk/js?client-id=foo&components=buttons"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); -test("should error out with no path", () => { - const sdkUrl = "https://www.paypal.com?client-id=foo"; - let error; + test("should unpack a valid sdk meta bundle with multiple components", () => { + const sdkUrl = + "https://www.paypal.com/sdk/js?client-id=foo&components=buttons,hosted-fields"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); -test("should error out with an invalid path", () => { - const sdkUrl = "https://www.paypal.com/sdk/meep?client-id=foo"; - let error; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); - try { - unpackSDKMeta( + test("should unpack a valid sdk meta bundle with multiple merchant-id email addresses", () => { + const emails = [ + "test@gmail.com", + "foo@bar.com", + "test@test.org.uk", + "test-test@test.com", + "test.test@test.com", + "test@test@test.com", + ]; + + const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${emails + .map((anEmail) => encodeURIComponent(anEmail)) + .join(",")}`; + + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); - -test("should error out with an invalid legacy path", () => { - const sdkUrl = "https://www.paypalobjects.com/foo.js"; - let error; + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + test("should error out from invalid merchant-id email addresses", () => { + const emails = ["@", "@io", "@test.com", "name@"]; + + emails.forEach((email) => { + const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${email}`; + let error; + + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } + + if (!error) { + throw new Error(`Expected error to be thrown for ${sdkUrl}`); + } + }); + }); -test("should error out with an empty query param", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id="; - let error; + test("should error from very long merchant-id email addresses", () => { + const longEmail = `${"a-very-long-email".repeat( + 20 + )}@a-very-long-domain.com`; + const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${longEmail}`; + let error; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown for ${sdkUrl}`); + } + }); -test("should error out with a duplicated query param", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&client-id=bar"; - let error; + test("should construct a valid script url with multiple merchant ids", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; + const merchantId = "abcd1234, abcd5678"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, + attrs: { + "data-merchant-id": merchantId, + }, }) ).toString("base64") ); - } catch (err) { - error = err; - } - - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); -test("should error out with an invalid query param", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&foo=bar"; - let error; + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + const dataMerchantId = $("script").attr("data-merchant-id"); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + if (dataMerchantId !== merchantId) { + throw new Error( + `Expected data-merchant-id to be ${merchantId} - got ${dataMerchantId}` + ); + } + }); -test("should error out with an invalid query value", () => { - const sdkUrl = 'https://www.paypal.com/sdk/js?client-id="foo"'; - let error; + test("should construct a valid script url with a single merchant id in the url", () => { + const merchantId = "UYEGJNV75RAJQ"; + const sdkUrl = `https://www.paypal.com/sdk/js?client-id=foo&merchant-id=${merchantId}`; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); -test("should error out with a hash", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo#bar"; - let error; + test("should construct a valid script url without invalid attributes", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, + attrs: { + "data-dummy-id": "abcd", + }, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); - -test("should construct a valid loader even when no url passed", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.js"; - - const { getSDKLoader } = unpackSDKMeta(); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); - - let scriptTag; - - // eslint-disable-next-line no-unused-vars - const window = { - name: "xcomponent__ppcheckout__latest__abc12345", - }; - - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + const result = $("script").attr("data-dummy-id"); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); - - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script[data-paypal-checkout]"); - const src = scriptz.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } -test("should construct a valid minified loader even when no url passed", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.min.js"; + if (result !== undefined) { + throw new Error( + `Expected invalid attribute to be undefined - got ${result}` + ); + } + }); - const { getSDKLoader } = unpackSDKMeta(); + test("should error out with an unsecure protocol", () => { + const sdkUrl = "http://www.paypal.com/sdk/js?client-id=foo&"; + let error; - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); - - let scriptTag; - - // eslint-disable-next-line no-unused-vars - const window = { - name: "xcomponent__ppcheckout__min__abc12345", - }; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + test("should error out with an invalid protocol", () => { + const sdkUrl = "meep://www.paypal.com/sdk/js?client-id=foo"; + let error; - const $$ = cheerio.load(scriptTag); - const src = $$("script").attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should construct a valid version loader even when no url passed", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.435.js"; + test("should error out with an invalid protocol in localhost", () => { + const sdkUrl = "meep://localhost.paypal.com:8000/sdk/js?client-id=foo"; + let error; - const { getSDKLoader } = unpackSDKMeta(); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - let scriptTag; + test("should error out with an empty query param", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id="; + let error; - // eslint-disable-next-line no-unused-vars - const window = { - name: "xcomponent__ppcheckout__4_0_435__abc12345", - }; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + test("should error out with a duplicated query param", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&client-id=bar"; + let error; - const $$ = cheerio.load(scriptTag); - const src = $$("script").attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should construct a valid loader even when no url passed with version 4", () => { - const sdkUrl = "https://www.paypalobjects.com/api/checkout.js"; + test("should error out with an invalid query param", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&foo=bar"; + let error; - const { getSDKLoader } = unpackSDKMeta(); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - let scriptTag; + test("should error out with an invalid query value", () => { + const sdkUrl = 'https://www.paypal.com/sdk/js?client-id="foo"'; + let error; - // eslint-disable-next-line no-unused-vars - const window = { - name: "xcomponent__ppcheckout__4__abc12345", - }; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + test("should error out with a hash", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo#bar"; + let error; - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script[data-paypal-checkout]"); - const src = scriptz.attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should construct a valid loader even when no url passed with version 5 in a popup", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foobarbaz"; + test("should construct a valid loader even when no url passed with version 5 in a popup", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foobarbaz"; - const { getSDKLoader } = unpackSDKMeta(); + const { getSDKLoader } = unpackSDKMeta(); - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - let scriptTag; + let scriptTag; - // eslint-disable-next-line no-unused-vars - const window = { - opener: { - document: { - querySelector: (selector) => { - if (selector !== 'script[src*="/sdk/js"]') { - throw new Error( - `Expected selector to be 'script[src*="/sdk/js"]', got ${selector}` - ); - } + // eslint-disable-next-line no-unused-vars + const window = { + opener: { + document: { + querySelector: (selector) => { + if (selector !== 'script[src*="/sdk/js"]') { + throw new Error( + `Expected selector to be 'script[src*="/sdk/js"]', got ${selector}` + ); + } - return { - src: sdkUrl, - }; + return { + src: sdkUrl, + }; + }, }, }, - }, - }; + }; - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script"); - const src = scriptz.attr("src"); + const $$ = cheerio.load(scriptTag); + const scriptz = $$("script"); + const src = scriptz.attr("src"); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); -test("should construct a valid loader even when no url passed with version 5 in an iframe", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foobarbaz"; + test("should construct a valid loader even when no url passed with version 5 in an iframe", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foobarbaz"; - const { getSDKLoader } = unpackSDKMeta(); + const { getSDKLoader } = unpackSDKMeta(); - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - let scriptTag; + let scriptTag; - // eslint-disable-next-line no-unused-vars - const window = { - parent: { - document: { - querySelector: (selector) => { - if (selector !== 'script[src*="/sdk/js"]') { - throw new Error( - `Expected selector to be 'script[src*="/sdk/js"]', got ${selector}` - ); - } + // eslint-disable-next-line no-unused-vars + const window = { + parent: { + document: { + querySelector: (selector) => { + if (selector !== 'script[src*="/sdk/js"]') { + throw new Error( + `Expected selector to be 'script[src*="/sdk/js"]', got ${selector}` + ); + } - return { - src: sdkUrl, - }; + return { + src: sdkUrl, + }; + }, }, }, - }, - }; - - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; - - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); - - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script"); - const src = scriptz.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should error out if a non http or https url passed", () => { - const sdkUrl = "data://www.paypalobjects.com/api/checkout.js"; + }; - let error; + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + const $$ = cheerio.load(scriptTag); + const scriptz = $$("script"); + const src = scriptz.attr("src"); -test("should error out if a non http or https url passed for the sdk", () => { - const sdkUrl = "data://www.paypal.com/sdk/js?client-id=foo"; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); - let error; + test("should error out if a non http or https url passed for the sdk", () => { + const sdkUrl = "data://www.paypal.com/sdk/js?client-id=foo"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let error; - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } -test("should error out if special characters are passed in the checkout.js path", () => { - const sdkUrl = "https://www.paypalobjects.com/**/checkout.js"; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - let error; + test("should error out if a double && passed in the sdk url", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&¤cy=USD"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let error; - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } -test("should error out if a double && passed in the sdk url", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&¤cy=USD"; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - let error; + test("should error out if sdk url ends with &", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let error; - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } -test("should error out if sdk url ends with &", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&"; + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); - let error; + test("should construct a valid script url hosted on www.paypal.cn", () => { + const sdkUrl = "https://www.paypal.cn/sdk/js?client-id=foo"; - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); - -test("should construct a valid script url with paypalobjects on http", () => { - const sdkUrl = "http://www.paypalobjects.com/api/checkout.js"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); - -test("should construct a valid min script url with paypalobjects on http", () => { - const sdkUrl = "http://www.paypalobjects.com/api/checkout.min.js"; + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + }); - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); + test('should error when the script url does not start with "https://" or "http://"', () => { + const sdkUrl = "\uFEFFhttps://www.paypal.com/sdk/js?client-id=foo"; - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + let error; -test("should construct a valid script url hosted on objects.paypal.cn", () => { - const sdkUrl = "http://www.objects.paypal.cn/api/checkout.js"; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + if (!error) { + throw new Error(`Expected error to be thrown`); + } - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrlLegacy, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should construct a valid script url hosted on www.paypal.cn", () => { - const sdkUrl = "https://www.paypal.cn/sdk/js?client-id=foo"; + test("should error when invalid characters are found in the subdomain - we allow letters, numbers, . and -", () => { + const sdkUrl = + "https://\uff3cU0022\uff3cU003E\uff3cU003C\uff3cU002Fscript\uff3cU003E\uff3cU003Ciframe\uff3cU0020srcdoc\uff3cU003D\uff3cU0027.www.paypal.com/sdk/js?client-id=foo"; - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); + let error; - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); }); -test('should error when the script url does not start with "https://" or "http://"', () => { - const sdkUrl = "\uFEFFhttps://www.paypal.com/sdk/js?client-id=foo"; - const sdkUrlLegacy = "\uFEFFhttp://www.paypalobjects.com/api/checkout.js"; +describe("loading /web-sdk/v6", () => { + test("should construct a valid web-sdk bridge url", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000"; + const sdkUID = "abc123"; - let error; - - try { - unpackSDKMeta( + const { getSDKLoader } = unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, + attrs: { + "data-uid": sdkUID, + }, }) ).toString("base64") ); - } catch (err) { - error = err; - } - if (!error) { - throw new Error(`Expected error to be thrown`); - } + const $ = cheerio.load(getSDKLoader()); + const script = $("script"); + const src = script.attr("src"); + const uid = script.attr("data-uid"); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrlLegacy, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } + if (uid !== sdkUID) { + throw new Error(`Expected data UID be ${sdkUID} - got ${uid}`); + } + }); - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + test("should error when extra parameters are present", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000&name=value"; -test("should error when invalid characters are found in the subdomain - we allow letters, numbers, . and -", () => { - const sdkUrl = - "https://\uff3cU0022\uff3cU003E\uff3cU003C\uff3cU002Fscript\uff3cU003E\uff3cU003Ciframe\uff3cU0020srcdoc\uff3cU003D\uff3cU0027.www.paypal.com/sdk/js?client-id=foo"; + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - let error; + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + test("should error when the version parameter is missing", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?origin=https%3A%2F%2Fwww.example.com%3A8000"; - if (!error) { - throw new Error(`Expected error to be thrown`); - } -}); + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } -test("should construct a valid web-sdk bridge url", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000"; - const sdkUID = "abc123"; - - const { getSDKLoader } = unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": sdkUID, - }, - }) - ).toString("base64") - ); + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); - const $ = cheerio.load(getSDKLoader()); - const script = $("script"); - const src = script.attr("src"); - const uid = script.attr("data-uid"); - - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - if (uid !== sdkUID) { - throw new Error(`Expected data UID be ${sdkUID} - got ${uid}`); - } -}); + test("should error when the version parameter is invalid", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=^1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000"; -test("should error when extra parameters are present", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000&name=value"; + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); - if (!error) { - throw new Error("Expected error to be thrown"); - } -}); + test("should error when the origin parameter is missing", () => { + const sdkUrl = "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3"; -test("should error when the version parameter is missing", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?origin=https%3A%2F%2Fwww.example.com%3A8000"; + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); - if (!error) { - throw new Error("Expected error to be thrown"); - } -}); + test("should error when the origin parameter is invalid", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=example"; -test("should error when the version parameter is invalid", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=^1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000"; + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); + + test("should error when the origin parameter is not just the origin", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000%2Fpath"; + + let error = null; + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + attrs: { + "data-uid": "abc123", + }, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (!error) { - throw new Error("Expected error to be thrown"); - } + if (!error) { + throw new Error("Expected error to be thrown"); + } + }); }); -test("should error when the origin parameter is missing", () => { - const sdkUrl = "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3"; +describe("loading invalid urls", () => { + test("should error out with an invalid host", () => { + const sdkUrl = "https://?client-id=foo"; + let error; - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (!error) { - throw new Error("Expected error to be thrown"); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should error when the origin parameter is invalid", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=example"; + test("should error out with no path", () => { + const sdkUrl = "https://www.paypal.com?client-id=foo"; + let error; - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - if (!error) { - throw new Error("Expected error to be thrown"); - } -}); + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); -test("should error when the origin parameter is not just the origin", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=https%3A%2F%2Fwww.example.com%3A8000%2Fpath"; + test("should error out with an invalid path", () => { + const sdkUrl = "https://www.paypal.com/sdk/meep?client-id=foo"; + let error; - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } + + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); + + test("should error out with an invalid legacy path", () => { + const sdkUrl = "https://www.paypalobjects.com/foo.js"; + let error; - if (!error) { - throw new Error("Expected error to be thrown"); - } + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } + + if (!error) { + throw new Error(`Expected error to be thrown`); + } + }); });