Skip to content

test(contract): add the §10.1 rule-8 guardrail regression tests - #15

Merged
ilpanich merged 1 commit into
mainfrom
claude/rule8-guardrail-tests
Aug 4, 2026
Merged

test(contract): add the §10.1 rule-8 guardrail regression tests#15
ilpanich merged 1 commit into
mainfrom
claude/rule8-guardrail-tests

Conversation

@ilpanich

@ilpanich ilpanich commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Adds the CONTRACT.md §10.1 rule-8 guardrail regression tests — the one part of the local-verification set with no test outside PHP, TypeScript and Python.

The rule

Rules 1-7 ask whether the token is good. Rule 8 asks whether it is the token the decision is even about. SEC-085 satisfied all seven and was still an authentication bypass: the PHP guard routed a failed verification into a second, successful one against the application's own session, admitting the caller as the app's service account.

Why this SDK matters more than the ones handed a bare verifier

The C guard takes an axiam_client_t *, which carries its own session state — authenticated, the CSRF token, the resolved tenant/org. That is the same structural shape SEC-085 exploited: a stateful client reachable from the guard. It is correct today (it verifies the token pulled from the request headers and nothing else), but nothing pinned that.

Tests (4)

  • rule8_failed_caller_token_rejected_with_live_client_session — publishes the app's key and asserts the precondition that its admin-roled token really does pass this guard, so a fallback would have succeeded. The caller's unverifiable credential must still be refused.
  • rule8_an_expired_caller_token_is_not_swapped_for_a_valid_one — sharper: this token's own key is the published one, so its signature is genuinely valid and it fails on exp alone. Nothing but the caller's own expiry can be the reason for the refusal.
  • rule8_authz_check_carries_the_callers_subject_not_the_apps — the consequence that made SEC-085 a bypass: the authorization check ran for the wrong subject. Asserts the propagated subject_id is the caller's and that no app principal appears.
  • rule8_rejection_performs_no_authz_check_at_all — had a fallback substituted another credential, a check would have been issued; an empty recorded body is the evidence none was.

Falsified: injecting the SEC-085 fallback into verify_and_claims fails test 1 (Expected 401 Was 200).

Verified locally: test_guard — 17 tests, 0 failures.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SkTHvZQMV47t3UwkEtmB1D


Generated by Claude Code

CONTRACT.md §10.1 rule 8 — "subject of the decision" — was the one part
of the local-verification set with no regression test outside PHP,
TypeScript and Python. §15.1 hand-verified that every guard rejects
correctly, so this closes a missing guardrail rather than a live defect.

Rules 1-7 ask whether the token is good. Rule 8 asks whether it is the
token the decision is even about. SEC-085 satisfied all seven and was
still an authentication bypass: the PHP guard routed a failed
verification into a second, successful one against the application's
own session, admitting the caller as the app's service account.

Each test asserts its precondition rather than assuming it: a second,
fully valid credential for a more privileged principal is first shown
to pass the same guard, so a fallback would genuinely have succeeded.
Without that, the tests would pass merely because nothing was available
to substitute — the trap the PHP reference test documents at length.

Every test was falsified by injecting the SEC-085 fallback into the
guard and confirming it fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkTHvZQMV47t3UwkEtmB1D
@ilpanich
ilpanich merged commit 2f970f8 into main Aug 4, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants