tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mnetwerk/protocol/http/nsHttpChannel.cpp | 18++++++++++++++++--
Mnetwerk/test/unit/test_dictionary_storage.js | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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