commit 21f209efa4a1c4ef1acff2de26b4033070bc8b33
parent bf41a6f6a7d2d45cd66da70dc86bc7c3e56d4fb0
Author: Randell Jesup <rjesup@mozilla.com>
Date: Fri, 28 Nov 2025 16:39:30 +0000
Bug 2002915: Check if a Use-As-Dictionary 'match' pattern has RegExp groups r=necko-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D274376
Diffstat:
2 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -136,6 +136,8 @@
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/OpaqueResponseUtils.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
+#include "mozilla/net/URLPatternGlue.h"
+#include "mozilla/net/urlpattern_glue.h"
#include "HttpTrafficAnalyzer.h"
#include "mozilla/net/SocketProcessParent.h"
#include "mozilla/dom/SecFetch.h"
@@ -6192,14 +6194,26 @@ bool nsHttpChannel::ParseDictionary(nsICacheEntry* aEntry,
return false;
}
+ // Verify if the matchVal has regexp groups. If so, reject it
+ UrlpPattern pattern;
+ UrlpOptions options;
+ if (!urlp_parse_pattern_from_string(&matchVal, &mSpec, options, &pattern)) {
+ LOG_DICTIONARIES(
+ ("Failed to parse dictionary pattern %s", matchVal.get()));
+ return false;
+ }
+ if (urlp_get_has_regexp_groups(pattern)) {
+ LOG_DICTIONARIES(("Pattern %s has regexp groups", matchVal.get()));
+ return false;
+ }
+
nsCString hash;
// Available now for use
RefPtr<DictionaryCache> dicts(DictionaryCache::GetInstance());
LOG_DICTIONARIES(
("Adding DictionaryCache entry for %s: key %s, matchval %s, id=%s, "
"match-dest[0]=%s, type=%s",
- mURI->GetSpecOrDefault().get(), key.get(), matchVal.get(),
- matchIdVal.get(),
+ mSpec.get(), key.get(), matchVal.get(), matchIdVal.get(),
matchDestItems.Length() > 0 ? matchDestItems[0].get() : "<none>",
typeVal.get()));
diff --git a/netwerk/test/unit/test_dictionary_storage.js b/netwerk/test/unit/test_dictionary_storage.js
@@ -41,6 +41,12 @@ const TEST_DICTIONARIES = {
pattern: "too_large",
type: "raw",
},
+ regexp_group: {
+ id: "test-regexp-group",
+ content: "content",
+ pattern: "api/:version(v[0-9]+)/*",
+ type: "raw",
+ },
};
let server = null;
@@ -199,6 +205,30 @@ async function setupServer() {
}
);
+ // pattern with a regexp (should be ignored)
+ await httpServer.registerPathHandler(
+ "/api/regexp",
+ function (request, response) {
+ // Test data constants
+ const TEST_DICTIONARIES = {
+ regexp_group: {
+ id: "test-regexp-group",
+ content: "content",
+ pattern: "/api/:version(v[0-9]+)/*",
+ type: "raw",
+ },
+ };
+
+ let dict = TEST_DICTIONARIES.regexp_group;
+ response.writeHead(200, {
+ "Content-Type": "application/octet-stream",
+ "Use-As-Dictionary": `match="${dict.pattern}", id="${dict.id}", type=${dict.type}`,
+ "Cache-Control": "max-age=3600",
+ });
+ response.end(dict.content, "binary");
+ }
+ );
+
registerCleanupFunction(async () => {
try {
await httpServer.stop();
@@ -605,6 +635,47 @@ add_task(async function test_too_long_dictionary_url() {
}
});
+// Test that regexp groups cause it to not be stored as a dictionary
+add_task(async function test_regexp_group() {
+ // Clear any existing cache
+ evict_cache_entries("all");
+
+ let url = `https://localhost:${server.port()}/api/regexp`;
+ let dict = TEST_DICTIONARIES.regexp_group;
+
+ let chan = makeChan(url);
+ let [req, data] = await channelOpenPromise(chan);
+
+ Assert.equal(data, dict.content, "Dictionary content matches");
+
+ // Check that dictionary is stored in cache (even if it's not a dictionary)
+ await new Promise(resolve => {
+ verifyDictionaryStored(url, true, resolve);
+ });
+
+ // Verify Use-As-Dictionary header was NOT processed and active due to 64K limit to metadata
+ // Since we can't store it on disk, we can't offer it as a dictionary. If we change the
+ // metadata limit, this will need to change
+ url = `https://localhost:${server.port()}/api/v2/test.js`;
+ chan = makeChan(url);
+ [req, data] = await channelOpenPromise(chan);
+
+ try {
+ // we're just looking to see if it throws
+ // eslint-disable-next-line no-unused-vars
+ let headerValue = req.getRequestHeader("Available-Dictionary");
+ Assert.ok(
+ false,
+ "Dictionary with regexp group was offered in Available-Dictionary"
+ );
+ } catch (e) {
+ Assert.ok(
+ true,
+ "Available-Dictionary header should not be present for a dictionary with regexp groups"
+ );
+ }
+});
+
// Cleanup
add_task(async function cleanup() {
// Clear cache