How to Programmatically Add Dynamic Filter Rules in uBlock Origin

You can programmatically add dynamic filter rules in uBlock Origin by invoking the Chromium declarativeNetRequest API directly, using chrome.declarativeNetRequest.updateDynamicRules() with rule objects that match the internal conversion logic found in src/js/dynamic-net-filtering.js and platform/mv3/extension/js/ruleset-manager.js.

uBlock Origin implements its dynamic filtering engine on top of the Chromium Manifest V3 declarativeNetRequest (DNR) API, storing user-created rules as textual representations in µBlock.permanentFirewall and converting them to DNR format during startup. Understanding this pipeline allows external scripts and companion extensions to manage network filtering programmatically using the same APIs that uBlock uses internally. This article examines the exact source code mechanisms in the gorhill/uBlock repository and provides practical implementations for adding, replacing, and removing dynamic rules.

Understanding the Dynamic Filtering Architecture

When a user creates a rule in uBlock Origin's "Dynamic filtering" pane, the UI writes a textual representation (e.g., example.com * 3p block) into the dynamic-filtering string stored in µBlock.permanentFirewall. During extension startup, this string is parsed by src/js/dynamic-net-filtering.js via DynamicHostRuleFiltering.fromString and converted into a set of DNR-compatible rule objects.

The bridge that pushes these rules to the browser lives in platform/mv3/extension/js/ruleset-manager.js. Here, the RulesetManager.updateDynamicRules() method (lines 189-197) calls chrome.declarativeNetRequest.updateDynamicRules() with the generated rule set. The extension distinguishes between session rules (temporary, cleared on reload) and permanent rules (persisted in chrome.storage.local), with session updates utilizing chrome.declarativeNetRequest.updateSessionRules().

The Rule Conversion Pipeline

The conversion from uBlock's internal format to DNR rules involves three key stages:

  • Parsing: DynamicHostRuleFiltering.fromString() (lines 994-998 in src/js/dynamic-net-filtering.js) parses the textual representation into an internal bitmap structure.
  • Serialization: DynamicHostRuleFiltering.toArray() (lines 998-1014) builds an array of human-readable rules, each transformed into a DNR rule object containing id, priority, action, and condition properties.
  • Application: RulesetManager.updateDynamicRules() translates these objects into the final format required by the browser API.

When the UI removes a rule via the X button in the dynamic-filtering pane, src/js/logger-ui.js (lines 777-805) triggers the messaging layer in src/js/messaging.js (lines 1240-1250), which updates µb.sessionFirewall and reapplies dynamic rules using the same removal-then-addition pattern.

Programmatically Managing Dynamic Rules

Any external script or companion extension with the declarativeNetRequest permission can manipulate dynamic rules by calling the same DNR APIs that uBlock Origin uses internally.

Adding a New Dynamic Rule

To add a rule that blocks all third-party scripts from a specific domain, construct a DNR rule object with a unique numeric ID and appropriate conditions:

// Add a rule that blocks all third-party scripts from example.com
chrome.declarativeNetRequest.updateDynamicRules({
  addRules: [{
    id: 1001,                         // Unique numeric ID (must not clash with existing rules)
    priority: 1,                      // Higher priority wins
    action: { type: "block" },
    condition: {
      urlFilter: "*://example.com/*", // Matches any scheme + host
      resourceTypes: ["script"],      // Only script resources
      requestDomains: ["*"],          // Any request domain → treated as third-party by uBlock
    }
  }],
  removeRuleIds: []                  // No removals in this call
}, () => {
  if (chrome.runtime.lastError) {
    console.error(chrome.runtime.lastError);
  } else {
    console.log('Dynamic rule added.');
  }
});

This corresponds to uBlock's internal flow: src/js/dynamic-net-filtering.js parses the textual rule, then platform/mv3/extension/js/ruleset-manager.js calls updateDynamicRules.

Replacing an Existing Rule

To modify an existing rule, you must remove the old ID and add the new configuration in a single call:

// Replace rule 1001 with a higher-priority allow rule
chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1001],
  addRules: [{
    id: 1001,
    priority: 10,                     // Higher priority overrides previous block
    action: { type: "allow" },
    condition: {
      urlFilter: "*://example.com/*",
      resourceTypes: ["script"]
    }
  }]
});

The removal-then-addition pattern mirrors how uBlock updates its dynamicFilteringString, as seen in src/js/messaging.js where the code calls µb.sessionFirewall.fromString() followed by µb.applyDynamicRules().

Removing a Dynamic Rule

To delete a rule permanently, specify its ID in the removeRuleIds array:

// Remove the rule whose ID is 1001
chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1001],
  addRules: []                      // No new rules to add
});

This mimics the behavior triggered when users click the remove button in the logger UI (src/js/logger-ui.js handling onDynamicRuleRemove).

Working with Session-Only Rules

For temporary rules that clear when the browser restarts, use updateSessionRules() instead:

// Session-only rule: block all 3p images from any site while the tab is alive
chrome.declarativeNetRequest.updateSessionRules({
  addRules: [{
    id: 2001,
    priority: 1,
    action: { type: "block" },
    condition: {
      urlFilter: "*",
      resourceTypes: ["image"],
      // No requestDomains → treated as third-party by uBlock's logic
    }
  }],
  removeRuleIds: []
});

uBlock calls chrome.declarativeNetRequest.updateSessionRules() when users add temporary rules via the "Temporary" toggle in the interface.

Key Source Files and Implementation Details

File Role Critical Lines
src/js/dynamic-net-filtering.js Parses textual representation (fromString) and builds internal bitmap; provides toArray() which yields the rule list fed to DNR 998-1014 (toArray), 994-998 (fromString)
platform/mv3/extension/js/ruleset-manager.js Translates uBlock's internal rule objects into DNR rule objects and calls chrome.declarativeNetRequest.updateDynamicRules / updateSessionRules 189-197 (updateDynamicRules)
src/js/messaging.js Receives messages from the UI to add/remove dynamic rules; updates µb.permanentFirewall and triggers rule recomputation 1240-1250 (rule-add handling)
src/js/logger-ui.js UI component that lets users edit dynamic rules; ultimately invokes the messaging layer 777-805 (dynamic rule UI actions)
platform/mv3/extension/js/manifest.json Declares the required "declarativeNetRequest" permission necessary for any external manipulation "permissions": ["declarativeNetRequest"]

Summary

  • uBlock Origin converts textual dynamic filter rules into DNR-compatible objects through src/js/dynamic-net-filtering.js before applying them via platform/mv3/extension/js/ruleset-manager.js.
  • External scripts can bypass uBlock's UI layer and call chrome.declarativeNetRequest.updateDynamicRules() directly to add permanent rules, using unique numeric IDs and proper priority values.
  • Session rules provide temporary filtering that persists only for the current browser session, managed through chrome.declarativeNetRequest.updateSessionRules().
  • Rule modifications require the removal-then-addition pattern to ensure atomic updates and prevent ID conflicts.
  • All programmatic manipulation requires the declarativeNetRequest permission declared in manifest.json.

Frequently Asked Questions

Can external extensions modify uBlock Origin's dynamic rules directly?

External extensions cannot directly modify uBlock Origin's internal µBlock.permanentFirewall storage or its parsed rule bitmaps. However, any extension with the declarativeNetRequest permission can call chrome.declarativeNetRequest.updateDynamicRules() to add rules to the browser's shared DNR pool, which uBlock Origin will respect alongside its own rules. These externally added rules operate at the browser level and affect network requests globally.

What is the difference between session and permanent dynamic rules in uBlock Origin?

Permanent dynamic rules are stored in chrome.storage.local via uBlock's µBlock.permanentFirewall and persist across browser restarts, converted to DNR format during startup in platform/mv3/extension/js/ruleset-manager.js. Session rules are temporary filters cleared when the browser closes, managed through chrome.declarativeNetRequest.updateSessionRules() and used when uBlock's "Temporary" toggle is active in the dynamic filtering pane.

How does uBlock Origin convert textual filter rules to DNR format?

The conversion occurs in two stages within src/js/dynamic-net-filtering.js. First, DynamicHostRuleFiltering.fromString() (lines 994-998) parses text like example.com * 3p block into an internal bitmap structure. Then, DynamicHostRuleFiltering.toArray() (lines 998-1014) serializes this structure into objects with id, priority, action, and condition properties that match the Chrome DNR schema required by chrome.declarativeNetRequest.updateDynamicRules().

What permissions are required to programmatically manage dynamic filter rules?

You must declare the "declarativeNetRequest" permission in your extension's manifest.json, as seen in uBlock Origin's platform/mv3/extension/js/manifest.json. For modifying session rules specifically, no additional permissions are required beyond the base DNR permission. However, to replicate uBlock's persistent storage behavior, you would also need access to chrome.storage.local or an equivalent persistence mechanism.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →