Skip to content

Add Risk Surface Guard, a deterministic PR-diff risk scanner - #139

Merged
nateshpp merged 4 commits into
mainfrom
feature/risk-surface-guard
Jul 28, 2026
Merged

Add Risk Surface Guard, a deterministic PR-diff risk scanner#139
nateshpp merged 4 commits into
mainfrom
feature/risk-surface-guard

Conversation

@nateshpp

@nateshpp nateshpp commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds Risk Surface Guard (ADR-047, WI-055): a deterministic diff scanner that flags PR changes expanding execution, credential, workflow, model-loading, container, or network-egress risk surface. Runs independently alongside the existing gate-integrity ratchet, no shared rule logic.

  • Scanner + rule catalog covering 15 categories. Warn-by-default; --mode fail blocks on high/critical findings.
  • SARIF upload to the Security tab.
  • Base-branch-loaded suppression allowlist, so a PR can't suppress its own findings.
  • Full docs in docs/risk-surface-guard.md; design decisions in docs/adr/ADR-047-risk-surface-guard.md.

Not a replacement for SAST, secret scanning, or human review — deterministic line-based scanning by design, documented as a limitation.

Governance checklist

  • npm run verify passes locally
  • No em dashes / AI trailers in commits

Test evidence

npm run verify passes locally: style, hygiene, self-application conformance, full test suite, AgentProof 25/25 HARDENED. tests/risk-surface-guard.test.mjs (78 tests) and tests/check-risk-surface-allowlist.test.mjs (11 tests) cover the scanner and allowlist, including adversarial cases.

nateshpp added 2 commits July 27, 2026 21:42
…ner (WI-055)

Adds a second, fully separate deterministic PR-diff scanner alongside the
anti-gaming ratchet: scripts/risk-surface-guard.mjs and its rule catalog
scripts/lib/risk-surface-rules.mjs (60 rules across 15 categories) flag diffs
that expand execution, credential, workflow, dataset/model-loading,
container, network, or sandbox risk, independent of whether any test gate
stays intact. See ADR-047 for the design rationale, including the two
self-reference safeguards required because this repo's own CI scans its own
PRs: content rules are scoped to real source/config extensions (so the
should-flag/should-allow fixtures under fixtures/risk-surface/ cannot trigger
the scanner shipped beside them), and the scanner's own implementation files
are exempt from content rules while remaining protected paths.

Ships warn-only by default (CLI --mode warn, action.yml's new risk-surface
input), registered as an advisory (non-blocking) entry in
check-self-application.mjs and base-pinned like the ratchet. Includes 52
tests, 45 fixtures, and full documentation (docs/risk-surface-guard.md,
README/SECURITY/CHANGELOG updates). Does not claim breach prevention and does
not replace SAST, secret scanning, dependency review, cloud security, or
sandboxing.

Known pre-existing, unrelated issue: scripts/check-repo-hygiene.mjs fails on
this branch's name (a model-identifier prefix assigned by the task dispatch
system), which also drops the passing-gate count reflected in
RELEASE-EVIDENCE.md and site/index.html. This is independent of branch
content and was confirmed present before any change in this commit.
… Surface Guard (WI-055)

Addresses four of the five limitations named when Risk Surface Guard first
shipped (ADR-047), continuing the same work item since nothing had merged
yet.

Findings now upload to the GitHub Security tab as SARIF (action.yml),
sharing the ratchet's existing upload-sarif toggle under a distinct
modonome-risk-surface category. RS106, RS703, and RS707 were tuned against
more realistic code shapes: RS106 exempts a short list of
near-universally-benign process.env variable names, RS703 widens its Python
verify=False co-occurrence window from adjacent lines to a 4-line span, and
RS707 requires a structural egress/outbound match instead of a bare nearby
word.

Adds a base-branch-loaded suppression allowlist (ADR-047 decisions 9-13):
.modonome/risk-surface-allowlist.json and its loader
scripts/lib/risk-surface-allowlist.mjs are both base-pinned in ci.yml, so a
pull request's own new entry cannot suppress a finding in that same pull
request. A new blocking gate, scripts/check-risk-surface-allowlist.mjs,
validates a pull request's proposed allowlist edits pre-base-checkout.
Suppressed findings stay visible and marked in human and JSON output, are
excluded from SARIF, and never count toward the exit code in any mode.
Fail-closed is enforced twice: a malformed allowlist file suppresses
nothing, and isSuppressed independently re-validates expiry shape rather
than trusting the generic schema validator already ran.

The fifth limitation (no AST/semantic analysis) remains a deliberate
architectural boundary per ADR-045/047 and is unchanged.

tests/risk-surface-guard.test.mjs grows to 78 tests; a new
tests/check-risk-surface-allowlist.test.mjs adds 11. Seven new fixtures
cover the tuned heuristics and the allowlist's valid, expired, glob, and
malformed shapes.
@nateshpp
nateshpp requested a review from techseek4vr as a code owner July 28, 2026 02:57
nateshpp added 2 commits July 28, 2026 06:50
…guard

# Conflicts:
#	.modonome/snapshot/map.json
#	.modonome/snapshot/map.md
#	.modonome/snapshot/signature.json
Consistency fix: the gate-integrity SARIF upload two steps above was
already bumped to v4 in a separate PR; this repeats it for the newer
Risk Surface Guard upload step, which didn't exist yet when that bump
landed.
@nateshpp
nateshpp merged commit aca7729 into main Jul 28, 2026
8 checks passed
@nateshpp
nateshpp deleted the feature/risk-surface-guard branch July 28, 2026 15:19
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.

1 participant