Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/mv3-chromium.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ jobs:
build:
name: Lint and build on Windows
runs-on: windows-latest
timeout-minutes: 45
# The builds check every stock regex with Chrome, and the packages are
# tested in Chrome.
timeout-minutes: 60

steps:
- name: Clone repository and submodules
Expand Down Expand Up @@ -54,6 +56,16 @@ jobs:
- name: Validate MV3 Chromium artifact
run: node tools/validate-mv3.mjs dist/build/uBlockPlus.chromium --release

# windows-latest ships Google Chrome. Each test uses a fresh temporary
# profile, closes the Chrome it started and has its own hard timeout.
- name: Test static regex rules in Chrome
timeout-minutes: 10
run: node tools/test-static-regex-chrome.mjs --extension dist/build/uBlockPlus.chromium --output dist/chrome-tests/static-regex-chrome.json

- name: Test strict blocking in Chrome
timeout-minutes: 15
run: node tools/test-strictblock-chrome.mjs --extension dist/build/uBlockPlus.chromium --output dist/chrome-tests/strictblock-chrome.json

- name: Upload unpacked extension and zip
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand All @@ -71,6 +83,23 @@ jobs:
- name: Validate experimental WebRequest artifact
run: node tools/validate-mv3.mjs dist/build/uBlockPlus.experimental.chromium --release --experimental-webrequest

- name: Test experimental static regex rules in Chrome
timeout-minutes: 10
run: node tools/test-static-regex-chrome.mjs --extension dist/build/uBlockPlus.experimental.chromium --output dist/chrome-tests/static-regex-chrome.experimental.json

# The experimental package learns blocked addresses from webRequest.
- name: Test experimental strict blocking in Chrome
timeout-minutes: 10
run: node tools/test-strictblock-chrome.mjs --extension dist/build/uBlockPlus.experimental.chromium --measure-seconds 0 --output dist/chrome-tests/strictblock-chrome.experimental.json

- name: Upload Chrome test reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: chrome-test-reports-${{ github.sha }}
if-no-files-found: ignore
path: dist/chrome-tests/

- name: Upload experimental extension and launcher
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down
22 changes: 21 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ jobs:
build:
name: Build and verify
runs-on: windows-latest
timeout-minutes: 60
# The builds check every stock regex with Chrome, and the packages are
# tested in Chrome.
timeout-minutes: 75
permissions:
contents: read
env:
Expand Down Expand Up @@ -86,13 +88,31 @@ jobs:
- name: Validate standard package
run: node tools/validate-mv3.mjs dist/build/uBlockPlus.chromium --release

# windows-latest ships Google Chrome. Each test uses a fresh temporary
# profile, closes the Chrome it started and has its own hard timeout.
- name: Test standard package static regex rules in Chrome
timeout-minutes: 10
run: node tools/test-static-regex-chrome.mjs --extension dist/build/uBlockPlus.chromium

- name: Test standard package strict blocking in Chrome
timeout-minutes: 10
run: node tools/test-strictblock-chrome.mjs --extension dist/build/uBlockPlus.chromium --measure-seconds 0

- name: Build experimental WebRequest package
shell: pwsh
run: ./tools/make-mv3.ps1 -Platform chromium -Version '${{ steps.version.outputs.version }}' -ExperimentalWebRequest

- name: Validate experimental package
run: node tools/validate-mv3.mjs dist/build/uBlockPlus.experimental.chromium --release --experimental-webrequest

- name: Test experimental package static regex rules in Chrome
timeout-minutes: 10
run: node tools/test-static-regex-chrome.mjs --extension dist/build/uBlockPlus.experimental.chromium

- name: Test experimental package strict blocking in Chrome
timeout-minutes: 10
run: node tools/test-strictblock-chrome.mjs --extension dist/build/uBlockPlus.experimental.chromium --measure-seconds 0

- name: Upload packages
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,62 @@ Releases of this community fork. The upstream uBlock Origin changelog follows fu

## Unreleased

### Strict blocking and regex capacity

Strict blocking (Chromium):
- Strict-block rules no longer use one regex rule each. They are plain redirects to the warning page that keep the filter's own address condition, so only filters that are regexes need a regex rule: 111 of 1,106 rules with the default lists, instead of 1,132.
- Every valid stock strict-block rule is now installed: 1,106 with the default lists, up from 826. The 278 that did not fit before are back, so some sites that loaded before now show the warning page.
- The warning page shows the exact blocked address. The browser reports it when the page is redirected: through the optional network-observation (`webRequest`) permission, or, on unpacked installations, through Chrome's matched-rule events. The page then keeps the address in its own URL, so Back, Forward, reload and duplicated tabs show it too. Tested in Chrome 153:
- a new tab, a typed address, a link and a `window.open` popup;
- after a server redirect, Back, Forward, reload and in a duplicated tab;
- with the service worker stopped, and for two quick navigations;
- for a stock list and for My filters, including after a server redirect;
- with both sources. After a server redirect Chrome reports the step to the warning page only as a new request, which the network-observation source now follows; the page shows the address within about 100–140 ms instead of after a 1.5 s wait.
- When only the start of the navigation is known (packed installations without the optional permission, or the low-memory profile), the page says that the address is approximate and does not offer **Don't warn me again about this site**: after a server redirect that address belongs to the site which redirected, so **Proceed** may lead back to the warning. When no address is known, it says so and disables **Proceed** and **Don't warn me again about this site**. It never opens an address other than `http:` or `https:`.
- The service worker forgets a blocked address as soon as the warning page has read it.
- The page names what blocked the site: the stock list, "Imported lists" or "My filters". Before, the stock list was found by testing the address against the list's regexes.
- `$doc`, `$document` and `$all` filters in My filters and imported lists show the warning page, with **Proceed** and **Don't warn me again about this site**, when strict blocking is on, the extension has access to all sites and the exact address is available. Otherwise they block as before, with the browser's error page. Hostname-only filters (`||example.com^`) in My filters and imported lists are not strict-blocked yet. Firefox and Safari are unchanged.
- Chrome lets a page load when a redirect matches without host access. Strict-block redirects are therefore removed as soon as access to all sites is withdrawn, and checked again at every service-worker start.
- Turning strict blocking off, or losing access to all sites, no longer erases the **Don't warn me again about this site** list.
- **Dashboard → Diagnostics** states how the warning page learns the address: exact, approximate or not available. It offers a button to grant the optional network-observation permission, the cheapest exact source.
- Without that permission, unpacked installations listen to Chrome's matched-rule events. These arrive for every blocked request, so the service worker stays awake while you browse.
- The low-memory profile does not use these events, so that the service worker can stop: the address is then approximate unless the permission is granted, and `$doc` filters in My filters and imported lists block with the browser's error page.
- Measured on one machine: pages with 300 blocked images loaded 12–26 ms slower, and the worker did not stop while a page requested a blocked image every 2 seconds.

Regex capacity:
- Stock regex rules are part of each list's packaged ruleset and work as soon as the list is enabled; they are no longer installed as dynamic rules at runtime.
- This frees Chrome's shared limit of 1,000 regex rules for dynamic and session rules, which the default lists used to fill completely. My filters and imported lists can now use about 889 regex rules with the default lists (877 with every list).
- When that limit is full, only regex rules of imported lists that are not exceptions are skipped, from the end, with a warning. Before, the whole activation failed.
- The Filter Store's regex estimate uses the capacity actually left instead of assuming 1,000.
- New **Regex rule capacity** panel in **Dashboard → Diagnostics**:
- built-in regex rules in use out of 1,000, and the Chrome version that checked them when the package was built;
- built-in regex filters the browser cannot run;
- **Check now**, which counts the built-in regex rules this browser skips;
- the shared limit: rules used by My filters and imported lists and by strict blocking, free slots, and rules not installed because the limit is full;
- selected lists the browser did not enable.
- Troubleshooting reports include the regex rule usage and the address source, never an address.

Honest counts:
- Regex filters that Chrome's RE2 engine cannot run are now counted in each list's tooltip as filters that could not be converted, with the reason codes `unsupported-regex-memory` (160) and `unsupported-regex-syntax` (5).
- The number grows by 108 in the default lists and by 165 across all lists, to 1,378 filters in 36 of the 55 lists.
- Nothing blocks less: Chrome already skipped these filters before, silently, at runtime.
- The 28 strict-block regex rules in the default lists (31 across all lists) that RE2 rejects are counted in `strictblockRejected` in `ruleset-details.json`. As before, those pages get no warning page. For 24 of them (27 across all lists) the filter blocks only documents, so that rule was all there was of it: those pages are not blocked at all, as before, and the filters are counted among those that could not be converted.

Security:
- On Firefox, which reports no sender origin, a message counts as coming from an extension page only when its extension ID and URL match. Before, the missing origin was enough, so content scripts could send those messages.
- User scripts can send only an allowlisted set of messages.
- Only the warning page, in the top frame of a tab, can ask which address its tab was redirected from. Only extension pages can read the regex capacity.

Build and validation:
- Chromium and Edge builds check every static regex twice before packaging it: against the portable RE2 subset, then with Chrome's own `isRegexSupported`. Chrome refuses to load an unpacked extension when any static regex fails to parse.
- The check runs Chrome headless, in a new temporary profile that is deleted afterwards.
- Release builds (`-Version`) need Google Chrome, found in the standard install folders or through `CHROME_PATH`.
- New `rulesets/regex-details.json` (count, checking Chrome version, digest).
- `tools/validate-mv3.mjs` also checks the static regex rules, their total against Chrome's limit of 1,000, and the shape of the strict-block rules.
- The CI and release workflows load both packages in a real Chrome (`tools/test-static-regex-chrome.mjs`, `tools/test-strictblock-chrome.mjs`). Their job timeouts were raised.
- Imported lists are recompiled once after the update (compiled-cache revision 5) to add their strict-block redirects.
- Rule-ID salvage (`make-mv3.ps1 -Before`, `make-mv3.sh BEFORE=`) keeps each list's static regex rules after its other rules, where the package validator expects them.

### Honest filter compilation

Stock lists (build time):
Expand Down Expand Up @@ -51,6 +107,8 @@ Not yet done:
- Remote fonts and `report-uri` CSP reports can be blocked by DNR; they are missing features, not browser limits.
- The `chrome.debugger` route misses responses built by a page's service worker and the first document of a new tab, prompts for Local Network Access on local iframes, and needs auto-attach for subresources of cross-site iframes.
- New tests: `tools/test-stock-compiler-honesty.mjs` and `tools/test-runtime-filter-options.mjs`.
- The research marks roadmap step 2 as done. It records the measured strict-block options, the exact-address sources, the fail-open finding without host access and the cost of Chrome's matched-rule events. The feature matrix, runtime, architecture, threat model and privacy documents describe the two regex limits, the build-time RE2 check and the headless Chrome it starts, and the blocked addresses kept per tab.
- New tests for step 2: `test-strictblock-rules`, `test-dnr-namespaces`, `test-strictblock-tracker`, `test-regex-capacity`, `test-regex-capacity-ui`, `test-re2-portable`, `test-stock-regex-placement`, `test-validate-mv3-regex` and `test-sender-trust`, plus the two real-Chrome tests above (all in `tools/`).

## 1.2.0

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ Userscripts can extend DOM and page-level JavaScript filtering, but do not grant
| Response-body rewriting, DNS/CNAME inspection, exact response-size blocking | No equivalent implementation through this build's normal public MV3 APIs. |
| Managed settings and additional engines | Supported administrator settings are available; a managed blocking adapter and a filtering native companion remain research items. The only native component is the optional Windows updater, which runs only after you install it yourself; nothing native is installed silently. |

Chrome can reject an allow-exception regex because its compiled RE2 program exceeds the browser's limit. Some packaged lists contain such expressions, so a list-selection change can be rejected even when its rule count fits the quota. The extension restores the previous configuration and rules instead of dropping the exception. See the [native regex limit and recovery details](docs/MV3-PARITY-IMPLEMENTATION-2026-09-06.md).
Chrome cannot run a regex which its RE2 engine rejects, for example because the compiled program exceeds the browser's memory limit. On Chromium, the build checks every packaged regex rule with Chrome: the rules Chrome accepts are packaged in the lists' own static rulesets, and the others, exceptions included, are left out and counted among the filters the list could not convert. Choosing lists is therefore no longer rejected for this reason. Regex rules of imported lists and My filters share a separate limit of 1,000 with strict blocking and are checked when they are installed: when that limit is full, imported regex block rules are left out from the end, with a warning, while an exception or a My filters rule which cannot be installed keeps the previous configuration and rules. Firefox builds keep the previous behaviour for packaged lists. Diagnostics shows both limits. See the [native regex limit and recovery details](docs/MV3-PARITY-IMPLEMENTATION-2026-09-06.md) and [Power Runtime](docs/POWER-RUNTIME.md).

**Fail-open is deliberate.** If a popup decision requires context that is missing or an exception cannot be represented safely, the relevant decision defers instead of approximating a block. When the matching work budget runs out, it defers only while an exception could still apply; otherwise the unevaluated block rules are skipped and the Smart/Strict policy decides. Deferred allow conditions can force deferral; they cannot manufacture an approximate allow/block decision. Unsupported restrictive rules are not broadened by stripping their conditions. This reduces false positives and also means some unwanted popups can pass.

Expand Down
Loading