Bound source line numbers from above - #45
Open
pucedoteth wants to merge 1 commit into
Open
pucedoteth wants to merge 1 commit into
pucedoteth wants to merge 1 commit into
Conversation
The schema types `line` as an integer with `minimum: 1` and no upper bound, and `Number.isInteger` is true for 1e21 and 1e308, so a finding could claim a line no file has and still validate. JSON re-serializes those values in exponential form, which a consumer reading the report into a fixed-width integer cannot parse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jeremiahjmorris1126-coder
approved these changes
Sep 20, 2026
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.
The gap
lineis typed in the schema as:There is no upper bound, and
Number.isIntegeristruefor1e21and1e308. The semantic check incollectFindingSemanticErrorsboundslinefrom below only:So a findings document that is valid in every other respect passes with a line number no file has. Against
main:Two consequences, both on the machine-readable contract rather than on the validator itself:
JSON.stringify(1e21)is"1e+21". A consumer readinglineinto a fixed-width integer — Goint32, Rustu32, a database column — cannot parse that, so a report that passed validation fails downstream.9007199254740993round-trips as9007199254740992, so two distinct claimed locations can compare equal.Findings arrive from a hunter agent, and the validator already treats that input as untrusted elsewhere — terminal control bytes, lone surrogates, unsafe paths, symlinked inputs. An unbounded line number is the same class: garbage that survives the gate and lands in a report labelled verified.
The change
lineis validated in the semantic layer rather than the schema, the same wayfilesafety is, so the bound goes there too:2^31 - 1is far beyond any real file and is the widest value every common fixed-width integer type can hold, so a boundedlineis safe for any consumer.The keyword surface is unchanged — this adds no
maximumsupport to the hand-rolled schema validator, andreport-schema.jsonis untouched.Scope check:
lineappears in the schema only undertraceandevidence, which are exactly the two collections that check already walks.remediation.code_changeshasfile_nameandfixed_codeand no line.Tests
Three added to
validate-findings.test.cjs: values above the bound are rejected in bothtraceandevidence,1and the bound itself are accepted, and a bounded value is asserted to re-serialize without an exponent.Reverting only
validate-findings.cjsand keeping the tests gives34 pass, 3 fail. The meaningful failure is the first — the other two referenceLIMITS.sourceLine, which does not exist before the change; the standalone reproduction above is the honest red.With the change,
validate-findings.test.cjsis 37 pass, 0 fail (was 34), andvalidate-coverage-ledger.test.cjsis unchanged at 31 pass, 0 fail.How I found it
Mutation testing against the schema — roughly forty malformed variants of a valid
confirmedfinding. Everything else I tried was already rejected: path traversal and Windows reserved names,__proto__as a realJSON.parseown property, lone surrogates, invisible and bidi characters, duplicate fingerprints, empty required collections, wrong enum values and wrong types. This was the only input that got through.AI disclosure
Written with AI assistance (Claude Code): the investigation, the change, the tests, and this description.
🤖 Generated with Claude Code