fix(885): a refusal carrying NO challenge never ran the token leg, so it stops claiming one

`probe` enters `acquire_token` on a `401` only. A registry answering `403` on the
first read — or a `401` with no `Www-Authenticate` — therefore leaves
`token_leg_done=0` and `token=""`, the guard at the `401|403` arm is false, and the
run fell through to the message that says the read was refused "even after a Bearer
token was obtained". Probed 2026-09-05 with a curl shim answering `403` and dumping
only `HTTP/1.1 403 Forbidden`: that message is printed, EXIT=1, and no token was
ever requested. The fail direction was safe; the diagnosis was not. It sends an
operator to package visibility on evidence that does not exist
(`dont-narrate-mechanisms-you-didnt-measure`) — in a script whose whole design is
that its refusal messages are worded apart on purpose.

The arm now branches on what actually ran, `token` first so the never-asked case
cannot borrow either other mechanism:

  * `token` non-empty      -> refused after a GOOD bearer (an answer about the PACKAGE)
  * token leg attempted    -> challenged but produced no token (about the TOKEN ENDPOINT)
  * neither                -> refused with no challenge at all (about ACCESS)

The pre-existing `403` test could not reach this: `CURL_SHIM` answered `401` + a
challenge to every unauthenticated read regardless of the configured code, so the
`403` parameter was only ever observable AFTER the token leg. The shim grew a
challenge-less behaviour (`CHALLENGE=none`, `REFUSAL=403|401`) rather than the
assertion being written against the old one, and both codes are driven because they
take different paths — the challenge-less `401` still enters and abandons the token
leg. Witnessed red on the predecessor script (2 failed) and green on the fix.

`docs/ci-cd.md`'s "Cutting a release" runbook — the section an operator reads at cut
time — gains the `v*` tag protection, the account it whitelists, the fact that its
positive half is unverified, and the `DELETE .../tag_protections/1` unblock. The
tag-protection note already in this file sits inside the `main`-direct-push
discussion, which is not where a release cut is driven from, and
`release.tag-protection-v-star` names its own failure mode as a cut that will not
push.

`ci.pr-route-carries-no-stored-credential` records that
`docs/remote-state-inventory.md`'s row for the preflight still lists "an unusable
credential" among the shapes that fail the job, which this issue deleted. That file
is held by a concurrent change, so the one-clause edit is tracked as #909 rather
than made here.

Refs #885, #909

Decisions-Edit: yes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
This commit is contained in:
2026-09-05 15:15:42 +02:00
co-authored by Claude Fable 5.1
parent b71bf4d7b2
commit bbd919bfe3
3 changed files with 90 additions and 11 deletions
@@ -85,6 +85,17 @@ code = codes.get(tag, codes.get("*", "200"))
if code == "TRANSPORT":
sys.exit(7)
if authorization != "Bearer anon-token" and codes.get("CHALLENGE") == "none":
# A registry that REFUSES an unauthenticated read outright instead of challenging: no
# Www-Authenticate header at all, so a client has nowhere to ask for a token. `REFUSAL` picks
# the code, because a 403 and a challenge-less 401 take DIFFERENT paths through the script --
# `probe` enters the token leg on 401 only.
refusal = codes.get("REFUSAL", "403")
if dump:
pathlib.Path(dump).write_text("HTTP/1.1 %s Refused\r\n" % refusal)
print("{}\n%s" % refusal, end="")
sys.exit(0)
if authorization != "Bearer anon-token":
# 401 for a live tag and a deleted one alike -- the challenge is the ONLY thing that tells a
# client where a token can be had.
@@ -271,8 +282,52 @@ def test_a_401_or_403_AFTER_the_token_leg_REFUSES_rather_than_passing(preflight,
result = preflight.run()
assert result.returncode != 0
assert "refused an ANONYMOUS read" in result.stderr
assert "even after a Bearer token was obtained" in result.stderr, (
"this arm is the one where a token really was obtained, so it is the only one allowed to "
f"say so. stderr={result.stderr}"
)
assert "PUBLIC" in result.stderr, "the message must name the cause an operator can act on"
assert preflight.token_calls(), "the token leg must have been attempted before refusing"
assert preflight.authenticated_manifest_calls(), (
"a message claiming the read survived a Bearer token must be reached with a read that "
"CARRIED one — `token_calls` only shows the token was asked for"
)
@pytest.mark.parametrize("refusal", ["403", "401"])
def test_a_refusal_with_NO_CHALLENGE_never_claims_a_token_was_obtained(preflight, refusal):
"""The message must not name a mechanism the run did not perform.
`probe` enters the token leg on a `401` only, so a registry answering `403` on the first read —
or a `401` carrying no `Www-Authenticate` — leaves the script with no token having asked for
nothing. Measured 2026-09-05 on the predecessor at 59003d5a3, the `403` shape reported "even
after a Bearer token was obtained", which sends an operator to package visibility on evidence
that does not exist (`dont-narrate-mechanisms-you-didnt-measure`). The two codes are BOTH driven
because they take different paths: the challenge-less `401` still enters and abandons the token
leg, the `403` never enters it.
The shim answering 401 + a challenge to every unauthenticated read is why the pre-existing
`403` case could not reach this — it could only ever be observed AFTER the token leg — so the
shim grew a challenge-less behaviour rather than the assertion being written against the old one.
"""
preflight.set_codes({"*": "200", "CHALLENGE": "none", "REFUSAL": refusal})
result = preflight.run()
assert result.returncode != 0, "an unreadable registry is not a pass"
assert "even after a Bearer token was obtained" not in result.stderr, (
f"no token was obtained on this path. stderr={result.stderr}"
)
assert preflight.authenticated_manifest_calls() == [], "no read can have carried a token here"
if refusal == "403":
assert preflight.token_calls() == [], "a 403 first read must not even ask for a token"
assert "WITHOUT issuing a Bearer challenge" in result.stderr, result.stderr
assert "PUBLIC" in result.stderr, "the message must still name a cause an operator can act on"
else:
# A challenge-less 401 DOES enter the token leg (and abandons it for want of a realm), so it
# is the token-endpoint diagnosis rather than the never-asked one.
assert preflight.token_calls() == [], "there was no realm to request a token from"
assert "could NOT OBTAIN an anonymous pull token" in result.stderr, result.stderr
assert "no Www-Authenticate challenge" in result.stderr, (
f"the token-leg message must admit the challenge was missing. stderr={result.stderr}"
)
def test_the_TOKEN_LEG_actually_runs_and_the_bearer_REACHES_the_registry(preflight):