Keep the knowingly-vulnerable demo target out of the dependency graph - #194
Merged
Conversation
|
Demo isolation is clearly implemented with solid error handling and tests. 🎯 Quality: 80% Excellent · 📦 Size: Large — consider splitting if possible 🛡️ Standards: no pre-flight fit check ran for this change — wire 📈 This month: Your 151st PR — above team average · Averaging Good |
patchstackdave
force-pushed
the
fix/dependency-signal
branch
from
August 31, 2026 13:12
2a139af to
900f817
Compare
A knowingly vulnerable package named in a committed manifest enters this repository's dependency graph, where its advisories cannot be told apart from advisories about the package that ships. The demos exploit lodash@4.17.11 deliberately, so they install it on demand (`npm run setup`) rather than declaring it. `examples/protect/demo-target.mjs` is the single place the version is written down. The version is part of what the demos prove: against a patched version the exploit fails on its own, both demos still print their proof, and the guard is credited for a block that never happened. `tests/demo-target.test.ts` pins it and asserts the target is absent from every dependency-graph section of both the example and root manifests — optional, peer and both bundled spellings included, through one shared list so neither manifest is checked against a shorter one. `.github/dependabot.yml` scopes update pull requests to the root manifest and the workflows. It selects which manifests get update pull requests, not which produce alerts: alerts follow the dependency graph, so keeping the package out of the graph is what keeps them meaningful. The example's lockfile is gitignored for the same reason. The demos load the built runtime, which is what an application loads, and the instructions give the sequence: root install and build, then `npm run setup`, then the demo. The gallery needs the build but not the target. An absent build exits with the command that produces it; a build that exists and fails to load keeps its own error, since presence is checked before loading. `npm run test:demos` asserts, per demo and independently, that it exits zero, prints no failed step, reaches its own verdict line, and prints its proof — with a proof pattern an empty or inert run cannot satisfy. The gallery treats zero demonstrations as a failure. `tests/demo-rules.test.ts` puts both demo bundles through `validateBundle`, the gate a delivered bundle passes, and requires the surviving rule count to equal the declared count. Every rule in the demo bundle has an exploit that it blocks and a benign control that it allows, so each rule is shown to discriminate rather than merely to fire. Rule parameters name keyed sources only: `raw` takes no key, and a keyed `raw.<name>` is not enforceable under the contract. Dev-dependency advisories are resolved: esbuild via an override to `^0.28.1`, since tsup pins `^0.27.0`, and nanoid via the available fix. `npm audit` reports zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
patchstackdave
force-pushed
the
fix/dependency-signal
branch
from
August 31, 2026 13:34
900f817 to
ce0077f
Compare
Contributor
Author
|
/review |
daniloradovic
approved these changes
Aug 31, 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.
A knowingly vulnerable package named in a committed manifest enters this repository's dependency graph, where its advisories cannot be told apart from advisories about the package that ships. The demos exploit
lodash@4.17.11deliberately, so it stays out of the graph.The dependency signal
The demos install their target on demand (
npm run setup).examples/protect/demo-target.mjsis the single place the version is written down, and the example's lockfile is gitignored.The version is part of what the demos prove, not an installation detail: against a patched version the exploit fails on its own, both demos still print their proof, and the guard is credited for a block that never happened.
tests/demo-target.test.tspins it and asserts the target is absent from every dependency-graph section of both the example and root manifests —dependencies,devDependencies,optionalDependencies,peerDependenciesand both bundled spellings — through one shared list, so neither manifest can be checked against a shorter one than the other..github/dependabot.ymlscopes update pull requests to the root manifest and the workflows. Worth being exact about what that file does: it selects which manifests get update pull requests, not which produce alerts. Alerts follow the dependency graph, so anignoreentry would suppress maintenance while leaving the graph — and the alerts — unchanged.Dev-dependency advisories are resolved: esbuild via an override to
^0.28.1(tsup pins^0.27.0, so an override is the available route — build, typecheck, tests, side-effect audit and bundled proof all pass on 0.28.2), and nanoid via the available fix.npm auditreports zero.The demos
They load the built runtime, which is what an application loads. The instructions give the sequence — root install and build, then
npm run setup, then the demo — and the gallery section states that it needs the build but not the target.Two runtime-load failures stay distinguishable, because presence is checked before loading: an absent build exits 2 with the command that produces it, and a build that exists but fails to load keeps its own error rather than being reported as a missing build.
npm run test:demoschecks each demo for four things independently: exit status, absence of a failed-step marker, its own verdict line, and its proof. Separate assertions, because a process can exit zero having printed failures, and can print a banner naming a CVE while demonstrating nothing. The proof patterns are ones an inert run cannot satisfy — the gallery's requires a non-zero, fully-passing count — and the gallery treats zero demonstrations as a failure. Wired into CI.The demo rules
tests/demo-rules.test.tsputs both demo bundles throughvalidateBundle— the gate a delivered bundle passes — and requires the surviving rule count to equal the declared count, so a rule dropped for any reason is caught.Every rule in the demo bundle has an exploit it blocks and a benign control it allows. The benign half is what makes each case evidence: a rule that blocks its exploit and everything else has not been shown to discriminate, and a demo bundle is where an over-broad rule looks fine.
Rule parameters name keyed sources only.
rawtakes no key, so a keyedraw.<name>parameter is not enforceable under the contract — which the bundle validation above now holds the bundles to.Checks
1749 tests, typecheck, build,
npm auditzero, demos, bundled proof, consumer matrix. Commit signed.