@oddessentials/odd-ai-reviewers 1.10.0 → 1.10.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.
@@ -22,13 +22,52 @@ import { buildLineResolver, normalizeFindingsForDiff, computeDriftSignal, comput
22
22
  export function normalizeUnicode(text) {
23
23
  return text.replace(/[\u200B-\u200F\u2028\u2029\uFEFF]/g, '');
24
24
  }
25
+ /**
26
+ * Patterns that indicate a finding is self-dismissing.
27
+ *
28
+ * IMPORTANT: Pattern match alone NEVER suppresses a finding.
29
+ * All three gates must pass for suppression:
30
+ * 1. Finding severity must be 'info' (NEVER warning/error)
31
+ * 2. Finding message must match one of these patterns
32
+ * 3. Finding must have NO actionable suggestion in residual text
33
+ *
34
+ * This three-gate architecture is a deliberate security boundary.
35
+ * Do NOT relax any gate without a spec amendment.
36
+ */
25
37
  const DISMISSIVE_PATTERNS = [
26
38
  /\bno action required\b/i,
27
39
  /\bacceptable as[- ]is\b/i,
28
40
  /\bnot blocking\b/i,
29
41
  /\bno change needed\b/i,
30
42
  /\bcan be ignored\b/i,
43
+ /\bworking as intended\b/i,
44
+ /\bno issues found\b/i,
45
+ /\bnon-critical\b/i,
46
+ /\blow priority\b/i,
31
47
  ];
48
+ /**
49
+ * Patterns that indicate a finding is cautionary hedging advice
50
+ * rather than identifying an actual defect. Common in gpt-4o outputs
51
+ * where the model flags code as "ensure X is properly set" or
52
+ * "consider Y to prevent potential issues" without a concrete bug.
53
+ *
54
+ * Suppression requires ALL gates:
55
+ * 1. Finding severity must be 'info'
56
+ * 2. Combined message+suggestion must match one of these patterns
57
+ * 3. Combined text must NOT contain security-related terms
58
+ */
59
+ const CAUTIONARY_ADVICE_PATTERNS = [
60
+ // "Ensure/Verify/Make sure (that) X is properly/correctly Y"
61
+ /\b(?:ensure|verify|make\s+sure|double[- ]?check)\s+(?:that\b|the\b|all\b)/i,
62
+ // "Consider X to prevent non-obvious/potential/unexpected issues"
63
+ /\bconsider\b.*\b(?:to\s+prevent|to\s+avoid|for\s+safety)\b/i,
64
+ ];
65
+ /**
66
+ * Security-related terms that BLOCK cautionary advice suppression.
67
+ * If the combined message+suggestion contains any of these, the finding
68
+ * is treated as a legitimate concern, not hedging advice.
69
+ */
70
+ const SECURITY_BLOCKLIST = /\b(?:sql|injection|xss|cross.?site|sanitiz|escap|authenti|authoriz|csrf|ssrf|path.?traversal|command.?inject|exec\s*\(|eval\s*\(|deseria|privilege|encrypt|password|credential|secret|vulnerab|exploit|attack|malicious|buffer.?overflow|bypass|jwt|token|signature|session|cors|cookie|rate.?limit(?:ing)?|redirect)\b/i;
32
71
  /**
33
72
  * Check if a suggestion is actionable (contains concrete guidance beyond dismissive language).
34
73
  */
@@ -180,6 +219,7 @@ export function validateFindingsSemantics(findings, prDescription) {
180
219
  valid: 0,
181
220
  filteredByLine: 0,
182
221
  filteredBySelfContradiction: 0,
222
+ filteredByCautionaryAdvice: 0,
183
223
  filteredByPRIntent: 0,
184
224
  byClassification: {
185
225
  inline: 0,
@@ -236,6 +276,34 @@ export function validateFindingsSemantics(findings, prDescription) {
236
276
  reason: result.filterReason,
237
277
  });
238
278
  }
279
+ // Pass 3.5: Cautionary advice detection
280
+ // Catches info-severity findings where the LLM hedges with "ensure/verify/consider"
281
+ // without identifying a concrete defect. Blocked for security-related findings.
282
+ for (const result of results) {
283
+ if (!result.valid)
284
+ continue;
285
+ // Gate 1: Only info severity — NEVER filter warning/error
286
+ if (result.finding.severity !== 'info')
287
+ continue;
288
+ const combinedText = normalizeUnicode(result.finding.message + ' ' + (result.finding.suggestion ?? ''));
289
+ // Gate 2: Must match a cautionary advice pattern
290
+ const matchedCautionary = CAUTIONARY_ADVICE_PATTERNS.find((p) => p.test(combinedText));
291
+ if (!matchedCautionary)
292
+ continue;
293
+ // Gate 3: BLOCK suppression if security-related terms are present
294
+ if (SECURITY_BLOCKLIST.test(combinedText))
295
+ continue;
296
+ // All gates passed — suppress this cautionary advice finding
297
+ result.valid = false;
298
+ result.filterReason = `Cautionary advice: info severity with hedging language (${matchedCautionary.source}) and no security concern`;
299
+ result.filterType = 'cautionary_advice';
300
+ stats.filteredByCautionaryAdvice++;
301
+ console.log('[router] [finding-validator] [filtered:cautionary]', {
302
+ file: result.finding.file,
303
+ line: result.finding.line,
304
+ reason: result.filterReason,
305
+ });
306
+ }
239
307
  // Build final arrays
240
308
  let validFindings = [];
241
309
  const filtered = [];
@@ -278,6 +346,7 @@ export function validateNormalizedFindings(findings, lineResolver, diffFiles) {
278
346
  valid: 0,
279
347
  filteredByLine: 0,
280
348
  filteredBySelfContradiction: 0,
349
+ filteredByCautionaryAdvice: 0,
281
350
  filteredByPRIntent: 0,
282
351
  byClassification: {
283
352
  inline: 0,
@@ -352,6 +421,28 @@ export function validateNormalizedFindings(findings, lineResolver, diffFiles) {
352
421
  reason: result.filterReason,
353
422
  });
354
423
  }
424
+ // Pass 3.5: Cautionary advice detection (same as in validateFindingsSemantics)
425
+ for (const result of results) {
426
+ if (!result.valid)
427
+ continue;
428
+ if (result.finding.severity !== 'info')
429
+ continue;
430
+ const combinedText = normalizeUnicode(result.finding.message + ' ' + (result.finding.suggestion ?? ''));
431
+ const matchedCautionary = CAUTIONARY_ADVICE_PATTERNS.find((p) => p.test(combinedText));
432
+ if (!matchedCautionary)
433
+ continue;
434
+ if (SECURITY_BLOCKLIST.test(combinedText))
435
+ continue;
436
+ result.valid = false;
437
+ result.filterReason = `Cautionary advice: info severity with hedging language (${matchedCautionary.source}) and no security concern`;
438
+ result.filterType = 'cautionary_advice';
439
+ stats.filteredByCautionaryAdvice++;
440
+ console.log('[router] [finding-validator] [filtered:cautionary]', {
441
+ file: result.finding.file,
442
+ line: result.finding.line,
443
+ reason: result.filterReason,
444
+ });
445
+ }
355
446
  // Build final arrays
356
447
  const validFindings = [];
357
448
  const filtered = [];
@@ -379,7 +470,8 @@ export function normalizeAndValidateFindings(findings, diffFiles, platform) {
379
470
  if (stage2Result.filtered.length > 0) {
380
471
  console.log(`[${platform}] Stage 2 validation: ${stage2Result.stats.valid} valid, ` +
381
472
  `${stage2Result.stats.filteredByLine} filtered by line, ` +
382
- `${stage2Result.stats.filteredBySelfContradiction} self-contradicting`);
473
+ `${stage2Result.stats.filteredBySelfContradiction} self-contradicting, ` +
474
+ `${stage2Result.stats.filteredByCautionaryAdvice} cautionary advice`);
383
475
  }
384
476
  const driftSignal = computeDriftSignal(normalizationResult.stats, normalizationResult.invalidDetails);
385
477
  const inlineDriftSignal = computeInlineDriftSignal(normalizationResult.stats, normalizationResult.invalidDetails);
@@ -1 +1 @@
1
- {"version":3,"file":"finding-validator.js","sourceRoot":"","sources":["../../src/report/finding-validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,kBAAkB,EAClB,wBAAwB,GAIzB,MAAM,oBAAoB,CAAC;AAsC5B;;;;GAIG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,mBAAmB,GAAa;IACpC,yBAAyB;IACzB,0BAA0B;IAC1B,mBAAmB;IACnB,uBAAuB;IACvB,qBAAqB;CACtB,CAAC;AAEF;;GAEG;AACH,SAAS,uBAAuB,CAAC,UAA8B;IAC7D,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,GAAG,CACjD,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAC9C,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE5C,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB;SACjC,MAAM,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC;SAC1E,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC;SAC1B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IAEV,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,iBAAiB,GAAG,mDAAmD,CAAC;AAE9E;;;GAGG;AACH,MAAM,6BAA6B,GAAG,IAAI,GAAG,CAAC;IAC5C,eAAe;IACf,OAAO;IACP,UAAU;IACV,aAAa;CACd,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,6BAA6B,GAA6B;IAC9D,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACzB,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,MAAM,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAmB,EACnB,aAAqB,EACrB,mBAAmB,GAAG,IAAI;IAE1B,MAAM,QAAQ,GAA8B,EAAE,CAAC;IAE/C,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,kBAAkB,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;IAE/D,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,SAAS,GAAc,EAAE,CAAC;IAEhC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,6BAA6B;QAC7B,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAChC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,mGAAmG;QACnG,IAAI,QAAQ,GAAG,EAAE,CAAC;QAClB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1D,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;gBAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,6BAA6B,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAEnD,6EAA6E;QAC7E,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtE,MAAM,eAAe,GACnB,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACxD,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,wDAAwD;QACxD,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,2CAA2C;QAC3C,OAAO,CAAC,GAAG,CAAC,mDAAmD,EAAE;YAC/D,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ;YACR,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YAC7B,iBAAiB,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC/E,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;SAC9C,CAAC,CAAC;QAEH,QAAQ,CAAC,IAAI,CAAC;YACZ,OAAO;YACP,cAAc,EAAE,OAAO,CAAC,IAAI;gBAC1B,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS;oBAC1B,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,YAAY;gBAChB,CAAC,CAAC,QAAQ;YACZ,KAAK,EAAE,KAAK;YACZ,YAAY,EAAE,qCAAqC,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,iCAAiC;YAChH,UAAU,EAAE,yBAAyB;SACtC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAmB,EACnB,aAAsB;IAEtB,MAAM,OAAO,GAA8B,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG;QACZ,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,KAAK,EAAE,CAAC;QACR,cAAc,EAAE,CAAC;QACjB,2BAA2B,EAAE,CAAC;QAC9B,kBAAkB,EAAE,CAAC;QACrB,gBAAgB,EAAE;YAChB,MAAM,EAAE,CAAC;YACT,YAAY,EAAE,CAAC;YACf,MAAM,EAAE,CAAC;YACT,YAAY,EAAE,CAAC;SACyB;KAC3C,CAAC;IAEF,mFAAmF;IACnF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,cAAqC,CAAC;QAE1C,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,cAAc,GAAG,QAAQ,CAAC;QAC5B,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACtC,cAAc,GAAG,YAAY,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,cAAc,GAAG,QAAQ,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,CAAC;QAEzC,OAAO,CAAC,IAAI,CAAC;YACX,OAAO;YACP,cAAc;YACd,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAE9E,uCAAuC;IACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,SAAS;QAE5B,yDAAyD;QACzD,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,KAAK,MAAM;YAAE,SAAS;QAEjD,mFAAmF;QACnF,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,cAAc,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,cAAc;YAAE,SAAS;QAE9B,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU;YACpD,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YAC7C,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,uBAAuB,CAAC,oBAAoB,CAAC;YAAE,SAAS;QAE5D,qEAAqE;QACrE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,MAAM,CAAC,YAAY,GAAG,+DAA+D,cAAc,CAAC,MAAM,GAAG,CAAC;QAC9G,MAAM,CAAC,UAAU,GAAG,oBAAoB,CAAC;QACzC,KAAK,CAAC,2BAA2B,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,kDAAkD,EAAE;YAC9D,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,MAAM,EAAE,MAAM,CAAC,YAAY;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,qBAAqB;IACrB,IAAI,aAAa,GAAc,EAAE,CAAC;IAClC,MAAM,QAAQ,GAA8B,EAAE,CAAC;IAE/C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACnC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,0FAA0F;IAC1F,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,cAAc,GAAG,4BAA4B,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAClF,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,cAAc,CAAC,QAAQ,EAAE,CAAC;YACxC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAmB,EACnB,YAAiC,EACjC,SAAoB;IAEpB,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,OAAO,GAA8B,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG;QACZ,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,KAAK,EAAE,CAAC;QACR,cAAc,EAAE,CAAC;QACjB,2BAA2B,EAAE,CAAC;QAC9B,kBAAkB,EAAE,CAAC;QACrB,gBAAgB,EAAE;YAChB,MAAM,EAAE,CAAC;YACT,YAAY,EAAE,CAAC;YACf,MAAM,EAAE,CAAC;YACT,YAAY,EAAE,CAAC;SACyB;KAC3C,CAAC;IAEF,gCAAgC;IAChC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,cAAqC,CAAC;QAE1C,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,cAAc,GAAG,QAAQ,CAAC;QAC5B,CAAC;aAAM,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAClE,cAAc,GAAG,YAAY,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,uDAAuD,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACrF,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACtC,cAAc,GAAG,YAAY,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,cAAc,GAAG,QAAQ,CAAC;QAC5B,CAAC;QAED,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,CAAC;QAEzC,OAAO,CAAC,IAAI,CAAC;YACX,OAAO;YACP,cAAc;YACd,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC;IAED,iDAAiD;IACjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,cAAc,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5E,MAAM,UAAU,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvF,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;gBACrB,MAAM,CAAC,YAAY,GAAG,QAAQ,MAAM,CAAC,OAAO,CAAC,IAAI,0BAA0B,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjG,MAAM,CAAC,UAAU,GAAG,cAAc,CAAC;gBACnC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,qDAAqD,EAAE;oBACjE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;oBACzB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;oBACzB,MAAM,EAAE,MAAM,CAAC,YAAY;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,SAAS;QAE5B,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,KAAK,MAAM;YAAE,SAAS;QAEjD,mFAAmF;QACnF,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,cAAc,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,cAAc;YAAE,SAAS;QAE9B,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU;YACpD,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YAC7C,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,uBAAuB,CAAC,oBAAoB,CAAC;YAAE,SAAS;QAE5D,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,MAAM,CAAC,YAAY,GAAG,+DAA+D,cAAc,CAAC,MAAM,GAAG,CAAC;QAC9G,MAAM,CAAC,UAAU,GAAG,oBAAoB,CAAC;QACzC,KAAK,CAAC,2BAA2B,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,kDAAkD,EAAE;YAC9D,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,MAAM,EAAE,MAAM,CAAC,YAAY;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,qBAAqB;IACrB,MAAM,aAAa,GAAc,EAAE,CAAC;IACpC,MAAM,QAAQ,GAA8B,EAAE,CAAC;IAE/C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACnC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAkBD,MAAM,UAAU,4BAA4B,CAC1C,QAAmB,EACnB,SAAqB,EACrB,QAAgB;IAEhB,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACvD,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAE7E,IAAI,mBAAmB,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACtF,OAAO,CAAC,GAAG,CACT,IAAI,QAAQ,sBAAsB,mBAAmB,CAAC,KAAK,CAAC,KAAK,UAAU;YACzE,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,gBAAgB,mBAAmB,CAAC,KAAK,CAAC,OAAO,UAAU,CACrG,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,0BAA0B,CAC7C,mBAAmB,CAAC,QAAQ,EAC5B,YAAY,EACZ,aAAa,CACd,CAAC;IAEF,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CACT,IAAI,QAAQ,yBAAyB,YAAY,CAAC,KAAK,CAAC,KAAK,UAAU;YACrE,GAAG,YAAY,CAAC,KAAK,CAAC,cAAc,qBAAqB;YACzD,GAAG,YAAY,CAAC,KAAK,CAAC,2BAA2B,qBAAqB,CACzE,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CACpC,mBAAmB,CAAC,KAAK,EACzB,mBAAmB,CAAC,cAAc,CACnC,CAAC;IAEF,MAAM,iBAAiB,GAAG,wBAAwB,CAChD,mBAAmB,CAAC,KAAK,EACzB,mBAAmB,CAAC,cAAc,CACnC,CAAC;IAEF,OAAO;QACL,iBAAiB,EAAE,YAAY,CAAC,aAAa;QAC7C,cAAc;QACd,WAAW;QACX,iBAAiB;QACjB,kBAAkB,EAAE,mBAAmB,CAAC,KAAK;QAC7C,cAAc,EAAE,mBAAmB,CAAC,cAAc;KACnD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAmB,EACnB,YAAiC,EACjC,SAAoB;IAEpB,OAAO,0BAA0B,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AACvE,CAAC"}
1
+ {"version":3,"file":"finding-validator.js","sourceRoot":"","sources":["../../src/report/finding-validator.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * using a closed, default-deny matcher table. Runs in Stage 1 validation
6
6
  * (after self-contradiction filter, before Stage 2 diff-bound validation).
7
7
  *
8
- * The matcher table is CLOSED: only these 5 matchers exist.
8
+ * The matcher table is CLOSED: only these 9 matchers exist.
9
9
  * Adding a new matcher requires a spec amendment.
10
10
  */
11
11
  import type { Finding } from '../agents/types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"framework-pattern-filter.d.ts","sourceRoot":"","sources":["../../src/report/framework-pattern-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAMlD,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IACtE,0DAA0D;IAC1D,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AA4ND;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,OAAO,EAAE,EACnB,WAAW,EAAE,MAAM,GAClB,sBAAsB,CAuCxB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,EAAE,CAE3E"}
1
+ {"version":3,"file":"framework-pattern-filter.d.ts","sourceRoot":"","sources":["../../src/report/framework-pattern-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAMlD,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IACtE,0DAA0D;IAC1D,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AAqnBD;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,OAAO,EAAE,EACnB,WAAW,EAAE,MAAM,GAClB,sBAAsB,CAuCxB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,EAAE,CAE3E"}
@@ -5,7 +5,7 @@
5
5
  * using a closed, default-deny matcher table. Runs in Stage 1 validation
6
6
  * (after self-contradiction filter, before Stage 2 diff-bound validation).
7
7
  *
8
- * The matcher table is CLOSED: only these 5 matchers exist.
8
+ * The matcher table is CLOSED: only these 9 matchers exist.
9
9
  * Adding a new matcher requires a spec amendment.
10
10
  */
11
11
  // =============================================================================
@@ -67,14 +67,14 @@ function extractLinesNearFinding(diffSection, findingLine, windowSize = 10) {
67
67
  }
68
68
  // =============================================================================
69
69
  // Closed Matcher Table — DEFAULT DENY
70
- // Only these 5 matchers. No additions without spec change.
70
+ // Only these 9 matchers. No additions without spec change.
71
71
  // =============================================================================
72
72
  const FRAMEWORK_MATCHERS = [
73
73
  // T019: Express Error Middleware
74
74
  {
75
75
  id: 'express-error-mw',
76
76
  name: 'Express Error Middleware',
77
- messagePattern: /unused.*param/i,
77
+ messagePattern: /unused.*param|declared\s+but\s+never\s+referenced|dead\s+code.*never\s+called|parameter\s+not\s+referenced/i,
78
78
  evidenceValidator(finding, diffContent) {
79
79
  const fileSection = extractFileDiffSection(finding, diffContent);
80
80
  if (!fileSection)
@@ -134,11 +134,11 @@ const FRAMEWORK_MATCHERS = [
134
134
  },
135
135
  suppressionReason: 'Exhaustive switch with assertNever/throw — all cases handled at compile time',
136
136
  },
137
- // T022: React Query Deduplication
137
+ // T022: React Query Advisory (dedup, error handling, data fetching concerns)
138
138
  {
139
139
  id: 'react-query-dedup',
140
- name: 'React Query Dedup',
141
- messagePattern: /duplicate|double.?fetch|redundant.*query|multiple.*useQuery/i,
140
+ name: 'React Query Advisory',
141
+ messagePattern: /duplicate|double.?fetch|redundant.*query|multiple.*useQuery|(?:verify|ensure|validate).*(?:endpoint|api|fetch).*(?:return|format|response|error|handle)|missing.*error.*handling.*(?:fetch|query|useQuery)|error.?handling.*(?:useQuery|useSWR)/i,
142
142
  evidenceValidator(finding, diffContent) {
143
143
  const fileSection = extractFileDiffSection(finding, diffContent);
144
144
  if (!fileSection)
@@ -159,15 +159,69 @@ const FRAMEWORK_MATCHERS = [
159
159
  if (/api\s*call|http\s*request|\bfetch\s*\(/.test(finding.message.toLowerCase())) {
160
160
  return false;
161
161
  }
162
+ // Evidence 4: When the finding is about missing error handling, require BOTH:
163
+ // (a) error/isError is destructured from the hook result, AND
164
+ // (b) the destructured binding is used in a conditional branch on error state
165
+ // (if-check, short-circuit, ternary). Property access alone (error?.message)
166
+ // is NOT sufficient — logging or rendering a field without branching does
167
+ // not prove the component handles the error for the user.
168
+ const isErrorHandlingFinding = /missing.*error|error.*handling/i.test(finding.message);
169
+ if (isErrorHandlingFinding) {
170
+ // Step (a): error/isError must appear in a destructuring pattern.
171
+ // Extract the actual binding name (handles aliases like { error: queryError }).
172
+ const errorBindings = [];
173
+ // Match shorthand `{ ..., error, ... }` or `{ ..., isError, ... }`
174
+ // and aliased `{ ..., error: NAME, ... }` or `{ ..., isError: NAME, ... }`
175
+ const destructuringBlock = nearbyText.match(/\{\s*([^}]*\b(?:error|isError)\b[^}]*)\}/);
176
+ if (!destructuringBlock?.[1])
177
+ return false;
178
+ const blockContent = destructuringBlock[1];
179
+ // Check for alias pattern: `error: someAlias` or `isError: someAlias`
180
+ const aliasMatches = blockContent.matchAll(/\b(?:error|isError)\s*:\s*(\w+)/g);
181
+ for (const m of aliasMatches) {
182
+ if (m[1])
183
+ errorBindings.push(m[1]);
184
+ }
185
+ // Check for shorthand: `error` or `isError` without `: alias`
186
+ if (/\berror\b(?!\s*:)/.test(blockContent)) {
187
+ errorBindings.push('error');
188
+ }
189
+ if (/\bisError\b(?!\s*:)/.test(blockContent)) {
190
+ errorBindings.push('isError');
191
+ }
192
+ if (errorBindings.length === 0)
193
+ return false;
194
+ // Step (b): At least one extracted binding must appear in a conditional branch
195
+ // on error state: if-check, short-circuit (&&), or ternary (?).
196
+ // SAFETY: bindings are from \w+ match — only [a-zA-Z0-9_], no regex special chars.
197
+ //
198
+ // Fail-open patterns (not checked, finding passes through):
199
+ // - binding used only as callback argument
200
+ // - binding only logged (console.log/console.error)
201
+ // - property access without conditional guard (error?.message)
202
+ // - nested destructuring from the binding
203
+ const hasErrorUsage = errorBindings.some((binding) => {
204
+ // eslint-disable-next-line security/detect-non-literal-regexp
205
+ const ifCheck = new RegExp('\\bif\\s*\\(\\s*' + binding + '\\b');
206
+ // eslint-disable-next-line security/detect-non-literal-regexp
207
+ const shortCircuit = new RegExp('\\b' + binding + '\\s*&&');
208
+ // Ternary: `binding ? ... : ...` — must exclude optional chaining `binding?.`
209
+ // eslint-disable-next-line security/detect-non-literal-regexp
210
+ const ternary = new RegExp('\\b' + binding + '\\s*\\?(?!\\.)');
211
+ return (ifCheck.test(nearbyText) || shortCircuit.test(nearbyText) || ternary.test(nearbyText));
212
+ });
213
+ if (!hasErrorUsage)
214
+ return false;
215
+ }
162
216
  return true;
163
217
  },
164
- suppressionReason: 'Query library deduplicates by cache keynot double-fetching',
218
+ suppressionReason: 'Query library handles caching, dedup, and error state advisory is redundant',
165
219
  },
166
- // T023: Promise.allSettled Order Preservation
220
+ // T023: Promise.allSettled Convention (Order + Error Handling)
167
221
  {
168
222
  id: 'promise-allsettled-order',
169
- name: 'Promise.allSettled Order',
170
- messagePattern: /allSettled.*(?:order|sequence)|(?:order|sequence).*allSettled|allSettled.*results.*not.*(?:match|correspond|align)/i,
223
+ name: 'Promise.allSettled Convention',
224
+ messagePattern: /allSettled.*(?:order|sequence|reject|unhandled|error.?handling|silent)|(?:order|sequence).*allSettled|(?:unhandled|missing|silent).*(?:reject|error|exception).*(?:promise|settled)|allSettled.*results.*not.*(?:match|correspond|align)|(?:additional|need).*error.*handling.*(?:promise|fetch|request|response|processing)|verify.*(?:fetch|request).*(?:error|handling|additional|response)/i,
171
225
  evidenceValidator(finding, diffContent) {
172
226
  const fileSection = extractFileDiffSection(finding, diffContent);
173
227
  if (!fileSection)
@@ -177,13 +231,302 @@ const FRAMEWORK_MATCHERS = [
177
231
  const nearbyText = nearbyLines.join('\n');
178
232
  if (!/Promise\.allSettled\s*\(/.test(nearbyText))
179
233
  return false;
180
- // Evidence 2: Result iteration (indexed or sequential access)
181
- const hasResultAccess = /\.\s*forEach|\.map\s*\(|\[(\w+|\d+)\]|for\s*\(.*\s+of\s/.test(nearbyText);
182
- if (!hasResultAccess)
234
+ // Evidence 2+3: Iteration and .status must be BOUND to the allSettled result variable.
235
+ // Unscoped checks (any .forEach + any .status in nearbyText) allow false suppression
236
+ // when unrelated iteration or HTTP .status references exist nearby.
237
+ // Step 2a: Extract the result variable name.
238
+ // Primary: `const/let/var X = await Promise.allSettled(...)`
239
+ const allSettledVarMatch = nearbyText.match(/\b(?:const|let|var)\s+(\w+)\s*=\s*await\s+Promise\.allSettled\s*\(/);
240
+ let varName = allSettledVarMatch?.[1];
241
+ // Fallback: `.then()` chain — `Promise.allSettled(...).then((X) => ...)` or
242
+ // `.then(X => ...)` or `.then(function(X) { ... })`.
243
+ // NOTE: This is a bounded heuristic over the ±10-line diff window, not a
244
+ // structurally correct parser. The lazy [\s\S]*? relies on regex backtracking
245
+ // to find the closing paren of allSettled(...) when nested parens are present.
246
+ // This is acceptable given the small window size but is not balanced-paren parsing.
247
+ // Fail-open patterns (not checked):
248
+ // - separated await: `const p = Promise.allSettled(...); const r = await p;`
249
+ // - named function reference: `.then(handleResults)`
250
+ // - generator patterns
251
+ if (!varName) {
252
+ const thenMatch = nearbyText.match(/Promise\.allSettled\s*\([\s\S]*?\)\.then\s*\(\s*(?:function\s*\(\s*(\w+)|(\w+)\s*=>|\(\s*(\w+)\s*\)\s*=>)/);
253
+ varName = thenMatch?.[1] ?? thenMatch?.[2] ?? thenMatch?.[3];
254
+ }
255
+ if (!varName)
256
+ return false; // Cannot identify result variable — fail open
257
+ // Step 2b: Require iteration to reference the allSettled result variable.
258
+ // SAFETY: varName is from \w+ match — only [a-zA-Z0-9_], no regex special chars.
259
+ // eslint-disable-next-line security/detect-non-literal-regexp
260
+ const iterationPattern = new RegExp('\\b' +
261
+ varName +
262
+ '\\s*\\.\\s*forEach\\s*\\(' +
263
+ '|for\\s*\\([^)]*\\s+of\\s+' +
264
+ varName +
265
+ '\\b' +
266
+ '|\\b' +
267
+ varName +
268
+ '\\s*\\[');
269
+ if (!iterationPattern.test(nearbyText))
270
+ return false;
271
+ // Step 2c: .status check must appear on the iteration callback parameter,
272
+ // not on an unrelated variable. Extract the callback/loop variable name
273
+ // and require PARAM.status in nearbyText.
274
+ let hasStatusCheck = false;
275
+ // Pattern A: VARNAME.forEach((PARAM, ...) => { ... PARAM.status ... })
276
+ const forEachParamMatch = nearbyText.match(
277
+ // eslint-disable-next-line security/detect-non-literal-regexp
278
+ new RegExp('\\b' + varName + '\\s*\\.\\s*forEach\\s*\\(\\s*(?:\\(\\s*)?(\\w+)'));
279
+ if (forEachParamMatch?.[1]) {
280
+ const cbParam = forEachParamMatch[1];
281
+ // eslint-disable-next-line security/detect-non-literal-regexp
282
+ hasStatusCheck = new RegExp('\\b' + cbParam + '\\.status\\b').test(nearbyText);
283
+ }
284
+ // Pattern B: for (const LOOPVAR of VARNAME) { ... LOOPVAR.status ... }
285
+ if (!hasStatusCheck) {
286
+ const forOfMatch = nearbyText.match(
287
+ // eslint-disable-next-line security/detect-non-literal-regexp
288
+ new RegExp('for\\s*\\(\\s*(?:const|let|var)\\s+(\\w+)\\s+of\\s+' + varName + '\\b'));
289
+ if (forOfMatch?.[1]) {
290
+ const loopVar = forOfMatch[1];
291
+ // eslint-disable-next-line security/detect-non-literal-regexp
292
+ hasStatusCheck = new RegExp('\\b' + loopVar + '\\.status\\b').test(nearbyText);
293
+ }
294
+ }
295
+ // Pattern C: indexed access VARNAME[i].status
296
+ if (!hasStatusCheck) {
297
+ // eslint-disable-next-line security/detect-non-literal-regexp
298
+ hasStatusCheck = new RegExp('\\b' + varName + '\\s*\\[\\w+\\]\\s*\\.\\s*status\\b').test(nearbyText);
299
+ }
300
+ if (!hasStatusCheck)
301
+ return false;
302
+ return true;
303
+ },
304
+ suppressionReason: 'Promise.allSettled convention — results handled per ECMAScript spec',
305
+ },
306
+ // T025: Safe Local File Read
307
+ {
308
+ id: 'safe-local-file-read',
309
+ name: 'Safe Local File Read',
310
+ messagePattern: /path.*traversal|directory.*traversal|local.*file.*read|file.*inclusion|readFileSync.*block|synchronous.*file.*read|block.*event.*loop.*(?:read|file)/i,
311
+ evidenceValidator(finding, diffContent) {
312
+ const fileSection = extractFileDiffSection(finding, diffContent);
313
+ if (!fileSection)
314
+ return false;
315
+ const nearbyLines = extractLinesNearFinding(fileSection, finding.line, 10);
316
+ // Single-line only: check each line individually (per FR-011 scope limitation)
317
+ const canonicalPattern = /path\.(join|resolve)\s*\(\s*(?:__dirname|__filename|import\.meta\.(?:dirname|filename|url))\s*(?:,\s*(['"])[^'"]*\2\s*)*\)/;
318
+ let match = null;
319
+ for (const line of nearbyLines) {
320
+ match = canonicalPattern.exec(line);
321
+ if (match)
322
+ break;
323
+ }
324
+ if (!match)
325
+ return false;
326
+ // Extract the full matched path expression for safety checks
327
+ const matchedExpr = match[0];
328
+ // B1: Reject if any string literal segment contains '..' (path traversal)
329
+ const stringSegments = matchedExpr.match(/(['"])[^'"]*\1/g);
330
+ if (stringSegments) {
331
+ for (const seg of stringSegments) {
332
+ const content = seg.slice(1, -1);
333
+ if (content.includes('..'))
334
+ return false;
335
+ }
336
+ }
337
+ // B2: Reject if any string literal segment starts with '/' or drive letter (absolute path)
338
+ if (stringSegments) {
339
+ for (const seg of stringSegments) {
340
+ const content = seg.slice(1, -1);
341
+ if (content.startsWith('/'))
342
+ return false;
343
+ if (/^[a-zA-Z]:/.test(content))
344
+ return false;
345
+ }
346
+ }
347
+ // B3: Performance findings (sync I/O blocking) require module-top-level scope.
348
+ // Path safety only proves the read is traversal-safe, NOT that blocking I/O
349
+ // is acceptable. Sync reads inside functions, callbacks, or request handlers
350
+ // are legitimate performance concerns that must not be suppressed.
351
+ //
352
+ // Criteria: a finding is "performance-typed" if its message matches the sync-read
353
+ // patterns but NOT the security patterns (traversal/inclusion). For such findings:
354
+ // 1. The readFileSync call must be a direct module-scope declaration (const/let/var
355
+ // at ≤2 leading spaces — not nested inside any function, arrow, or callback body)
356
+ // 2. No request-handler or event-listener context within ±10 lines
357
+ const isPerformanceFinding = /readFileSync.*block|synchronous.*file.*read|block.*event.*loop/i.test(finding.message) &&
358
+ !/path.*traversal|directory.*traversal|file.*inclusion/i.test(finding.message);
359
+ if (isPerformanceFinding) {
360
+ // Require that at least one nearby line is a module-top-level declaration
361
+ // (starts with at most 2 spaces of indentation followed by const/let/var/export).
362
+ // Lines indented ≥4 spaces are inside a function body (not top-level).
363
+ const hasTopLevelDecl = nearbyLines.some((l) => /^\s{0,2}(?:export\s+)?(?:const|let|var)\s+\w+\s*=/.test(l));
364
+ if (!hasTopLevelDecl)
365
+ return false;
366
+ // Reject if a request-handler, middleware, or event-listener pattern appears
367
+ // anywhere within the ±10-line window (nearbyText).
368
+ const nearbyText = nearbyLines.join('\n');
369
+ if (/\b(?:app|router)\s*\.\s*(?:get|post|put|patch|delete|use|all)\s*\(/.test(nearbyText) ||
370
+ /\.on\s*\(\s*['"]/.test(nearbyText) ||
371
+ /addEventListener\s*\(/.test(nearbyText) ||
372
+ /(?:req|request)\s*,\s*(?:res|response)\s*[,)]/.test(nearbyText))
373
+ return false;
374
+ }
375
+ return true;
376
+ },
377
+ suppressionReason: 'Safe local file read — path.join/resolve with __dirname and string literals only',
378
+ },
379
+ // T026: Exhaustive Type-Narrowed Switch
380
+ {
381
+ id: 'exhaustive-type-narrowed-switch',
382
+ name: 'Exhaustive Type-Narrowed Switch',
383
+ messagePattern: /missing.*(?:case|default)|no.*default|add.*default|non-?exhaustive/i,
384
+ evidenceValidator(finding, diffContent) {
385
+ const fileSection = extractFileDiffSection(finding, diffContent);
386
+ if (!fileSection)
387
+ return false;
388
+ const nearbyLines = extractLinesNearFinding(fileSection, finding.line, 10);
389
+ const nearbyText = nearbyLines.join('\n');
390
+ // Evidence 1: switch target must be a simple identifier (not a property access).
391
+ // Property-access targets like switch(node.type) or switch(event.kind) cannot
392
+ // have their type proven from a local annotation — fail open (do not suppress).
393
+ const switchTargetMatch = nearbyText.match(/\bswitch\s*\((\w+)\)/);
394
+ if (!switchTargetMatch)
395
+ return false;
396
+ const varName = switchTargetMatch[1];
397
+ // Safety constraint: reject if the switch target variable is typed as string or number.
398
+ // A string/number-typed switch is inherently open-domain — not exhaustive.
399
+ // SAFETY: varName is from \w+ match — only [a-zA-Z0-9_], no regex special chars.
400
+ // eslint-disable-next-line security/detect-non-literal-regexp
401
+ const varPrimitivePattern = new RegExp('\\b' + varName + '\\s*:\\s*(?:string|number)\\b');
402
+ if (varPrimitivePattern.test(nearbyText))
403
+ return false;
404
+ // Evidence 2: the switch variable must have a named type annotation (PascalCase),
405
+ // and that exact named type must be declared as a string-literal union in the
406
+ // visible diff/file section. Inferred types (no annotation) and imported types
407
+ // (not defined in the diff) MUST NOT trigger suppression — fail open.
408
+ //
409
+ // Step 2a: extract the type name from the variable's annotation in ±10 lines.
410
+ // e.g., `function f(theme: Theme)` → typeName = 'Theme'
411
+ // SAFETY: varName is from \w+ match — only [a-zA-Z0-9_], no regex special chars.
412
+ // eslint-disable-next-line security/detect-non-literal-regexp
413
+ const varTypePattern = new RegExp('\\b' + varName + '\\s*:\\s*([A-Z][\\w]*)');
414
+ const typeNameMatch = nearbyText.match(varTypePattern);
415
+ if (!typeNameMatch?.[1])
416
+ return false; // no visible annotation → cannot prove union
417
+ const typeName = typeNameMatch[1];
418
+ // Step 2b: verify that typeName is defined as a string-literal union in the file
419
+ // diff section. Only string-literal unions declared in the visible diff count.
420
+ // e.g., `type Theme = 'light' | 'dark'`
421
+ // SAFETY: typeName is from [A-Z][\w]* match — only [a-zA-Z0-9_], no regex special chars.
422
+ // eslint-disable-next-line security/detect-non-literal-regexp
423
+ const unionDeclarationPattern = new RegExp('\\btype\\s+' + typeName + '\\s*=\\s*((?:[\'"][^\'"]+[\'"]\\s*\\|?\\s*)+)');
424
+ const unionMatch = fileSection.match(unionDeclarationPattern);
425
+ if (!unionMatch?.[1])
426
+ return false;
427
+ // Step 2c: verify every union member VALUE has a corresponding case branch.
428
+ // Uses set-membership (not count comparison) to prevent duplicate case values
429
+ // from inflating the count. e.g., case 'light', case 'light' = 1 unique value,
430
+ // not 2 — so a 3-member union with a duplicate case is correctly rejected.
431
+ const unionMemberQuoted = unionMatch[1].match(/['"][^'"]+['"]/g) ?? [];
432
+ if (unionMemberQuoted.length === 0)
433
+ return false;
434
+ // Extract raw string values (without quotes) from union members
435
+ const unionMemberValues = unionMemberQuoted.map((m) => m.slice(1, -1));
436
+ // Extract raw string values from case branches (deduplicated via Set)
437
+ const caseBranchMatches = nearbyText.match(/\bcase\s+['"]([^'"]+)['"]\s*:/g) ?? [];
438
+ const caseValues = new Set(caseBranchMatches.map((m) => {
439
+ const val = m.match(/['"]([^'"]+)['"]/);
440
+ return val?.[1] ?? '';
441
+ }));
442
+ // Every union member must have a matching case branch (set membership)
443
+ const allMembersCovered = unionMemberValues.every((member) => caseValues.has(member));
444
+ if (!allMembersCovered)
445
+ return false;
446
+ return true;
447
+ },
448
+ suppressionReason: 'Exhaustive type-narrowed switch — union type with all members covered',
449
+ },
450
+ // Convention 18: Error Object XSS
451
+ {
452
+ id: 'error-object-xss',
453
+ name: 'Error Object XSS',
454
+ messagePattern: /(?:xss|inject).*(?:error|err)\b.*(?:message|\.message)|(?:error|err)\b.*(?:message|\.message).*(?:xss|inject|innerHTML|template)|(?:xss|inject).*error.*(?:directly|message)|error\s+message.*(?:xss|inject|innerHTML)/i,
455
+ evidenceValidator(finding, diffContent) {
456
+ const fileSection = extractFileDiffSection(finding, diffContent);
457
+ if (!fileSection)
458
+ return false;
459
+ const nearbyLines = extractLinesNearFinding(fileSection, finding.line, 10);
460
+ const nearbyText = nearbyLines.join('\n');
461
+ // MANDATORY: catch clause visible (structural proof of error origin)
462
+ // No naming heuristics, no function-name matching (security-engineer mandate)
463
+ if (!/\bcatch\s*\(\s*\w+/.test(nearbyText))
464
+ return false;
465
+ // MANDATORY: error.message usage visible (the flagged construct)
466
+ if (!/\.\s*message\b/.test(nearbyText))
467
+ return false;
468
+ // REJECT: error constructed from user input or external API data.
469
+ // Errors built from req.body, query params, or external input can contain
470
+ // attacker-controlled data — suppression would hide real XSS.
471
+ if (/new\s+(?:\w+)?Error\s*\(\s*(?:req\.|request\.|body\.|params\.|query\.|input\.|data\.|payload\.)/.test(nearbyText))
472
+ return false;
473
+ // REJECT: direct DOM manipulation (browser-side sinks)
474
+ if (/\.innerHTML\s*=|\.outerHTML\s*=|document\.write\s*\(|insertAdjacentHTML\s*\(/.test(nearbyText))
475
+ return false;
476
+ // REJECT: React dangerouslySetInnerHTML (always renders raw HTML)
477
+ if (/dangerouslySetInnerHTML/.test(nearbyText))
478
+ return false;
479
+ // REJECT: server-side HTTP response sinks that render error.message as HTML.
480
+ // Only triggers when BOTH a response output call AND .message appear within
481
+ // the same ±10-line window (nearbyText), proving the error data flows into
482
+ // the response. Plain-text responses (res.send(err.message) without HTML
483
+ // markup) are excluded by requiring an HTML indicator (< or template literal).
484
+ if (/\bres\s*\.\s*(?:send|write|end)\s*\(/.test(nearbyText)) {
485
+ // Check for HTML evidence: template literal with tags, or string with '<'
486
+ const hasHtmlInResponse = /\bres\s*\.\s*(?:send|write|end)\s*\(\s*`[^`]*</.test(nearbyText) ||
487
+ /\bres\s*\.\s*(?:send|write|end)\s*\([^)]*['"][^'"]*</.test(nearbyText) ||
488
+ /\bres\s*\.\s*(?:send|write|end)\s*\([^)]*\+[^)]*['"]?\s*</.test(nearbyText);
489
+ if (hasHtmlInResponse)
490
+ return false;
491
+ }
492
+ // REJECT: template engine render calls (always produce HTML output)
493
+ if (/\bres\s*\.\s*render\s*\(/.test(nearbyText) ||
494
+ /\b(?:ejs|pug|handlebars|hbs|nunjucks|mustache)\s*[.(]/.test(nearbyText))
495
+ return false;
496
+ return true;
497
+ },
498
+ suppressionReason: 'Error from catch clause — error.message is runtime exception, not user input',
499
+ },
500
+ // Convention 19: Thin Wrapper Stdlib
501
+ {
502
+ id: 'thin-wrapper-stdlib',
503
+ name: 'Thin Wrapper Stdlib',
504
+ messagePattern: /(?:missing|add|no).*try.?catch|(?:could|may|might).*throw|unhandled.*(?:error|exception).*(?:JSON\.parse|parseInt|parseFloat|new\s+URL|Buffer\.from|decodeURI)|directly.*(?:return|call).*(?:JSON\.parse|parseInt|parseFloat)/i,
505
+ evidenceValidator(finding, diffContent) {
506
+ const fileSection = extractFileDiffSection(finding, diffContent);
507
+ if (!fileSection)
508
+ return false;
509
+ const nearbyLines = extractLinesNearFinding(fileSection, finding.line, 5);
510
+ const nearbyText = nearbyLines.join('\n');
511
+ // Evidence 1: WHITELISTED stdlib call present (no open patterns)
512
+ const SAFE_STDLIB = /\b(?:JSON\.parse|JSON\.stringify|parseInt|parseFloat|Number\(|new\s+URL|Buffer\.from|decodeURIComponent|decodeURI|atob|btoa)\s*\(/;
513
+ if (!SAFE_STDLIB.test(nearbyText))
514
+ return false;
515
+ // Evidence 2: thin wrapper structure (return + stdlib)
516
+ if (!/\breturn\s+/.test(nearbyText))
517
+ return false;
518
+ // REJECT: I/O operations (not pure stdlib delegation)
519
+ if (/\b(?:fs\.|fetch\s*\(|await\s|\.readFile|\.writeFile|database|\.query\s*\()/.test(nearbyText))
520
+ return false;
521
+ // REJECT: conditional logic (not a thin wrapper)
522
+ if (/\b(?:if\s*\(|else\b|switch\s*\()/.test(nearbyText))
523
+ return false;
524
+ // REJECT: request handler context (caller responsibility matters here)
525
+ if (/\b(?:req\.|request\.|res\.|response\.|app\.\w+\(|router\.\w+\()/.test(nearbyText))
183
526
  return false;
184
527
  return true;
185
528
  },
186
- suppressionReason: 'Promise.allSettled preserves input order per ECMAScript spec',
529
+ suppressionReason: 'Thin wrapper around stdlib function try-catch is caller responsibility',
187
530
  },
188
531
  ];
189
532
  // =============================================================================