diff --git a/toolkit/components/extensions/ExtensionDNR.sys.mjs b/toolkit/components/extensions/ExtensionDNR.sys.mjs index 7acc73e1543e..e42462cbd30d 100644 --- a/toolkit/components/extensions/ExtensionDNR.sys.mjs +++ b/toolkit/components/extensions/ExtensionDNR.sys.mjs @@ -744,9 +744,10 @@ class ModifyResponseHeaders extends ModifyHeadersBase { } class RuleValidator { - constructor(alreadyValidatedRules) { + constructor(alreadyValidatedRules, { isSessionRuleset = false } = {}) { this.rulesMap = new Map(alreadyValidatedRules.map(r => [r.id, r])); this.failures = []; + this.isSessionRuleset = isSessionRuleset; } removeRuleIds(ruleIds) { @@ -845,6 +846,15 @@ class RuleValidator { // Checks: tabIds & excludedTabIds #checkCondTabIds(rule) { const { tabIds, excludedTabIds } = rule.condition; + + if ((tabIds || excludedTabIds) && !this.isSessionRuleset) { + this.#collectInvalidRule( + rule, + "tabIds and excludedTabIds can only be specified in session rules" + ); + return false; + } + if (this.#hasOverlap(tabIds, excludedTabIds)) { this.#collectInvalidRule( rule, diff --git a/toolkit/components/extensions/parent/ext-declarativeNetRequest.js b/toolkit/components/extensions/parent/ext-declarativeNetRequest.js index 71d9ba8cdb27..914566cf7843 100644 --- a/toolkit/components/extensions/parent/ext-declarativeNetRequest.js +++ b/toolkit/components/extensions/parent/ext-declarativeNetRequest.js @@ -31,7 +31,8 @@ this.declarativeNetRequest = class extends ExtensionAPI { updateSessionRules({ removeRuleIds, addRules }) { const ruleManager = ExtensionDNR.getRuleManager(extension); let ruleValidator = new ExtensionDNR.RuleValidator( - ruleManager.getSessionRules() + ruleManager.getSessionRules(), + { isSessionRuleset: true } ); if (removeRuleIds) { ruleValidator.removeRuleIds(removeRuleIds); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js index 569362961d5a..40e05e28041d 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js @@ -1276,3 +1276,77 @@ add_task(async function test_static_rulesets_limits() { await extension.unload(); }); + +add_task(async function test_tabId_conditions_invalid_in_static_rules() { + const ruleset1_with_tabId_condition = [ + getDNRRule({ id: 1, condition: { tabIds: [1] } }), + getDNRRule({ id: 3, condition: { urlFilter: "valid-ruleset1-rule" } }), + ]; + + const ruleset2_with_excludeTabId_condition = [ + getDNRRule({ id: 2, condition: { excludedTabIds: [1] } }), + getDNRRule({ id: 3, condition: { urlFilter: "valid-ruleset2-rule" } }), + ]; + + const rule_resources = [ + { + id: "ruleset1_with_tabId_condition", + enabled: true, + path: "ruleset1.json", + }, + { + id: "ruleset2_with_excludeTabId_condition", + enabled: true, + path: "ruleset2.json", + }, + ]; + + const files = { + "ruleset1.json": JSON.stringify(ruleset1_with_tabId_condition), + "ruleset2.json": JSON.stringify(ruleset2_with_excludeTabId_condition), + }; + + const extension = ExtensionTestUtils.loadExtension( + getDNRExtension({ + id: "tabId-invalid-in-session-rules@mochitest", + rule_resources, + files, + }) + ); + + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + ExtensionTestUtils.failOnSchemaWarnings(false); + await extension.startup(); + ExtensionTestUtils.failOnSchemaWarnings(true); + await extension.awaitMessage("bgpage:ready"); + await assertDNRGetEnabledRulesets(extension, [ + "ruleset1_with_tabId_condition", + "ruleset2_with_excludeTabId_condition", + ]); + }); + + AddonTestUtils.checkMessages(messages, { + expected: [ + { + message: /ruleset1.json: tabIds and excludedTabIds can only be specified in session rules/, + }, + { + message: /ruleset2.json: tabIds and excludedTabIds can only be specified in session rules/, + }, + ], + }); + + info("Expect the invalid rule to not be enabled"); + const dnrStore = ExtensionDNRStore._getStoreForTesting(); + // Expect the two valid rules to have been loaded as expected. + await assertDNRStoreData(dnrStore, extension, { + ruleset1_with_tabId_condition: getSchemaNormalizedRules(extension, [ + ruleset1_with_tabId_condition[1], + ]), + ruleset2_with_excludeTabId_condition: getSchemaNormalizedRules(extension, [ + ruleset2_with_excludeTabId_condition[1], + ]), + }); + + await extension.unload(); +});