circle-ir-ai 4.28.0 → 4.30.0-rc.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/agents/mastra/workflow.d.ts +10 -2
  3. package/dist/agents/mastra/workflow.d.ts.map +1 -1
  4. package/dist/agents/mastra/workflow.js +92 -39
  5. package/dist/agents/mastra/workflow.js.map +1 -1
  6. package/dist/cache/discovery-cache.d.ts +17 -0
  7. package/dist/cache/discovery-cache.d.ts.map +1 -1
  8. package/dist/cache/discovery-cache.js +42 -7
  9. package/dist/cache/discovery-cache.js.map +1 -1
  10. package/dist/cache/tree-cache.d.ts +6 -0
  11. package/dist/cache/tree-cache.d.ts.map +1 -1
  12. package/dist/cache/tree-cache.js.map +1 -1
  13. package/dist/cache/verify-cache.js +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/llm/ax-client.d.ts +19 -1
  19. package/dist/llm/ax-client.d.ts.map +1 -1
  20. package/dist/llm/ax-client.js +130 -32
  21. package/dist/llm/ax-client.js.map +1 -1
  22. package/dist/llm/call-logger.d.ts +7 -1
  23. package/dist/llm/call-logger.d.ts.map +1 -1
  24. package/dist/llm/call-logger.js.map +1 -1
  25. package/dist/llm/config.d.ts +2 -0
  26. package/dist/llm/config.d.ts.map +1 -1
  27. package/dist/llm/config.js +16 -8
  28. package/dist/llm/config.js.map +1 -1
  29. package/dist/llm/discovery.d.ts.map +1 -1
  30. package/dist/llm/discovery.js +2 -1
  31. package/dist/llm/discovery.js.map +1 -1
  32. package/dist/llm/llm-error-counter.d.ts.map +1 -1
  33. package/dist/llm/llm-error-counter.js +1 -0
  34. package/dist/llm/llm-error-counter.js.map +1 -1
  35. package/dist/llm/pre-filter.d.ts +9 -12
  36. package/dist/llm/pre-filter.d.ts.map +1 -1
  37. package/dist/llm/pre-filter.js +23 -20
  38. package/dist/llm/pre-filter.js.map +1 -1
  39. package/dist/llm/prompt-template.d.ts +10 -0
  40. package/dist/llm/prompt-template.d.ts.map +1 -0
  41. package/dist/llm/prompt-template.js +12 -0
  42. package/dist/llm/prompt-template.js.map +1 -0
  43. package/dist/llm/prompts/index.d.ts +2 -1
  44. package/dist/llm/prompts/index.d.ts.map +1 -1
  45. package/dist/llm/prompts/index.js +23 -3
  46. package/dist/llm/prompts/index.js.map +1 -1
  47. package/dist/llm/prompts/languages.d.ts +2 -0
  48. package/dist/llm/prompts/languages.d.ts.map +1 -1
  49. package/dist/llm/prompts/languages.js +26 -0
  50. package/dist/llm/prompts/languages.js.map +1 -1
  51. package/dist/llm/prompts/rust.d.ts +1 -0
  52. package/dist/llm/prompts/rust.d.ts.map +1 -1
  53. package/dist/llm/prompts/rust.js +37 -0
  54. package/dist/llm/prompts/rust.js.map +1 -1
  55. package/dist/llm/schemas.d.ts +82 -0
  56. package/dist/llm/schemas.d.ts.map +1 -1
  57. package/dist/llm/schemas.js +8 -0
  58. package/dist/llm/schemas.js.map +1 -1
  59. package/dist/llm/token-meter.d.ts +9 -1
  60. package/dist/llm/token-meter.d.ts.map +1 -1
  61. package/dist/llm/token-meter.js +14 -2
  62. package/dist/llm/token-meter.js.map +1 -1
  63. package/dist/llm/verification.d.ts +4 -1
  64. package/dist/llm/verification.d.ts.map +1 -1
  65. package/dist/llm/verification.js +110 -51
  66. package/dist/llm/verification.js.map +1 -1
  67. package/dist/secret-scan/history-patterns.d.ts.map +1 -1
  68. package/dist/secret-scan/history-patterns.js +47 -0
  69. package/dist/secret-scan/history-patterns.js.map +1 -1
  70. package/dist/secret-scan/scanner.d.ts.map +1 -1
  71. package/dist/secret-scan/scanner.js +11 -0
  72. package/dist/secret-scan/scanner.js.map +1 -1
  73. package/dist/security-scan/index.d.ts +1 -0
  74. package/dist/security-scan/index.d.ts.map +1 -1
  75. package/dist/security-scan/index.js +1 -0
  76. package/dist/security-scan/index.js.map +1 -1
  77. package/dist/security-scan/scanner.d.ts +23 -0
  78. package/dist/security-scan/scanner.d.ts.map +1 -1
  79. package/dist/security-scan/scanner.js +72 -12
  80. package/dist/security-scan/scanner.js.map +1 -1
  81. package/dist/security-scan/sink-filters.d.ts +2 -0
  82. package/dist/security-scan/sink-filters.d.ts.map +1 -1
  83. package/dist/security-scan/sink-filters.js +44 -0
  84. package/dist/security-scan/sink-filters.js.map +1 -1
  85. package/dist/security-scan/test-tree-excludes.d.ts +10 -0
  86. package/dist/security-scan/test-tree-excludes.d.ts.map +1 -0
  87. package/dist/security-scan/test-tree-excludes.js +38 -0
  88. package/dist/security-scan/test-tree-excludes.js.map +1 -0
  89. package/dist/skills/code-threat-patterns.d.ts +65 -0
  90. package/dist/skills/code-threat-patterns.d.ts.map +1 -1
  91. package/dist/skills/code-threat-patterns.js +729 -41
  92. package/dist/skills/code-threat-patterns.js.map +1 -1
  93. package/dist/utils/analyze-pool.d.ts.map +1 -1
  94. package/dist/utils/analyze-pool.js +6 -1
  95. package/dist/utils/analyze-pool.js.map +1 -1
  96. package/package.json +6 -6
  97. package/dist/coverage/index.d.ts +0 -50
  98. package/dist/coverage/index.d.ts.map +0 -1
  99. package/dist/coverage/index.js +0 -73
  100. package/dist/coverage/index.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.30.0-rc.0] - 2026-10-29
11
+
12
+ Release candidate, published under the npm `next` tag for the cost gates before a final 4.30.0. It contains everything below, merged since 4.29.0 (4.29.1 and 4.29.2 were bumped in the repo but never published).
13
+
14
+ ### Changed
15
+ - **The LLM-path dependencies are pinned to exact versions (cognium-ai#810):** `@ax-llm/ax` 25.4.5, `@mastra/core` 1.79.0, `zod` 4.6.7, the versions in the lockfile CI tests against. A range let the same release install a different LLM client depending on the install date.
16
+
17
+ ### Fixed
18
+ - **Nested test-tree excludes (cognium-ai#801)** — restore `**/tests/**` and exclude
19
+ nested `.../test/...` outside `src/main/` while keeping #615 Java `test` packages
20
+ scannable. Shared `shouldExcludeTestTreePath` for `SecurityScanner` and cognium-ai
21
+ `init` / `collectFiles`.
22
+
23
+ ### Changed
24
+ - **Combined enrichment discovery (cognium-ai#780 S3)** — `runEnrich` issues one
25
+ `discoverSourcesAndSinksForLanguage` LLM call per file (was two parallel calls).
26
+ Prompts use a cache-friendly layout: static instructions and JSON schema before
27
+ `[CODE START]`, per-file context after. Language-discoverer cache sentinel
28
+ `lang-disc-v4`.
29
+
30
+ ### Fixed
31
+ - **Truncation retry (cognium-ai#776 S2)** — `chatJSON` retries once with a larger output cap
32
+ when `finish_reason=length` leaves empty `content` (up to `REASONING_MAX_TOKENS`), reads
33
+ `reasoning_content` when present, and no longer counts truncation toward the project-dead
34
+ breaker.
35
+ - **DeepSeek V4 reasoning budget (cognium-ai#776)** — `isThinkingModel()` now recognizes
36
+ `deepseek-v4*` ids (e.g. Fireworks `deepseek-v4p1-flash`) so they get the 16k completion
37
+ budget and 360s phase timeout instead of the 8k default.
38
+ - **Concurrent `chatJSON` telemetry (cognium-ai#778)** — batch verification and parallel
39
+ enrichment discovery each use a dedicated `AxLLMClient` (`fork()` / fresh instance) so
40
+ per-call `lastUsage` and `lastErrorCategory` are not clobbered across concurrent requests.
41
+ - **Verifier omission on thinking models (cognium-ai#777)** — when the verification-phase model
42
+ is a thinking/reasoning model (`isThinkingModel`), a successful batch answer with an empty
43
+ `pairs` array no longer labels static findings `refuted_by: 'omission'`; they stay
44
+ `UNVERIFIED` (`mode: attempted`) so default `{"pairs":[]}` responses do not mass-refute.
45
+ - **Thinking verifier explicit verdicts (cognium-ai#777 S3)** — batch verification on
46
+ thinking/reasoning models (`isThinkingModel`) uses a prompt contract that requires an
47
+ explicit TRUE_POSITIVE / FALSE_POSITIVE / UNCERTAIN entry for every submitted pair so
48
+ omission is not mistaken for refutation; verify-cache `PROMPT_VER` bumped to `verify-v5`.
49
+ - **High verifier omission telemetry (cognium-ai#777 S2)** — `ScanMeta.verification` now
50
+ includes `verifierModel` and sets `omissionRateElevated` (with a stderr `VERIFY_OMISSION`
51
+ warning) when omissionRate ≥ 99% on ≥ 20 submitted pairs.
52
+ - **Prompt template substitution (cognium-ai#779)** — `fillPromptTemplate()` substitutes
53
+ `{name}` placeholders in one pass without interpreting `$&`, `$'`, `` $` `` or `$$` in scanned
54
+ code. Batch/single verification, discovery `getPrompt()`, and ax-client enrichment prompts
55
+ now use it instead of `String.replace` with string replacements.
56
+
10
57
  ### Added
11
58
  - **The ten MCP tools declare annotations** (4.18.0), each measured before it was written down.
12
59
  All ten are `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true`: none writes,
@@ -116,6 +163,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
116
163
  Unchanged on 4.9.29: OWASP 1415/1415, Juliet 243/243, SecuriBench 97.2%/6.7%, Go 89.5%,
117
164
  NodeJS 92.9%, CWE-Bench-Rust 100%, bash and HTML 100%.
118
165
 
166
+ ## [4.29.0] - 2026-10-17
167
+
168
+ Minor release: `circle-ir` 4.15.0 → 4.16.1, LLM enrichment sinks anchored on the `scan --llm`
169
+ merge path, and corrected SecuriBench and C# oracle scoring. 4.16.0 was not pinned: it carried
170
+ four regressions, fixed upstream in 4.16.1 before this bump.
171
+
172
+ ### Changed
173
+ - **`circle-ir` 4.15.0 → 4.16.1** (exact pin). 4.16.0 adds the navigation API (callers per
174
+ overload and through dispatch), `navigationTypes` extraction, `AnalyzerOptions.resources`, and
175
+ many recall and precision fixes across Java, C#, JS/TS, Go, Python and Rust. 4.16.1 fixes four
176
+ regressions found while benchmarking this bump. Java `analyze()` is back to 4.15.0 speed on
177
+ large classes (one NiFi file took 638 s on 4.16.0 and 0.83 s on 4.16.1; cognium-dev#806). A
178
+ `Character.is*` predicate on tainted text is no longer an `external_taint_escape`, which clears
179
+ SecuriBench Sanitizers2/6 (cognium-dev#807). Two C# false positives are fixed: a field
180
+ sanitized in another method (cognium-dev#808) and an `Array.IndexOf` allowlist guard
181
+ (cognium-dev#809). The underlying Java name-collision cause of #807 is cognium-dev#814.
182
+
183
+ Static suites, compared with 4.15.0 (packed 4.16.1, 27 runners):
184
+ - SecuriBench 97.2% / 6.7% → 97.7% / 6.7%;
185
+ - C# oracle 76.6% / 0.8% (3/367) → 77.1% / 0.3% (1/367);
186
+ - CWE-Bench-Java static 64 → 65 of 120;
187
+ - Python synthetic 72.2% → 74.0%;
188
+ - Vulnerability-goapp FPR 14.3% → 0.0%;
189
+ - the other 21 identical.
190
+
191
+ Figures before the scoring changes below.
192
+
193
+ ### Fixed
194
+ - **LLM enrichment sinks are anchored in `runMerge`** (#330). The `scan --llm` merge path now
195
+ applies `resolveDiscoveryAnchor`, so an enrichment sink lands on a call node or carries
196
+ `llm-unanchored`, as in discovery mode. `runPatternMatch` forwards the IR calls for anchoring,
197
+ and LLM sinks merge on their post-anchor key.
198
+ - **SecurityScanner test-tree excludes (cognium-ai#615)**. The engine walker no longer uses
199
+ unanchored `**/test/**` / `**/tests/**`, which also matched a Java package segment named
200
+ `test` under main source. It now mirrors the `init` template: `test/**`, `tests/**`,
201
+ `**/src/test/**`, `**/__tests__/**`.
202
+
203
+ ### Benchmarks
204
+ - **SecuriBench Micro labels.** The runner read only the `@servlet vuln_count` annotation. Where
205
+ that annotation contradicts both `getVulnerabilityCount()` and the `/* BAD */` markers, the
206
+ method and markers now win, and every disagreement is printed (`benchmarks/lib/securibench-labels.ts`).
207
+ - Overrides: `pred/Pred2.java` 0 → 1 (the suite's one FP was a true positive),
208
+ `collections/Collections10.java` 0 → 1, `inter/Inter10.java` 2 → 1.
209
+ - Six other disagreements keep the annotation, because the markers side with it.
210
+ - On circle-ir 4.16.1: **TPR 97.3%, FPR 0.0%** (107/110, 0/13). With the old labels it was
211
+ 97.7% / 6.7% (105/108, 1/15).
212
+ - **C# oracle clean controls are scored against their category's taint sinks**
213
+ (`cleanScope()`, `benchmarks/lib/csharp-oracle-scoring.ts`).
214
+ - A placeholder `category: "clean"` case resolves through its paired `*Tp.cs`.
215
+ - 33 controls for a non-taint signal (for example `IncorrectDefaultPermsSafe.cs`, the
216
+ CWE-276 twin) leave the denominator.
217
+ - 95 general-FP or unmapped cases keep the any-flow rule.
218
+ - On circle-ir 4.16.1: **TPR 77.1% (340/441), FPR 0.0% (0/334)**.
219
+
119
220
  ## [4.28.0] - 2026-10-15
120
221
 
121
222
  Minor release: `circle-ir` 4.14.2 → 4.15.0, discovery prompts carry line numbers, verifier
@@ -9,9 +9,10 @@
9
9
  */
10
10
  import type { DFG, ProjectProfile, TaintSanitizer } from 'circle-ir';
11
11
  import type { TypeShape } from '../../security-scan/source-classifier.js';
12
- import type { LLMConfig } from '../../llm/config.js';
12
+ import { type LLMConfig } from '../../llm/config.js';
13
13
  import type { PackageCoordinate } from '../../cache/coordinate.js';
14
14
  import { type ClusterVector } from '../../cluster/cluster.js';
15
+ import { type AnchorCallNode } from '../../llm/anchoring.js';
15
16
  export { patternMatchStep, enrichStep, mergeStep, verifyStep, reportStep, } from './steps.js';
16
17
  export type VerificationStrategy = 'complete' | 'sampled' | 'prioritized';
17
18
  export interface WorkflowInput {
@@ -97,7 +98,7 @@ export interface WorkflowOutput {
97
98
  }
98
99
  /** cognium-ai#591 — why `runEnrich` made no discovery call for a file. */
99
100
  export type EnrichmentSkipReason = 'prefilter' | 'minified';
100
- export declare function runMerge(patternSources: any[], patternSinks: any[], enrichmentResult: any | null, confidenceThreshold: number, sourceCode?: string, filePath?: string, types?: ReadonlyArray<TypeShape>, language?: string): {
101
+ export declare function runMerge(patternSources: any[], patternSinks: any[], enrichmentResult: any | null, confidenceThreshold: number, sourceCode?: string, filePath?: string, types?: ReadonlyArray<TypeShape>, language?: string, irCalls?: ReadonlyArray<AnchorCallNode>): {
101
102
  mergedSources: any[];
102
103
  mergedSinks: any[];
103
104
  };
@@ -135,6 +136,13 @@ verifyAccounting?: {
135
136
  * callers omit it and keep the pre-#471 `UNVERIFIED` label.
136
137
  */
137
138
  submittedPairs?: UnverifiedPair[];
139
+ /**
140
+ * cognium-ai#777 — when false, a successful verifier answer that omits every
141
+ * pair stays `UNVERIFIED` instead of `refuted_by: 'omission'`. Set false for
142
+ * thinking/reasoning verifier models that often return `pairs: []` without
143
+ * assessing the batch.
144
+ */
145
+ treatOmissionAsRefutation?: boolean;
138
146
  },
139
147
  /**
140
148
  * cognium-ai#460 — the rest of `generateFindings`' argument list. With
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/agents/mastra/workflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/agents/mastra/workflow.ts"],"names":[],"mappings":"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"}
@@ -8,9 +8,10 @@
8
8
  * analysis pipeline using the core analysis logic.
9
9
  */
10
10
  import { generateFindings, getRuleInfo } from 'circle-ir';
11
- import { isSuppressedSinkShape } from '../../security-scan/sink-filters.js';
11
+ import { isPhantomLineSink, isSuppressedSinkShape } from '../../security-scan/sink-filters.js';
12
12
  import { classifySource, applyClassificationGate, } from '../../security-scan/source-classifier.js';
13
13
  import { shouldGateInterproceduralParam, classifyEntryPointTier, } from '../../analysis/entry-point-detection.js';
14
+ import { getDefaultLLMConfig, isThinkingModel } from '../../llm/config.js';
14
15
  import { withLLMContext } from '../../llm/debug-context.js';
15
16
  import { fileNeedsEnrichment } from '../../llm/pre-filter.js';
16
17
  import { toSupportedLLMLanguage } from '../../llm/prompts/index.js';
@@ -20,6 +21,7 @@ import { resetLLMCallLimiter, isSwarmActive } from '../../llm/call-limiter.js';
20
21
  import { resetLLMPerFileBreaker, resetLLMBreakerForScan } from '../../llm/ax-client.js';
21
22
  import { getTreeCache, computeTreeCacheKey, computeRuleSetFingerprint, } from '../../cache/tree-cache.js';
22
23
  import { extractClusterVector } from '../../cluster/cluster.js';
24
+ import { resolveDiscoveryAnchor, } from '../../llm/anchoring.js';
23
25
  // Re-export the Mastra step definitions
24
26
  export { patternMatchStep, enrichStep, mergeStep, verifyStep, reportStep, } from './steps.js';
25
27
  // ============================================================================
@@ -145,6 +147,9 @@ coordinate) {
145
147
  // pre-2.33.0 cached entries will deserialize with `clusterVector`
146
148
  // undefined and be excluded from clustering on that scan.
147
149
  clusterVector: extractClusterVector(result),
150
+ // cognium-ai#330 — forward calls so enrichment sinks can be anchored in
151
+ // `runMerge` the same way discovery mode does in `DiscoveryEngine`.
152
+ calls: (result.calls ?? []),
148
153
  };
149
154
  // cognium-ai#153 — populate persistent tree cache. Disk-write errors
150
155
  // never fail the workflow. #158 Round 2 — coordinate is honored by
@@ -222,7 +227,7 @@ async function runEnrich(filePath, sourceCode, types, imports, patternSources, p
222
227
  skippedBy: 'minified',
223
228
  };
224
229
  }
225
- if (!fileNeedsEnrichment(sourceCode)) {
230
+ if (!fileNeedsEnrichment(sourceCode, language)) {
226
231
  // Synthetic JSONL entries so `analyze-llm-log.ts` can surface the
227
232
  // skip rate. Two entries (one per call we *would* have fired) keep
228
233
  // the per-signature aggregate numerator/denominator coherent.
@@ -246,7 +251,7 @@ async function runEnrich(filePath, sourceCode, types, imports, patternSources, p
246
251
  };
247
252
  }
248
253
  const { getAxLLMClient } = await import('../../llm/ax-client.js');
249
- const client = getAxLLMClient(llmConfig);
254
+ const discoveryClient = getAxLLMClient(llmConfig);
250
255
  const functionNames = types[0]?.methods?.map((m) => m.name).join(', ') || '';
251
256
  const functionName = functionNames.split(',')[0]?.trim() || 'main';
252
257
  // cognium-ai#591 — pass the real language through. Before this, every
@@ -265,23 +270,15 @@ async function runEnrich(filePath, sourceCode, types, imports, patternSources, p
265
270
  .map((s) => `${s.type}:${s.line}`)
266
271
  .sort()
267
272
  .join(', ');
268
- // 2 LLM calls in parallel (source + sink discovery). cognium-ai#139.
269
- const [rawSources, rawSinks] = await withLLMContext({ file: filePath, method: moduleName, phase: 'enrichment' }, () => Promise.all([
270
- // Source discovery
271
- client.discoverSourcesForLanguage(llmLanguage, {
272
- code: sourceCode,
273
- functionName,
274
- moduleRole: 'unknown', // Role intentionally not classified here — see runEnrich docstring
275
- existingSources: canonPatternPairs(patternSources),
276
- }),
277
- // Sink discovery
278
- client.discoverSinksForLanguage(llmLanguage, {
279
- code: sourceCode,
280
- functionName,
281
- methodCalls: '',
282
- existingSinks: canonPatternPairs(patternSinks),
283
- }),
284
- ]));
273
+ // Single combined discovery call (cognium-ai#780 S3); was 2 parallel calls (#139).
274
+ const { additionalSources: rawSources, additionalSinks: rawSinks } = await withLLMContext({ file: filePath, method: moduleName, phase: 'enrichment' }, () => discoveryClient.discoverSourcesAndSinksForLanguage(llmLanguage, {
275
+ code: sourceCode,
276
+ functionName,
277
+ moduleRole: 'unknown', // Role intentionally not classified here — see runEnrich docstring
278
+ methodCalls: '',
279
+ existingSources: canonPatternPairs(patternSources),
280
+ existingSinks: canonPatternPairs(patternSinks),
281
+ }));
285
282
  // #35: validate LLM-discovered line numbers against file bounds. Small/
286
283
  // weak models often echo the few-shot example line literally (e.g.
287
284
  // line=15 from the sink prompt template), producing CWE-89 sinks at
@@ -358,7 +355,17 @@ function findEnclosing(types, line) {
358
355
  }
359
356
  return {};
360
357
  }
361
- export function runMerge(patternSources, patternSinks, enrichmentResult, confidenceThreshold, sourceCode, filePath, types, language) {
358
+ function methodLineBounds(types, line) {
359
+ const { type, method } = findEnclosing(types, line);
360
+ if (method) {
361
+ return { start: method.start_line, end: method.end_line };
362
+ }
363
+ if (type) {
364
+ return { start: type.start_line, end: type.end_line };
365
+ }
366
+ return undefined;
367
+ }
368
+ export function runMerge(patternSources, patternSinks, enrichmentResult, confidenceThreshold, sourceCode, filePath, types, language, irCalls) {
362
369
  // #48: previously these maps were keyed by `line` alone, which
363
370
  // collapsed any two sources / sinks that landed on the same source
364
371
  // line — even when they had different types or different variables.
@@ -435,34 +442,61 @@ export function runMerge(patternSources, patternSinks, enrichmentResult, confide
435
442
  }
436
443
  if (enrichmentResult) {
437
444
  for (const llmSink of enrichmentResult.additionalSinks) {
438
- const key = sinkKey(llmSink);
439
- const existing = sinksByKey.get(key);
445
+ const llmSinkCode = sliceLine(sourceCode, llmSink.line);
446
+ // cognium-ai#808 — run phantom/sink-shape guards on the LLM's
447
+ // original line before re-anchoring. Relocating onto a real call
448
+ // must not bypass suppression of comment/Javadoc/blank LLM lines.
449
+ const originalLineTrimmed = (llmSinkCode ?? '').trim();
450
+ if (originalLineTrimmed) {
451
+ if (isPhantomLineSink(llmSinkCode)
452
+ || isSuppressedSinkShape(llmSinkCode, llmSink.type, filePath, null, llmSink.line, sourceCode)) {
453
+ continue;
454
+ }
455
+ }
456
+ let sinkLine = llmSink.line;
457
+ let anchor;
458
+ const bounds = methodLineBounds(types, llmSink.line);
459
+ if (irCalls && bounds) {
460
+ const snippet = llmSinkCode && llmSinkCode.trim().length > 0
461
+ ? llmSinkCode
462
+ : llmSink.method
463
+ ? `${llmSink.method}(`
464
+ : '';
465
+ const resolved = resolveDiscoveryAnchor(llmSink.line, patternSinks, irCalls, bounds.start, bounds.end, llmSink.type, snippet);
466
+ sinkLine = resolved.line;
467
+ if (resolved.anchor !== 'ir-sink') {
468
+ anchor = resolved.anchor;
469
+ }
470
+ }
471
+ // Product enrichment path: unanchored LLM sinks are not emitted (#808).
472
+ if (anchor === 'llm-unanchored') {
473
+ continue;
474
+ }
475
+ const anchoredKey = sinkKey({ line: sinkLine, type: llmSink.type });
476
+ const originalKey = sinkKey({ line: llmSink.line, type: llmSink.type });
477
+ const existingAtAnchored = sinksByKey.get(anchoredKey);
478
+ const existingAtOriginal = sinksByKey.get(originalKey);
479
+ const existing = existingAtAnchored ?? existingAtOriginal;
480
+ const mergeKey = existingAtAnchored ? anchoredKey : (existingAtOriginal ? originalKey : anchoredKey);
440
481
  if (existing) {
441
- sinksByKey.set(key, {
482
+ sinksByKey.set(mergeKey, {
442
483
  ...existing,
443
484
  confidence: Math.min(1.0, (existing.confidence || 0.8) + 0.2),
444
485
  });
445
486
  }
446
487
  else if (llmSink.confidence >= confidenceThreshold) {
447
- // #90/#91/#92/#93/#94: previously LLM-discovered sinks omitted
448
- // the `code` field entirely. Downstream `isSuppressedSinkShape`
449
- // safely returns false on undefined → phantom-line findings
450
- // leak through to the report. Look up the source line so the
451
- // existing sink-shape filters (and the new phantom-line guard)
452
- // can do their job.
453
- const llmSinkCode = sliceLine(sourceCode, llmSink.line);
454
- sinksByKey.set(key, {
455
- line: llmSink.line,
488
+ sinksByKey.set(anchoredKey, {
489
+ line: sinkLine,
456
490
  type: llmSink.type,
457
491
  cwe: llmSink.cwe,
458
- location: `LLM-discovered at line ${llmSink.line}`,
492
+ location: `LLM-discovered at line ${sinkLine}`,
459
493
  confidence: llmSink.confidence,
460
494
  method: llmSink.method,
461
495
  argPositions: llmSink.argPositions,
462
- code: llmSinkCode,
496
+ code: sliceLine(sourceCode, sinkLine) ?? llmSinkCode,
463
497
  file: filePath,
464
- // #26: pure-LLM-discovered sink — see source-side comment above.
465
498
  discoveryMethod: 'llm',
499
+ ...(anchor ? { anchor } : {}),
466
500
  });
467
501
  }
468
502
  }
@@ -552,6 +586,13 @@ language,
552
586
  // rubric on library/tool repos and cut the CWE-22 hallucination
553
587
  // bucket documented in cognium-dev#245 RC3.
554
588
  contextProfile) {
589
+ const verifierModel = {
590
+ ...getDefaultLLMConfig(),
591
+ ...llmConfig,
592
+ }.phases.verification.model;
593
+ // cognium-ai#777 — reasoning models often return `{"pairs":[]}` without assessing
594
+ // pairs; treating omission as refutation over-labels real findings FALSE_POSITIVE.
595
+ const treatOmissionAsRefutation = !isThinkingModel(verifierModel);
555
596
  if (!enableVerification || mergedSources.length === 0 || mergedSinks.length === 0) {
556
597
  return {
557
598
  verificationResults: [],
@@ -568,6 +609,7 @@ contextProfile) {
568
609
  // cognium-ai#471 — nothing was sent to the verifier.
569
610
  submittedPairs: [],
570
611
  verificationError: undefined,
612
+ treatOmissionAsRefutation,
571
613
  processingTimeMs: 0,
572
614
  };
573
615
  }
@@ -703,6 +745,7 @@ contextProfile) {
703
745
  // "refuted by omission" from "never asked" (sampling) when it labels the
704
746
  // static findings it keeps.
705
747
  submittedPairs: limitedSources.flatMap((s) => limitedSinks.map((k) => ({ sourceLine: s.line, sinkLine: k.line }))),
748
+ treatOmissionAsRefutation,
706
749
  processingTimeMs: batchResult.processingTimeMs,
707
750
  };
708
751
  }
@@ -728,6 +771,7 @@ contextProfile) {
728
771
  unverifiedPairs,
729
772
  submittedPairs: [], // #471 — nothing was answered
730
773
  verificationError: (error instanceof Error ? error.message : String(error)) || 'verification_failed',
774
+ treatOmissionAsRefutation,
731
775
  processingTimeMs: Date.now() - startTime,
732
776
  };
733
777
  }
@@ -1075,7 +1119,8 @@ taintContext) {
1075
1119
  // sampler never sent was never asked about);
1076
1120
  // - no contributing pair sits in a failed chunk (#564 keeps those open).
1077
1121
  const submittedPairKeys = new Set((verifyAccounting?.submittedPairs ?? []).map((p) => `${p.sourceLine}:${p.sinkLine}`));
1078
- const omissionIsRefutation = submittedPairKeys.size > 0 &&
1122
+ const omissionIsRefutation = (verifyAccounting?.treatOmissionAsRefutation ?? true) &&
1123
+ submittedPairKeys.size > 0 &&
1079
1124
  (verifyAccounting?.pairsDroppedByBudget ?? 0) === 0 &&
1080
1125
  (verifyAccounting?.pairsUnmapped ?? 0) === 0;
1081
1126
  const refutedByOmission = (finding) => {
@@ -1327,7 +1372,7 @@ export async function analyzeFile(filePath, sourceCode, options) {
1327
1372
  enrichResult = await runEnrich(filePath, sourceCode, patternResult.types, patternResult.imports, patternResult.patternSources, patternResult.patternSinks, enableEnrichment, language, llmConfig);
1328
1373
  }
1329
1374
  const mergeResult = runMerge(patternResult.patternSources, patternResult.patternSinks, enrichResult.enrichmentResult, confidenceThreshold, sourceCode, filePath, patternResult.types, // cognium-ai#128 gate
1330
- language);
1375
+ language, patternResult.calls);
1331
1376
  // L2 — same pattern as enrich: wrap when the operator has opted in
1332
1377
  // via CP_VERIFY_TIMEOUT_MS.
1333
1378
  let verifyResult;
@@ -1351,6 +1396,7 @@ export async function analyzeFile(filePath, sourceCode, options) {
1351
1396
  unverifiedPairs: verifyResult.unverifiedPairs,
1352
1397
  verificationError: verifyResult.verificationError,
1353
1398
  submittedPairs: verifyResult.submittedPairs, // cognium-ai#471
1399
+ treatOmissionAsRefutation: verifyResult.treatOmissionAsRefutation,
1354
1400
  },
1355
1401
  // cognium-ai#460 — full generateFindings argument list.
1356
1402
  { language, sanitizers: patternResult.sanitizers });
@@ -1388,7 +1434,7 @@ export async function* analyzeFileStream(filePath, sourceCode, options) {
1388
1434
  yield { step: 'enrich', status: 'completed', data: enrichResult };
1389
1435
  yield { step: 'merge', status: 'running' };
1390
1436
  const mergeResult = runMerge(patternResult.patternSources, patternResult.patternSinks, enrichResult.enrichmentResult, 0.7, sourceCode, filePath, patternResult.types, // cognium-ai#128 gate
1391
- language);
1437
+ language, patternResult.calls);
1392
1438
  yield { step: 'merge', status: 'completed', data: mergeResult };
1393
1439
  yield { step: 'verify', status: 'running' };
1394
1440
  const verifyResult = await runVerify(filePath, sourceCode, mergeResult.mergedSources, mergeResult.mergedSinks, patternResult.types, enableVerification, maxVerificationTargets, verificationStrategy, llmConfig, language, contextProfile);
@@ -1407,6 +1453,7 @@ export async function* analyzeFileStream(filePath, sourceCode, options) {
1407
1453
  unverifiedPairs: verifyResult.unverifiedPairs,
1408
1454
  verificationError: verifyResult.verificationError,
1409
1455
  submittedPairs: verifyResult.submittedPairs, // cognium-ai#471
1456
+ treatOmissionAsRefutation: verifyResult.treatOmissionAsRefutation,
1410
1457
  },
1411
1458
  // cognium-ai#460 — full generateFindings argument list.
1412
1459
  { language, sanitizers: patternResult.sanitizers });
@@ -1437,6 +1484,12 @@ export function resolveVerifiedSeverity(verifierSeverity, baseSeverity, detector
1437
1484
  if (detectorSeverity && (rank[detectorSeverity] ?? 0) < (rank[severity] ?? 0)) {
1438
1485
  severity = detectorSeverity;
1439
1486
  }
1487
+ // cognium-ai#811 / #672 — gated detector rejected the sink (no index entry).
1488
+ // Without a per-sink cap, verified findings take the rule ceiling (#264).
1489
+ if (detectorSeverity === undefined &&
1490
+ (rank[severity] ?? 0) > (rank['medium'] ?? 0)) {
1491
+ severity = 'medium';
1492
+ }
1440
1493
  return severity;
1441
1494
  }
1442
1495
  //# sourceMappingURL=workflow.js.map