| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| provenance:privacy:browser_extensions [2026/09/22 07:48] – Review layer completed: the six deferred findings from the 2026-09-02 fable pass are closed (vacuous checks 1 and 9, the unfetched MDN quote, the Carnus row, the people-half guard, the freeze naming), and a fifth generic (fable) pass over freeze3 is logge karel.kubicek.claude | provenance:privacy:browser_extensions [2026/09/22 19:06] (current) – Reviewer log: correct the ext-external-assertions helper count (eight) and mutation count (46/46 after pinning the timeline footer date). Authored by Claude karel.kubicek.claude |
|---|
| ===== External sources ===== | ===== External sources ===== |
| |
| Everything below was fetched on **2026-09-02** and is re-checkable with ''bash scripts/external_checks_browser_extensions.sh''. That script prints ''FAILED'' explicitly per check, so a blocked or rate-limited call cannot read as a pass. | Everything below was fetched on **2026-09-02**, and every row was re-checked on **2026-09-22**, with ''bash scripts/external_checks_browser_extensions.sh''. **All 26 of that script's checks now assert**: each names the needle, status code or floor it requires, a non-200 fetch is itself a failure, and the run exits non-zero when any of them is gone. Until 2026-09-22 only three did, and the sentence that stood here — //"that script prints FAILED explicitly per check, so a blocked or rate-limited call cannot read as a pass"// — was false for the other twenty-three. The conversion and its mutation test are in the reviewer log under ''ext-external-assertions''. |
| |
| ==== Verified, and used ==== | ==== Verified, and used ==== |
| | ''data_collection_permissions'' coverage | the same API, 400 most-installed | 314 empty (78.5%), 63 ''["none"]'' (15.8%), 23 named (5.8%); ''sandbox/amo_data_collection.py'' | | | ''data_collection_permissions'' coverage | the same API, 400 most-installed | 314 empty (78.5%), 63 ''["none"]'' (15.8%), 23 named (5.8%); ''sandbox/amo_data_collection.py'' | |
| | Edge has no bulk listing; Opera has none | Microsoft Learn; ''addons.opera.com'' | Edge's REST API covers your own submissions; Opera's ''robots.txt'' and ''sitemap.xml'' both 404 | | | Edge has no bulk listing; Opera has none | Microsoft Learn; ''addons.opera.com'' | Edge's REST API covers your own submissions; Opera's ''robots.txt'' and ''sitemap.xml'' both 404 | |
| | Manifest V2 timeline, all six milestones | ''developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline'' | page footer //"Last updated 2026-07-08 UTC"//; every quoted phrase on the page is verbatim from it | | | Manifest V2 timeline, all six milestones | ''developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline'' | page footer //"Last updated 2026-09-09 UTC"// on the 2026-09-22 re-check, //"Last updated 2026-07-08 UTC"// on 2026-09-02; every quoted phrase on the page is verbatim from it. **Google reworded the 2026-08-31 entry in between** and the page's quotes were updated to match — see the reviewer log. Check 8 now asserts each quoted phrase and the footer date | |
| | Edge has set no MV2 date | ''learn.microsoft.com'' ''ExtensionManifestV2Availability'' | //"More information about and the timeline of the migration hasn't been established."// | | | Edge has set no MV2 date | ''learn.microsoft.com'' ''ExtensionManifestV2Availability'' | //"More information about and the timeline of the migration hasn't been established."// | |
| | Firefox keeps blocking ''webRequest'' | MDN ''webRequest'' vs Chrome ''webRequest'' | MDN documents ''webRequestBlocking'' as an ordinary permission with no MV3 note; Chrome's says //"only available to policy installed extensions"// | | | Firefox keeps blocking ''webRequest'' | MDN ''webRequest'' vs Chrome ''webRequest'' | MDN documents ''webRequestBlocking'' as an ordinary permission with no MV3 note; Chrome's says //"only available to policy installed extensions"// | |
| what was published before any of today's edits were applied. **If you edit one of | what was published before any of today's edits were applied. **If you edit one of |
| these pages in the browser, back-port it the same day, or the next build eats it.** | these pages in the browser, back-port it the same day, or the next build eats it.** |
| | |
| | ==== ext-external-assertions, closed 2026-09-22 ==== |
| | |
| | Pass 5's sharpest finding was that the external-check script could not fail: three |
| | of its 26 checks called ''fail()'', five printed the word FAILED without |
| | incrementing anything, and eighteen were regex printers that emit **nothing** on a |
| | 403, a 429 or a reworded source. The header was corrected to say so and the |
| | conversion was filed as the ''ext-external-assertions'' item. This is that item. |
| | |
| | All 26 checks now assert. Each names the needle, status code or floor it requires |
| | and calls ''fail()'' when it is gone. The mechanism is eight new helpers alongside the |
| | existing ''expect'': ''fetch'' (anything but HTTP 200 is itself a failure, which |
| | is the direct fix for //a 403 on the AdGuard store page yields an empty section//), |
| | ''expect_text'' for a phrase the source renders as a list item, ''expect_flat'' |
| | for a phrase that wraps across lines in a source file, ''expect_re'' for a shape, |
| | ''expect_code'' and ''expect_final'' for a status or a redirect the page states as |
| | a fact, ''expect_dead'' for a host the page asserts is gone, and ''expect_ge'' for |
| | a count that must not collapse. An ''ONLY="5 13"'' filter runs a subset, which is |
| | what makes the mutation test affordable. |
| | |
| | **It was mutation-tested, not read.** ''scripts/mutate_external_checks_browser_extensions.py'' |
| | corrupts one needle, constant, status or floor at a time and re-runs only the |
| | affected check: **46 mutations, 46 killed the run**, at least one per check, log at |
| | ''out/ext/mutation_browser_extensions.txt''. A conversion that cannot be shown to |
| | fail is not a conversion. |
| | |
| | ^ What the conversion found ^ Disposition ^ |
| | | **Check 16 had been fetching a 404.** Its source, ''%%extensionworkshop.com/documentation/develop/best-practices-for-collecting-user-data-consent/%%'', is gone — the live documentation for the ''data_collection_permissions'' key is at ''%%.../develop/firefox-builtin-data-consent/%%''. Because the check was a printer, it emitted an empty section inside a run that exited 0, for an unknown length of time | **accepted, fixed.** The check fetches the live page and asserts two phrases: //"data_collection_permissions key in the manifest"// and the //"none"// value the page's 16.5% bucket counts. This is the predicted failure caught in the act, in the one check nobody had singled out. **No figure on either page depended on the dead URL** — the 78.5/15.8/5.8 split comes from the AMO API, not from that doc — so nothing published was wrong; it was simply unchecked | |
| | | **Chrome reworded the Manifest V2 timeline and the page went on quoting the old words.** The footer moved from //"Last updated 2026-07-08 UTC"// to //2026-09-09//; //"All remaining Manifest V2 extensions **are** removed"// became //"**have been** removed"//, and //"will remain installed, but will be unable to receive any updates and cannot be reinstalled"// was rewritten to //"Existing installs on Chrome 138 or earlier will continue to run, but they can no longer receive updates or be reinstalled if removed."// The footnote claims every quoted phrase in the table is verbatim, and two were not | **accepted, fixed, and the date is now pinned.** The page's quotes were **verbatim when fetched** — the Internet Archive captures of 2026-08-26, 2026-09-01 and 2026-09-06 all carry the old wording and the old footer — so this is currency, not misquotation. Both quotes and the third instance in the opening WRAP are updated, the footnote gives the new footer date and records the rewording, and check 8 now asserts the footer string itself. Pinning a footer date will fail the run on an edit that leaves the quotes intact; that is deliberate, because the quotes then have to be re-read | |
| | | **This provenance page carried the same overclaim the script header did.** //"That script prints FAILED explicitly per check, so a blocked or rate-limited call cannot read as a pass"// was false for 23 of 26 checks when it was written, and stayed false after the script's own header was corrected on 2026-09-02 | **accepted, fixed.** A correction applied to one artefact does not propagate to the page that describes it — the //residue table does not fix the prose// failure, one level up | |
| | | **Eight of the eighteen ''%%external_checks_*.sh%%'' scripts cannot fail either, and four of them carry the same header sentence.** ''crawler_detection'', ''dns'', ''filter_lists'', ''internet_scanning'', ''llm_agents'', ''preregistration'', ''requests'' and ''traffic_files'' have no failure counter, no exit gate, or both; ''dns'' says //"Every check prints FAILED explicitly, so a 403 or a rate limit cannot read as a pass"//, which is word-for-word the claim this file made and did not meet | **recorded, not fixed — out of this item's scope.** The audit, with the verdict read out of each file's fail helper and exit gate rather than grepped for the word FAILED, is at ''out/ext/sibling_assertion_audit.txt''. Ten of the eighteen do assert, including ''email_tracking'', which was fixed by an earlier review of exactly this kind | |
| | | **The ''expect'' helper cannot see a phrase in a bullet list.** It requires a sentence-terminating period within 200 characters, and the Cyberhaven post renders //"The malicious code was active between 1:32 AM UTC on December 25 and 2:50 AM UTC on December 26"// as a list item. The check failed on a page that carries the quote perfectly well | **accepted, and fixed by a second helper rather than a wider window.** ''expect_text'' drops the sentence requirement and keeps everything else. Widening the window in ''expect'' instead would have let every other needle match across a sentence boundary — a fallback that rescues one case by weakening the check that caught it | |
| | | **Pinning the Chrome Web Store's live counts was considered and rejected.** 43 sitemap shards, 8,338 ''%%<loc>%%'' entries in shard 0, 103,979 AMO extensions, 83,246 EasyList lines — all drift daily, and all are published on the page as dated snapshots | **rejected, with the reason in the file header.** These go through ''expect_ge'' against a floor far below the quoted figure, so the check fires on an empty parse or a collapsed response and not on ordinary store churn. A check that fails every morning is a check that gets ignored | |
| | | **What check 5 does not establish.** It asserts that the install count the store //shows// is rounded to a thousand. The page's claim is stronger — //"no precise figure appears anywhere in either response"// — which is a negative over a whole document that no regex here supports | **recorded as a stated limit**, in a comment at the check and here, rather than dressed up. It was read by hand on 2026-09-02 and again on 2026-09-22 | |
| | |
| | Unchanged by the conversion: every figure on both pages. Nothing here moved a |
| | number. The 26-check run after conversion is at ''out/ext/external.txt'' and |
| | exits 0. |
| |
| ==== What no reviewer caught ==== | ==== What no reviewer caught ==== |