diff --git a/server/meta.jsx b/server/meta.jsx index 041bb44d..91062e7e 100644 --- a/server/meta.jsx +++ b/server/meta.jsx @@ -15,6 +15,8 @@ type SDKMeta = {| getSDKLoader : (options? : {| baseURL? : string, nonce? : string |}) => string |}; +const emailRegex = /^.+@.+$/; + function validatePaymentsSDKUrl({ pathname, query, hash }) { if (pathname !== SDK_PATH) { @@ -37,9 +39,22 @@ function validatePaymentsSDKUrl({ pathname, query, hash }) { throw new TypeError(`Unexpected non-string key for sdk url: ${ key }`); } - if (!val.match(/^[a-zA-Z0-9_,-@.]+$/) && !val.match(/^\*$/)) { + if (!val.match(/^[a-zA-Z0-9+_,-@.]+$/) && !val.match(/^\*$/)) { throw new Error(`Unexpected characters in query key for sdk url: ${ key }=${ val }`); } + + if (key === SDK_QUERY_KEYS.MERCHANT_ID) { + const merchantValues = val.split(","); + merchantValues.forEach(merchantValue => { + if (merchantValue.length > 320) { + throw new Error(`Email is too long: ${merchantValue}`) + } + if (!emailRegex.test(merchantValue)) { + throw new Error(`Malformed. merchant email: ${merchantValue}`); + } + }); + } + } if (hash) { diff --git a/test/server/meta.test.js b/test/server/meta.test.js index 7e7e8ab7..113593f5 100644 --- a/test/server/meta.test.js +++ b/test/server/meta.test.js @@ -235,6 +235,75 @@ test('should unpack a valid sdk meta bundle with multiple components', () => { } }); +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')); + + 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 from invalid merchant-id email addresses', () => { + const emails = [ + '@', + '@io', + '@test.com', + 'name@', + 'no_at_sign' + ]; + + 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 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; + } + + 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';