circle-ir-ai 4.26.0 → 4.28.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 +176 -0
- package/dist/agents/mastra/instance.d.ts +1 -1
- package/dist/agents/mastra/workflow.d.ts +30 -1
- package/dist/agents/mastra/workflow.d.ts.map +1 -1
- package/dist/agents/mastra/workflow.js +83 -5
- package/dist/agents/mastra/workflow.js.map +1 -1
- package/dist/cache/discovery-cache.d.ts.map +1 -1
- package/dist/cache/discovery-cache.js +11 -2
- package/dist/cache/discovery-cache.js.map +1 -1
- package/dist/cache/tree-cache.d.ts +8 -1
- package/dist/cache/tree-cache.d.ts.map +1 -1
- package/dist/cache/tree-cache.js +4 -1
- package/dist/cache/tree-cache.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/ax-client.d.ts +102 -10
- package/dist/llm/ax-client.d.ts.map +1 -1
- package/dist/llm/ax-client.js +12 -3
- package/dist/llm/ax-client.js.map +1 -1
- package/dist/llm/call-logger.d.ts +1 -1
- package/dist/llm/call-logger.d.ts.map +1 -1
- package/dist/llm/call-logger.js.map +1 -1
- package/dist/llm/discovery.d.ts +39 -0
- package/dist/llm/discovery.d.ts.map +1 -1
- package/dist/llm/discovery.js +115 -61
- package/dist/llm/discovery.js.map +1 -1
- package/dist/llm/line-numbers.d.ts +36 -0
- package/dist/llm/line-numbers.d.ts.map +1 -0
- package/dist/llm/line-numbers.js +47 -0
- package/dist/llm/line-numbers.js.map +1 -0
- package/dist/llm/prompts/index.d.ts.map +1 -1
- package/dist/llm/prompts/index.js +5 -0
- package/dist/llm/prompts/index.js.map +1 -1
- package/dist/llm/prompts/languages.d.ts +0 -12
- package/dist/llm/prompts/languages.d.ts.map +1 -1
- package/dist/llm/prompts/languages.js +5 -0
- package/dist/llm/prompts/languages.js.map +1 -1
- package/dist/llm/prompts/rust.d.ts.map +1 -1
- package/dist/llm/prompts/rust.js +5 -0
- package/dist/llm/prompts/rust.js.map +1 -1
- package/dist/llm/verification.d.ts +8 -0
- package/dist/llm/verification.d.ts.map +1 -1
- package/dist/llm/verification.js +14 -0
- package/dist/llm/verification.js.map +1 -1
- package/dist/security-scan/scanner.d.ts +22 -0
- package/dist/security-scan/scanner.d.ts.map +1 -1
- package/dist/security-scan/scanner.js +33 -11
- package/dist/security-scan/scanner.js.map +1 -1
- package/dist/skills/bundle-loader.d.ts.map +1 -1
- package/dist/skills/bundle-loader.js +6 -2
- package/dist/skills/bundle-loader.js.map +1 -1
- package/dist/skills/capability-mismatch.js +25 -1
- package/dist/skills/capability-mismatch.js.map +1 -1
- package/dist/skills/code-threat-patterns.d.ts +42 -3
- package/dist/skills/code-threat-patterns.d.ts.map +1 -1
- package/dist/skills/code-threat-patterns.js +99 -25
- package/dist/skills/code-threat-patterns.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/instruction-safety.d.ts +18 -1
- package/dist/skills/instruction-safety.d.ts.map +1 -1
- package/dist/skills/instruction-safety.js +45 -3
- package/dist/skills/instruction-safety.js.map +1 -1
- package/dist/skills/skill-analyzer.d.ts +7 -6
- package/dist/skills/skill-analyzer.d.ts.map +1 -1
- package/dist/skills/skill-analyzer.js +27 -2
- package/dist/skills/skill-analyzer.js.map +1 -1
- package/dist/skills/types.d.ts +7 -0
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/trust/compare.d.ts +13 -1
- package/dist/trust/compare.d.ts.map +1 -1
- package/dist/trust/compare.js +105 -33
- package/dist/trust/compare.js.map +1 -1
- package/dist/trust/index.d.ts +1 -1
- package/dist/trust/index.d.ts.map +1 -1
- package/dist/trust/index.js.map +1 -1
- package/dist/utils/file-skip.d.ts +17 -3
- package/dist/utils/file-skip.d.ts.map +1 -1
- package/dist/utils/file-skip.js +36 -6
- package/dist/utils/file-skip.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -116,6 +116,182 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
116
116
|
Unchanged on 4.9.29: OWASP 1415/1415, Juliet 243/243, SecuriBench 97.2%/6.7%, Go 89.5%,
|
|
117
117
|
NodeJS 92.9%, CWE-Bench-Rust 100%, bash and HTML 100%.
|
|
118
118
|
|
|
119
|
+
## [4.28.0] - 2026-10-15
|
|
120
|
+
|
|
121
|
+
Minor release: `circle-ir` 4.14.2 → 4.15.0, discovery prompts carry line numbers, verifier
|
|
122
|
+
refutations get their own label, and skill-detector fixes. Includes 4.27.1, which was not
|
|
123
|
+
published on its own.
|
|
124
|
+
|
|
125
|
+
### Changed
|
|
126
|
+
- **`circle-ir` 4.14.2 → 4.15.0** (exact pin). A precision release upstream: new sources
|
|
127
|
+
(Express destructured requests, GraphQL resolver-map arguments) and sinks (Spring handlers
|
|
128
|
+
returning request input as HTML, `loadClass` on context class loaders), and many cleared false
|
|
129
|
+
positives (const-origin `fetch` ssrf, const-only `RegExp` redos, `JSON.stringify` and C# hashes
|
|
130
|
+
or dictionary lookups as `external_taint_escape`, length/count/boolean tests of tainted text).
|
|
131
|
+
See circle-ir's changelog for the list.
|
|
132
|
+
|
|
133
|
+
### Fixed
|
|
134
|
+
- **Skill code-threat detectors judge a comment by the file's own comment syntax** (cognium-ai#647
|
|
135
|
+
§1, follow-up to #648/#653). `isCommentOrDoc`, the trailing-comment cut and the #352 block mask
|
|
136
|
+
used the union of every language's openers, so real code passed as a comment and every detector
|
|
137
|
+
skipped it: Python `n = 4 // 2; os.system(…)` (floor division), Python `note: int = …` and JS
|
|
138
|
+
`example: execSync(…)` (an annotated assignment and a label, read as `Note:` / `Example:` prose),
|
|
139
|
+
Python `*_, = [os.system(…)]` and a JS generator method `* run() {…}` (read as JSDoc
|
|
140
|
+
continuations), shell `ls /*` (opened a block comment that masked the rest of the file) and
|
|
141
|
+
shell `echo ${#HOME}; curl … | bash` (a length expansion read as a `#` comment). Each hid a
|
|
142
|
+
`curl … | sh`. Python/shell/Ruby/YAML-family files now use `#` only (shell: at the start of a
|
|
143
|
+
word), C-family files `//` and `/* */`, PHP both; a file with no recognised extension
|
|
144
|
+
(SKILL.md, references) keeps the old union. Real comments and docstrings stay suppressed.
|
|
145
|
+
- **Malformed model output no longer aborts or voids a skill scan in LLM mode** (cognium-ai#647
|
|
146
|
+
§2, follow-up to #648). `capability-mismatch` iterated the model's reply as-is: an object reply
|
|
147
|
+
threw "mismatches is not iterable" and a `null` entry threw on `.type`, and both escaped
|
|
148
|
+
`analyzeSkillBundle` as "Skill analysis failed", taking the deterministic findings with them.
|
|
149
|
+
`instruction-safety` threw on a numeric `severity` or non-string `evidence`, which turned a real
|
|
150
|
+
answer into "could not analyze". Entries with no type are dropped, other fields are coerced
|
|
151
|
+
(`sanitizeRawThreat`, exported).
|
|
152
|
+
- **`cross_skill_interference` from the instruction-safety LLM needs another skill, plugin, tool
|
|
153
|
+
or agent as the target** (cognium-ai#415, the LLM half; the static half shipped in 4.25.0).
|
|
154
|
+
`downweightInstructionOverreads` floors the finding to `info` unless its quote passes the static
|
|
155
|
+
detector's governed-verb rule (`isCrossSkillImperative`, exported), with sibling-qualified tools,
|
|
156
|
+
agents and MCP servers also accepted. Both #415 repros ("These override the generic
|
|
157
|
+
file-writing … instructions below", "Remove the red-eye from this image") floor; "disable the
|
|
158
|
+
other installed skills" keeps its severity. Category 19 of the prompt now says the same.
|
|
159
|
+
Kill switch: `SKILL_INSTRUCTION_OVERREAD_GATE=0`.
|
|
160
|
+
|
|
161
|
+
skills-validation static gate (`.synced` adapter, `SKIP_INSTRUCTION=1`, oracle v21): 57/152
|
|
162
|
+
required types, 0 safe/benign fixtures with findings, before and after; byte-identical except
|
|
163
|
+
ordering. That adapter is not the one behind #647's 97/152 figure.
|
|
164
|
+
- **A verifier refutation is no longer reported as "no verdict"** (cognium-ai#471). The batch
|
|
165
|
+
verifier prompt has the model omit every pair it does not judge a TRUE_POSITIVE. When a file's
|
|
166
|
+
answer held no TRUE_POSITIVE, `runReport` fell through to the static branch and kept every
|
|
167
|
+
detector finding with `verification.verdict: 'UNVERIFIED'`, which circle-pack maps to
|
|
168
|
+
`llm_result.verdict: 'UNCERTAIN'`. That is how 81,842 skill-scan findings showed 31%
|
|
169
|
+
TRUE_POSITIVE, 69% UNCERTAIN and no FALSE_POSITIVE: the refutations were there, under the wrong
|
|
170
|
+
label. Such a finding now carries `verdict: 'FALSE_POSITIVE'`, `refuted_by: 'omission'`,
|
|
171
|
+
`mode: 'llm'`, `llm_verified: false`. It is kept, not dropped, so finding counts do not change;
|
|
172
|
+
`exploitable` stays `false`, as it was for `UNVERIFIED`. The label applies only when the answer
|
|
173
|
+
is unambiguous: every pair behind the finding was submitted, none was in a failed chunk (#564
|
|
174
|
+
still keeps those open), no pair was dropped by the fan-out budget, and no returned pair failed
|
|
175
|
+
to map. Otherwise the finding stays `UNVERIFIED`. `ScanMeta.verification` gains
|
|
176
|
+
`findingsRefutedByOmission`, present only when non-zero. The prompt is unchanged. An explicit
|
|
177
|
+
FALSE_POSITIVE pair is still dropped by the verified branch, as before, so it never reaches a
|
|
178
|
+
finding. `tests/verifier-false-positive-471.test.ts` puts a stubbed FALSE_POSITIVE and an empty
|
|
179
|
+
`pairs` answer through the parser, `runReport` and `scanDirectory`.
|
|
180
|
+
- **Discovery prompts show the line numbers they ask for** (#658). `DiscoveryEngine.analyzeMethod`
|
|
181
|
+
asked the model for an absolute `"line"` but sent the method code without line numbers, so the
|
|
182
|
+
model guessed file lines from the text; a guessed line is how a discovery finding lands on a
|
|
183
|
+
signature, a `throw` or a closing brace (#330). Each line of the method is now prefixed with
|
|
184
|
+
its file line number (`42 | …`), and the prompt says `line` must be one of those numbers. A
|
|
185
|
+
line-number prefix the model copies into `code` is stripped. The #657 anchoring (move onto a
|
|
186
|
+
call the snippet names, else `llm-unanchored`) stays as the safety net. The language
|
|
187
|
+
discoverers on the `scan --llm` path (`discoverSourcesForLanguage` /
|
|
188
|
+
`discoverSinksForLanguage`, called by `runEnrich`) had the same defect on the whole file and
|
|
189
|
+
get the same fix, numbered from line 1. The batch verifier already numbered its windows. The
|
|
190
|
+
user prompt is now built by the exported `buildDiscoveryUserPrompt`.
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
- **Discovery cache key versions `disc-v3` → `disc-v4` and `lang-disc-v2` → `lang-disc-v3`**
|
|
194
|
+
(#658). Warm entries were answered from unnumbered code, so every local and R2 discovery entry
|
|
195
|
+
misses once and re-asks the model. The method cache key still holds no line numbers: an entry
|
|
196
|
+
now records the start line its prompt was numbered from, and a hit on a byte-identical method
|
|
197
|
+
elsewhere shifts its `line` and `source.line` by the difference before anchoring (before, a
|
|
198
|
+
hit kept the other method's lines and was clamped into this one).
|
|
199
|
+
|
|
200
|
+
### Benchmarks (local, circle-ir 4.15.0)
|
|
201
|
+
26 static suites run on 4.14.2 and 4.15.0 back to back, same source, same machine (2026-10-15).
|
|
202
|
+
All 26 score identically: OWASP 1415/1415, Juliet 243/243, CWE-Bench-Java static 64/120
|
|
203
|
+
(64/111 evaluable), secrets recall 100% / F1 98.2%, csharp-oracle and C# Juliet unchanged.
|
|
204
|
+
4.15.0's fixes land on shapes these corpora do not contain. The openclaw `beam-session.js`
|
|
205
|
+
redos (cognium-ai#671) still reports on 4.15.0: circle-ir #681 does not cover that file's
|
|
206
|
+
pattern. No LLM benchmark was re-run for this release; #658's before/after discovery run on
|
|
207
|
+
gemma3:12b is still outstanding.
|
|
208
|
+
|
|
209
|
+
## [4.27.1] - 2026-10-14
|
|
210
|
+
|
|
211
|
+
Patch release: one precision fix on the LLM scan path. `circle-ir` stays on 4.14.2.
|
|
212
|
+
|
|
213
|
+
### Fixed
|
|
214
|
+
- **The LLM scan path applies the same precision gates as `scan --no-llm`** (#460; PR #672).
|
|
215
|
+
`runReport` built its detector index (the no-verification findings and the #264 verify-parity
|
|
216
|
+
gate) with `generateFindings(sources, sinks, dfg, file)` only, so the non-executable-line gate,
|
|
217
|
+
sanitizer credit and the method-scoped proximity gate were all off. It now forwards
|
|
218
|
+
`sourceCode` and `types`, plus a new optional trailing `taintContext { language, sanitizers }`;
|
|
219
|
+
`PatternMatchResult` gains `sanitizers`. `flows` is still not forwarded, because in circle-ir it
|
|
220
|
+
also emits a finding per unsanitized flow (cognium-dev#372). An LLM-discovered pair that crosses
|
|
221
|
+
a function boundary, sits on a comment line or is covered by a sanitizer is now dropped, from
|
|
222
|
+
both the unverified output and the verify-parity gate.
|
|
223
|
+
|
|
224
|
+
### Changed
|
|
225
|
+
- **Tree-cache key version `tree-v1` → `tree-v2`.** Warm entries carry no sanitizers, so every
|
|
226
|
+
local and R2 tree-cache entry re-parses once. The cost is one parse per file; it makes no LLM
|
|
227
|
+
calls.
|
|
228
|
+
|
|
229
|
+
### Benchmarks (local, gemma3:12b)
|
|
230
|
+
A/B on 154 OWASP files, run 2026-10-11 before circle-ir 4.14.x and not repeated on 4.14.2: TP/FN
|
|
231
|
+
67/10 → 70/7, FP/TN 40/37 → 39/38, findings 976 → 975, LLM calls 462 → 462 with 0 failures. One
|
|
232
|
+
run per arm, so this is within LLM noise: no measured recall loss, no measured precision gain.
|
|
233
|
+
The static suites do not exercise `runReport`.
|
|
234
|
+
|
|
235
|
+
## [4.27.0] - 2026-10-12
|
|
236
|
+
|
|
237
|
+
Minor release: one change to which files `scan` analyses, and additive output fields.
|
|
238
|
+
`circle-ir` moves a patch, 4.14.1 → 4.14.2.
|
|
239
|
+
|
|
240
|
+
### Changed
|
|
241
|
+
- **A file with an over-long line is analysed without that line and marked partial** (PR #686,
|
|
242
|
+
follows #594). Through 4.26.0 a file with any line over 10,000 chars was skipped whole. Since
|
|
243
|
+
circle-ir 4.14.0 the engine blanks such a line and analyses the rest (cognium-dev#593). The
|
|
244
|
+
preflight now lets the file through when a non-blank line the engine can analyse remains, and
|
|
245
|
+
`SecurityScanner` blanks the long lines itself, so the LLM prompts never carry them, and marks
|
|
246
|
+
the file: `files[].partial { reason, linesNotAnalyzed }`, `summary.filesPartial` (present only
|
|
247
|
+
when not zero), a `[PARTIAL]` stderr line and a line in the text report. A file that is nothing
|
|
248
|
+
but over-long lines is still skipped. An explicit `MAX_LINE_LENGTH` stays a hard cap.
|
|
249
|
+
On a Flask handler padded with one 10,500-char literal: 0 findings and a skip before, the
|
|
250
|
+
`command_injection` on its real line after.
|
|
251
|
+
|
|
252
|
+
### Fixed
|
|
253
|
+
- **`compare`'s finding diff is line-insensitive and relative to each analysed root** (#585;
|
|
254
|
+
PR #685). `compareTrustResults` keyed a finding on `pass|ruleId|file|line` with an often
|
|
255
|
+
absolute `file`, so a blank line above a finding reported it as both fixed and new, and two
|
|
256
|
+
checkouts of one tree reported every absolute-path finding twice (11 new and 11 fixed for two
|
|
257
|
+
blank lines). The diff is now a multiset match on pass, rule and root-relative file. Roots come
|
|
258
|
+
from the new optional third argument `{ rootA, rootB }` or are inferred.
|
|
259
|
+
- **Findings on a stand-in instruction file name that file, not `SKILL.md`** (#670; PR #681). A
|
|
260
|
+
bundle with no `SKILL.md` is analysed with `README.md` or `INSTRUCTIONS.md` standing in, and its
|
|
261
|
+
findings said `artifact: 'SKILL.md'`. They now carry the real file and
|
|
262
|
+
`evidence.instruction_file_stand_in`. `SkillBundle` gains `skillMdFile`.
|
|
263
|
+
- **A call the open circuit breaker short-circuited is logged as `circuit_open`** (#675;
|
|
264
|
+
PR #680). The JSONL call log recorded `parse_error`, so an endpoint outage read as model parse
|
|
265
|
+
failures in `analyze-llm-log`. `LLMErrorCategory` gains `circuit_open`.
|
|
266
|
+
- **A verify chunk refused by a call cap no longer shrinks the adaptive window or chunk size**
|
|
267
|
+
(#675; PR #680). Its findings are still kept unverified (#564).
|
|
268
|
+
|
|
269
|
+
### Added
|
|
270
|
+
- Exports: `TrustCompareOptions`, `labelInstructionArtifact`, `blankOverlongLines`,
|
|
271
|
+
`hasAnalyzableLine`.
|
|
272
|
+
|
|
273
|
+
#### Consumer Impact
|
|
274
|
+
- Files with a 10k+ char line that used to be skipped now produce findings. `filesSkipped` drops
|
|
275
|
+
and finding counts can rise on repositories that have such files. `partial` does not make a
|
|
276
|
+
scan degraded and does not affect `--exit-code`.
|
|
277
|
+
- `dead-code`, `health` and `generate-spec` share the preflight and now analyse those files too,
|
|
278
|
+
with no partial marker.
|
|
279
|
+
- A consumer that filters skill findings on `artifact === 'SKILL.md'` no longer matches findings
|
|
280
|
+
from README-only bundles.
|
|
281
|
+
- `newFindings` / `fixedFindings` from `compareTrustResults` shrink to real changes.
|
|
282
|
+
- circle-pack's HTTP skill path builds its own pipeline and does not relabel the instruction
|
|
283
|
+
artifact until it calls `labelInstructionArtifact`.
|
|
284
|
+
|
|
285
|
+
### Benchmarks (local, circle-ir 4.14.2)
|
|
286
|
+
26 static suites run on 4.14.1 and 4.14.2 back to back, same source, same machine (2026-10-12).
|
|
287
|
+
24 score identically, among them csharp-oracle 76.6 % TPR / 0.8 % FPR and C# Juliet 100 % / 0 %.
|
|
288
|
+
Two move, both up and both from the engine:
|
|
289
|
+
- **CWE-Bench-Java static 63 → 64 of 120** (64 of 111 evaluable). CVE-2018-1047 (WildFly,
|
|
290
|
+
CWE-022) is now detected at `ServletResourceManager.getResource`; no CVE is lost.
|
|
291
|
+
- **Secrets recall 92.6 % → 100 %, F1 94.3 % → 98.2 %**, precision 96.2 % → 96.4 %. The two
|
|
292
|
+
pattern misses (one `github`, one `openai`) are found; working-tree secrets are circle-ir's
|
|
293
|
+
`ScanSecretsPass`. The one `stripe` false positive remains, so FPR is still 7.1 %.
|
|
294
|
+
|
|
119
295
|
## [4.26.0] - 2026-10-11
|
|
120
296
|
|
|
121
297
|
Minor release: `circle-ir` moves a minor, 4.13.2 → 4.14.1. No change to this package's API or
|
|
@@ -190,7 +190,7 @@ export declare function createMastraInstance(options?: MastraInstanceOptions): {
|
|
|
190
190
|
reasoning: string;
|
|
191
191
|
}, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "cross-file-taint", unknown>;
|
|
192
192
|
}, undefined, any, import("@mastra/core/agent").AgentEditorConfig | undefined>;
|
|
193
|
-
}, Record<string, import("@mastra/core/workflows").AnyWorkflow>, Record<string, import("@mastra/core/vector").MastraVector<any>>, Record<string, import("@mastra/core/tts").MastraTTS>, import("@mastra/core/logger").IMastraLogger, Record<string, import("@mastra/core/mcp").MCPServerBase<any>>, Record<string, import("@mastra/core/evals").MastraScorer<any, any, any, any>>, Record<string, import("@mastra/core/tools").ToolAction<any, any, any, any, any, any, unknown>>, Record<string, import("@mastra/core/processors").Processor<any, unknown>>, Record<string, import("@mastra/core/memory").MastraMemory>, Record<string, import("@mastra/core/channels").ChannelProvider>, Record<string, import("@mastra/core/classifier").Classifier<any
|
|
193
|
+
}, Record<string, import("@mastra/core/workflows").AnyWorkflow>, Record<string, import("@mastra/core/vector").MastraVector<any>>, Record<string, import("@mastra/core/tts").MastraTTS>, import("@mastra/core/logger").IMastraLogger, Record<string, import("@mastra/core/mcp").MCPServerBase<any>>, Record<string, import("@mastra/core/evals").MastraScorer<any, any, any, any>>, Record<string, import("@mastra/core/tools").ToolAction<any, any, any, any, any, any, unknown>>, Record<string, import("@mastra/core/processors").Processor<any, unknown>>, Record<string, import("@mastra/core/memory").MastraMemory>, Record<string, import("@mastra/core/channels").ChannelProvider>, Record<string, import("@mastra/core/classifier").Classifier<any>>, Record<string, import("@mastra/core/knowledge").Knowledge>>;
|
|
194
194
|
agents: {
|
|
195
195
|
enrichment: import("@mastra/core/agent").Agent<"enrichment-agent", {
|
|
196
196
|
classifyRole: import("@mastra/core/tools").Tool<{
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* This module provides convenience functions for running the complete
|
|
8
8
|
* analysis pipeline using the core analysis logic.
|
|
9
9
|
*/
|
|
10
|
-
import type { DFG, ProjectProfile } from 'circle-ir';
|
|
10
|
+
import type { DFG, ProjectProfile, TaintSanitizer } from 'circle-ir';
|
|
11
11
|
import type { TypeShape } from '../../security-scan/source-classifier.js';
|
|
12
12
|
import type { LLMConfig } from '../../llm/config.js';
|
|
13
13
|
import type { PackageCoordinate } from '../../cache/coordinate.js';
|
|
@@ -61,6 +61,12 @@ export interface WorkflowOutput {
|
|
|
61
61
|
pairsFailed?: number;
|
|
62
62
|
/** cognium-ai#564 — findings kept unverified because their chunk failed (`verification.verification_error`). */
|
|
63
63
|
findingsUnverified?: number;
|
|
64
|
+
/**
|
|
65
|
+
* cognium-ai#471 — findings kept with `verification.verdict: 'FALSE_POSITIVE'`
|
|
66
|
+
* (`refuted_by: 'omission'`): every pair behind them was submitted to the
|
|
67
|
+
* verifier and omitted from a successful answer. Optional; absent means 0.
|
|
68
|
+
*/
|
|
69
|
+
findingsRefutedByOmission?: number;
|
|
64
70
|
/** cognium-ai#564 — the failure reason(s), present only when a chunk failed. */
|
|
65
71
|
verificationError?: string;
|
|
66
72
|
totalTimeMs: number;
|
|
@@ -121,6 +127,29 @@ verifyAccounting?: {
|
|
|
121
127
|
unverifiedPairs?: UnverifiedPair[];
|
|
122
128
|
/** cognium-ai#564 — the failure reason, stamped as `verification_error`. */
|
|
123
129
|
verificationError?: string;
|
|
130
|
+
/**
|
|
131
|
+
* cognium-ai#471 — every (source, sink) pair submitted to the verifier.
|
|
132
|
+
* With it, a static finding whose pairs were all submitted, none failed,
|
|
133
|
+
* and none came back is labelled `FALSE_POSITIVE` (refuted by omission —
|
|
134
|
+
* the batch prompt's contract) instead of `UNVERIFIED`. Optional: legacy
|
|
135
|
+
* callers omit it and keep the pre-#471 `UNVERIFIED` label.
|
|
136
|
+
*/
|
|
137
|
+
submittedPairs?: UnverifiedPair[];
|
|
138
|
+
},
|
|
139
|
+
/**
|
|
140
|
+
* cognium-ai#460 — the rest of `generateFindings`' argument list. With
|
|
141
|
+
* only (sources, sinks, dfg, file) every precision gate is off: the
|
|
142
|
+
* non-executable-line gate (needs `sourceCode` + `language`), sanitizer
|
|
143
|
+
* credit, and the method-scoped proximity gate (`types`). The static
|
|
144
|
+
* scanner passes all of them; this makes the detector index — and so the
|
|
145
|
+
* #264 verify-parity gate — use the same gates. (`flows` is not forwarded:
|
|
146
|
+
* it would turn on cognium-dev#372 flow emission, an additive change.) Optional: legacy
|
|
147
|
+
* callers (tests) omit it and keep the pre-#460 behaviour, except that
|
|
148
|
+
* `types` / `sourceCode` (already parameters) are now forwarded too.
|
|
149
|
+
*/
|
|
150
|
+
taintContext?: {
|
|
151
|
+
language?: string;
|
|
152
|
+
sanitizers?: TaintSanitizer[];
|
|
124
153
|
}): WorkflowOutput;
|
|
125
154
|
/**
|
|
126
155
|
* Run security analysis on a single file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/agents/mastra/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAoC,GAAG,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/agents/mastra/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -135,6 +135,9 @@ coordinate) {
|
|
|
135
135
|
// DFG/proximity gating + dedup-merge with provenance collapse,
|
|
136
136
|
// replacing the previous N×M cross-product.
|
|
137
137
|
dfg: result.dfg,
|
|
138
|
+
// cognium-ai#460 — sanitizers for `generateFindings` parity with
|
|
139
|
+
// the static scanner (see `runReport`'s `taintContext`).
|
|
140
|
+
sanitizers: result.taint.sanitizers ?? [],
|
|
138
141
|
// cognium-ai#154 (L5c) — extract the CK + import/annotation fingerprint
|
|
139
142
|
// while the IR is live so the top-level cluster pass in
|
|
140
143
|
// `SecurityScanner.scan()` can consume it without re-invoking
|
|
@@ -562,6 +565,8 @@ contextProfile) {
|
|
|
562
565
|
pairsDroppedByBudget: 0,
|
|
563
566
|
pairsFailed: 0,
|
|
564
567
|
unverifiedPairs: [],
|
|
568
|
+
// cognium-ai#471 — nothing was sent to the verifier.
|
|
569
|
+
submittedPairs: [],
|
|
565
570
|
verificationError: undefined,
|
|
566
571
|
processingTimeMs: 0,
|
|
567
572
|
};
|
|
@@ -691,6 +696,13 @@ contextProfile) {
|
|
|
691
696
|
? (batchResult.unverified_pairs ?? []).map((p) => ({ sourceLine: p.source_line, sinkLine: p.sink_line }))
|
|
692
697
|
: [],
|
|
693
698
|
verificationError: batchResult.status === 'error' ? (batchResult.error ?? 'verification_chunk_failed') : undefined,
|
|
699
|
+
// cognium-ai#471 — the (source, sink) pairs this file put to the
|
|
700
|
+
// verifier. The batch prompt has the model OMIT every non-TRUE_POSITIVE
|
|
701
|
+
// pair, so a submitted pair that comes back absent from a successful
|
|
702
|
+
// chunk is the verifier's refutation. runReport needs the set to tell
|
|
703
|
+
// "refuted by omission" from "never asked" (sampling) when it labels the
|
|
704
|
+
// static findings it keeps.
|
|
705
|
+
submittedPairs: limitedSources.flatMap((s) => limitedSinks.map((k) => ({ sourceLine: s.line, sinkLine: k.line }))),
|
|
694
706
|
processingTimeMs: batchResult.processingTimeMs,
|
|
695
707
|
};
|
|
696
708
|
}
|
|
@@ -714,6 +726,7 @@ contextProfile) {
|
|
|
714
726
|
pairsDroppedByBudget: 0,
|
|
715
727
|
pairsFailed: unverifiedPairs.length,
|
|
716
728
|
unverifiedPairs,
|
|
729
|
+
submittedPairs: [], // #471 — nothing was answered
|
|
717
730
|
verificationError: (error instanceof Error ? error.message : String(error)) || 'verification_failed',
|
|
718
731
|
processingTimeMs: Date.now() - startTime,
|
|
719
732
|
};
|
|
@@ -752,7 +765,19 @@ sourceCode,
|
|
|
752
765
|
* much the verifier was asked about. Optional: legacy callers (tests) omit
|
|
753
766
|
* it and the stats fall back to the returned counts.
|
|
754
767
|
*/
|
|
755
|
-
verifyAccounting
|
|
768
|
+
verifyAccounting,
|
|
769
|
+
/**
|
|
770
|
+
* cognium-ai#460 — the rest of `generateFindings`' argument list. With
|
|
771
|
+
* only (sources, sinks, dfg, file) every precision gate is off: the
|
|
772
|
+
* non-executable-line gate (needs `sourceCode` + `language`), sanitizer
|
|
773
|
+
* credit, and the method-scoped proximity gate (`types`). The static
|
|
774
|
+
* scanner passes all of them; this makes the detector index — and so the
|
|
775
|
+
* #264 verify-parity gate — use the same gates. (`flows` is not forwarded:
|
|
776
|
+
* it would turn on cognium-dev#372 flow emission, an additive change.) Optional: legacy
|
|
777
|
+
* callers (tests) omit it and keep the pre-#460 behaviour, except that
|
|
778
|
+
* `types` / `sourceCode` (already parameters) are now forwarded too.
|
|
779
|
+
*/
|
|
780
|
+
taintContext) {
|
|
756
781
|
const vulnerabilities = [];
|
|
757
782
|
// cognium-ai#264 Defect 1 — detector candidate index for verify-path parity.
|
|
758
783
|
// `generateFindings()` applies `canSourceReachSink` (type coverage) + the
|
|
@@ -766,7 +791,7 @@ verifyAccounting) {
|
|
|
766
791
|
// gap #258 left open (path_traversal's ceiling is `high`, so the rule cap alone
|
|
767
792
|
// still reads C+H). Computed once and shared with the no-verification branch.
|
|
768
793
|
const effectiveDfg = dfg ?? { defs: [], uses: [], chains: [] };
|
|
769
|
-
const detectorFindings = generateFindings(mergedSources, mergedSinks, effectiveDfg, filePath);
|
|
794
|
+
const detectorFindings = generateFindings(mergedSources, mergedSinks, effectiveDfg, filePath, sourceCode, taintContext?.language, taintContext?.sanitizers ?? [], (types ?? []));
|
|
770
795
|
const detectorSevRank = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
771
796
|
const detectorSinkSeverity = new Map();
|
|
772
797
|
for (const df of detectorFindings) {
|
|
@@ -1033,6 +1058,32 @@ verifyAccounting) {
|
|
|
1033
1058
|
const srcs = finding.evidence?.sources ?? [{ line: finding.source.line }];
|
|
1034
1059
|
return srcs.some((s) => failedPairKeys.has(`${s.line}:${finding.sink.line}`));
|
|
1035
1060
|
};
|
|
1061
|
+
// cognium-ai#471 — a static finding the verifier was asked about and did not
|
|
1062
|
+
// confirm. The batch prompt has the model OMIT every non-TRUE_POSITIVE pair
|
|
1063
|
+
// ("an omitted pair is counted as assessed-and-safe"), so when a file's
|
|
1064
|
+
// verifier answer contains no TRUE_POSITIVE this branch used to keep every
|
|
1065
|
+
// static finding as `UNVERIFIED` — the verifier's refutation was relabelled
|
|
1066
|
+
// "no verdict", which circle-pack maps to `llm_result.verdict: UNCERTAIN`.
|
|
1067
|
+
// That is how 81,842 skill-scan findings produced 0 FALSE_POSITIVE.
|
|
1068
|
+
//
|
|
1069
|
+
// A finding is refuted by omission only when the answer is unambiguous:
|
|
1070
|
+
// - the caller passed the submitted-pair set (legacy callers: no change);
|
|
1071
|
+
// - no pair was dropped by our fan-out budget, and none of the pairs the
|
|
1072
|
+
// model returned failed to map (a hallucinated line could be a TP we
|
|
1073
|
+
// cannot place);
|
|
1074
|
+
// - every contributing (source, sink) pair was submitted (a pair the
|
|
1075
|
+
// sampler never sent was never asked about);
|
|
1076
|
+
// - no contributing pair sits in a failed chunk (#564 keeps those open).
|
|
1077
|
+
const submittedPairKeys = new Set((verifyAccounting?.submittedPairs ?? []).map((p) => `${p.sourceLine}:${p.sinkLine}`));
|
|
1078
|
+
const omissionIsRefutation = submittedPairKeys.size > 0 &&
|
|
1079
|
+
(verifyAccounting?.pairsDroppedByBudget ?? 0) === 0 &&
|
|
1080
|
+
(verifyAccounting?.pairsUnmapped ?? 0) === 0;
|
|
1081
|
+
const refutedByOmission = (finding) => {
|
|
1082
|
+
if (!omissionIsRefutation || hitsFailedPair(finding))
|
|
1083
|
+
return false;
|
|
1084
|
+
const srcs = finding.evidence?.sources ?? [{ line: finding.source.line }];
|
|
1085
|
+
return srcs.length > 0 && srcs.every((s) => submittedPairKeys.has(`${s.line}:${finding.sink.line}`));
|
|
1086
|
+
};
|
|
1036
1087
|
const verifiedBranch = verificationEnabled && verificationResults.length > 0;
|
|
1037
1088
|
const verifiedLocs = new Set(vulnerabilities.map((v) => `${v.sink?.line}:${v.type}`));
|
|
1038
1089
|
if (!verifiedBranch || failedPairKeys.size > 0) {
|
|
@@ -1151,7 +1202,25 @@ verifyAccounting) {
|
|
|
1151
1202
|
mode: 'attempted',
|
|
1152
1203
|
verification_error: verificationError ?? 'verification_chunk_failed',
|
|
1153
1204
|
}
|
|
1154
|
-
:
|
|
1205
|
+
: refutedByOmission(finding)
|
|
1206
|
+
? {
|
|
1207
|
+
// cognium-ai#471 — the verifier answered, and its answer
|
|
1208
|
+
// (per the batch prompt's omit-non-TP contract) is "not a
|
|
1209
|
+
// true positive". Kept, not dropped: dropping would change
|
|
1210
|
+
// recall on every LLM scan, which is a product decision;
|
|
1211
|
+
// labelling it lets consumers see the refutation.
|
|
1212
|
+
verdict: 'FALSE_POSITIVE',
|
|
1213
|
+
attempted: true,
|
|
1214
|
+
llm_verified: false,
|
|
1215
|
+
mode: 'llm',
|
|
1216
|
+
refuted_by: 'omission',
|
|
1217
|
+
reasoning_source: 'template',
|
|
1218
|
+
reasoning: 'Submitted to the LLM verifier, which did not confirm it: the batch verifier ' +
|
|
1219
|
+
'omits every pair it does not judge a TRUE_POSITIVE (cognium-ai#471).',
|
|
1220
|
+
confidence: 0,
|
|
1221
|
+
exploitability: 'none',
|
|
1222
|
+
}
|
|
1223
|
+
: { verdict: 'UNVERIFIED', attempted: true, llm_verified: false, mode: 'attempted' }, // #331 three-state
|
|
1155
1224
|
// #26: surface the provenance label so downstream consumers
|
|
1156
1225
|
// (cognium-ai CLI output, SARIF emitter) can distinguish
|
|
1157
1226
|
// static-only / LLM-only / mixed findings without re-deriving.
|
|
@@ -1177,6 +1246,9 @@ verifyAccounting) {
|
|
|
1177
1246
|
// cognium-ai#564 — failed-chunk pairs, and the findings kept for them.
|
|
1178
1247
|
pairsFailed: verifyAccounting?.pairsFailed ?? 0,
|
|
1179
1248
|
findingsUnverified: vulnerabilities.filter((v) => v.verification?.verification_error !== undefined).length,
|
|
1249
|
+
// cognium-ai#471 — findings kept with `verdict: 'FALSE_POSITIVE'`
|
|
1250
|
+
// because the verifier omitted (refuted) every pair behind them.
|
|
1251
|
+
findingsRefutedByOmission: vulnerabilities.filter((v) => v.verification?.refuted_by === 'omission').length,
|
|
1180
1252
|
...(verificationError !== undefined ? { verificationError } : {}),
|
|
1181
1253
|
totalTimeMs: patternMatchTimeMs + enrichTimeMs + verifyTimeMs,
|
|
1182
1254
|
},
|
|
@@ -1278,7 +1350,10 @@ export async function analyzeFile(filePath, sourceCode, options) {
|
|
|
1278
1350
|
pairsFailed: verifyResult.pairsFailed,
|
|
1279
1351
|
unverifiedPairs: verifyResult.unverifiedPairs,
|
|
1280
1352
|
verificationError: verifyResult.verificationError,
|
|
1281
|
-
|
|
1353
|
+
submittedPairs: verifyResult.submittedPairs, // cognium-ai#471
|
|
1354
|
+
},
|
|
1355
|
+
// cognium-ai#460 — full generateFindings argument list.
|
|
1356
|
+
{ language, sanitizers: patternResult.sanitizers });
|
|
1282
1357
|
// cognium-ai#154 (L5c) — surface the cluster vector extracted while the
|
|
1283
1358
|
// IR was live in `runPatternMatch`. Keeps `runReport`'s signature
|
|
1284
1359
|
// untouched (its job is source×sink → vuln synthesis; the vector is
|
|
@@ -1331,7 +1406,10 @@ export async function* analyzeFileStream(filePath, sourceCode, options) {
|
|
|
1331
1406
|
pairsFailed: verifyResult.pairsFailed,
|
|
1332
1407
|
unverifiedPairs: verifyResult.unverifiedPairs,
|
|
1333
1408
|
verificationError: verifyResult.verificationError,
|
|
1334
|
-
|
|
1409
|
+
submittedPairs: verifyResult.submittedPairs, // cognium-ai#471
|
|
1410
|
+
},
|
|
1411
|
+
// cognium-ai#460 — full generateFindings argument list.
|
|
1412
|
+
{ language, sanitizers: patternResult.sanitizers });
|
|
1335
1413
|
yield { step: 'report', status: 'completed', data: reportResult };
|
|
1336
1414
|
}
|
|
1337
1415
|
// Legacy export for compatibility
|