From f2276a2b23036cacd156e409c8ce37352cfd8479 Mon Sep 17 00:00:00 2001 From: Shane Brunson Date: Thu, 20 Jun 2024 13:51:56 -0500 Subject: [PATCH] Revert "keep v4 patch versions at 132 or above (#213)" (#215) This reverts commit ecbc2f9e598270fe1dc9b6adec8d271531aa4fc0. --- package.json | 3 +- server/meta.jsx | 27 +- server/meta.test.js | 2034 ++++++++++++++++++++++++------------------- 3 files changed, 1134 insertions(+), 930 deletions(-) diff --git a/package.json b/package.json index c87ecffa..02b63e6d 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,9 @@ "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 8b86061e..0e4ed4e4 100644 --- a/server/meta.jsx +++ b/server/meta.jsx @@ -5,6 +5,7 @@ import urlLib from "url"; import { + ENV, SDK_PATH, SDK_QUERY_KEYS, SDK_SETTINGS, @@ -166,8 +167,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)) ); @@ -176,7 +177,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`); } @@ -205,7 +206,6 @@ function validateSDKUrl(sdkUrl: string) { ); } - // eslint-disable-next-line no-useless-escape const hostnameMatchResults = hostname.match(/[a-z0-9\.\-]+/); if (!hostnameMatchResults || hostnameMatchResults[0] !== hostname) { @@ -360,27 +360,6 @@ 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 18a87e5e..2e32da53 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, describe, expect, vi } from "vitest"; +import { test, afterEach } from "vitest"; import { unpackSDKMeta } from "."; @@ -11,211 +11,363 @@ afterEach(() => { process.env.NODE_ENV = "test"; }); -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( +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, + url: sdkUrl, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const script = $("script[data-paypal-checkout]"); - const src = script.attr("src"); + if (error) { + throw new Error(`Should construct script with localhost url`); + } +}); - expect(src).toEqual(expected ? expected : 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"; - 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(); + const sdkUrl = "http://not.a.supported.url:8000/api/checkout.js"; + + let error; + + 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( + `Should construct script with supported local urls: (localhost, loca.lt)` + ); + } +}); - let scriptTag; +test("should construct a valid minified script url with paypalobjects", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.min.js"; - // eslint-disable-next-line no-unused-vars - const window = { - name: windowName, - }; + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + const $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script[data-paypal-checkout]"); - const src = scriptz.attr("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"; - expect(src).toEqual(expected); - } + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") ); - 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 $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - const sdkUrl = "http://localhost:8000/api/checkout.js"; + // 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(); - let error; + if (src !== expectedUrl) { + throw new Error(`Expected script url to be ${expectedUrl} - got ${src}`); + } +}); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } +test("should construct a valid versioned script url with paypalobjects", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.125.js"; - expect(error).toEqual(undefined); - }); + 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}`); + } }); -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; +test("should construct a valid versioned minified script url with paypalobjects", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.125.min.js"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); - expect(error).toEqual(new Error(expected)); - }); + const $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - 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(); - } + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); + +test("should construct a valid localhost script url", () => { + const sdkUrl = "http://localhost.paypal.com:8000/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 unpack a valid sdk meta bundle with a component", () => { + const sdkUrl = + "https://www.paypal.com/sdk/js?client-id=foo&components=buttons"; + + 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 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") + ); + + 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 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("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 $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); + + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - const sdkUrl = "http://not.a.supported.url:8000/api/checkout.js"; +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 { @@ -231,945 +383,1019 @@ describe("invalid checkout.js loading scenarios", () => { } if (!error) { - throw new Error( - `Should construct script with supported local urls: (localhost, loca.lt)` - ); + throw new Error(`Expected error to be thrown for ${sdkUrl}`); } }); }); -describe("loading /sdk/js", () => { - test("should construct a valid script url", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; +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; - const { getSDKLoader } = unpackSDKMeta( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); + if (!error) { + throw new Error(`Expected error to be thrown for ${sdkUrl}`); + } +}); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - }); +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}`); + } + + if (result !== undefined) { + throw new Error( + `Expected invalid attribute to be undefined - got ${result}` + ); + } +}); - test("should construct a valid script url with data-popups-disabled attribute", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; +test("should error out with an unsecure protocol", () => { + const sdkUrl = "http://www.paypal.com/sdk/js?client-id=foo&"; + let error; - const { getSDKLoader } = unpackSDKMeta( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, - attrs: { "data-popups-disabled": "true" }, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const dataPopUsDisabled = $("script").attr("data-popups-disabled"); - - if (dataPopUsDisabled !== "true") { - throw new Error( - `Expected dataPopUsDisabled to be true - got ${dataPopUsDisabled}` - ); - } - }); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - test("should construct a valid localhost script url", () => { - const sdkUrl = "http://localhost.paypal.com:8000/sdk/js?client-id=foo"; +test("should error out with an invalid protocol", () => { + const sdkUrl = "meep://www.paypal.com/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 src = $("script").attr("src"); - - 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 unpack a valid sdk meta bundle with a component", () => { - const sdkUrl = - "https://www.paypal.com/sdk/js?client-id=foo&components=buttons"; +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 src = $("script").attr("src"); - - 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 unpack a valid sdk meta bundle with multiple components", () => { - const sdkUrl = - "https://www.paypal.com/sdk/js?client-id=foo&components=buttons,hosted-fields"; +test("should error out with an invalid host", () => { + const sdkUrl = "https://?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 src = $("script").attr("src"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - 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 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( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - }); +test("should error out with an invalid path", () => { + const sdkUrl = "https://www.paypal.com/sdk/meep?client-id=foo"; + let error; - 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}`); - } - }); - }); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - 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; + 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 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 for ${sdkUrl}`); - } - }); + 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 construct a valid script url with multiple merchant ids", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; - const merchantId = "abcd1234, abcd5678"; +test("should error out with an empty query param", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id="; + let error; - const { getSDKLoader } = unpackSDKMeta( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, - attrs: { - "data-merchant-id": merchantId, - }, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - const dataMerchantId = $("script").attr("data-merchant-id"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } +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; - if (dataMerchantId !== merchantId) { - throw new Error( - `Expected data-merchant-id to be ${merchantId} - got ${dataMerchantId}` - ); - } - }); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - 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}`; + 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 { getSDKLoader } = unpackSDKMeta( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - - 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 without invalid attributes", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo"; +test("should error out with an invalid query value", () => { + const sdkUrl = 'https://www.paypal.com/sdk/js?client-id="foo"'; + let error; - const { getSDKLoader } = unpackSDKMeta( + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, - attrs: { - "data-dummy-id": "abcd", - }, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const src = $("script").attr("src"); - const result = $("script").attr("data-dummy-id"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - 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; - if (result !== undefined) { - throw new Error( - `Expected invalid attribute to be undefined - got ${result}` - ); - } - }); + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } - test("should error out with an unsecure protocol", () => { - const sdkUrl = "http://www.paypal.com/sdk/js?client-id=foo&"; - 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 construct a valid loader even when no url passed", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.js"; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + const { getSDKLoader } = unpackSDKMeta(); - test("should error out with an invalid protocol", () => { - const sdkUrl = "meep://www.paypal.com/sdk/js?client-id=foo"; - let error; + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let scriptTag; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + // eslint-disable-next-line no-unused-vars + const window = { + name: "xcomponent__ppcheckout__latest__abc12345", + }; - test("should error out with an invalid protocol in localhost", () => { - const sdkUrl = "meep://localhost.paypal.com:8000/sdk/js?client-id=foo"; - 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[data-paypal-checkout]"); + const src = scriptz.attr("src"); - test("should error out with an empty query param", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id="; - let error; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } +test("should construct a valid minified loader even when no url passed", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.min.js"; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + const { getSDKLoader } = unpackSDKMeta(); - 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(getSDKLoader()); + const script = $("script").html(); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let scriptTag; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + // eslint-disable-next-line no-unused-vars + const window = { + name: "xcomponent__ppcheckout__min__abc12345", + }; - test("should error out with an invalid query param", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&foo=bar"; - 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 src = $$("script").attr("src"); - test("should error out with an invalid query value", () => { - const sdkUrl = 'https://www.paypal.com/sdk/js?client-id="foo"'; - let error; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } +test("should construct a valid version loader even when no url passed", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.4.0.435.js"; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + const { getSDKLoader } = unpackSDKMeta(); - test("should error out with a hash", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo#bar"; - let error; + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + let scriptTag; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + // eslint-disable-next-line no-unused-vars + const window = { + name: "xcomponent__ppcheckout__4_0_435__abc12345", + }; - 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"; + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - const { getSDKLoader } = unpackSDKMeta(); + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + const $$ = cheerio.load(scriptTag); + const src = $$("script").attr("src"); - let scriptTag; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - // 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}` - ); - } +test("should construct a valid loader even when no url passed with version 4", () => { + const sdkUrl = "https://www.paypalobjects.com/api/checkout.js"; - return { - src: sdkUrl, - }; - }, - }, - }, - }; + const { getSDKLoader } = unpackSDKMeta(); - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; - }, - }; + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + let scriptTag; - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script"); - const src = scriptz.attr("src"); + // eslint-disable-next-line no-unused-vars + const window = { + name: "xcomponent__ppcheckout__4__abc12345", + }; - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - }); + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - 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"; + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - const { getSDKLoader } = unpackSDKMeta(); + const $$ = cheerio.load(scriptTag); + const scriptz = $$("script[data-paypal-checkout]"); + const src = scriptz.attr("src"); - const $ = cheerio.load(getSDKLoader()); - const script = $("script").html(); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - let scriptTag; +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"; - // 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}` - ); - } + const { getSDKLoader } = unpackSDKMeta(); - return { - src: sdkUrl, - }; - }, - }, - }, - }; + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - // eslint-disable-next-line no-unused-vars - const document = { - write: (html) => { - scriptTag = html; + 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}` + ); + } + + return { + src: sdkUrl, + }; + }, }, - }; + }, + }; - // eslint-disable-next-line no-eval, security/detect-eval-with-expression - eval(script); + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - const $$ = cheerio.load(scriptTag); - const scriptz = $$("script"); - const src = scriptz.attr("src"); + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - if (src !== sdkUrl) { - throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); - } - }); + 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 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"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + const { getSDKLoader } = unpackSDKMeta(); - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + const $ = cheerio.load(getSDKLoader()); + const script = $("script").html(); - 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"; + let scriptTag; - let error; + // 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}` + ); + } - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + return { + src: sdkUrl, + }; + }, + }, + }, + }; - if (!error) { - throw new Error(`Expected error to be thrown`); - } - }); + // eslint-disable-next-line no-unused-vars + const document = { + write: (html) => { + scriptTag = html; + }, + }; - test("should error out if sdk url ends with &", () => { - const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&"; + // eslint-disable-next-line no-eval, security/detect-eval-with-expression + eval(script); - let error; + const $$ = cheerio.load(scriptTag); + const scriptz = $$("script"); + 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 error out if a non http or https url passed", () => { + const sdkUrl = "data://www.paypalobjects.com/api/checkout.js"; - test("should construct a valid script url hosted on www.paypal.cn", () => { - const sdkUrl = "https://www.paypal.cn/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 src = $("script").attr("src"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - 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 for the sdk", () => { + const sdkUrl = "data://www.paypal.com/sdk/js?client-id=foo"; - 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"; + let error; - 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`); - } +test("should error out if special characters are passed in the checkout.js path", () => { + const sdkUrl = "https://www.paypalobjects.com/**/checkout.js"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrlLegacy, - }) - ).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 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"; + 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; + } + + if (!error) { + throw new Error(`Expected error to be thrown`); + } }); -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"; +test("should error out if sdk url ends with &", () => { + const sdkUrl = "https://www.paypal.com/sdk/js?client-id=foo&"; - const { getSDKLoader } = unpackSDKMeta( + let error; + + try { + unpackSDKMeta( Buffer.from( JSON.stringify({ url: sdkUrl, - attrs: { - "data-uid": sdkUID, - }, }) ).toString("base64") ); + } catch (err) { + error = err; + } - const $ = cheerio.load(getSDKLoader()); - const script = $("script"); - const src = script.attr("src"); - const uid = script.attr("data-uid"); + if (!error) { + throw new Error(`Expected error to be thrown`); + } +}); - 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 construct a valid script url with paypalobjects on http", () => { + const sdkUrl = "http://www.paypalobjects.com/api/checkout.js"; - 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"; + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + const $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - if (!error) { - throw new Error("Expected error to be thrown"); - } - }); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - 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"; +test("should construct a valid min script url with paypalobjects on http", () => { + const sdkUrl = "http://www.paypalobjects.com/api/checkout.min.js"; - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).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"); - 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"; + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - 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 script url hosted on objects.paypal.cn", () => { + const sdkUrl = "http://www.objects.paypal.cn/api/checkout.js"; - if (!error) { - throw new Error("Expected error to be thrown"); - } - }); + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); - test("should error when the origin parameter is missing", () => { - const sdkUrl = "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3"; + const $ = cheerio.load(getSDKLoader()); + const script = $("script[data-paypal-checkout]"); + const src = script.attr("src"); - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).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 the origin parameter is invalid", () => { - const sdkUrl = - "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=example"; + const { getSDKLoader } = unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrl, + }) + ).toString("base64") + ); - let error = null; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - attrs: { - "data-uid": "abc123", - }, - }) - ).toString("base64") - ); - } catch (err) { - error = err; - } + const $ = cheerio.load(getSDKLoader()); + const src = $("script").attr("src"); - if (!error) { - throw new Error("Expected error to be thrown"); - } - }); + if (src !== sdkUrl) { + throw new Error(`Expected script url to be ${sdkUrl} - got ${src}`); + } +}); - 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 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"; - 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; + } + + if (!error) { + throw new Error(`Expected error to be thrown`); + } + + try { + unpackSDKMeta( + Buffer.from( + JSON.stringify({ + url: sdkUrlLegacy, + }) + ).toString("base64") + ); + } catch (err) { + error = err; + } + + if (!error) { + throw new Error(`Expected error to be thrown`); + } }); -describe("loading invalid urls", () => { - test("should error out with an invalid host", () => { - const sdkUrl = "https://?client-id=foo"; - let error; +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"; - 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 with no path", () => { - const sdkUrl = "https://www.paypal.com?client-id=foo"; - 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 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 out with an invalid path", () => { - const sdkUrl = "https://www.paypal.com/sdk/meep?client-id=foo"; - let error; +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"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).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 out with an invalid legacy path", () => { - const sdkUrl = "https://www.paypalobjects.com/foo.js"; - let error; +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"; - try { - unpackSDKMeta( - Buffer.from( - JSON.stringify({ - url: sdkUrl, - }) - ).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 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; + } + + 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"; + + 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 invalid", () => { + const sdkUrl = + "https://www.paypal.com/web-sdk/v6/bridge?version=1.2.3&origin=example"; + + 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"); + } });