fullshield 0.4.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,124 @@
2
2
 
3
3
  All notable changes to FullShield are documented here.
4
4
 
5
+ ## 1.0.1 — 2026-10-20
6
+
7
+ Documentation-only release. Scanner behavior, default `scan --json` output, and exit
8
+ codes are unchanged. `default-scan-diff` against 1.0.0 is empty.
9
+
10
+ ### Contract correction (docs/stability.md)
11
+
12
+ 1.0.0's stability.md listed values in Stable that only experimental flags produce.
13
+ That was a documentation error. This release moves them to Experimental.
14
+
15
+ Moved from Stable to Experimental:
16
+ - `Finding.source` values `ai`, `user-rule`, `npm-audit`, `semgrep`, `osv-scanner`, `sarif`.
17
+ - `Finding.suppressedBy` (`types`, `model:<package>`).
18
+ - Error codes `FS-E005`–`FS-E009`, `FS-E012`–`FS-E016`.
19
+ - Config keys `ai`, `sast.sources`, `sast.sanitizers`.
20
+
21
+ Moved to Internal: `FS-E010`, `FS-E011` (contributor tooling only).
22
+
23
+ Added to Stable (shipped and documented as stable since 0.1.0 or 0.2.0): `watch`, `edge`,
24
+ `notify`, `doctor`, and the core `scan --json` finding fields.
25
+
26
+ If you depended on a moved value, it still works in 1.0.1. It may change in a later minor.
27
+
28
+ ### Documentation
29
+
30
+ - `CAPABILITIES.md` rewritten. The 1.0.0 file described 0.1.1 and carried finding
31
+ counts that no longer match. It now links to generated files instead of copying numbers.
32
+ - `docs/demo.md` and `BENCHMARK.md` regenerated from runs at 1.0.1.
33
+ - `COMPARISON.md` is marked not citable until the Semgrep run is re-checked.
34
+ - History notes moved to `docs/history/`.
35
+ - Corrected stale lines in `errors.md`, `QUALITY.md`, `priority.md`, `NOISE-REPORT.md`,
36
+ and `CONTRIBUTING.md`.
37
+
38
+ ### 1.0.0 notes corrected
39
+
40
+ - The 0.2.0 entry said 0.3.0 and 0.4.0 were not tagged. That was true when written.
41
+ Both were tagged later.
42
+ - 1.0.0 said Phases 52 and 78 were "merged locally". Both are in the `v1.0.0` commit
43
+ `d654c8b6f155bec88b2327a62402f0b35feb277d`, which contains `5a0d006` (phase 52) and
44
+ `4966d8b` (phase 78).
45
+ - Phase 77 is in 1.0.0 as `scripts/realworld-diff.mjs`. It is a dev gate and has no scan flag.
46
+ - Phase 81 is in 1.0.0 behind `FULLSHIELD_EXPERIMENTAL_MODELS`.
47
+ - Phase 82 is in 1.0.0 behind `FULLSHIELD_EXPERIMENTAL_SECRET_CHECK`.
48
+ - Phase 84 is in 1.0.0 behind `FULLSHIELD_EXPERIMENTAL_PRIORITY`.
49
+ - Phase 89 is in 1.0.0 behind `FULLSHIELD_EXPERIMENTAL_SPDX`.
50
+ - Phase 92 is in 1.0.0 as issue forms, companion-tool docs, and required check names. It has no scan flag.
51
+ - The Phase 75 and Phase 51 default-scan diffs were empty on every bench app. That shows
52
+ no regression. It does not show either change finds anything on those apps.
53
+
54
+ ### Release check
55
+
56
+ 1.0.0 was tagged before the six-job matrix was confirmed green on `main`.
57
+ The six required checks in `docs/branch-protection.md` passed on pull request #18:
58
+ https://github.com/kathanp/fullshield/actions/runs/39514123297
59
+ 1.0.1 is tagged only after those checks pass on `main`.
60
+
61
+ ## 1.0.0 — 2026-10-20
62
+
63
+ 1.0.0 freezes the stable CLI, config keys, exit codes, error codes `FS-E000`–`FS-E016`, and the library exports in `api/fullshield.api.json`. The promise is [docs/stability.md](docs/stability.md).
64
+
65
+ Experimental features stay behind `FULLSHIELD_EXPERIMENTAL_*` and are not part of that promise. Phase 56 (EPSS / CISA) is not in this release.
66
+
67
+ Reviewed recall on untouched apps is not in this release. The stored Next.js cold scan remains 71.7 seconds.
68
+
69
+ ### Precision sampling (dev tooling)
70
+
71
+ Phase 76 adds `scripts/precision-sample.mjs` and `scripts/precision-report.mjs`. Neither script nor `bench/precision/` is in the npm package. The sampler writes `label`, `reviewer`, and `note` as null. Default scan findings are unchanged.
72
+
73
+ ### Provenance signals
74
+
75
+ Phase 87 adds informational `SUPPLY-PROV-001` when `FULLSHIELD_EXPERIMENTAL_PROVENANCE=1`. Severity is medium. The signal is not a malicious-package claim. Default scan keys are unchanged with the flag off.
76
+
77
+ ### Experimental ingest
78
+
79
+ Phase 83 reads local Semgrep, npm audit, OSV-Scanner, and SARIF files and dedupes them when `FULLSHIELD_EXPERIMENTAL_INGEST=1`. It does not run those tools, does not fetch, and does not change their severity. Default scan is unchanged. `fullshieldPriority` is not set.
80
+
81
+ ### Phase 86 — dependency capability profile (experimental)
82
+
83
+ `FULLSHIELD_EXPERIMENTAL_CAPABILITIES=1` adds `fullshield deps --capabilities`, capability notes on `deps-diff`, and `SUPPLY-CAP-001` when `deps.capabilityPolicy` denies a statically read capability. Default scan does not emit `SUPPLY-CAP-001`. Package files are not executed and packages are not installed.
84
+
85
+ ### Fix verification
86
+
87
+ `FULLSHIELD_EXPERIMENTAL_FIX_VERIFY=1` (or `fix --experimental-fix-verify`) re-scans each proposed fix and each `explain` diff in memory. A patch is `removes-finding`, `does-not-remove`, or `introduces-finding`. A patch that introduces a finding is never written. `does-not-remove` needs `--force-unverified`. Dependency overrides and bumps are checked against the OSV cache only. Default scan output is unchanged.
88
+
89
+ ### Experimental
90
+
91
+ Phase 90 reads user-supplied Semgrep-style YAML behind `FULLSHIELD_EXPERIMENTAL_SEMGREP_RULES`. Default scan is unchanged. Results may differ from Semgrep.
92
+
93
+ ### Developer loop
94
+
95
+ `fullshield dev` is off unless `FULLSHIELD_EXPERIMENTAL_DEV=1`. It prints findings that appeared or went away after a save. Default scan output is unchanged.
96
+
97
+ ### Quality gate
98
+
99
+ Phase 94 adds `npm run quality` (Q1–Q12) for contributors. Default scan findings are unchanged.
100
+
101
+ Phase 85 adds `scan --paths` behind `FULLSHIELD_EXPERIMENTAL_PATHS`. A path lists findings that already exist. It does not add a finding or change severity. Default scan JSON is unchanged when the flag is off.
102
+
103
+ ### New/changed stable findings — may change CI results; use --update-baseline
104
+
105
+ Phase 75 router-level auth inheritance. Default-scan diff against `bench/snapshots/HEAD.json` (0.4.0) was empty on vulnerable-app, clean-app, and every bench app. Merged (D9).
106
+
107
+ Phase 51 follows a SQL string or Mongo filter returned from another file. Default-scan diff against `bench/snapshots/HEAD.json` (0.4.0) was empty on vulnerable-app, clean-app, and every bench app. Merged (D9).
108
+
109
+ Phase 50 flags Swig `{{ }}` when the view engine is Swig, including consolidate. NodeGoat gains 12 `SAST-XSS-001` keys on profile and contributions fields. `{{csrftoken}}` is not reported. Merged (D15).
110
+
111
+ Phase 52 adds `DEPS-PHANTOM-001` and `DEPS-LOCKMISMATCH-001` with import or manifest line numbers. Test, script, and seed imports declared in devDependencies are low severity. clean-app declares cors, express-rate-limit, and jsonwebtoken. Merged locally (D16).
112
+
113
+ Phase 65 taint summaries are already on this branch. The 5-second nest and undici target was not met.
114
+
115
+ Phase 78 cold-scan work stays behind `FULLSHIELD_EXPERIMENTAL_PERF2`. Merged locally. The quiet-machine speed comparison is not done, so the flag is not claimed to be faster.
116
+
117
+ ### Phase 80 — type-aware receivers
118
+
119
+ `FULLSHIELD_EXPERIMENTAL_TYPES=1` or `scan --experimental-types` loads the optional `typescript` peer and suppresses sink calls whose receiver resolves to a non-sink type (`Array`, `Map`, a user class). Mongoose models and `any` stay. Suppressed findings are listed in JSON `suppressed[]` with `suppressedBy: "types"`. The flag off leaves scan JSON unchanged. A missing `typescript` install prints FS-E013 and keeps the heuristics.
120
+
121
+ Phase 79 time budget stays behind `FULLSHIELD_EXPERIMENTAL_BUDGET`. `scan --max-time` stops SAST taint, still finishes deps, secrets, and config, and exits 3 when nothing scanned meets `--fail-on`. That exit is not a clean scan. A finding at or above `--fail-on` still exits 1. The flag off leaves output and exit codes unchanged.
122
+
5
123
  ## 0.4.0 — 2026-10-18
6
124
 
7
125
  Phase 46 still enforces Node >= 22.13 in `engines`, `doctor`, and CI. Phase 47 still owns the console view.
@@ -127,7 +245,7 @@ Off unless `FULLSHIELD_EXPERIMENTAL_DIFF=1` or `fullshield diff --experimental-d
127
245
 
128
246
  ## 0.2.0 — 2026-10-16
129
247
 
130
- Phases 36–45 ship in this version. `0.3.0` and `0.4.0` were not tagged.
248
+ Phases 36–45 ship in this version. (At the time of writing, `0.3.0` and `0.4.0` were not yet tagged. Both were tagged later.)
131
249
 
132
250
  ### Phase 36 — provenance and semver
133
251
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FullShield
2
2
 
3
- **v0.4.0 / alpha.** One install for Node.js/TypeScript backends: detect → classify → report → notify → fix → gate (CI `--fail-on` + runtime monitor). It does not stop attacks by itself.
3
+ **v1.0.1.** One install for Node.js/TypeScript backends: detect → classify → report → notify → fix → gate (CI `--fail-on` + runtime monitor). It does not stop attacks by itself.
4
4
 
5
5
  **Headline: dependency reachability.** A lockfile CVE is not the same as an unused package. FullShield classifies each OSV hit as `reachable` (imported and called), `imported-not-called` (−1), `reachable-via-dependency` (lockfile path from an imported direct dep — **severity kept**, path shown, e.g. `express > body-parser > qs`), `not-imported` (−2, only when no such path exists), or `reachability-unknown` (severity kept) when the lockfile format has no parsed dependency edges. DevDependency-only vulns are `info` with a `dev-only` note unless production sources import the package. Pair it with `fullshield sbom --format cyclonedx`.
6
6
 
@@ -10,10 +10,12 @@ npx fullshield scan --json --fail-on high
10
10
  npx fullshield scan --sarif fullshield.sarif
11
11
  ```
12
12
 
13
- This is a `0.x` release. APIs may change. Do not treat it as a penetration test.
13
+ 1.0.0 freezes the contract in [docs/stability.md](docs/stability.md). Experimental flags stay off unless you set them. Do not treat a scan as a penetration test. Reviewed recall for untouched apps is not published. The stored Next.js cold scan is 71.7 seconds, above the 60-second target.
14
14
 
15
15
  **Supported runtime is Node.js >= 22.13.** `package.json` `engines` and `doctor` enforce that floor. `node:sqlite` runs without `--experimental-sqlite` from 22.13.0 (the module is still experimental and is loaded only by history/confirm, not by `--import` register).
16
16
 
17
+ Contributor quality gate: [docs/QUALITY.md](docs/QUALITY.md) (`npm run quality`). It does not change scan results.
18
+
17
19
  ## Coverage map
18
20
 
19
21
  | Layer | Covered by |
@@ -73,6 +75,8 @@ This is a `0.x` release. APIs may change. Do not treat it as a penetration test.
73
75
  - FullShield does not stop OS or SSH exploits (including regreSSHion). It scans Node/TS apps you own.
74
76
  - Extra classes (ReDoS, deserialization, XXE, upload, cookie flags, CSRF, mass assignment, timing compare, hash cost, GraphQL limits, `trust proxy` + IP rate limit) run only with `FULLSHIELD_EXPERIMENTAL_CLASSES=1` or `scan --experimental-classes`. ReDoS is a same-file static quantifier check. CSRF looks for `csurf` or `sameSite: 'strict'|'lax'` in that file.
75
77
  - Git history, client-bundle, and live credential checks are **experimental** (`FULLSHIELD_EXPERIMENTAL_SECRETS_ADV=1`). Default `scan` is unchanged. `--verify-secrets` never runs without `--i-own-these-credentials`; AWS checks without a signing secret key report `unknown`.
78
+ - Dependency capability profiles (`FULLSHIELD_EXPERIMENTAL_CAPABILITIES=1`) read installed files only. They do not run install scripts. `deps-diff` reports a new capability only when the previous package integrity is already cached. Source files over 1 MB are skipped. A comment or string that names an API can count as that capability.
79
+ - Fix verification (`FULLSHIELD_EXPERIMENTAL_FIX_VERIFY=1`) re-scans the patched file and its direct importers. A line shift of an unchanged finding is not a new finding. A comment-only patch stays `does-not-remove`. A missing OSV cache entry for the planned version stays `does-not-remove` and is not treated as a cleared advisory. The check does not run project tests or `npm install`.
76
80
 
77
81
  Experimental RASP structure-change scoring on the vendored PayloadsAllTheThings corpus (not a claim that live attacks are blocked):
78
82
 
@@ -85,12 +89,13 @@ These fixtures are self-authored. Numeric SQL (`WHERE id = ` + input), ORM raw-q
85
89
 
86
90
  In-scope public-bench patterns we still do not claim (one line each):
87
91
 
88
- - Swig `{{firstName}}` in `.html` — XSS only if autoescape is off in another file; we flag `autoescape: false`, not the template line.
92
+ - Swig `{{ name }}` is `SAST-XSS-001` only when the view engine is Swig and that name is tainted from `req` or a database read. `{{csrftoken}}` and unrendered templates are not findings. Handlebars `{{ }}` stays unflagged. `autoescape: false` is still a finding.
89
93
  - Juice Shop track-order XSS — server returns `res.json`; we do not treat JSON fields as HTML sinks.
90
94
  - Juice Shop `url.includes(allowedUrl)` — weak allowlist helper is not a redirect sink; we flag `res.redirect(tainted)`.
91
95
  - Juice Shop `{ _id: req.body.id }` — flagged as `SAST-NOSQLI-001` (medium) unless coerced (`String()`/`Number()`/`Boolean()`), schema-validated, or mongo-sanitize is applied. Same for goof `User.find({ password: req.body.password })`.
92
96
  - Juice Shop `data/static/codefixes/*` — six `SAST-NOSQLI-001` hits are copies of the challenge fixes. They stay. There is no special-case exclusion for that directory.
93
97
  - `DEPS-VULN-001` — when an OSV hit exists, the finding is attributed to the `package.json` name line; `--offline-safe` on a cache-less clone still emits no OSV hits (`package.json:N` will not match).
98
+ - `SUPPLY-PROV-001` (`FULLSHIELD_EXPERIMENTAL_PROVENANCE=1`) — registry provenance, publisher, and release-age signals from `registry.npmjs.org` only. Offline with an empty cache stays unknown. These signals are not a malicious-package claim.
94
99
  - DVNA calculator “stack leak” — that line is `mathjs.eval` (`SAST-EVAL-001`), not `err.stack`.
95
100
  - Worker-pool timings from `node bench/perf.mjs --measure` on 2026-10-16 (`bench/perf-baseline.json`): next.js finished all 5 samples (cold p50 71.7s, warm 71.5s, incremental 70.6s, peak RSS 788 MB, every sample under 1 GB). nest cold p50 9.1s, warm 9.3s, peak 815 MB; undici cold p50 8.4s, warm 8.4s, peak 563 MB. nest and undici still miss the 5s incremental target (nest 9.4s, undici 8.2s). These are local timings, not a recall claim.
96
101
  - Real-world precision is not sampled. Nine of the ten pins in `bench/realworld.json` have finding counts in `BENCHMARK.md`. next.js produced no JSON. No false-positive rate is claimed.
@@ -132,7 +137,9 @@ app.use(shieldGuard({ mode: 'monitor', authRoutes: ['/login'] }));
132
137
 
133
138
  **Experimental CI reports (off by default):** `FULLSHIELD_EXPERIMENTAL_CI_REPORTS=1` or `scan --experimental-ci-reports`, then `--github-annotations`, `--junit`, and `--gitlab`. Default console and `--json` output stay the same. Critical and high become GitHub `error` commands and JUnit failures. Medium is a GitHub warning and a JUnit failure. Low and info are GitHub notices and JUnit skips. GitLab severities are info, minor, major, critical, and blocker.
134
139
 
135
- **Experimental scan diff (off by default):** `FULLSHIELD_EXPERIMENTAL_DIFF=1` or `fullshield diff --experimental-diff <before.json> <after.json>`. Compares two `scan --json` files. The match ignores the line number. It does not scan and does not decide which findings are real. Added findings at or above `--fail-on` (default high) exit 1. Baseline `suppressions` need an owner, a reason, and an expiry. The spec is [`docs/ADVANCED-SPEC.md`](docs/ADVANCED-SPEC.md).
140
+ **Experimental scan diff (off by default):** `FULLSHIELD_EXPERIMENTAL_DIFF=1` or `fullshield diff --experimental-diff <before.json> <after.json>`. Compares two `scan --json` files. The match ignores the line number. It does not scan and does not decide which findings are real. Added findings at or above `--fail-on` (default high) exit 1. Baseline `suppressions` need an owner, a reason, and an expiry. The spec is [`docs/history/ADVANCED-SPEC.md`](docs/history/ADVANCED-SPEC.md).
141
+
142
+ **Experimental ingest (off by default):** `FULLSHIELD_EXPERIMENTAL_INGEST=1` or `fullshield ingest --experimental-ingest <file...>`. Reads local SARIF 2.1.0, OSV-Scanner JSON, `npm audit --json`, and Semgrep JSON. It does not run those tools and does not fetch. Severity stays the tool's severity (`moderate` and Semgrep `WARNING` are stored as medium; the original token is `externalSeverity`). `fullshieldPriority` is not set.
136
143
 
137
144
  **Experimental RASP** (Frontier, off by default): structure-change sink hooks via `node --import fullshield/runtime/register app.js` or `import 'fullshield/runtime/auto'` as the first import. Enable only with `FULLSHIELD_EXPERIMENTAL_RASP=1`. Default mode is `monitor` (events only). `FULLSHIELD_RASP_MODE=block` throws on structure-changing SQL/shell/path/NoSQL/SSRF/prototype input. Hooks **fail open**: a hook error logs `RASP-FAIL-OPEN-001` and the original call proceeds. Do not use block mode in production until you have measured the benign corpus on your app.
138
145
 
@@ -146,14 +153,16 @@ app.use(shieldGuard({ mode: 'monitor', authRoutes: ['/login'] }));
146
153
  |---|---|
147
154
  | `scan` | Run deps, secrets, sast, config. `--json`, `--sarif`, `--markdown`, `--html`, `--fail-on`, `--offline`, `--staged`, `--update-baseline`. `--ai explain` is **experimental** (off by default; explanation text only, never a verdict). `--pr-summary` is **experimental** (`FULLSHIELD_EXPERIMENTAL_POLICY=1`): deterministic Markdown of new findings versus baseline for GitHub/GitLab PR comments (no AI; links to rule docs). |
148
155
  | `diff` | **Experimental** (`FULLSHIELD_EXPERIMENTAL_DIFF=1`). Compare two `scan --json` files. `--fail-on` applies to added findings only. |
156
+ | `ingest` | **Experimental** (`FULLSHIELD_EXPERIMENTAL_INGEST=1`). Read local SARIF, npm audit, OSV-Scanner, and Semgrep JSON and dedupe. Does not run those tools. |
149
157
  | `fullshield.policy.json` | **Experimental** policy-as-code (`FULLSHIELD_EXPERIMENTAL_POLICY=1`): `requiredModules`, per-rule `severityOverrides`, `failOn` per branch, and `exceptions` that require `owner`, `reason`, and ISO `expires`. Expired exceptions fail CI. Exceptions without owner or expiry are rejected. |
150
158
  | `fix` | Dry-run patches (unsafe suggestions included). `--apply` writes only `safe: true` patches on a clean git tree. `--unsafe` is required for `helmet()` insertion and secret → `process.env` replacement. `--force` skips files with uncommitted changes. `--ai` is **experimental**: drafts a verified, reviewable diff for findings without a rule-based fix (never auto-merged). |
151
159
  | `init` | Config + GitHub/GitLab CI + pre-commit hook |
152
160
  | `notify` | Slack / generic webhook / SMTP summary of the last scan |
153
- | `sbom` | CycloneDX 1.5 JSON (`--format cyclonedx`, `--out file`) |
161
+ | `sbom` | CycloneDX 1.5 JSON (`--format cyclonedx`, `--out file`). Experimental SPDX 2.3 (`FULLSHIELD_EXPERIMENTAL_SPDX=1` or `--experimental-spdx`, `--format spdx`, `--sbom-timestamp`, `--cyclonedx-out`). Licenses are copied from package data only. |
154
162
  | `watch` | Rescan dependencies; alert (webhook / exit 1) only on **new** OSV hits vs last scan. `init` writes a daily GitHub Action. |
155
163
  | `edge` | GET/HEAD posture check on a URL you pass: CDN/WAF, HTTPS redirect, security headers, `/.env` / `/.git` exposure. No crawl, no payloads. |
156
164
  | `install` | **Experimental** install-guard (`FULLSHIELD_EXPERIMENTAL_SUPPLY=1`). Static analysis only; never runs package scripts. Registry reads are `registry.npmjs.org` only. |
165
+ | `deps --capabilities` | **Experimental** (`FULLSHIELD_EXPERIMENTAL_CAPABILITIES=1`). Static capability profile of installed dependencies (process, network, fs write, env, eval, native addon, dynamic require, install scripts, obfuscation) including transitive packages. `deps-diff` adds `newCapabilities` only with the same flag. `SUPPLY-CAP-001` is a `deps.capabilityPolicy` deny hit and is never in the default scan. |
157
166
  | `confirm` | **Experimental** exploit confirm (`FULLSHIELD_EXPERIMENTAL_CONFIRM=1`). localhost / 127.0.0.1 / FullShield-started container only. Harmless canaries only. |
158
167
  | `mcp` | **Experimental** MCP server (`FULLSHIELD_EXPERIMENTAL_MCP=1`). Official SDK stdio: `fullshield mcp --stdio`. Requires optional peers `@modelcontextprotocol/sdk` and `zod` (`npm i -D @modelcontextprotocol/sdk zod`); missing peers print an install hint, not a stack dump. Read-only tools: `scan`, `get_findings`, `explain_finding`, `propose_fix` (diff only, never writes), `verify_fix` (temp copy). |
159
168
  | `honey init` | **Experimental** honeytokens (`FULLSHIELD_EXPERIMENTAL_HONEY=1`). Decoy routes are **low** severity and aggregated per IP; only use of a canary **credential** is critical. Auto-block is off unless `threatAutoBlock` is set (shared-IP / NAT risk; max duration applies). Decoys are fake values added to `.gitignore`. |
@@ -163,14 +172,17 @@ app.use(shieldGuard({ mode: 'monitor', authRoutes: ['/login'] }));
163
172
  | `iac` | **Experimental** light IaC (`FULLSHIELD_EXPERIMENTAL_IAC=1`): GHA, Terraform, Kubernetes, docker-compose. Not a full IaC scanner. |
164
173
  | `agent` | **Experimental** remediation agent (`FULLSHIELD_EXPERIMENTAL_AGENT=1`). Mock/localhost GitHub API + local git remote only — never a real network. Never merges, never pushes to `main`/`master`. `--max-prs` (default 3). |
165
174
  | `scan --changed` / `--since` | **Experimental** incremental scan (`FULLSHIELD_EXPERIMENTAL_PLATFORM=1`). |
175
+ | `dev` | **Experimental** save loop (`FULLSHIELD_EXPERIMENTAL_DEV=1`). Watches the project and prints only new and resolved findings. |
166
176
  | `scan --experimental-sast-gaps` | **Experimental** extra SAST sinks: ORM raw SQL, mongoose `$where`, `spawn('sh', ['-c'])`, execa `shell: true`, zx `$.raw` (`FULLSHIELD_EXPERIMENTAL_SAST_GAPS=1`). |
167
177
  | `scan --experimental-frameworks` | **Experimental** Next.js / NestJS / Hono / Koa taint sources and AUTH (`FULLSHIELD_EXPERIMENTAL_FRAMEWORKS=1`). Default `scan` is unchanged. Runtime adapters: `fullshield/runtime/koa`, `fullshield/runtime/hono` (optional peers). |
168
178
  | `scan --experimental-classes` | **Experimental** extra vuln classes: ReDoS, deser, XXE, upload, cookies, CSRF, mass assignment, timing compare, hash cost, GraphQL, trust-proxy (`FULLSHIELD_EXPERIMENTAL_CLASSES=1`). Default `scan` is unchanged. |
169
179
  | `scan --history` / `--bundle <dir>` / `--verify-secrets` | **Experimental** secrets advanced (`FULLSHIELD_EXPERIMENTAL_SECRETS_ADV=1` or `scan --experimental-secrets-adv`). History streams `git log -p` (first-introduced commit, still-at-HEAD). Bundle scans `dist/` `.next/static` `build/` for browser-exposed keys including `NEXT_PUBLIC_*`/`VITE_*`. `--verify-secrets` is never default and is **not** in generated CI templates; it needs `--i-own-these-credentials` and only hits read-only identity endpoints (AWS STS GetCallerIdentity, GitHub `GET /user`, Stripe `GET /v1/balance`, Slack `auth.test`). Results: `live` / `revoked` / `unknown`. The key is never logged. |
180
+ | `scan` provenance | **Experimental** (`FULLSHIELD_EXPERIMENTAL_PROVENANCE=1`). `SUPPLY-PROV-001` (medium) when an installed version drops `dist.attestations`, is published by a user absent from the previous 10 versions, or is inside the Phase 62 release-age window. `registry.npmjs.org` only. `--offline` uses the registry cache; a miss stays unknown and is not a finding. Not a malicious-package claim. |
170
181
  | `vex` | **Experimental** OpenVEX / CycloneDX VEX from reachability (`FULLSHIELD_EXPERIMENTAL_VEX=1`). `not_affected` + `vulnerable_code_not_in_execute_path` only when reachability is `not-imported`; otherwise `under_investigation`. Never guessed. Also enables worm/workflow indicators, unsafe `dependency-override` proposals, and a provenance coverage score. Apply writes `package.json` and prints `run npm install`; it does not edit the lockfile. |
171
182
  | `scan --experimental-mcpcfg` | **Experimental** MCP client/server config rules `MCP-001`–`MCP-005` (`FULLSHIELD_EXPERIMENTAL_MCPCFG=1`). Default `scan` emits no `MCP-*` IDs. |
172
183
  | `scan --include-home` | **Experimental** also scan MCP configs under the user home (`.cursor/mcp.json`, `.vscode/mcp.json`, `.mcp.json`, Claude Desktop). Requires `FULLSHIELD_EXPERIMENTAL_MCPCFG=1`. Never implied. |
173
184
  | `host --experimental-host` | **Experimental** Linux-only OpenSSH exposure check (`FULLSHIELD_EXPERIMENTAL_HOST=1`). Reports `HOST-SSH-001`..`003` (version, no `LoginGraceTime 0`, auth timeout attempts). Refuses without `--experimental-host`. Default `scan` is unchanged. Skips with an info message on macOS, Windows, containers without sshd, and PaaS. **What it does NOT do:** FullShield does not stop OS or SSH exploits. |
185
+ | `fix --experimental-fix-verify` | **Experimental** fix verification (`FULLSHIELD_EXPERIMENTAL_FIX_VERIFY=1`). Each `fix` patch and each `explain` diff is applied in memory and re-scanned. `introduces-finding` is never written. `does-not-remove` needs `--force-unverified`. Dependency version checks read the OSV cache only. |
174
186
 
175
187
  Inline ignore (reason required): `// fullshield-ignore SAST-SQLI-001: reviewed`
176
188