docs(web): script the half of #648's secure-context check a shell can do - #748
Merged
Conversation
… do, and say which half that is #648's last open item is secure-context re-verification, and `docs/remote-access.md` already records why it cannot be closed from a checkout: `http://127.0.0.1` is a secure context BY SPECIFICATION, so the service worker and manifest work today without anyone arranging it. Over an external origin that property comes from HTTPS instead and has to be re-verified there. This turns "needs a real origin" into a two-minute run once one exists. TWO PARTS, AND THE SPLIT IS THE HONEST SHAPE OF THE REQUIREMENT Part 1 is what HTTP can answer: the shell served over the external origin, the token exchanged for a `SameSite=Strict` cookie there, `manifest.webmanifest` and `sw.js` actually served, `nosniff` present, and -- the one that matters most -- A SPOOFED `Host:` STILL REFUSED. `--external-host` teaches the server one name, and a deployment where `Host: evil.example` is answered is one where the allowlist has been widened until it stopped being one. Non-zero exit on any failure. It refuses a plain `http://` origin outright rather than warning: every check in it is about what HTTPS provides, and run against `http://` it would report a pass for a page that is not a secure context. Part 2 is what only a browser can answer -- `window.isSecureContext`, whether the worker registered and with what scope, whether the cache is genuinely per-origin, whether the manifest installs, and how the installed console behaves when the tunnel dies. Printed as a checklist and NOT covered by the exit code. A script claiming `isSecureContext` from `curl` would be reporting something it never looked at, and a test asserts part 1 makes no such claim -- scoped to the executable region, so the header comment may still name the property in order to say it cannot check it. WHAT WAS ALREADY DONE, VERIFIED RATHER THAN ASSUMED Four of #648's five items shipped in #678 and #680. Rather than trust that, all six defences were mutation-tested: skipping the Host check, admitting a non-loopback name on a loopback bind, dropping the port check, accepting a wildcard external host, making the remote session immortal, and removing the wildcard-bind guard. All six killed. The script joins the shellcheck suite, because a shell bug in it is a bug in the one check standing between an operator and an exposed financial surface. #648 does not close here. It closes when part 2 has been done on a device and recorded on the issue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Partial for #648 — does not close it, and the reason is the point.
Four of #648's five items already shipped in #678 and #680. I mutation-tested all six defences rather than trusting they were covered — skipping the
Hostcheck, admitting a non-loopback name on a loopback bind, dropping the port check, accepting a wildcard external host, making the remote session immortal, removing the wildcard-bind guard — all six killed.The fifth item, secure-context re-verification, needs a real deployed origin. This turns that from an open research question into a two-minute run.
Two parts, and the split is the honest shape of the requirement
Part 1 — what HTTP can answer. The shell served over the external origin; the token exchanged for a
SameSite=Strictcookie there;manifest.webmanifestandsw.jsactually served;nosniffpresent; and the one that matters most — a spoofedHost:still refused.--external-hostteaches the server one name, and a deployment whereHost: evil.exampleis answered is one where the allowlist has been widened until it stopped being one. Non-zero exit on any failure.It refuses a plain
http://origin outright rather than warning: every check is about what HTTPS provides, and againsthttp://it would report a pass for a page that is not a secure context.Part 2 — what only a browser can answer.
window.isSecureContext, whether the worker registered and with what scope, whether the cache is genuinely per-origin, whether the manifest installs, and how the installed console behaves when the tunnel dies. Printed as a checklist and not covered by the exit code — a script claimingisSecureContextfromcurlwould be reporting something it never looked at.A test asserts part 1 makes no such claim, scoped to the executable region so the header comment may still name the property in order to say it cannot check it.
Why this doesn't close the issue
docs/remote-access.mdkeeps its ⛔ section and now points at the script. #648 closes when part 2 has been done on a device and recorded on the issue. Part 1 passing is not sufficient, and no amount of scripting makes it so.The script joins the shellcheck suite, because a shell bug in it is a bug in the one check standing between an operator and an exposed financial surface.
Gates
6,297 passed / 3 skipped; ruff clean (the one
E501is pre-existing); shellcheck clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6