fix(tr-sig): stop publishing a finding whose message names a different code - #76
Merged
imran-siddique merged 1 commit intoAug 23, 2026
Conversation
Contributor
|
❔ Contributor Check: UNKNOWN
Automated check by AgenTrust Contributor Check. |
lywinged
force-pushed
the
fix/tr-sig-message-code-mismatch
branch
from
August 23, 2026 07:54
cf8f5a7 to
bf308e8
Compare
lywinged
marked this pull request as ready for review
August 23, 2026 18:20
…t code
report.py publishes both halves: the JSON artifact carries `code` per finding
and the HTML table prints it beside the text. They disagreed. `_verify_ed25519`
returned messages prefixed with TR-SIG-001, TR-SIG-002 and TR-SIG-003, and two
callers attached those messages to findings of their own, `check` under
TR-SIG-005 and `check_cmcp_runtime` under TR-SIG-001. A code written in that
helper could only ever match one of the two, and matched neither.
Measured across every module over a mutation sweep, four distinct instances,
all of them in tr_sig:
code=TR-SIG-005 message names TR-SIG-001: signature verification failed
code=TR-SIG-005 message names TR-SIG-002: invalid public key in cnf.jwk.x
code=TR-SIG-005 message names TR-SIG-003: invalid base64url signature
code=TR-SIG-001 message names TR-SIG-002: invalid public key in cnf.jwk.x
So a malformed signature was forwarded as a TR-SIG-005 finding reading
"TR-SIG-003", naming a code the suite had not used and the docs described as
something else.
The helper's messages now name no code. Nothing else changes about them: the
reason for the failure is still there, and Finding.code is the only place a
code appears. Naming a module's own code in its message is the convention
everywhere else here and is left alone.
TR-SIG-003 had no other user. It was never a Finding.code anywhere; that
message prefix was the whole of its existence, so removing it leaves a code
documented in three pages and emitted by nothing. The rows are removed and the
condition they described, a signature that does not verify, is reported by
TR-SIG-005 for a plain record and TR-SIG-001 for a cmcp claim, both of which
are documented.
That also forced the two rows next to it to be read again. TR-SIG-001 was
documented as "Signature algorithm is not Ed25519", which nothing reports under
that code: in check_cmcp_runtime it means a missing signature or the
verification outcome itself. TR-SIG-002 was "cnf.jwk missing or malformed";
it means a key that is not OKP/Ed25519 or a missing x, and only in the cmcp
path. Both rows now say so, in docs/error-codes.md and docs/modules/tr-sig.md,
and the Level 0 failure list and the tutorial's reference table follow, since
both cited cmcp-only codes for plain records.
tests/test_findings_are_self_consistent.py asserts that no finding's message
names a code other than its own, over both entry points and a mutation set
aimed at the error branches rather than at breadth. Verified by restoring each
of the three prefixes on its own: each fails the guard, and each fails it in
both the plain and the cmcp case.
It counts the findings it looked at and fails on none. Its first version did
not: every record is skipped when a module raises, so a module that raised on
all of them reported a pass over nothing, measured by making check raise before
its first finding. That is the same degradation the sample guard on the
error-code branch was hardened against, reproduced one branch later.
The docs guard from the error-code branch needed strengthening to see any of
this. It matched codes by regex over module file text, so the docstring added
here explaining why TR-SIG-003 must not appear in a message was, on its own,
enough to keep the deleted code looking alive: with the row removed and no
emitter left, the guard still passed. It now reads string literals through
`ast`, skipping module, class and function docstrings, and comments are dropped
by the parse. Verified in both directions: re-adding the dead row fails, and
documenting a code no module emits fails; and on a probe carrying a code in a
module docstring, a function docstring, a comment and a real literal, only the
literal is seen.
Removing the prefix also settles that guard's own rationale, which said the
named set and the Finding.code set differ and gave TR-SIG-003 as the example.
Measured after this change they are identical, so the docstring now says that
and gives the reason the match stays on literals: it reads source text and
cannot tell which literal reaches a Finding.
Unrelated to the codes but found while checking where they appear:
docs/quickstart.md documented an output format the CLI does not produce. Its
sample put the error code in the first column, where the CLI prints the module,
and none of its three messages exists anywhere in the source. Replaced with
output from a real run, and the surrounding text now says where a code does and
does not appear.
This branch is stacked: the error-code documentation branch first, then the
raising-on-a-malformed-record branch, then this one. Each contains the commits
below it.
225 passed, 5 xpassed. The two test files this adds are ruff clean. ruff reports
six findings in tr_sig.py; the rule counts are identical on main, 4 E501, 1
F841 and 1 I001, and none of the six is on a line this changes.
Signed-off-by: LouieLuNZ <48041247+lywinged@users.noreply.github.com>
lywinged
force-pushed
the
fix/tr-sig-message-code-mismatch
branch
from
August 23, 2026 18:21
bf308e8 to
088b99b
Compare
imran-siddique
approved these changes
Aug 23, 2026
imran-siddique
left a comment
Member
There was a problem hiding this comment.
Reviewed after #74 and #75 re-scoped the stack. The helper now returns code-neutral reasons because its two callers deliberately assign different finding codes; the dead TR-SIG-003 documentation is removed; plain TRACE and cMCP-specific code mappings match their actual emitters; and the guards reject both foreign-code messages and zero-findings false positives. All Python and CodeQL checks are green.
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.
report.pypublishes both halves: the JSON artifact carriescodeper finding and the HTML table prints it beside the text. They disagreed._verify_ed25519returned messages prefixed withTR-SIG-001,TR-SIG-002andTR-SIG-003, and two callers attached those messages to findings of their own:checkunderTR-SIG-005,check_cmcp_runtimeunderTR-SIG-001. A code written in that helper could only ever match one of the two, and matched neither.Measured across every module over a mutation sweep, four distinct instances, all in
tr_sig:So a malformed signature was forwarded as a TR-SIG-005 finding reading "TR-SIG-003" — a code the suite had not used and the docs described as something else.
The fix
The helper's messages name no code. Nothing else about them changes: the reason for the failure is still there, and
Finding.codebecomes the only place a code appears. Naming a module's own code in its message is the convention everywhere else here and is left alone.What that exposed
TR-SIG-003had no other user. It was never aFinding.codeanywhere; the message prefix was the whole of its existence. Removing it leaves a code documented in three pages and emitted by nothing, so the rows are removed. The condition they described, a signature that does not verify, is reported byTR-SIG-005for a plain record andTR-SIG-001for a cmcp claim, both documented.The two rows beside it were also wrong.
TR-SIG-001was documented as "Signature algorithm is not Ed25519", which nothing reports under that code: incheck_cmcp_runtimeit means a missing signature, or the verification outcome itself.TR-SIG-002was "cnf.jwkmissing or malformed"; it means a key that is not OKP/Ed25519 or a missingx, and only in the cmcp path. Both rows now say so, and the Level 0 failure list and the tutorial's reference table follow, since both cited cmcp-only codes for plain records.The guard
tests/test_findings_are_self_consistent.pyasserts that no finding's message names a code other than its own, over both entry points and a mutation set aimed at the error branches rather than at breadth. Verified by restoring each of the three prefixes on its own: each fails the guard, in both the plain and the cmcp case.It also counts the findings it looked at and fails on none. Its first version did not: every record is skipped when a module raises, so a module that raised on all of them reported a pass over nothing — measured by making
checkraise before its first finding. That is the same degradation the sample guard on the error-code branch was hardened against, reproduced one branch later.The other guard needed strengthening to see any of this
The docs guard from the error-code branch matched codes by regex over module file text. The docstring added here, explaining why
TR-SIG-003must not appear in a message, was on its own enough to keep the deleted code looking alive: with the row removed and no emitter left, that guard still passed.It now reads string literals through
ast, skipping module, class and function docstrings; comments are dropped by the parse. Verified both ways: re-adding the dead row fails, and documenting a code no module emits fails. On a probe carrying a code in a module docstring, a function docstring, a comment and a real literal, only the literal is seen.Removing the prefix also settles that guard's own rationale, which said the named set and the
Finding.codeset differ and gaveTR-SIG-003as the example. Measured after this change they are identical, so the docstring now says that, and gives the reason the match stays on literals: it reads source text and cannot tell which literal reaches aFinding.Unrelated, found while checking where codes appear
docs/quickstart.mddocumented an output format the CLI does not produce. Its sample put the error code in the first column, where the CLI prints the module, and none of its three messages exists anywhere in the source:Replaced with output from a real run, and the surrounding text now says where a code does and does not appear.
Merge order
docs: align the published error codes and record samples with the modulesfix(tr-sig): report a malformed record instead of raising on it#74corrects the TR-SIG rows;#75adds the private-key condition back to TR-SIG-004; this one removes TR-SIG-003 and reclassifies TR-SIG-001 and TR-SIG-002 as cmcp-only. Reading them in that order is also the order they make sense in.225 passed, 5 xpassed. The two test files this adds are
ruffclean.ruffreports six findings intr_sig.py; the rule counts are identical onmain(4 E501, 1 F841, 1 I001) and none of the six is on a line this changes.Generated by Claude Code