GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Polyfill crypto.randomUUID() and crypto.subtle.digest() for non-secure contexts by benoit74 · Pull Request #392 · openzim/python-scraperlib

Conversation

@benoit74

@benoit74 benoit74 commented Nov 6, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Summary

Both crypto.randomUUID() and crypto.subtle (SubtleCrypto) are restricted by browsers to secure contexts (HTTPS, or localhost). A ZIM served over plain HTTP on a non-localhost host (LAN server, offline hotspot, a VPN/Tailscale hostname, ...) — an extremely common Kiwix deployment — is not a secure context, so the browser omits both entirely, and any page depending on them fails (randomUUID throws; code awaiting crypto.subtle.digest() tends to fail silently instead).

  • crypto.randomUUID(): crypto.getRandomValues() has no such restriction, so it's polyfilled on top of it (standard RFC 4122 v4 construction, via a separately-tested randomUUIDFromBytes helper).
  • crypto.subtle.digest(): no lower-level primitive to build it from, so this adds a small, dependency-free pure-JS digest(algorithm, data) supporting SHA-1 and SHA-256 — the two algorithms actually observed in use (our own vendored wombat.js optionally uses SHA-1 for response ETags; archived pages have been seen using SHA-256 to derive a content id) — matching the native SubtleCrypto.digest() → Promise<ArrayBuffer> contract.

Both are installed as a side effect of loading wombatSetup.js, which already runs before any page script (warc2zim's head_insert.html loads it first in <head>).

sha1Bytes/sha256Bytes are cross-validated against Node's own crypto module across many input lengths, including the Merkle-Damgard padding's block-boundary edge cases, rather than relying solely on hand-transcribed test vectors — one of which did turn out to have a dropped character during review; sha1Bytes itself was correct throughout, confirmed via Python's hashlib.

Fixes #391 and #393, found via openzim/warc2zim#327: after fixing #387/#389, a user testing over a Tailscale hostname (plain HTTP, non-localhost) hit Uncaught TypeError: crypto.randomUUID is not a function, aborting the page's JS entirely. After the randomUUID fix, MDN's live-sample iframes were still stuck at src="about:blank" with no visible error — traced to crypto.subtle being undefined, silently breaking the await crypto.subtle.digest("SHA-256", ...) call used to build their content id.

Both reproduced directly (not just inferred): confirmed window.isSecureContext === false and typeof crypto.randomUUID !== "function" / typeof crypto.subtle === "undefined" against a real non-localhost URL, then confirmed both resolved after this patch, with the live-sample demos rendering again end to end.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@benoit74 benoit74 self-assigned this Nov 6, 2026
@benoit74 benoit74 mentioned this pull request Nov 6, 2026
Polyfill crypto.subtle.digest() (SHA-1/SHA-256) for non-secure contexts #393
Open
@benoit74 benoit74 changed the title Polyfill crypto.randomUUID() for non-secure contexts Polyfill crypto.randomUUID() and crypto.subtle.digest() for non-secure contexts Nov 6, 2026
@benoit74

benoit74 commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

I'm a bit hesitant on this one, might have significant security implications IMHO. But at the same time, we definitely need that for these ZIMs to work offline.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

benoit74 added 2 commits November 8, 2026 20:50
@benoit74
Polyfill crypto.randomUUID() for non-secure contexts
00efe15
crypto.randomUUID() is restricted by browsers to secure contexts
(HTTPS, or localhost). ZIM content is routinely served over plain
HTTP from a non-localhost host (LAN server, offline hotspot, a VPN
hostname, ...), which is not a secure context, so the browser omits
randomUUID entirely and any page depending on it throws instead of
running.

crypto.getRandomValues() has no such restriction. Polyfill
randomUUID on top of it (standard RFC 4122 v4 construction) whenever
it's missing, as a side effect of loading wombatSetup.js, which
already runs before any page script (it's injected first in
warc2zim's head_insert.html).

Found via openzim/warc2zim#327: MDN's "Try it" live-sample widgets
use crypto.randomUUID() to build a session id, and silently failed
for a user testing a ZIM over a Tailscale hostname (plain HTTP,
non-localhost). (#391)
@benoit74
Polyfill crypto.subtle.digest() for non-secure contexts
390a545
Like crypto.randomUUID(), crypto.subtle is restricted to secure
contexts, but unlike randomUUID there is no lower-level primitive to
build it from - digest() needs an actual hash implementation. Add a
small, dependency-free digest(algorithm, data) polyfill supporting
SHA-1 and SHA-256, the two algorithms observed in use (our own
vendored wombat.js optionally uses SHA-1 for response ETags;
archived pages have been seen using SHA-256 to derive a content id),
matching the native SubtleCrypto.digest() -> Promise<ArrayBuffer>
contract. Installed on crypto.subtle the same way, only when missing.

sha1Bytes/sha256Bytes are cross-validated against Node's own crypto
module across many input lengths, including the Merkle-Damgard
padding's block-boundary edge cases, rather than relying solely on
hand-transcribed test vectors (one of which did turn out to have a
dropped a character - sha1Bytes itself was correct throughout).

Found via openzim/warc2zim#327: after the randomUUID polyfill,
MDN's live-sample iframes were still stuck at src="about:blank" with
no visible error, because the code computing their content id awaits
crypto.subtle.digest("SHA-256", ...), which silently never resolves
when crypto.subtle is undefined. (#393)
@benoit74
benoit74 force-pushed the benoit74/polyfill-crypto-randomuuid branch from c27024c to 390a545 Compare November 8, 2026 20:51
@benoit74
benoit74 requested a review from rgaudin November 8, 2026 20:54
@codecov

codecov Bot commented Nov 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (d1deb81) to head (390a545).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #392   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           42        42           
  Lines         2947      2947           
  Branches       452       452           
=========================================
  Hits          2947      2947           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@benoit74

benoit74 commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

@rgaudin WDYT about this?

Pretty sure these crypto functions are used in other websites, so adding support for them is pretty important IMHO, and in most cases they are not used for anything which needs them to be "secure". But there will inevitably be one case in few years where we've completely forgot about this and it will bit us.

I feel like the added value is way superior to the risk (especially since it is used on MDN which is an important ZIM to us), but I might be biased.

If we agree this is worth-it, I will open a follow-up issue to identify all "common" functions which might be hidden in our "unsecure" context but useful to JS scripts. This might be only the beginning of something larger. And of course we can imagine we add a flag to add these "polyfills" conditionally, should it add any added value in some context.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@rgaudin rgaudin Awaiting requested review from rgaudin

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@benoit74 benoit74

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Polyfill crypto.randomUUID() for ZIM content served over plain HTTP on a non-localhost host

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@benoit74
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.