From 51022b98b2030fbede7742cb17d3388e68e20584 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Mon, 16 Jun 2025 13:19:51 +0200 Subject: [PATCH] Issue #2734 - Add base-64 grammar check to CSP nonces where applicable. Resolves #2734 --- dom/security/nsCSPParser.cpp | 9 +++++++ dom/security/nsCSPUtils.h | 27 +++++++++++++++++++ dom/security/test/gtest/TestCSPParser.cpp | 32 +++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp index faf5edb2c4..584a5ae546 100644 --- a/dom/security/nsCSPParser.cpp +++ b/dom/security/nsCSPParser.cpp @@ -642,6 +642,11 @@ nsCSPParser::nonceSource() if (dashIndex < 0) { return nullptr; } + + if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1, expr.EndReading())) { + return nullptr; + } + // cache if encountering hash or nonce to invalidate unsafe-inline mHasHashOrNonce = true; return new nsCSPNonceSrc(Substring(expr, @@ -671,6 +676,10 @@ nsCSPParser::hashSource() return nullptr; } + if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1, expr.EndReading())) { + return nullptr; + } + nsAutoString algo(Substring(expr, 0, dashIndex)); nsAutoString hash(Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1)); diff --git a/dom/security/nsCSPUtils.h b/dom/security/nsCSPUtils.h index 13747a6b04..7780b55a1f 100644 --- a/dom/security/nsCSPUtils.h +++ b/dom/security/nsCSPUtils.h @@ -61,6 +61,33 @@ isValidHexDig(char16_t aHexDig) (aHexDig >= 'a' && aHexDig <= 'f')); } +// Checks grammar for valid base-64 strings. Does not verify decodability. +static bool +isValidBase64Value(const char16_t* cur, const char16_t* end) +{ + // Using grammar at https://w3c.github.io/webappsec-csp/#grammardef-nonce-source + + // May end with one or two = + if (end > cur && *(end-1) == EQUALS) end--; + if (end > cur && *(end-1) == EQUALS) end--; + + // Must have at least one character aside from any = + if (end == cur) { + return false; + } + + // Rest must all be A-Za-z0-9+/-_ + for (; cur < end; ++cur) { + if (!(isCharacterToken(*cur) || isNumberToken(*cur) || + *cur == PLUS || *cur == SLASH || + *cur == DASH || *cur == UNDERLINE)) { + return false; + } + } + + return true; +} + // ============================================ namespace mozilla { diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp index a5c2d8d770..3a3f0f751f 100644 --- a/dom/security/test/gtest/TestCSPParser.cpp +++ b/dom/security/test/gtest/TestCSPParser.cpp @@ -227,6 +227,10 @@ TEST(CSPParser, Directives) "report-uri http://www.example.com/" }, { "script-src 'nonce-correctscriptnonce'", "script-src 'nonce-correctscriptnonce'" }, + { "script-src 'nonce-a'", + "script-src 'nonce-a'" }, + { "script-src 'sha256-a'", + "script-src 'sha256-a'" }, { "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='", "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" }, { "require-sri-for script style", @@ -543,6 +547,34 @@ TEST(CSPParser, PoliciesWithInvalidSrc) "script-src 'none'" }, { "script-src http://www.example.com:*.", "script-src 'none'" }, + { "script-src 'nonce-{invalid}'", + "script-src 'none'" }, + { "script-src 'sha256-{invalid}'", + "script-src 'none'" }, + { "script-src 'nonce-in$valid'", + "script-src 'none'" }, + { "script-src 'sha256-in$valid'", + "script-src 'none'" }, + { "script-src 'nonce-invalid==='", + "script-src 'none'" }, + { "script-src 'sha256-invalid==='", + "script-src 'none'" }, + { "script-src 'nonce-==='", + "script-src 'none'" }, + { "script-src 'sha256-==='", + "script-src 'none'" }, + { "script-src 'nonce-=='", + "script-src 'none'" }, + { "script-src 'sha256-=='", + "script-src 'none'" }, + { "script-src 'nonce-='", + "script-src 'none'" }, + { "script-src 'sha256-='", + "script-src 'none'" }, + { "script-src 'nonce-'", + "script-src 'none'" }, + { "script-src 'sha256-'", + "script-src 'none'" }, { "connect-src http://www.example.com/foo%zz;", "connect-src 'none'" }, { "script-src https://foo.com/%$",