Repository navigation
feat(boundary): add a directed @boundary from <outer> to <inner> - #52
Merged
Merged
Conversation
A @boundary was an undirected pair, so the only way to tell its untrusted side was to infer it from an endpoint no @asset declares. That says nothing when both sides are declared assets, which is the usual case for an application and its database: the SARIF export could only report the side as unknown. The new form '@boundary from <outer> to <inner>' states it. The between, and and | forms are unchanged and stay undirected. - Parser: the directed form is read inline and from .gal sidecars. A line whose arguments open with 'from' and do not parse is malformed; 'from' and 'to' elsewhere in a sentence are not evidence, so prose stays a warning. - Model: boundaries[] records directed: true, with asset_a the outer side and asset_b the inner side. Undirected boundaries carry no new field, and their annotation hash and claim key do not move; a declared, dropped or reversed direction changes both. - SARIF: a declared side is basis 'declared', distinct from the inferred 'undeclared-endpoint' and from 'unknown', in run.graphs[0] guardlink/side (a directed edge runs outer to inner, guardlink/directed: true) and in the pentest profile's boundary-claim results, whose inner_routes now also cover a declared inner side. Exports of models with no directed boundary are unchanged; the github baselines still match byte for byte. The boundary-claim rule's help text describes the three bases, so the two pentest consumer fixtures are regenerated, and a third, boundary-direction.sarif, holds all three bases. - validate (CLI, MCP, TUI): a directed boundary whose outer or inner side is no declared asset and no @flows endpoint is an error, unresolved-boundary-side. diff reports a change of direction, and validate --code-graph uses a declared inner side. - conformance/boundaries.json pins the grammar (directed, undirected, malformed), the side the export states, and the validation result, and runs in tests/conformance-boundaries.test.ts. - SPEC 3.2, 2.12, 5.3, 6.6 and 6.8, the annotate prompt, the map playbook, the code-graph boundary suggestions and the agent instruction templates describe the directed form and ask agents to use it when the trust direction is known.
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
@boundarygains an optional directed form,@boundary from <outer> to <inner>. Thebetween,andand|forms are unchanged and stay undirected.Why
A boundary was an undirected pair. A consumer could find its untrusted side only by inferring it from an endpoint that no
@assetdeclares. That fails when both sides are declared assets, which is the usual case for an application and its database. Intests/fixtures/expense-api, the#api/#dbdata boundary in front of the SQL injection exports asbasis: "unknown", and so does#datainsarif-shop. The directed form lets the author state the side.Changes
Grammar and parser (
src/parser/parse-line.ts). The directed form is read inline and from.galsidecars. A line whose arguments start withfromand do not parse is reported as malformed (@boundary from #a and #b,between #a to #b,#a to #b,from #a -> #b, and so on).fromandtoelsewhere in a line are not evidence, because they are common English words:@boundary is used to mark a trust changestays a prose warning.Model. On a directed boundary,
boundaries[]recordsdirected: true, withasset_aas the outer side andasset_bas the inner side. Undirected boundaries get no new field. Their annotation hash and claim key are unchanged, so no hash version bump is needed and no existing ledger entry is re-keyed. Declaring, dropping or reversing a direction changes both, so a recorded test outcome does not carry over to a boundary whose direction changed.SARIF. A declared side is
basis: "declared". It is kept separate from the inferredundeclared-endpoint, which is unchanged for undirected boundaries, and fromunknown. It appears in two places:run.graphs[0]: in each boundary edge'sguardlink/side. A directed edge isguardlink/directed: true, withsourceNodeIdthe outer side andtargetNodeIdthe inner side.guardlink/boundary-claimresults. Theirinner_routesnow also list routes into a declared inner side.Validation (CLI, MCP and TUI
validate). A directed boundary whose outer or inner side resolves to nothing is an error,unresolved-boundary-side. A side resolves if it is a#idthat some@assetdefines, or a name that is a declared asset path or a@flowsendpoint. A repository-qualified#repo.idis left to the workspace merge. Undirected boundaries are not checked.Other readers of boundaries.
guardlink diffreports a change of direction.validate --code-graphchecks a directed boundary against its declared inner side, including one between two declared assets, which it used to skip.guardlink_lookup "boundary for X"andhypothesis boundaries --jsoninclude the direction.Conformance corpus. The new
conformance/boundaries.jsoncovers:tests/conformance-boundaries.test.tsruns it against both the parser and the exporter. It ships in the npm package next toflows.json, andconformance/README.mddocuments its format.Agent material. The annotate prompt, the
mapplaybook, the code-graph boundary suggestions and the generated agent instructions now tell agents to write the directed form when the code shows which side is less trusted, andbetweenotherwise.Docs. SPEC §3.2
@boundary(direction, malformed lines, validation, records), §2.12, §5.3, §6.6, §6.8, the quick reference and the EBNF;docs/GUARDLINK_REFERENCE.md; CHANGELOG; CONTRIBUTING.Compatibility
githubprofile. Exports are byte-identical for any model without a directed boundary.tests/sarif-pentest.test.tsstill matches bothtests/fixtures/sarif-baseline/*.github.sarifbaselines byte for byte. The graph description text changes only when a directed boundary exists.pentestprofile. The only change for existing models is theguardlink/boundary-claimrule'shelp.markdown, which now describes the three bases.expense-api.sarifandsarif-shop.sarifare regenerated for that one line. The newtests/fixtures/sarif-pentest/boundary-direction.sarif(from the newtests/fixtures/boundary-direction) contains all three bases.sarif_profile_versionstays1. No member is added;"declared"is a new value of the existingbasis, and it appears only for the new syntax. SPEC §6.6 tells older readers to treat any basis other thanunknownas naming both sides.Verification
npm run build && npm test: 117 files, 2139 tests pass (1 skipped).npm run lintandtsc --noEmitare clean.guardlink validate . --artifacts,status .andci .pass.guardlink syncandartifacts .were re-run, and the clean-tree check holds after them.tests/boundary-direction.test.ts: grammar, prose tiering, sidecars, hash and claim key, validation, lookup, diff, agent material.tests/conformance-boundaries.test.ts.tests/sarif-pentest.test.ts, including schema validation.tests/boundary-check.test.ts.