From 9ab06046c5e142030a0ab298166bfdef8635e364 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 21 Aug 2024 11:06:24 -0600 Subject: [PATCH 1/3] PDCL-12429 Return an empty string when invalid encoded URI components are encountered. --- .../injectAddQueryStringIdentityToPayload.js | 4 +-- .../in-app-message-actions/utils.js | 3 ++- src/utils/decodeUriComponentSafely.js | 19 +++++++++++++ .../utils/decodeUriComponentSafely.spec.js | 27 +++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/utils/decodeUriComponentSafely.js create mode 100644 test/unit/specs/utils/decodeUriComponentSafely.spec.js diff --git a/src/components/Identity/injectAddQueryStringIdentityToPayload.js b/src/components/Identity/injectAddQueryStringIdentityToPayload.js index 167457326..a74e67a0f 100644 --- a/src/components/Identity/injectAddQueryStringIdentityToPayload.js +++ b/src/components/Identity/injectAddQueryStringIdentityToPayload.js @@ -15,6 +15,7 @@ governing permissions and limitations under the License. import { queryString } from "../../utils/index.js"; import queryStringIdentityParam from "../../constants/queryStringIdentityParam.js"; import ecidNamespace from "../../constants/ecidNamespace.js"; +import decodeUriComponentSafely from "../../utils/decodeUriComponentSafely.js"; const LINK_TTL_SECONDS = 300; // 5 minute link time to live @@ -46,8 +47,7 @@ export default ({ locationSearch, dateProvider, orgId, logger }) => // We are using MCMID and MCORGID to be compatible with Visitor. const ts = parseInt(properties.TS, 10); const mcmid = properties.MCMID; - const mcorgid = decodeURIComponent(properties.MCORGID); - + const mcorgid = decodeUriComponentSafely(properties.MCORGID); if ( // When TS is not specified or not a number, the following inequality returns false. // All inequalities with NaN variables are false. diff --git a/src/components/Personalization/in-app-message-actions/utils.js b/src/components/Personalization/in-app-message-actions/utils.js index fc3704ddf..31f3976f0 100644 --- a/src/components/Personalization/in-app-message-actions/utils.js +++ b/src/components/Personalization/in-app-message-actions/utils.js @@ -11,6 +11,7 @@ governing permissions and limitations under the License. */ import { isNonEmptyArray, queryString } from "../../../utils/index.js"; import { removeNode, selectNodes } from "../../../utils/dom/index.js"; +import decodeUriComponentSafely from "../../../utils/decodeUriComponentSafely.js"; export const removeElementById = (id) => { const element = selectNodes(`#${id}`, document); @@ -42,7 +43,7 @@ export const parseAnchor = (anchor) => { if (isNonEmptyArray(hrefParts)) { const queryParams = queryString.parse(hrefParts[1]); interaction = queryParams.interaction || ""; - link = decodeURIComponent(queryParams.link || ""); + link = decodeUriComponentSafely(queryParams.link || ""); } return { action, diff --git a/src/utils/decodeUriComponentSafely.js b/src/utils/decodeUriComponentSafely.js new file mode 100644 index 000000000..87b923fb0 --- /dev/null +++ b/src/utils/decodeUriComponentSafely.js @@ -0,0 +1,19 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +export default (v) => { + try { + return decodeURIComponent(v); + } catch { + return ""; + } +}; diff --git a/test/unit/specs/utils/decodeUriComponentSafely.spec.js b/test/unit/specs/utils/decodeUriComponentSafely.spec.js new file mode 100644 index 000000000..2f8f64186 --- /dev/null +++ b/test/unit/specs/utils/decodeUriComponentSafely.spec.js @@ -0,0 +1,27 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import decodeUriComponentSafely from "../../../../src/utils/decodeUriComponentSafely.js"; + +describe("decodeUriComponentSafely", () => { + it("decodes a uri encoded string", () => { + expect(decodeUriComponentSafely("%3Fx%3Dtest")).toEqual("?x=test"); + }); + + it("returns an empty string when an invalid encoded URI component is provided", () => { + expect( + decodeUriComponentSafely( + "MCORGID%3D5BFE274A5F6980A50A495C08%2540AdobeOrg%ttt", + ), + ).toEqual(""); + }); +}); From a3cfb3b7d8841f72f04f3183ec7f9dcf52d1fbc4 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 21 Aug 2024 13:10:24 -0600 Subject: [PATCH 2/3] Fix tests. --- test/functional/specs/MediaCollection/MA1.js | 2 +- test/functional/specs/MediaCollection/MA2.js | 2 +- test/functional/specs/MediaCollection/MA3.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/specs/MediaCollection/MA1.js b/test/functional/specs/MediaCollection/MA1.js index e71c72a10..ddd3636ea 100644 --- a/test/functional/specs/MediaCollection/MA1.js +++ b/test/functional/specs/MediaCollection/MA1.js @@ -77,7 +77,7 @@ const assertSessionStartedInAutoPingMode = async (alloy) => { }; }, }); - await responseStatus(networkLogger.edgeEndpointLogs.requests, 200); + await responseStatus(networkLogger.edgeEndpointLogs.requests, [200, 207]); await t.expect(networkLogger.edgeEndpointLogs.requests.length).eql(1); const createSession = networkLogger.edgeEndpointLogs.requests[0]; diff --git a/test/functional/specs/MediaCollection/MA2.js b/test/functional/specs/MediaCollection/MA2.js index b57f26294..8f10fb3e1 100644 --- a/test/functional/specs/MediaCollection/MA2.js +++ b/test/functional/specs/MediaCollection/MA2.js @@ -57,7 +57,7 @@ test.meta({ const assertSessionStarted = async () => { await t.expect(networkLogger.edgeEndpointLogs.count(() => true)).gte(1); - await responseStatus(networkLogger.edgeEndpointLogs.requests, 200); + await responseStatus(networkLogger.edgeEndpointLogs.requests, [200, 207]); await t.expect(networkLogger.edgeEndpointLogs.requests.length).eql(1); const createSession = networkLogger.edgeEndpointLogs.requests[0]; diff --git a/test/functional/specs/MediaCollection/MA3.js b/test/functional/specs/MediaCollection/MA3.js index 15f4a5b33..429a81b0e 100644 --- a/test/functional/specs/MediaCollection/MA3.js +++ b/test/functional/specs/MediaCollection/MA3.js @@ -67,7 +67,7 @@ const assertSessionStarted = async (alloy) => { }, }, }); - await responseStatus(networkLogger.edgeEndpointLogs.requests, 200); + await responseStatus(networkLogger.edgeEndpointLogs.requests, [200, 207]); await t.expect(networkLogger.edgeEndpointLogs.requests.length).eql(1); const createSession = networkLogger.edgeEndpointLogs.requests[0]; From 0a97b9d8ac9838c9d55f1384ae306412dd4e2fc8 Mon Sep 17 00:00:00 2001 From: Serban Stancu Date: Wed, 21 Aug 2024 14:38:36 -0600 Subject: [PATCH 3/3] Fix test. --- test/functional/specs/Personalization/C17409728.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/specs/Personalization/C17409728.js b/test/functional/specs/Personalization/C17409728.js index 0ad0b9a98..e771946b4 100644 --- a/test/functional/specs/Personalization/C17409728.js +++ b/test/functional/specs/Personalization/C17409728.js @@ -348,7 +348,7 @@ test("Test C17409728: Automatically sends interact event when using applyProposi await t.click("#page-header"); - await responseStatus(edgeEndpointLogs.requests, [200, 204]); + await responseStatus(edgeEndpointLogs.requests, [200, 204, 207]); await t.expect(edgeEndpointLogs.count(() => true)).eql(1); // TODO: Testcafe no longer captures the request body for sendBeacon requests.