From 3b1a864bf31c81aa7d82f29299cd1c673be051cb Mon Sep 17 00:00:00 2001 From: mohammed adib Date: Fri, 3 Jul 2026 13:54:56 +0530 Subject: [PATCH] prevent prototype pollution in amp-analytics mergeObjects --- extensions/amp-analytics/0.1/config.js | 10 ++++++++++ extensions/amp-analytics/0.1/test/test-config.js | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/extensions/amp-analytics/0.1/config.js b/extensions/amp-analytics/0.1/config.js index 36b2d81b4d0b..56657253ffb2 100644 --- a/extensions/amp-analytics/0.1/config.js +++ b/extensions/amp-analytics/0.1/config.js @@ -495,6 +495,16 @@ export function mergeObjects(from, to, opt_predefinedVendorConfig) { ); for (const property in from) { + // Never merge keys that can reach Object.prototype. A remote or inline + // config returning {"__proto__": {...}} would otherwise recurse into the + // prototype and pollute it for every object on the page. + if ( + property === '__proto__' || + property === 'constructor' || + property === 'prototype' + ) { + continue; + } userAssert( opt_predefinedVendorConfig || property != 'iframePing', 'iframePing config is only available to vendor config.' diff --git a/extensions/amp-analytics/0.1/test/test-config.js b/extensions/amp-analytics/0.1/test/test-config.js index df7d45c17cb2..c2576fd9001e 100644 --- a/extensions/amp-analytics/0.1/test/test-config.js +++ b/extensions/amp-analytics/0.1/test/test-config.js @@ -338,6 +338,13 @@ describes.realWin( 'foobar': {'foobar': ['abc', 'def']}, }); }); + + it('does not pollute Object.prototype via __proto__', function () { + // JSON.parse produces a real own "__proto__" key, matching a remote + // config response. + mergeObjects(JSON.parse('{"__proto__": {"polluted": "yes"}}'), {}); + expect({}.polluted).to.be.undefined; + }); }); describe('vendor only configs', () => {