diff --git a/extensions/amp-a4a/0.1/head-validation.js b/extensions/amp-a4a/0.1/head-validation.js
index 3f0e86304602..942301845bfd 100644
--- a/extensions/amp-a4a/0.1/head-validation.js
+++ b/extensions/amp-a4a/0.1/head-validation.js
@@ -17,9 +17,13 @@ import {getMode} from '../../../src/mode';
*/
export let ValidatedHeadDef;
-// From validator/validator-main.protoascii
+// From validator/validator-main.protoascii. The validator matches this against
+// the whole attribute value (RE2::FullMatch), so anchor it here too: RegExp.test
+// is a substring match, which would accept any href that merely contains an
+// allowed provider (e.g. https://evil.example/#https://fast.fonts.net/).
const ALLOWED_FONT_REGEX = new RegExp(
- 'https://cdn\\.materialdesignicons\\.com/' +
+ '^(?:' +
+ 'https://cdn\\.materialdesignicons\\.com/' +
'([0-9]+\\.?)+/css/materialdesignicons\\.min\\.css|' +
'https://cloud\\.typography\\.com/' +
'[0-9]*/[0-9]*/css/fonts\\.css|' +
@@ -32,7 +36,8 @@ const ALLOWED_FONT_REGEX = new RegExp(
'https://(use|pro)\\.fontawesome\\.com/releases/v([0-9]+\\.?)+' +
'/css/[0-9a-zA-Z-]+\\.css|' +
'https://(use|pro)\\.fontawesome\\.com/[0-9a-zA-Z-]+\\.css|' +
- 'https://use\\.typekit\\.net/[\\w\\p{L}\\p{N}_]+\\.css'
+ 'https://use\\.typekit\\.net/[\\w\\p{L}\\p{N}_]+\\.css' +
+ ')$'
);
// If editing please also change:
diff --git a/extensions/amp-a4a/0.1/test/test-head-validation.js b/extensions/amp-a4a/0.1/test/test-head-validation.js
index bd7ca9c9642d..395c4d4a090c 100644
--- a/extensions/amp-a4a/0.1/test/test-head-validation.js
+++ b/extensions/amp-a4a/0.1/test/test-head-validation.js
@@ -187,6 +187,19 @@ describes.realWin('head validation', {amp: true}, (env) => {
expect(preloadStub).not.to.be.called;
});
+ it('does not allow a foreign origin that embeds an allowlisted provider substring', () => {
+ const preloadStub = env.sandbox.stub(
+ Services.preconnectFor(env.win),
+ 'preload'
+ );
+ head.innerHTML = `
+
+ `;
+ const validated = processHead(env.win, adElement, head);
+ expect(validated.head.querySelector('link')).not.to.exist;
+ expect(preloadStub).not.to.be.called;
+ });
+
it('keeps amp styles', () => {
head.innerHTML = `