Repository navigation
No frame of a publish holds the token as a plain value; the host's errors never quote their argument (typedstandards#141 P5) - #10
Merged
Conversation
…never quote their argument The token scanner now runs every publish path with a client the caller gives as well as with the client publish builds, and checks each traceback with frame locals when an exception escapes the HTTP client (from the transport, an event hook, an undecodable body, or an interrupt). GitHubPagesHost's argument errors are checked for the value given and for the token= given beside it, in str, repr and the traceback with locals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJBCtCpvKX2vdg53xqw8ji Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
…rors never quote their argument The token reaches each request through an httpx.Auth that holds it, as a _Token, in a slot and withholds it from its repr, with a client the caller gives and with the client publish builds. No header dict, the client's or a request's, carries it. GitHubPagesHost checks its arguments in a helper whose messages quote none of them, and its constructor keeps no argument as a frame local when it raises. A repository or branch shaped like a GitHub token is refused. The test of the host's errors calls through module constants, so the only frames it checks are the package's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJBCtCpvKX2vdg53xqw8ji Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
…haped names are refused tests/test_publish_transport.py drives httpx.HTTPTransport, so each request head is written through httpcore and h11, over a replaced sync network backend whose stream opens no socket and whose write fails: an interrupt, an error that is not a transport error, or a transport error. With publish's own client and with a given one, it walks every frame local of the exception that escapes, of its __cause__ and __context__, and the traceback formatted with frame locals. A control test shows the walk reads httpcore's frames. GitHubPagesHost accepts a short repository or branch name that starts with a token prefix, and refuses a token-shaped one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJBCtCpvKX2vdg53xqw8ji Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
…token; only token-shaped names are refused _Api._send clears the locals of every finished frame carried by an exception that escapes Client.request, and by the exceptions chained to it, for every exception kind: a transport error before it becomes publish's own error, and anything else, an interrupt included, before it is re-raised as itself. httpcore and h11 hold the request head while they write it; those frames are among the cleared. Frames still executing are left as they are. Nothing in publish catches such an exception before _send. GitHubPagesHost refuses a repository or branch part that is token-shaped: a GitHub token prefix followed by 30 or more token characters, below the 36 of the shortest documented token. A short name with a token prefix is accepted. The README, the CHANGELOG and the comment at _Api.__init__ state what the tests check, on httpx's mock transport and on its real transport over a failing stream. The token scanner's frame-local offender now raises from a step after the request, since publish clears the frames below one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LJBCtCpvKX2vdg53xqw8ji Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
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.
P5 of the notebook-publish sprint (npstorey/typedstandards#141; the owner's rulings P4-2 = A and P4-3 = A, 2026-10-09). There is no version bump in this PR: 0.2.1 is cut by a separate release PR after it merges, and the changes are under
## Unreleased.The rules this PR keeps.
httpx.Auththat holds it in a slot and withholds it from itsrepr, whether with aclient=the caller gives or with the client the call builds. No header dict carries it.KeyboardInterruptincluded, is re-raised as itself.GitHubPagesHost's errors never quote their argument. Arepositoryorbranchwith a part shaped like a GitHub token (a token prefix, then 30 or more token characters) is refused; a short name with a token prefix, such asghp_notes, is accepted.Branch and blast zone
ts141/p5-hardening, head003a7d32b0ebbbb22a23cde06e812875e2b20334, onbdcf377(main, 0.2.0). There are four commits, each SSH-signed with a sign-off. Round 1 isdf0bcc9(red) ande061ad8. Round 2, after the seat's review, is9d45109(red) and003a7d3. The numstat below is round 1's; round 2 addstests/test_publish_transport.py(+179) and changes_publish.py(+30 -4),tests/test_publish_token.py(+47 -2), README.md and CHANGELOG.md.uv.lockis unchanged.tests/guards.py,tests/test_guards.py,.gitleaks.toml,pyproject.toml,package.json,package-lock.jsonandsrc/typedstandards/__init__.pyare unchanged. No pinned test changed.publish_support.host()gained amodeargument, whose default is the earlier behaviour.Red, then green
At
df0bcc9, the red commit, ORCH reproduced:7 failed, 36 passed, 1 skippedintests/test_publish_token.py.e061ad8, the token scanner runs every publish path with a given client and with the client the call builds. It also checks each traceback with frame locals when an exception escapes the HTTP client, for each of four exception kinds. The event-hook case applies to a given client only; that is the one skip.e061ad8, walking every package frame's locals: none holds the token, with a given client or the call's own. Neither therepositorynor thebrancherror shows it.Checks (ORCH, at
e061ad8)The implementing session also ran Python 3.14 with Node 24, and Python 3.12 with Node 22 (
379 passed, 1 skippedeach). It ran the wheel job from a fresh clone too:uv build, a fresh install,scripts/smoke_wheel.py,smoke check passed. The keyword-scan emulation reports 0 hits.Round 2 (
9d45109red,003a7d3green), after the seat's review ofe061ad8The seat found that round 1's texts claimed more than its tests measured, because they drove httpx's mock transport only. Round 2 makes the rule hold on the real transport, and narrows the texts to what is tested.
tests/test_publish_transport.py). It useshttpx.HTTPTransportwith httpcore's sync backend replaced by a stream that opens no socket and whose write fails. It runs 3 failure kinds × publish's own client and a given client. It walks every frame local of the escaping exception and its chain, and the traceback formatted with locals. A control test shows the walk reads httpcore's frames.9d45109, reproduced by ORCH:8 failed, the 4 real-transport cases and 4 short-name cases. Green at003a7d3.KeyboardInterrupt: no frame holds the token at003a7d3, with publish's own client or a given one, and the formatted traceback does not show it.003a7d3(ORCH):392 passed, 1 skippedon Python 3.11, 3.12 and 3.14 with Node 24; ruff clean; gitleaks clean. The implementing session also ran Python 3.12 with Node 22, and the wheel job from a fresh clone (smoke check passed).test_the_scanner_fails_on_an_offender[frame local]. Its deliberately leaking frame sat inside the transport, which the fix now clears, so it now leaks from a later step instead. The assertion is unchanged.Model
Implemented by IMPL P2/P5 on Opus 5.5 at high effort, in two rounds. Re-verified by ORCH NOTEBOOK-PUBLISH on Opus 5.5.
🤖 Generated with Claude Code