Repository navigation
feat(sarif): carry each finding's declared context, additively; rewrite SPEC §6 - #49
Merged
Merged
Conversation
Each @exposes and @confirmed result now also carries, as SARIF members existing consumers ignore: logicalLocations and the anchor of the code it is attached to; taxa for its cwe:/owasp: refs, with run.taxonomies; relatedLocations for the @boundary, @assumes, @Handles, @Transfers and @Audit annotations on its asset; and codeFlows with the declared @flows chain into it. The run gains graphs[0] (every @flows and @boundary, with claim keys, crossings and the inferable outer side), automationDetails, versionControlProvenance when the repository has a web-hosted origin, and properties.sarif_profile_version. Rules gain help, tags and a GitHub security-severity band. No result is added, dropped or reordered, and no rule id, message.text, fingerprint or existing property changes. tests/sarif-enrichment.test.ts strips every new member and compares the rest byte for byte with exports cut before this change (tests/fixtures/sarif-baseline), and validates the output against the official SARIF 2.1.0 schema. A chain is emitted only when its last hop is declared on the claim's own handler or file; nothing is derived from @entitles or @Actor, which actor-entitlement.test.ts now also pins for the new members. Also fixes two front-end defects: the MCP guardlink_sarif tool now computes dangling refs as the CLI does, and --min-severity is documented and tested as filtering exposures only, never a @confirmed result. The provenance read gives Workspace.Metadata its first exposure (a git remote can embed credentials; only host and path are kept), which adds a node to this repository's whole-model threat graph: re-measured in Chrome with mermaid@11 at 1440x900 as 48 nodes and 179 links, matching the counter.
§6.1-§6.4 promised results for @accepts, @assumes, @Audit, @mitigates and @Handles, low severity as note, a cwe result property and TS200x rule ids; the exporter has produced none of them. They now state the five rules, the result order and levels, what every other annotation becomes and why it is not a result, the rule help and security-severity bands, and the taxa mapping, with an example cut from the sarif-shop fixture. §6.5 is unchanged. New §6.6 describes the declared context and how a chain is attributed; new §6.7 the run envelope and the compatibility invariants.
…here it is tested
Animesh-Sri-bugb
force-pushed
the
fm/fx-guardlink-sarif
branch
from
October 1, 2026 19:20
f291c8c to
d920ebc
Compare
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.
What changed
The SARIF export now carries the model around each finding, without changing any existing result. Each
@exposesand@confirmedresult also carries these SARIF members:locations[0].logicalLocations: the handler the claim is attached to.properties["guardlink/anchor"]holds the anchor span. Theregionstays on the annotation line.taxa: the claim'scwe:andowasp:refs, withrun.taxonomies.properties.externalRefsis kept.relatedLocations: the@boundary,@assumes,@handlesand@auditannotations on the claim's asset, plus@transfersof the claim's threat. Each one is sited and carries its claim key.codeFlows: the declared@flowschain into the claim. Each hop is at its own line, and a hop that crosses a boundary haskinds: ["flow","boundary-crossing"]. A route channel addswebRequest.The run also gains:
graphs[0]: every@flowsand@boundaryas an edge with its claim key. Each boundary lists the flows that cross it. When exactly one side of a boundary is undeclared, the edge says which side is outer.automationDetailsversionControlProvenance, only when the repository has a web-hosted origin. It is rebuilt from host and path, so credentials in the remote never reach the export.properties.sarif_profile_version: 1Rules gain
help,tagsand a GitHubsecurity-severityband: confirmed 9.0, critical/high 8.9, other exposures 5.0. Diagnostics are not tagged as security.A chain is never guessed.
@flowshops are not linked by call site, socodeFlowsis emitted only when the chain's last hop is declared on the claim's own handler or file.guardlink/flowAttributionsays which. Upstream hops that are joined only because one hop's target is the next hop's source are markedguardlink/hopAttribution: "graph".Two front-end defects:
guardlink_sarifpassed[]for dangling refs. It now computes them as the CLI does, so a model gives one SARIF whichever front end exports it.--min-severityhas only ever filtered unmitigated exposures. It is now documented and tested as never dropping a@confirmedresult, because a reproduced exploit is the strongest evidence the export carries.SPEC §6 rewrite. §6 promised the following, which the exporter never produced:
@accepts,@assumes,@audit,@mitigatesand@handlesnotecweresult propertyTS200xrule ids§6.1–§6.4 now describe what is exported, and say what each other annotation becomes and why it is not a result. §6.5 is unchanged. New §6.6 covers the declared context and how chains are attributed. New §6.7 covers the run envelope and the compatibility invariants.
What did not change, and how that is proven
No result is added, dropped or reordered, and no rule id,
message.text, fingerprint or existing property changes. No new member is derived from@entitlesor@actor.tests/sarif-enrichment.test.tsdeletes every new member. It then comparesresultsandtoolbyte for byte with exports cut before this change (tests/fixtures/sarif-baseline/, written byguardlink sarifat the base commit). It does this on two fixtures:expense-apiand the newsarif-shop.sarif-shopadds route channels, a@confirmed, OWASP refs, a boundary between two declared assets, a transfer, a covered exposure, a parse error and a dangling ref.ajv. The exports checked are both fixtures, this repository's own export, an export with provenance and a partial model.ajv,ajv-draft-04andajv-formatsare new devDependencies, pinned to theajvversion already in the lockfile.tests/actor-entitlement.test.tsgains a case that compares results, tool, graphs and taxonomies with and without entitlements and actors. The check is byte-identical, on a model where every new member is present.Things a reviewer should know
automationDetails.idisguardlink/threat-model/. A GitHub upload with no explicitcategorywill now file GuardLink alerts under that category. Settingcategoryon the upload overrides it.uriBaseId. Adding it would change the existing location objects, so artifact URIs stay relative as before.kindonlogicalLocations. The anchor knows the scope of the code, not whether it is a function, method or class.Workspace.Metadata (#report-metadata)its first exposure, with a mitigation. That adds one node to this repository's whole-model threat graph. I re-measured it in Chrome astests/query-views.test.tsrequires: mermaid@11 under the dashboard's options at a confirmed 1440x900 viewport gave filtered {34 nodes, 88 links} (unchanged) and whole model {48 nodes, 179 links}. The counter agrees.Local evidence (GitHub Actions is unavailable for this repository right now)
Measured on the exports:
expense-apisarif-shop