@clear-capabilities/agentic-security-scanner 0.137.1 → 0.139.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 +244 -0
- package/dist/113.index.js +2 -2
- package/dist/178.index.js +1 -1
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +29 -1
- package/dist/526.index.js +2 -2
- package/dist/637.index.js +1 -1
- package/dist/agentic-security.mjs +14 -14
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +20 -7
- package/src/dataflow/CLAUDE.md +30 -0
- package/src/dataflow/catalog.js +512 -14
- package/src/dataflow/engine.js +275 -27
- package/src/dataflow/summaries.js +30 -5
- package/src/engine.js +512 -120
- package/src/ir/CLAUDE.md +20 -5
- package/src/ir/balanced-call.js +11 -1
- package/src/ir/callgraph.js +34 -0
- package/src/ir/parser-cs.js +55 -6
- package/src/ir/parser-go.js +106 -2
- package/src/ir/parser-java.js +111 -10
- package/src/ir/parser-js.js +40 -0
- package/src/ir/parser-kt.js +194 -10
- package/src/ir/parser-php.js +108 -6
- package/src/ir/parser-py.helper.py +199 -10
- package/src/ir/parser-rb.js +405 -31
- package/src/mcp/tools.js +29 -1
- package/src/posture/accuracy-scorecard.js +103 -0
- package/src/runScan.js +5 -2
- package/src/sast/CLAUDE.md +1 -1
- package/src/sast/_auth-signals.js +141 -0
- package/src/sast/_comment-strip.js +80 -13
- package/src/sast/codegen-sink.js +110 -0
- package/src/sast/convention-deviation.js +235 -0
- package/src/sast/fastapi-hardening.js +45 -6
- package/src/sast/file-upload.js +29 -1
- package/src/sast/ownership-authz.js +245 -0
- package/src/sast/php.js +12 -2
- package/src/sast/rate-limit.js +2 -0
- package/src/sast/rbac-consistency.js +1 -1
- package/src/sast/redirect-toctou.js +167 -0
- package/src/sast/resource-exhaustion.js +217 -0
- package/src/sast/sibling-guard.js +176 -0
- package/src/sast/zip-slip.js +53 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,250 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 0.139.1 — The same scanner, published with provenance
|
|
13
|
+
|
|
14
|
+
**The shipped artifact is functionally identical to 0.139.0.** Only two commits
|
|
15
|
+
separate them, and neither is in the published package: a test-harness timeout
|
|
16
|
+
(`test/` does not ship) and a dependency hold (`.dependency-holds.json` does not
|
|
17
|
+
ship). Nothing about how the scanner behaves has changed.
|
|
18
|
+
|
|
19
|
+
This release exists for one reason: **0.139.0 was published without a provenance
|
|
20
|
+
attestation.** It went out from a maintainer's laptop because the CI token
|
|
21
|
+
lacked write access on the scoped package, so every tag-triggered publish failed
|
|
22
|
+
at `PUT` with `E404` — after having already signed provenance to the Sigstore
|
|
23
|
+
transparency log. The token has been replaced and verified (a re-run now returns
|
|
24
|
+
`E403 "cannot publish over the previously published versions"`, which is the
|
|
25
|
+
registry authenticating the write and rejecting only the duplicate version), so
|
|
26
|
+
this is the first release published by CI with `--provenance`.
|
|
27
|
+
|
|
28
|
+
For a security scanner that argues its case on *provable, measurable,
|
|
29
|
+
reproducible*, "you can cryptographically verify this artifact was built from
|
|
30
|
+
this commit in this repository" is a product property rather than a formality.
|
|
31
|
+
Provenance is signed at publish time and cannot be retrofitted, which is why it
|
|
32
|
+
takes a version number rather than an amendment to the last one.
|
|
33
|
+
|
|
34
|
+
Fixes carried along, neither user-visible:
|
|
35
|
+
|
|
36
|
+
- **`test/audit-cli.test.js` budgeted 4 s for a CLI that takes ~2.7 s idle** — a
|
|
37
|
+
1.5× margin in a suite that runs files concurrently. It failed as `status:
|
|
38
|
+
null` on a *different* test each run, was unreproducible in isolation, and
|
|
39
|
+
blocked a release push on a commit whose entire diff was 8 lines of JSON.
|
|
40
|
+
Now 30 s (~11× idle), still bounded so a genuinely hung CLI fails rather than
|
|
41
|
+
stalls.
|
|
42
|
+
- **`@types/vscode` is held rather than upgraded.** It declares the VS Code API
|
|
43
|
+
surface the extension compiles against and should track `engines.vscode` — the
|
|
44
|
+
*oldest* supported host — not the newest published types. Chasing latest lets
|
|
45
|
+
the extension compile against APIs absent from hosts it claims to support, and
|
|
46
|
+
that failure lands as a runtime `TypeError` in a user's editor. The hold
|
|
47
|
+
records a pre-existing mismatch (`engines ^1.95.0` vs types `^1.125.0`) for a
|
|
48
|
+
deliberate decision at review time, since narrowing the supported host range
|
|
49
|
+
is a product call, not a dependency bump.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 0.139.0 — Two detectors that were dead, a confinement rule that was documented but unenforced, and a new Go rule
|
|
53
|
+
|
|
54
|
+
Every defect in this release is the same shape: **a control that exists, is
|
|
55
|
+
documented, and does not hold end-to-end.** Unit tests passed in every case.
|
|
56
|
+
Only checks that exercised the whole pipeline found them.
|
|
57
|
+
|
|
58
|
+
### Two detectors produced nothing through a real scan
|
|
59
|
+
|
|
60
|
+
Both worked when called directly and returned zero through `runScan` — the
|
|
61
|
+
`rate-limit.js` signature (a rule that discarded 100% of its own findings,
|
|
62
|
+
project-wide, from the day it was written).
|
|
63
|
+
|
|
64
|
+
- **`k8s-admission`** — `_isIaCFile` admitted YAML as Kubernetes only when the
|
|
65
|
+
path contained a directory literally named `k8s/`. Its own comment claimed the
|
|
66
|
+
caller also checked for `kind:`; nothing ever did. **Manifests under
|
|
67
|
+
`deploy/`, `manifests/`, `charts/`, `kubernetes/` or the repository root were
|
|
68
|
+
invisible** — which is where most projects actually put them. Now admitted on
|
|
69
|
+
CONTENT (`apiVersion:` and `kind:` at line-start within the first 2 KB),
|
|
70
|
+
because the set of directory names people use is unbounded and the file
|
|
71
|
+
format is not.
|
|
72
|
+
|
|
73
|
+
The fix had to be applied at **both** admission gates: `runScan` admits a file
|
|
74
|
+
into `fileContents`, and then `runFullScan` independently re-filters that same
|
|
75
|
+
list with `shouldScan()`. With only the first opened, the predicate returned
|
|
76
|
+
true, the walker collected the files, and the scan still returned zero.
|
|
77
|
+
|
|
78
|
+
- **`install-script`** — `package.json` is routed to `depFileContents` and fails
|
|
79
|
+
`shouldScan()`, so the per-file SAST loop never visited it and
|
|
80
|
+
`scanInstallScripts` was wired in but never invoked. Fixed on the manifest
|
|
81
|
+
path rather than by admitting `package.json` into the SAST loop, which would
|
|
82
|
+
push a manifest through all ~117 code detectors to satisfy one rule.
|
|
83
|
+
|
|
84
|
+
Measured blast radius rather than assumed: 16 of 1129 YAML files in this tree
|
|
85
|
+
are newly admitted (1.4%), all in fixtures and caches. Self-scan unchanged.
|
|
86
|
+
|
|
87
|
+
### `_CONFINEMENT` rule 3 was documented and unimplemented
|
|
88
|
+
|
|
89
|
+
`agents/_CONFINEMENT.md` states three refusal rules for every edit-capable agent
|
|
90
|
+
and for the MCP write tools. The code enforced two. Rule 3 — *"a backup, lock, or
|
|
91
|
+
build-output file (`*.bak`, `*.lock`, `dist/`, `build/`, `target/`)"* — was not
|
|
92
|
+
enforced at all.
|
|
93
|
+
|
|
94
|
+
The tests failed by **successfully writing**, which is what makes this more than
|
|
95
|
+
pedantry: `scanner/dist/` holds the shipped bundle, which carries its own
|
|
96
|
+
SHA-256 integrity sidecar precisely because its contents matter. `apply_fix`
|
|
97
|
+
would have rewritten it and reported success. A contract an agent is instructed
|
|
98
|
+
to follow, enforced less strictly than it claims, is worse than no contract.
|
|
99
|
+
|
|
100
|
+
Build output is matched as a **path segment at any depth** — `packages/web/dist/`
|
|
101
|
+
is the normal shape — while a source file *named* `dist.js` or `build.py` stays
|
|
102
|
+
writable.
|
|
103
|
+
|
|
104
|
+
### New: `sibling-guard-omission` (CWE-22, Go)
|
|
105
|
+
|
|
106
|
+
From `GHSA-95cv-r8x4-vh75`: two fields of one request struct reach a filesystem
|
|
107
|
+
rename and the project's **own** guard is applied to only one. High precision by
|
|
108
|
+
construction — the rule never decides what a guard *is*, it observes one being
|
|
109
|
+
applied to a sibling, so every finding reduces to "this file guards X and forgets
|
|
110
|
+
Y", falsifiable from a single screen of code, with the guard name and both field
|
|
111
|
+
names carried as evidence.
|
|
112
|
+
|
|
113
|
+
Fires on the real advisory in `pre` and goes silent there in `post`. FP budget
|
|
114
|
+
measured across 72 real Go packages: **1.12%** of findings, max 5 per repo.
|
|
115
|
+
|
|
116
|
+
**It could never have shipped without a second fix.** `dropGuardedFindings` drops
|
|
117
|
+
a CWE-22 finding when its window contains a containment guard — and for this
|
|
118
|
+
family the window *always* contains one, because the guard on the sibling **is**
|
|
119
|
+
the finding. The centralized precision filter was reading the evidence of the bug
|
|
120
|
+
as proof of safety and would have deleted 100% of the family regardless of
|
|
121
|
+
detector quality. Exemption keyed on `family`, deliberately narrow.
|
|
122
|
+
|
|
123
|
+
A false positive in this rule was then found on real code (an `\n` escape read as
|
|
124
|
+
a path separator) and fixed, with a regression test written from that code.
|
|
125
|
+
|
|
126
|
+
### Instruments
|
|
127
|
+
|
|
128
|
+
- **The shipped LSP is smoke-tested.** `bin/agentic-security-lsp.js` ships inside
|
|
129
|
+
the JetBrains and Neovim plugins and was referenced by no test, script or
|
|
130
|
+
workflow. It now runs as a subprocess speaking real LSP over stdio —
|
|
131
|
+
`initialize` → `didOpen` → `publishDiagnostics` → `shutdown` → `exit` — plus a
|
|
132
|
+
malformed-frame survival case.
|
|
133
|
+
- **Per-language layer attribution** on the independent population, with taint's
|
|
134
|
+
attributable share stated outright each run.
|
|
135
|
+
- **Gate integrity** (P0 of `docs/WORLD_CLASS_HARNESS_PRD.md`): CI-parity checks
|
|
136
|
+
so the local gate cannot disagree with hosted CI; layer-recall gates on
|
|
137
|
+
equality rather than a floor, so an unrecorded *improvement* fails too; a
|
|
138
|
+
shared per-entry bench watchdog; and a detector-liveness guard that requires
|
|
139
|
+
every rule fixture to produce a finding *through* `runScan`. That guard is what
|
|
140
|
+
found both dead detectors above.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## 0.138.0 — Findings come from code, not comments; container taint; a benchmark that can no longer hang
|
|
144
|
+
|
|
145
|
+
### Comment blindness, enforced instead of documented
|
|
146
|
+
|
|
147
|
+
`src/sast/CLAUDE.md` has always named this as its first gotcha for detector
|
|
148
|
+
authors — "Comments confuse detectors. Always go through `blankComments()`" —
|
|
149
|
+
but nothing enforced it, so whether a rule ignored comments depended on whether
|
|
150
|
+
its author remembered. A probe over nine languages, in which *every* dangerous
|
|
151
|
+
construct sat inside a comment and the only executable statement was
|
|
152
|
+
`return 1`, produced **15 findings from code that can never run**. Three
|
|
153
|
+
independent defects, each fixed after a failing test proved it:
|
|
154
|
+
|
|
155
|
+
- `runFullScan` handed the **raw** file to every SAST module. 64 of 117 modules
|
|
156
|
+
called `blankComments()` themselves; roughly 40 of the rest ran regexes over
|
|
157
|
+
raw text. The per-file dispatch now computes one comment-blanked,
|
|
158
|
+
offset-preserving view and passes it to 99 detector call sites.
|
|
159
|
+
- The engine's own `stripNoise()` understood `//` and `/* */` but **not `#`**,
|
|
160
|
+
so every engine-internal regex pass leaked on Python/Ruby/shell/HCL comments.
|
|
161
|
+
All 15 call sites now pass a path, and the mapping is language-aware because
|
|
162
|
+
it has to be: `#` opens a comment in Python but is `#include`/`#region` in the
|
|
163
|
+
C family, and `//` is a comment in C-family languages but **floor division in
|
|
164
|
+
Python**, where blanking it would delete real code.
|
|
165
|
+
- `blankComments()` let a `'`/`"` string span a newline. An odd number of quotes
|
|
166
|
+
on one line — which regex literals produce routinely; `/"(?:sh|bash)"\s*,\s*(?!"[^"]*")/`
|
|
167
|
+
has seven — left the scanner stuck in string mode for the **entire rest of the
|
|
168
|
+
file**, silently disabling comment stripping from that point on. Found on this
|
|
169
|
+
repository's own `sast/go-extended.js`, where it resurrected a false positive
|
|
170
|
+
from a comment six lines below such a regex.
|
|
171
|
+
|
|
172
|
+
Ruby `=begin`/`=end` blocks are now stripped too (a `#`-only pass left them
|
|
173
|
+
wholly intact).
|
|
174
|
+
|
|
175
|
+
**Measured, finding-by-finding rather than by totals.** Self-scan on this
|
|
176
|
+
repository fell 458 → 427: **33 findings removed, all 33 on comment lines, zero
|
|
177
|
+
real findings lost**; the 2 added were previously *masked* by the string-mode
|
|
178
|
+
defect. On the 315-entry independent population of real upstream advisories,
|
|
179
|
+
localized precision rose **40.74% → 44.00%** with true positives unchanged at
|
|
180
|
+
11 — the fix buys precision, not recall, which is exactly what was predicted
|
|
181
|
+
once it was established that none of the credited true positives sat on a
|
|
182
|
+
comment line. Deliberately still reading raw source: the prompt-injection
|
|
183
|
+
family (for an agentic tool, instructions hidden in a comment *are* the attack),
|
|
184
|
+
the secrets scanners (a committed credential is leaked whether or not its line
|
|
185
|
+
executes), and `scanTodosNearSecurity`, whose entire subject is comments.
|
|
186
|
+
|
|
187
|
+
The property is pinned end-to-end by `test/comment-blindness.test.js`, including
|
|
188
|
+
a positive control and a Python-floor-division guard, so a regression surfaces
|
|
189
|
+
as a test failure rather than as quietly inflated findings.
|
|
190
|
+
|
|
191
|
+
### Container / collection-element taint (PRD T3.3)
|
|
192
|
+
|
|
193
|
+
Probed ten container shapes before writing anything, which showed the JS array
|
|
194
|
+
cases already worked. Coverage went from 5/10 to 9/10:
|
|
195
|
+
|
|
196
|
+
- A computed write with a non-literal key (`bag[k] = tainted`) lowers to access
|
|
197
|
+
path `bag.*`, and `'*'` is a *literal* property name, not a wildcard — the
|
|
198
|
+
lattice propagates only downward from a prefix, so the write was unreachable
|
|
199
|
+
from every correctly-computed read. An unknown key could be any key, so it now
|
|
200
|
+
widens to the container.
|
|
201
|
+
- `Map.set` / `Set.add` joined the mutator list.
|
|
202
|
+
- **The mutator rule required `callee.kind === 'member'`, a shape only Babel
|
|
203
|
+
emits**, so container writes in Python/Ruby/PHP/Go/Java/C#/Kotlin never matched
|
|
204
|
+
at all. The miss was invisible because a PY-SAST pattern rule caught the same
|
|
205
|
+
sink.
|
|
206
|
+
- `__setitem__` is in the mutator list because `parser-py.js` lowers a Python
|
|
207
|
+
subscript *assignment* to a call node, not an assign node — established by
|
|
208
|
+
dumping the IR, not by inference.
|
|
209
|
+
|
|
210
|
+
**Reported as capability, not as a recall gain:** `bench:layer-recall` is
|
|
211
|
+
unchanged at 116/215 and the dataflow-shaped subset unchanged at 115/137,
|
|
212
|
+
because the corpus holds no container-shaped entries. Pinned by
|
|
213
|
+
`test/container-taint.test.js`, including two precision cases that must *not*
|
|
214
|
+
fire.
|
|
215
|
+
|
|
216
|
+
### Performance
|
|
217
|
+
|
|
218
|
+
The comment work initially cost **19.7%** of end-to-end scan time on a 307-file
|
|
219
|
+
entry, because `stripNoise` is called ~15× per file and an interpreted character
|
|
220
|
+
loop had replaced two native regex passes. A single-slot memo (safe because the
|
|
221
|
+
engine processes one file at a time and `fileContents[p]` returns the same
|
|
222
|
+
string *reference*) plus a rewrite of `blankComments` to emit bulk slices
|
|
223
|
+
instead of per-character appends brought that to **5.7%**. Byte offsets and line
|
|
224
|
+
counts are preserved exactly — every finding's line number depends on it.
|
|
225
|
+
|
|
226
|
+
### The independent benchmark could hang indefinitely
|
|
227
|
+
|
|
228
|
+
A full run wedged at entry 186 of 315 on `GHSA-hcm8-x79p-wx2w` (apache/camel,
|
|
229
|
+
649 MB): process alive, state `S`, 0.0% CPU, no progress for six hours, killed
|
|
230
|
+
with 129 entries never scored. Reproduced on a clean checkout, so it was
|
|
231
|
+
pre-existing. `runScan` carries a deep-mode walltime budget and a per-file
|
|
232
|
+
timeout, but nothing bounded a whole-entry scan. There is now a per-entry
|
|
233
|
+
watchdog (`AGENTIC_SECURITY_BENCH_ENTRY_TIMEOUT_MS`, default 600 s) that marks
|
|
234
|
+
the entry UNSCORED — the harness's own documented doctrine for an entry that
|
|
235
|
+
could not be run, never a miss. The run that produced this release's numbers
|
|
236
|
+
named 6 such entries (all Java, all very large) and completed.
|
|
237
|
+
|
|
238
|
+
Worth stating plainly: **every independent-population figure published before
|
|
239
|
+
this release came from a harness that could silently stall partway through.**
|
|
240
|
+
|
|
241
|
+
### Documentation corrected
|
|
242
|
+
|
|
243
|
+
`docs/METRICS.md`'s taint table was ~5× stale (23/210 = 11%; actual 116/215 =
|
|
244
|
+
54%) and its central claim that kotlin still had **0%** taint recall was false
|
|
245
|
+
(48%). Both `bench/layer-recall/baseline.json` and the table are regenerated.
|
|
246
|
+
The cause is recorded alongside the fix, because it is a gate-design lesson:
|
|
247
|
+
`bench:layer-recall:check` compares against a **floor**, so an improvement from
|
|
248
|
+
31 to 116 passed it exactly as a no-change run would.
|
|
249
|
+
|
|
250
|
+
Also fixed two long-standing lifecycle failures: `jsUnits` — Theme 6's JS/TS
|
|
251
|
+
extractor, which carries 6 of the 10 known sibling-omission entries — was wired
|
|
252
|
+
into production with **no test at all**, and now has four; and a stale
|
|
253
|
+
`profile.js::DEFAULTS` dead-code allowlist entry was removed.
|
|
254
|
+
|
|
255
|
+
|
|
12
256
|
## 0.137.1 — Dependabot policy for the deliberately-vulnerable fixtures
|
|
13
257
|
|
|
14
258
|
Housekeeping release. Adds `.github/dependabot.yml` so Dependabot leaves the
|
package/dist/113.index.js
CHANGED
|
@@ -415,8 +415,8 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
415
415
|
var external_node_fs_ = __webpack_require__(3024);
|
|
416
416
|
// EXTERNAL MODULE: external "node:path"
|
|
417
417
|
var external_node_path_ = __webpack_require__(6760);
|
|
418
|
-
// EXTERNAL MODULE: ./src/engine.js +
|
|
419
|
-
var engine = __webpack_require__(
|
|
418
|
+
// EXTERNAL MODULE: ./src/engine.js + 203 modules
|
|
419
|
+
var engine = __webpack_require__(6622);
|
|
420
420
|
;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
|
|
421
421
|
// Deterministic honesty gates on fix / finding output (#7).
|
|
422
422
|
//
|
package/dist/178.index.js
CHANGED
|
@@ -13,7 +13,7 @@ export const modules = {
|
|
|
13
13
|
/* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
|
|
14
14
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
|
|
15
15
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
|
|
16
|
-
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
16
|
+
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6622);
|
|
17
17
|
// Time-travel + counterfactual scanning (v0.68).
|
|
18
18
|
//
|
|
19
19
|
// Two new modes that exploit the pure-input shape of runFullScan:
|
package/dist/384.index.js
CHANGED
|
@@ -8,7 +8,7 @@ export const modules = {
|
|
|
8
8
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
9
|
/* harmony export */ scanCredentials: () => (/* reexport safe */ _engine_js__WEBPACK_IMPORTED_MODULE_0__.Sv)
|
|
10
10
|
/* harmony export */ });
|
|
11
|
-
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
11
|
+
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6622);
|
|
12
12
|
// Secrets submodule view of the engine — credential + entropy + TODO scanning.
|
|
13
13
|
|
|
14
14
|
|
package/dist/435.index.js
CHANGED
|
@@ -572,7 +572,29 @@ const RESERVED_WRITE_SUFFIXES = [
|
|
|
572
572
|
'.tfvars',
|
|
573
573
|
'docker-compose.yml',
|
|
574
574
|
'docker-compose.yaml',
|
|
575
|
+
// _CONFINEMENT rule 3 — backup and lock files. The specific lock BASENAMES
|
|
576
|
+
// above cover the ecosystems we know; this catches the rest (`deps.lock`,
|
|
577
|
+
// `foo.bak`) without needing to enumerate them. Nothing an autofix should
|
|
578
|
+
// ever be rewriting: a `.bak` is someone's safety copy and a `.lock` is
|
|
579
|
+
// generated state.
|
|
580
|
+
'.bak',
|
|
581
|
+
'.lock',
|
|
575
582
|
];
|
|
583
|
+
// _CONFINEMENT rule 3 — build output. Matched as a PATH SEGMENT at any depth,
|
|
584
|
+
// not as a top-level prefix, because build output is routinely nested
|
|
585
|
+
// (`packages/web/dist/`, `services/api/target/`) and a top-level-only check
|
|
586
|
+
// would refuse the monorepo root and allow every package inside it.
|
|
587
|
+
//
|
|
588
|
+
// This matters most in THIS repository: `scanner/dist/` holds the shipped
|
|
589
|
+
// bundle, which carries its own SHA-256 integrity sidecar precisely because
|
|
590
|
+
// what it contains matters. Before this, `apply_fix` would rewrite it and
|
|
591
|
+
// report success.
|
|
592
|
+
//
|
|
593
|
+
// NOTE for a future change, deliberately not made here: the PREFIX list above
|
|
594
|
+
// (`node_modules/`, `.git/`, …) is still top-level-only, so a nested
|
|
595
|
+
// `packages/a/node_modules/` is not covered by it. That is a separate widening
|
|
596
|
+
// with its own blast radius and belongs in its own change with its own tests.
|
|
597
|
+
const RESERVED_WRITE_DIR_SEGMENTS = new Set(['dist', 'build', 'target']);
|
|
576
598
|
function _isReservedWritePath(sessionRoot, absFile) {
|
|
577
599
|
// Resolve sessionRoot symlinks so the relative path is computed against
|
|
578
600
|
// the same canonical root as `absFile` (which _confine already realpath'd).
|
|
@@ -581,9 +603,15 @@ function _isReservedWritePath(sessionRoot, absFile) {
|
|
|
581
603
|
const rootReal = external_node_fs_.realpathSync(external_node_path_.resolve(sessionRoot));
|
|
582
604
|
const rel = external_node_path_.relative(rootReal, absFile).replace(/\\/g, '/');
|
|
583
605
|
if (RESERVED_WRITE_PREFIXES.some(p => rel === p.replace(/\/$/, '') || rel.startsWith(p))) return true;
|
|
584
|
-
const
|
|
606
|
+
const segments = rel.split('/');
|
|
607
|
+
const base = segments[segments.length - 1] || '';
|
|
585
608
|
if (RESERVED_WRITE_BASENAMES.has(base)) return true;
|
|
586
609
|
if (RESERVED_WRITE_SUFFIXES.some(s => base === s || base.endsWith(s))) return true;
|
|
610
|
+
// Any DIRECTORY segment that names build output — checked over
|
|
611
|
+
// `segments.length - 1` so a source file legitimately called `build` or
|
|
612
|
+
// `dist` is not refused for its own name; only living inside such a
|
|
613
|
+
// directory counts.
|
|
614
|
+
if (segments.slice(0, -1).some(seg => RESERVED_WRITE_DIR_SEGMENTS.has(seg))) return true;
|
|
587
615
|
return false;
|
|
588
616
|
}
|
|
589
617
|
|
package/dist/526.index.js
CHANGED
|
@@ -234,8 +234,8 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
234
234
|
var external_node_fs_ = __webpack_require__(3024);
|
|
235
235
|
// EXTERNAL MODULE: external "node:path"
|
|
236
236
|
var external_node_path_ = __webpack_require__(6760);
|
|
237
|
-
// EXTERNAL MODULE: ./src/engine.js +
|
|
238
|
-
var engine = __webpack_require__(
|
|
237
|
+
// EXTERNAL MODULE: ./src/engine.js + 203 modules
|
|
238
|
+
var engine = __webpack_require__(6622);
|
|
239
239
|
;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
|
|
240
240
|
// Deterministic honesty gates on fix / finding output (#7).
|
|
241
241
|
//
|
package/dist/637.index.js
CHANGED
|
@@ -11,7 +11,7 @@ export const modules = {
|
|
|
11
11
|
/* harmony export */ renderPrDeltaText: () => (/* binding */ renderPrDeltaText)
|
|
12
12
|
/* harmony export */ });
|
|
13
13
|
/* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
|
|
14
|
-
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
14
|
+
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6622);
|
|
15
15
|
// Shadowscan / security-DELTA on PR (v0.72).
|
|
16
16
|
//
|
|
17
17
|
// Most SAST PR-comment integrations show absolute counts — "12 findings
|