fullshield 0.3.0 → 1.0.0

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,102 @@
2
2
 
3
3
  All notable changes to FullShield are documented here.
4
4
 
5
+ ## 1.0.0 — 2026-10-20
6
+
7
+ 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).
8
+
9
+ Experimental features stay behind `FULLSHIELD_EXPERIMENTAL_*` and are not part of that promise. Phase 56 (EPSS / CISA) is not in this release.
10
+
11
+ Reviewed recall on untouched apps is not in this release. The stored Next.js cold scan remains 71.7 seconds.
12
+
13
+ ### Precision sampling (dev tooling)
14
+
15
+ 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.
16
+
17
+ ### Provenance signals
18
+
19
+ 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.
20
+
21
+ ### Experimental ingest
22
+
23
+ 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.
24
+
25
+ ### Phase 86 — dependency capability profile (experimental)
26
+
27
+ `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.
28
+
29
+ ### Fix verification
30
+
31
+ `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.
32
+
33
+ ### Experimental
34
+
35
+ Phase 90 reads user-supplied Semgrep-style YAML behind `FULLSHIELD_EXPERIMENTAL_SEMGREP_RULES`. Default scan is unchanged. Results may differ from Semgrep.
36
+
37
+ ### Developer loop
38
+
39
+ `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.
40
+
41
+ ### Quality gate
42
+
43
+ Phase 94 adds `npm run quality` (Q1–Q12) for contributors. Default scan findings are unchanged.
44
+
45
+ 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.
46
+
47
+ ### New/changed stable findings — may change CI results; use --update-baseline
48
+
49
+ 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).
50
+
51
+ 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).
52
+
53
+ 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).
54
+
55
+ 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).
56
+
57
+ Phase 65 taint summaries are already on this branch. The 5-second nest and undici target was not met.
58
+
59
+ 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.
60
+
61
+ ### Phase 80 — type-aware receivers
62
+
63
+ `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.
64
+
65
+ 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.
66
+
67
+ ## 0.4.0 — 2026-10-18
68
+
69
+ Phase 46 still enforces Node >= 22.13 in `engines`, `doctor`, and CI. Phase 47 still owns the console view.
70
+
71
+ ### New/changed stable findings — may change CI results; use --update-baseline
72
+
73
+ Same-function Mongo filters are reported by the existing `SAST-NOSQLI-001` rule (approved under D7). vulnerable-node gains two keys: `routes/shop.js` lines 41 and 58, both high. Every other tuning app is unchanged. Ground truth was not edited.
74
+
75
+ ### Upgrade plan, evidence, explain, SBOM, supply heuristics, lockfile diff, modern lockfiles
76
+
77
+ Phases 49, 61, 60, 66, 70, 62, and 63. The upgrade plan is a sentence built from OSV `fixed` versions. The other six are off unless their `FULLSHIELD_EXPERIMENTAL_*` flag is set. Default `(ruleId, file, line, severity)` keys are unchanged.
78
+
79
+ ### Validation, workspaces, custom taint, summaries, SARIF
80
+
81
+ Phases 53, 54, 64, 65, and 74. Each stays off unless its flag is set. Phase 54 reads `package.json` workspaces. Phase 65 caches taint summaries; the 5-second nest and undici target was not met. Default scan keys stay the same when the flags are off.
82
+
83
+ ### Unused direct dependencies
84
+
85
+ A scan names direct `dependencies` that no project file imports and that no imported package requires. Imported and imported-not-called packages stay out. `fix` prints the removal plan and writes nothing. `fix --unsafe` deletes those names from `package.json` only, leaves the lockfile alone, and prints `run npm install`. The finding list is unchanged, including a `DEPS-MALICIOUS-001` on an unused package, which stays critical.
86
+
87
+ ### Console
88
+
89
+ Default console prints the human-decision shown rows (2026-10-18), grouped one row per package name. JSON, SARIF, Markdown, HTML, the baseline, and `--fail-on` stay the full list. `--all` prints every finding, ungrouped. This is not a stable-output change.
90
+
91
+ Recount on `next` after restore-0.4.0, from `scan --json --offline-safe`, unreviewed. The counts match the 2026-10-18 decision (D4). There is no row-count threshold. The shown rule, the 20-row cap, and severities were not changed.
92
+
93
+ | app | shown rows | shown findings | folded findings | shown findings by reachability |
94
+ |---|---|---|---|---|
95
+ | nodegoat | 36 | 62 | 194 | reachable-via-dependency 41, reachable 12, none 8, not-imported 1 |
96
+ | juice-shop | 49 | 59 | 46 | none 46, reachable 13 |
97
+ | dvna | 14 | 16 | 7 | none 12, reachable 4 |
98
+ | goof | 63 | 157 | 114 | reachable-via-dependency 86, reachable 60, none 11 |
99
+ | vulnerable-node | 41 | 43 | 20 | none 38, reachable-via-dependency 4, reachable 1 |
100
+
5
101
  ## 0.3.0 — 2026-10-16
6
102
 
7
103
  ### Default scan changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FullShield
2
2
 
3
- **v0.2.6 / 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.0.** 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,9 +10,11 @@ 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
- **Requires Node.js >= 20.11.** Scan, fix, and init run on Node 20.11+. History storage (`report --serve`) needs `node:sqlite` and prints `requires Node 22.13+` on older Node instead of failing at import. `node:sqlite` is still experimental and is not loaded by `--import` register.
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
+
17
+ Contributor quality gate: [docs/QUALITY.md](docs/QUALITY.md) (`npm run quality`). It does not change scan results.
16
18
 
17
19
  ## Coverage map
18
20
 
@@ -68,11 +70,13 @@ This is a `0.x` release. APIs may change. Do not treat it as a penetration test.
68
70
 
69
71
  ## Known limitations
70
72
 
71
- - Platforms: Linux, macOS, and Windows on Node 20.11, 22, and 24. Quoted filenames in dirty-tree tests are POSIX-only (Windows forbids `"` in names). History storage needs Node 22.13+.
73
+ - Platforms: Linux, macOS, and Windows on Node >= 22.13. Quoted filenames in dirty-tree tests are POSIX-only (Windows forbids `"` in names).
72
74
  - `node:sqlite` is loaded only by history/confirm, not by `--import fullshield/runtime/register`. Dashboard closes every handle in `try/finally` (Windows cannot delete an open `history.db`).
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
 
@@ -91,6 +95,7 @@ In-scope public-bench patterns we still do not claim (one line each):
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.
@@ -134,6 +139,8 @@ app.use(shieldGuard({ mode: 'monitor', authRoutes: ['/login'] }));
134
139
 
135
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/ADVANCED-SPEC.md`](docs/ADVANCED-SPEC.md).
136
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.
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
 
139
146
  `npm run bench` reports `inspect()` p50/p95 (must stay under 1ms) and full Express request overhead p50/p95 with the middleware on the stack.
@@ -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
 
@@ -208,7 +220,7 @@ npx fullshield scan --debug
208
220
 
209
221
  `doctor` prints the Node version, lockfile, cache directory, experimental flags that are set to `1`, whether `fullshield.config.json` parses, and whether OSV is allowed to use the network. `--offline` skips that network. CLI failures use `FS-E###` codes. The message stays the same. A stack is printed only with `--debug`.
210
222
 
211
- Console output shows the highest 10 findings, then `N more — use --json`. Each line has the file, the finding message, a fix line, and `rules/<rule-id>.md`.
223
+ Default console prints shown rows only, capped at 20. A dependency advisory is shown when it is `DEPS-MALICIOUS-001`, or when it is `DEPS-VULN-001`, not dev-only, and reachability is reachable, symbol-reachable, via a dependency, or unknown. Severity and confidence do not show a dependency row. SAST, secrets, and config are shown when severity is high or critical, or confidence is high. Shown dependency findings are one row per package. `--all` prints every finding. JSON, SARIF, Markdown, HTML, the baseline, and `--fail-on` stay the full list. On the 2026-10-18 offline-safe recount, grouped rows were NodeGoat 36, goof 63, juice-shop 49, dvna 14, and vulnerable-node 41. Ground truth is still `"reviewed": false`.
212
224
 
213
225
  Experimental taint propagation (`FULLSHIELD_EXPERIMENTAL_TAINT_V2=1`) and the parse pool (`FULLSHIELD_PARSE_POOL=1`) are off by default. Symbol reachability runs only when an OSV advisory lists `affected_functions`. Advisories without that list keep the import-level class.
214
226