Build log · Software & apps

Every blocked extension, recovered — 129 of 131 signed CRX bundles

Software & appsdone3 versionsForensicsChrome policy129 CRXSHA-256School

reflection not written yet · every fact below is generated from this project’s own spec and files

What it is

The school's Chrome policy removed 131 extensions by ID — and an extension ID is also the address the browser uses to ask Google for updates, so 129 of the 131 still resolve to signed bundles. All 129 are now downloadable as folders: unzip one, press Load unpacked in chrome://extensions, done. 28,887 files, every asset digest-checked against GitHub's own copy, the untouched .crx beside each folder, and the two that answer HTTP 204 named rather than quietly dropped.

Version history

Straight from the project’s own card data — what each version added.

V1

129 of 131 recovered · 1.46 GiB · 0 corrupt · 2 delisted (HTTP 204 on every version tried)

V2

Published for download: GitHub release, 129 .crx assets, 1,567,361,468 bytes, server digests matched.

V3

Republished as folders: 129 .zip assets · 28,887 files · 1.46 GiB · tree SHA-256 per folder; a downloaded 1,578-file folder re-hashes identical.

What broke, and what I’d change

Not written yet. This is the part only I can write — what actually went wrong, and what I’d do differently. It lives in builds/reflections/chrome-crx-archive.md (see _TEMPLATE.md); as soon as that file has words in it, this section fills in on the next build.

Everything else on this page — the spec, the version history, the file counts — is generated from the project’s own build notes and files, so nothing here is guesswork.

Build spec

The project’s own build notes, rendered. Open the raw .md ↗

Show the spec

Every blocked extension, recovered — 129 of 131 CRX bundles

Goal. The school's Chrome policy removed 131 extensions by ID. An extension ID is also the token the browser uses to ask Google for updates, so most of those "removed" extensions are still downloadable binaries. Fetch them all, verify them, and publish the evidence — while being explicit about the two that no longer exist anywhere.

Companion project: Island College Chrome policy — Audit (/analysis/island-chrome-policy/) decoded the policy these IDs came from. This project asks a different question — can the blocked things still be obtained? — and is a separate card, page and artefact. Not merged, cross-linked both ways.

Constraints

  • Read-only with respect to the managed profile: no account, no cookie, no policy change, no attempt to
  • make anything installable on a school machine. The deliverable is an archive + a report.

  • No API key, no login. The Web Store CRX update service is open; that is the whole method.
  • Every number in the report is computed from the capture manifest at build time, never typed.
  • The page must be self-contained (no external CSS/JS/fonts) so it prints identically to PDF.
  • Never round 129 up to 131. Non-recovery is stated in the deck, a stat tile, a section, the table and
  • the footer.

Method

  1. Parse the 131 IDs out of the cached ExtensionSettings policy (see the companion prompt for the
  2. protobuf decode). Keep the policy's own category groups.

  3. Ask the update service, once per ID, as a macOS arm64 Chrome 126 client:
   https://clients2.google.com/service/update2/crx?response=redirect
     &os=mac&arch=arm64&os_arch=arm64&nacl_arch=arm64
     &prod=chromiumcrx&prodchannel=unknown&prodversion=126.0.0.0
     &acceptformat=crx2,crx3&x=id%3D<ID>%26uc

8 workers, 4 retries with backoff, 90 s timeout.

  1. Classify the reply: 204 / 0 bytes = delisted (terminal — do not retry, do not report as a
  2. network error). 200 with Cr24 magic = a real CRX3.

  3. Validate three ways: magic bytes 43 72 32 34, header version 3, then strip 12 + header_length
  4. and run zipfile.testzip() over the payload — a full CRC sweep, not a header peek. Parse the version out of the redirect filename (/ext_126.0.0.0_<version>.crx) and cross-check it against the manifest.json inside the archive.

  5. SHA-256 the delivered bytes (signature header included) into crx-manifest.json.
  6. For anything that 204s, probe every mirror before declaring it gone: legacy endpoint without
  7. acceptformat, crx4chrome, crxsoso, extpose, chrome-stats, Wayback CDX + availability API. Record the result of each route in the report's evidence table.

Numbers this run produced

| | |
|---|---|
| Listed | 131 |
| Recovered | 129 (98.5%) |
| Bytes | 1,567,361,468 (1.46 GiB) |
| Files if unpacked | 35,269 (2.3 GiB) |
| Corrupt | 0 — 129/129 pass the CRC sweep |
| Over 25 MiB host cap | 19 files, 1,034 MiB |
| Under the cap | 110 files, 483 MB — still 10× the rest of the site |
| Delisted | 2 — Gauth, ZenMate |

Design decisions

  • Ship folders, not just .crx. Isaac's correction, and it is the right one: a .crx is a zip
  • with a CRX3 signature prefix, so a normal person who downloads one gets an "the file may be damaged" dialog and no obvious next step. So each extension is published as <Name>__<id>.zip whose single top-level entry is the extension's directory — unzip once, get a folder with manifest.json at its root, then chrome://extensions → Developer mode → Load unpacked. Keep the untouched .crx beside it for anyone who prefers drag-install.

  • Verify the folder with a tree hash. A directory has no SHA-256, so record
  • sha256(sorted "relpath\0filehash\n") per extension. Proof it works: a downloaded 1,578-file folder re-hashes to the recorded value after round-tripping through the release.

  • The binaries are not served from this domain — they are published elsewhere. 19 of 129 exceed
  • the 25 MiB single-file limit of a static host, and the uploadable remainder is 483 MB across ~20,000 files. So the archive lives on a GitHub release (testisaac1804-byte/crx-archive, tag v1.0): 258 assets (129 folders + 129 bundles), 2 GiB per asset, no account to download, CDN-served, and it does not depend on this Mac being awake. The page stays the index: every one of the 131 rows carries its own folder link plus .crx, there is one button for the whole set, and downloads.json maps ID → zip → crx → digests so a script can mirror the lot.

  • Verify downloads against the server's digest, not your own arithmetic. After upload, pull
  • releases/tags/v1.0 from the API and compare each asset's digest with the locally computed SHA-256: 258/258 matched. That is the end-to-end proof that what a stranger downloads is byte-identical to what was captured.

  • Two stat tiles carry the bad news (2 delisted, and later 19 over cap) so the gap is visible
  • before the reader scrolls.

  • Section order: two-projects table → the protocol → what came back → the full 131-row list →
  • the two that don't exist → integrity → why not hosted → reproduce.

Traps

  • 204 is not an error. urllib raises nothing; you get an empty body and would happily write a
  • 0-byte file. Check the body, not the status alone.

  • The redirect filename carries the version — parse it there rather than trusting the archive.
  • mirrors 404 on a delisted ID, they do not 500. chrome-stats answers 403 (Cloudflare), extpose's
  • viewer proxies the same dead Google URL, so it looks alive and returns nothing.

  • Watch the disk: 1.5 GiB of CRX and 2.3 GiB unpacked. Unpacking is optional; if Isaac only wants
  • packed files, delete unpacked/ afterwards (he asked for that here).

  • A __MSG_ name in an unpacked manifest is not a broken unzip — it means the extension uses
  • i18n and the display name lives in _locales/*/messages.json.

  • /analysis/chrome-crx-archive is registered in ~/scripts/theme-inject.py SKIP_PREFIX: the report
  • is a print-precise artifact and the skin/beacon injection would disturb the measured layout.

Deliverable

portfolio-deploy/
  analysis/chrome-crx-archive/
    report.html           8 sections, 7 tables, 131-row catalogue, self-contained
    crx-catalogue.csv     131 rows — category, name, ID, version, size, files, sha256, status
    crx-manifest.json     129 SHA-256 entries + the 2 delisted IDs marked status:delisted
  prompts/chrome-crx-archive-prompt.md   (this file)

Card actions: Open the report · Catalogue (CSV) · Hash manifest (JSON) · Companion: policy audit.