vaspera 2.9.0 → 2.10.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 (166) hide show
  1. package/CHANGELOG.md +122 -7
  2. package/README.md +58 -1
  3. package/dist/__tests__/autofix/branch-manager.test.d.ts +2 -0
  4. package/dist/__tests__/autofix/branch-manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/autofix/branch-manager.test.js +60 -0
  6. package/dist/__tests__/autofix/branch-manager.test.js.map +1 -0
  7. package/dist/__tests__/autofix/commit-generator.test.d.ts +2 -0
  8. package/dist/__tests__/autofix/commit-generator.test.d.ts.map +1 -0
  9. package/dist/__tests__/autofix/commit-generator.test.js +147 -0
  10. package/dist/__tests__/autofix/commit-generator.test.js.map +1 -0
  11. package/dist/__tests__/autofix/constitution.test.d.ts +9 -0
  12. package/dist/__tests__/autofix/constitution.test.d.ts.map +1 -0
  13. package/dist/__tests__/autofix/constitution.test.js +421 -0
  14. package/dist/__tests__/autofix/constitution.test.js.map +1 -0
  15. package/dist/__tests__/autofix/pr-generator.test.d.ts +2 -0
  16. package/dist/__tests__/autofix/pr-generator.test.d.ts.map +1 -0
  17. package/dist/__tests__/autofix/pr-generator.test.js +152 -0
  18. package/dist/__tests__/autofix/pr-generator.test.js.map +1 -0
  19. package/dist/__tests__/property-test-helpers.d.ts +87 -0
  20. package/dist/__tests__/property-test-helpers.d.ts.map +1 -0
  21. package/dist/__tests__/property-test-helpers.js +136 -0
  22. package/dist/__tests__/property-test-helpers.js.map +1 -0
  23. package/dist/__tests__/scanners/dast/index.test.d.ts +2 -0
  24. package/dist/__tests__/scanners/dast/index.test.d.ts.map +1 -0
  25. package/dist/__tests__/scanners/dast/index.test.js +183 -0
  26. package/dist/__tests__/scanners/dast/index.test.js.map +1 -0
  27. package/dist/__tests__/scanners/dast/nuclei.test.d.ts +2 -0
  28. package/dist/__tests__/scanners/dast/nuclei.test.d.ts.map +1 -0
  29. package/dist/__tests__/scanners/dast/nuclei.test.js +166 -0
  30. package/dist/__tests__/scanners/dast/nuclei.test.js.map +1 -0
  31. package/dist/__tests__/scanners/dast/zap.test.d.ts +2 -0
  32. package/dist/__tests__/scanners/dast/zap.test.d.ts.map +1 -0
  33. package/dist/__tests__/scanners/dast/zap.test.js +158 -0
  34. package/dist/__tests__/scanners/dast/zap.test.js.map +1 -0
  35. package/dist/__tests__/scanners/fp-feedback.test.d.ts +2 -0
  36. package/dist/__tests__/scanners/fp-feedback.test.d.ts.map +1 -0
  37. package/dist/__tests__/scanners/fp-feedback.test.js +202 -0
  38. package/dist/__tests__/scanners/fp-feedback.test.js.map +1 -0
  39. package/dist/__tests__/scanners/fp-filter.property.test.d.ts +9 -0
  40. package/dist/__tests__/scanners/fp-filter.property.test.d.ts.map +1 -0
  41. package/dist/__tests__/scanners/fp-filter.property.test.js +253 -0
  42. package/dist/__tests__/scanners/fp-filter.property.test.js.map +1 -0
  43. package/dist/__tests__/scanners/fp-filter.test.d.ts +2 -0
  44. package/dist/__tests__/scanners/fp-filter.test.d.ts.map +1 -0
  45. package/dist/__tests__/scanners/fp-filter.test.js +234 -0
  46. package/dist/__tests__/scanners/fp-filter.test.js.map +1 -0
  47. package/dist/__tests__/scanners/fp-tracker.test.d.ts +2 -0
  48. package/dist/__tests__/scanners/fp-tracker.test.d.ts.map +1 -0
  49. package/dist/__tests__/scanners/fp-tracker.test.js +262 -0
  50. package/dist/__tests__/scanners/fp-tracker.test.js.map +1 -0
  51. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts +10 -0
  52. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts.map +1 -0
  53. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js +238 -0
  54. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js.map +1 -0
  55. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts +2 -0
  56. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts.map +1 -0
  57. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js +55 -0
  58. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js.map +1 -0
  59. package/dist/__tests__/scanners/logic/index.test.d.ts +2 -0
  60. package/dist/__tests__/scanners/logic/index.test.d.ts.map +1 -0
  61. package/dist/__tests__/scanners/logic/index.test.js +165 -0
  62. package/dist/__tests__/scanners/logic/index.test.js.map +1 -0
  63. package/dist/__tests__/scanners/logic/types.test.d.ts +2 -0
  64. package/dist/__tests__/scanners/logic/types.test.d.ts.map +1 -0
  65. package/dist/__tests__/scanners/logic/types.test.js +85 -0
  66. package/dist/__tests__/scanners/logic/types.test.js.map +1 -0
  67. package/dist/action/pr-comment.test.js +4 -0
  68. package/dist/action/pr-comment.test.js.map +1 -1
  69. package/dist/action/sarif-upload.test.js +4 -0
  70. package/dist/action/sarif-upload.test.js.map +1 -1
  71. package/dist/autofix/branch-manager.d.ts +115 -0
  72. package/dist/autofix/branch-manager.d.ts.map +1 -0
  73. package/dist/autofix/branch-manager.js +308 -0
  74. package/dist/autofix/branch-manager.js.map +1 -0
  75. package/dist/autofix/commit-generator.d.ts +55 -0
  76. package/dist/autofix/commit-generator.d.ts.map +1 -0
  77. package/dist/autofix/commit-generator.js +277 -0
  78. package/dist/autofix/commit-generator.js.map +1 -0
  79. package/dist/autofix/constitution.d.ts +77 -0
  80. package/dist/autofix/constitution.d.ts.map +1 -0
  81. package/dist/autofix/constitution.js +261 -0
  82. package/dist/autofix/constitution.js.map +1 -0
  83. package/dist/autofix/constitution.schema.d.ts +441 -0
  84. package/dist/autofix/constitution.schema.d.ts.map +1 -0
  85. package/dist/autofix/constitution.schema.js +144 -0
  86. package/dist/autofix/constitution.schema.js.map +1 -0
  87. package/dist/autofix/index.d.ts +13 -0
  88. package/dist/autofix/index.d.ts.map +1 -0
  89. package/dist/autofix/index.js +15 -0
  90. package/dist/autofix/index.js.map +1 -0
  91. package/dist/autofix/pr-generator.d.ts +57 -0
  92. package/dist/autofix/pr-generator.d.ts.map +1 -0
  93. package/dist/autofix/pr-generator.js +597 -0
  94. package/dist/autofix/pr-generator.js.map +1 -0
  95. package/dist/autofix/types.d.ts +151 -0
  96. package/dist/autofix/types.d.ts.map +1 -0
  97. package/dist/autofix/types.js +22 -0
  98. package/dist/autofix/types.js.map +1 -0
  99. package/dist/eval/fixtures.d.ts +20 -0
  100. package/dist/eval/fixtures.d.ts.map +1 -1
  101. package/dist/eval/fixtures.js +430 -0
  102. package/dist/eval/fixtures.js.map +1 -1
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +84 -1
  105. package/dist/index.js.map +1 -1
  106. package/dist/scanners/cache.d.ts.map +1 -1
  107. package/dist/scanners/cache.js +4 -0
  108. package/dist/scanners/cache.js.map +1 -1
  109. package/dist/scanners/dast/index.d.ts +39 -0
  110. package/dist/scanners/dast/index.d.ts.map +1 -0
  111. package/dist/scanners/dast/index.js +259 -0
  112. package/dist/scanners/dast/index.js.map +1 -0
  113. package/dist/scanners/dast/nuclei.d.ts +26 -0
  114. package/dist/scanners/dast/nuclei.d.ts.map +1 -0
  115. package/dist/scanners/dast/nuclei.js +354 -0
  116. package/dist/scanners/dast/nuclei.js.map +1 -0
  117. package/dist/scanners/dast/types.d.ts +306 -0
  118. package/dist/scanners/dast/types.d.ts.map +1 -0
  119. package/dist/scanners/dast/types.js +52 -0
  120. package/dist/scanners/dast/types.js.map +1 -0
  121. package/dist/scanners/dast/zap.d.ts +26 -0
  122. package/dist/scanners/dast/zap.d.ts.map +1 -0
  123. package/dist/scanners/dast/zap.js +453 -0
  124. package/dist/scanners/dast/zap.js.map +1 -0
  125. package/dist/scanners/fp-feedback.d.ts +140 -0
  126. package/dist/scanners/fp-feedback.d.ts.map +1 -0
  127. package/dist/scanners/fp-feedback.js +292 -0
  128. package/dist/scanners/fp-feedback.js.map +1 -0
  129. package/dist/scanners/fp-filter.d.ts +94 -0
  130. package/dist/scanners/fp-filter.d.ts.map +1 -0
  131. package/dist/scanners/fp-filter.js +397 -0
  132. package/dist/scanners/fp-filter.js.map +1 -0
  133. package/dist/scanners/fp-tracker.d.ts +125 -0
  134. package/dist/scanners/fp-tracker.d.ts.map +1 -0
  135. package/dist/scanners/fp-tracker.js +330 -0
  136. package/dist/scanners/fp-tracker.js.map +1 -0
  137. package/dist/scanners/index.d.ts.map +1 -1
  138. package/dist/scanners/index.js +56 -0
  139. package/dist/scanners/index.js.map +1 -1
  140. package/dist/scanners/index.test.js +6 -6
  141. package/dist/scanners/index.test.js.map +1 -1
  142. package/dist/scanners/logic/auth-flow-analyzer.d.ts +18 -0
  143. package/dist/scanners/logic/auth-flow-analyzer.d.ts.map +1 -0
  144. package/dist/scanners/logic/auth-flow-analyzer.js +384 -0
  145. package/dist/scanners/logic/auth-flow-analyzer.js.map +1 -0
  146. package/dist/scanners/logic/endpoint-analyzer.d.ts +29 -0
  147. package/dist/scanners/logic/endpoint-analyzer.d.ts.map +1 -0
  148. package/dist/scanners/logic/endpoint-analyzer.js +528 -0
  149. package/dist/scanners/logic/endpoint-analyzer.js.map +1 -0
  150. package/dist/scanners/logic/index.d.ts +41 -0
  151. package/dist/scanners/logic/index.d.ts.map +1 -0
  152. package/dist/scanners/logic/index.js +268 -0
  153. package/dist/scanners/logic/index.js.map +1 -0
  154. package/dist/scanners/logic/types.d.ts +254 -0
  155. package/dist/scanners/logic/types.d.ts.map +1 -0
  156. package/dist/scanners/logic/types.js +142 -0
  157. package/dist/scanners/logic/types.js.map +1 -0
  158. package/dist/scanners/types.d.ts +1 -1
  159. package/dist/scanners/types.d.ts.map +1 -1
  160. package/dist/scanners/types.js +4 -0
  161. package/dist/scanners/types.js.map +1 -1
  162. package/dist/telemetry/usage.d.ts +1 -1
  163. package/dist/telemetry/usage.d.ts.map +1 -1
  164. package/dist/telemetry/usage.js +14 -6
  165. package/dist/telemetry/usage.js.map +1 -1
  166. package/package.json +6 -8
@@ -0,0 +1,140 @@
1
+ /**
2
+ * False Positive Feedback
3
+ *
4
+ * Handles user feedback on findings and maintains a feedback database
5
+ * for improving FP detection over time.
6
+ *
7
+ * @module scanners/fp-feedback
8
+ */
9
+ import type { ScannerType, DeterministicFinding } from "./types.js";
10
+ import type { Severity } from "../certification/types.js";
11
+ /**
12
+ * Reason for marking a finding as FP
13
+ */
14
+ export type FPReason = "test-code" | "false-pattern-match" | "sanitized-elsewhere" | "intentional" | "vendor-code" | "generated-code" | "example-code" | "configuration" | "other";
15
+ /**
16
+ * FP reason descriptions for UI
17
+ */
18
+ export declare const FP_REASON_DESCRIPTIONS: Record<FPReason, string>;
19
+ /**
20
+ * A single feedback entry
21
+ */
22
+ export interface FeedbackEntry {
23
+ /** Unique feedback ID */
24
+ id: string;
25
+ /** Finding ID this feedback relates to */
26
+ findingId: string;
27
+ /** Scanner that generated the finding */
28
+ scanner: ScannerType;
29
+ /** Rule ID */
30
+ ruleId: string;
31
+ /** File path */
32
+ file: string;
33
+ /** Line number */
34
+ line?: number;
35
+ /** Original severity */
36
+ severity: Severity;
37
+ /** Verdict: true positive or false positive */
38
+ verdict: "tp" | "fp";
39
+ /** Reason for FP (if verdict is FP) */
40
+ reason?: FPReason;
41
+ /** Additional details/notes */
42
+ details?: string;
43
+ /** Who submitted the feedback */
44
+ submittedBy?: string;
45
+ /** When the feedback was submitted */
46
+ submittedAt: string;
47
+ /** Hash of the code at feedback time (for invalidation) */
48
+ codeHash?: string;
49
+ }
50
+ /**
51
+ * Feedback database
52
+ */
53
+ export interface FeedbackDatabase {
54
+ version: string;
55
+ projectPath: string;
56
+ entries: FeedbackEntry[];
57
+ stats: {
58
+ totalFeedback: number;
59
+ tpCount: number;
60
+ fpCount: number;
61
+ byScanner: Record<string, {
62
+ tp: number;
63
+ fp: number;
64
+ }>;
65
+ byReason: Record<string, number>;
66
+ };
67
+ }
68
+ /**
69
+ * Load feedback database
70
+ */
71
+ export declare function loadFeedbackDatabase(projectPath: string): Promise<FeedbackDatabase>;
72
+ /**
73
+ * Save feedback database
74
+ */
75
+ export declare function saveFeedbackDatabase(projectPath: string, db: FeedbackDatabase): Promise<void>;
76
+ /**
77
+ * Submit feedback for a finding
78
+ */
79
+ export declare function submitFeedback(projectPath: string, finding: DeterministicFinding, verdict: "tp" | "fp", options?: {
80
+ reason?: FPReason;
81
+ details?: string;
82
+ submittedBy?: string;
83
+ codeHash?: string;
84
+ }): Promise<FeedbackEntry>;
85
+ /**
86
+ * Get feedback for a specific finding
87
+ */
88
+ export declare function getFeedbackForFinding(projectPath: string, scanner: ScannerType, ruleId: string, file: string, line?: number): Promise<FeedbackEntry[]>;
89
+ /**
90
+ * Get all feedback for a rule
91
+ */
92
+ export declare function getFeedbackForRule(projectPath: string, scanner: ScannerType, ruleId: string): Promise<FeedbackEntry[]>;
93
+ /**
94
+ * Check if a finding has existing feedback
95
+ */
96
+ export declare function hasFeedback(projectPath: string, scanner: ScannerType, ruleId: string, file: string, line?: number): Promise<boolean>;
97
+ /**
98
+ * Get suppression suggestions based on feedback
99
+ */
100
+ export declare function getSuppressionSuggestions(projectPath: string, options?: {
101
+ minFPRate?: number;
102
+ minSampleSize?: number;
103
+ }): Promise<Array<{
104
+ scanner: ScannerType;
105
+ ruleId: string;
106
+ fpRate: number;
107
+ sampleSize: number;
108
+ suggestion: "disable" | "review" | "keep";
109
+ commonReasons: FPReason[];
110
+ }>>;
111
+ /**
112
+ * Generate feedback summary report
113
+ */
114
+ export declare function generateFeedbackReport(projectPath: string): Promise<{
115
+ overview: {
116
+ totalFeedback: number;
117
+ tpCount: number;
118
+ fpCount: number;
119
+ overallFPRate: number;
120
+ };
121
+ byScanner: Array<{
122
+ scanner: ScannerType;
123
+ total: number;
124
+ tp: number;
125
+ fp: number;
126
+ fpRate: number;
127
+ }>;
128
+ topFPReasons: Array<{
129
+ reason: FPReason;
130
+ count: number;
131
+ percentage: number;
132
+ }>;
133
+ recentFeedback: FeedbackEntry[];
134
+ suppressionSuggestions: Awaited<ReturnType<typeof getSuppressionSuggestions>>;
135
+ }>;
136
+ /**
137
+ * Clear old feedback entries
138
+ */
139
+ export declare function pruneOldFeedback(projectPath: string, maxAgeDays?: number): Promise<number>;
140
+ //# sourceMappingURL=fp-feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fp-feedback.d.ts","sourceRoot":"","sources":["../../src/scanners/fp-feedback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,292 @@
1
+ /**
2
+ * False Positive Feedback
3
+ *
4
+ * Handles user feedback on findings and maintains a feedback database
5
+ * for improving FP detection over time.
6
+ *
7
+ * @module scanners/fp-feedback
8
+ */
9
+ import { readFile, writeFile, mkdir } from "fs/promises";
10
+ import { join, dirname } from "path";
11
+ import { markFalsePositive, markTruePositive } from "./fp-tracker.js";
12
+ import { logger } from "../logger.js";
13
+ /**
14
+ * FP reason descriptions for UI
15
+ */
16
+ export const FP_REASON_DESCRIPTIONS = {
17
+ "test-code": "Finding is in test code and doesn't affect production",
18
+ "false-pattern-match": "Pattern matched but context shows it's not a real issue",
19
+ "sanitized-elsewhere": "Input is sanitized/validated elsewhere in the codebase",
20
+ "intentional": "This is intentional behavior (documented risk acceptance)",
21
+ "vendor-code": "Third-party/vendored code that cannot be modified",
22
+ "generated-code": "Auto-generated code that will be regenerated",
23
+ "example-code": "Example/demo code not used in production",
24
+ "configuration": "Scanner configuration issue (rule too broad)",
25
+ "other": "Other reason (specify in details)",
26
+ };
27
+ /**
28
+ * Get feedback file path
29
+ */
30
+ function getFeedbackFilePath(projectPath) {
31
+ return join(projectPath, ".vaspera", "fp-feedback.json");
32
+ }
33
+ /**
34
+ * Generate unique feedback ID
35
+ */
36
+ function generateFeedbackId() {
37
+ return `fb-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
38
+ }
39
+ /**
40
+ * Load feedback database
41
+ */
42
+ export async function loadFeedbackDatabase(projectPath) {
43
+ const filePath = getFeedbackFilePath(projectPath);
44
+ try {
45
+ const content = await readFile(filePath, "utf-8");
46
+ return JSON.parse(content);
47
+ }
48
+ catch {
49
+ return {
50
+ version: "1.0.0",
51
+ projectPath,
52
+ entries: [],
53
+ stats: {
54
+ totalFeedback: 0,
55
+ tpCount: 0,
56
+ fpCount: 0,
57
+ byScanner: {},
58
+ byReason: {},
59
+ },
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Save feedback database
65
+ */
66
+ export async function saveFeedbackDatabase(projectPath, db) {
67
+ const filePath = getFeedbackFilePath(projectPath);
68
+ await mkdir(dirname(filePath), { recursive: true });
69
+ await writeFile(filePath, JSON.stringify(db, null, 2), "utf-8");
70
+ logger.debug("fp_feedback.saved", { filePath, entries: db.entries.length });
71
+ }
72
+ /**
73
+ * Submit feedback for a finding
74
+ */
75
+ export async function submitFeedback(projectPath, finding, verdict, options) {
76
+ const db = await loadFeedbackDatabase(projectPath);
77
+ // Create feedback entry
78
+ const entry = {
79
+ id: generateFeedbackId(),
80
+ findingId: `${finding.scanner}-${finding.ruleId}-${finding.file}:${finding.line}`,
81
+ scanner: finding.scanner,
82
+ ruleId: finding.ruleId,
83
+ file: finding.file,
84
+ line: finding.line,
85
+ severity: finding.severity,
86
+ verdict,
87
+ reason: verdict === "fp" ? options?.reason : undefined,
88
+ details: options?.details,
89
+ submittedBy: options?.submittedBy,
90
+ submittedAt: new Date().toISOString(),
91
+ codeHash: options?.codeHash,
92
+ };
93
+ // Add to database
94
+ db.entries.push(entry);
95
+ // Update stats
96
+ db.stats.totalFeedback++;
97
+ if (verdict === "tp") {
98
+ db.stats.tpCount++;
99
+ }
100
+ else {
101
+ db.stats.fpCount++;
102
+ if (options?.reason) {
103
+ db.stats.byReason[options.reason] = (db.stats.byReason[options.reason] || 0) + 1;
104
+ }
105
+ }
106
+ // Update scanner stats
107
+ if (!db.stats.byScanner[finding.scanner]) {
108
+ db.stats.byScanner[finding.scanner] = { tp: 0, fp: 0 };
109
+ }
110
+ db.stats.byScanner[finding.scanner][verdict]++;
111
+ await saveFeedbackDatabase(projectPath, db);
112
+ // Also update the FP tracker
113
+ if (verdict === "fp") {
114
+ await markFalsePositive(projectPath, finding.scanner, finding.ruleId, options?.reason);
115
+ }
116
+ else {
117
+ await markTruePositive(projectPath, finding.scanner, finding.ruleId);
118
+ }
119
+ logger.info("fp_feedback.submitted", {
120
+ id: entry.id,
121
+ scanner: finding.scanner,
122
+ ruleId: finding.ruleId,
123
+ verdict,
124
+ reason: options?.reason,
125
+ });
126
+ return entry;
127
+ }
128
+ /**
129
+ * Get feedback for a specific finding
130
+ */
131
+ export async function getFeedbackForFinding(projectPath, scanner, ruleId, file, line) {
132
+ const db = await loadFeedbackDatabase(projectPath);
133
+ return db.entries.filter((entry) => {
134
+ const matchesScanner = entry.scanner === scanner;
135
+ const matchesRule = entry.ruleId === ruleId;
136
+ const matchesFile = entry.file === file;
137
+ const matchesLine = line === undefined || entry.line === line;
138
+ return matchesScanner && matchesRule && matchesFile && matchesLine;
139
+ });
140
+ }
141
+ /**
142
+ * Get all feedback for a rule
143
+ */
144
+ export async function getFeedbackForRule(projectPath, scanner, ruleId) {
145
+ const db = await loadFeedbackDatabase(projectPath);
146
+ return db.entries.filter((entry) => entry.scanner === scanner && entry.ruleId === ruleId);
147
+ }
148
+ /**
149
+ * Check if a finding has existing feedback
150
+ */
151
+ export async function hasFeedback(projectPath, scanner, ruleId, file, line) {
152
+ const feedback = await getFeedbackForFinding(projectPath, scanner, ruleId, file, line);
153
+ return feedback.length > 0;
154
+ }
155
+ /**
156
+ * Get suppression suggestions based on feedback
157
+ */
158
+ export async function getSuppressionSuggestions(projectPath, options) {
159
+ const minFPRate = options?.minFPRate ?? 0.5;
160
+ const minSampleSize = options?.minSampleSize ?? 5;
161
+ const db = await loadFeedbackDatabase(projectPath);
162
+ // Group feedback by scanner/rule
163
+ const ruleGroups = new Map();
164
+ for (const entry of db.entries) {
165
+ const key = `${entry.scanner}:${entry.ruleId}`;
166
+ const existing = ruleGroups.get(key) || [];
167
+ existing.push(entry);
168
+ ruleGroups.set(key, existing);
169
+ }
170
+ const suggestions = [];
171
+ for (const [key, entries] of ruleGroups) {
172
+ const [scanner, ruleId] = key.split(":");
173
+ if (entries.length < minSampleSize) {
174
+ continue;
175
+ }
176
+ const fpCount = entries.filter((e) => e.verdict === "fp").length;
177
+ const fpRate = fpCount / entries.length;
178
+ if (fpRate < minFPRate) {
179
+ continue;
180
+ }
181
+ // Get common FP reasons
182
+ const reasonCounts = new Map();
183
+ for (const entry of entries) {
184
+ if (entry.verdict === "fp" && entry.reason) {
185
+ reasonCounts.set(entry.reason, (reasonCounts.get(entry.reason) || 0) + 1);
186
+ }
187
+ }
188
+ const commonReasons = Array.from(reasonCounts.entries())
189
+ .sort((a, b) => b[1] - a[1])
190
+ .slice(0, 3)
191
+ .map(([reason]) => reason);
192
+ // Determine suggestion
193
+ let suggestion;
194
+ if (fpRate >= 0.8) {
195
+ suggestion = "disable";
196
+ }
197
+ else if (fpRate >= 0.5) {
198
+ suggestion = "review";
199
+ }
200
+ else {
201
+ suggestion = "keep";
202
+ }
203
+ suggestions.push({
204
+ scanner,
205
+ ruleId,
206
+ fpRate,
207
+ sampleSize: entries.length,
208
+ suggestion,
209
+ commonReasons,
210
+ });
211
+ }
212
+ // Sort by FP rate descending
213
+ return suggestions.sort((a, b) => b.fpRate - a.fpRate);
214
+ }
215
+ /**
216
+ * Generate feedback summary report
217
+ */
218
+ export async function generateFeedbackReport(projectPath) {
219
+ const db = await loadFeedbackDatabase(projectPath);
220
+ const suppressionSuggestions = await getSuppressionSuggestions(projectPath);
221
+ const totalFeedback = db.stats.totalFeedback;
222
+ const overallFPRate = totalFeedback > 0 ? db.stats.fpCount / totalFeedback : 0;
223
+ // By scanner
224
+ const byScanner = Object.entries(db.stats.byScanner).map(([scanner, stats]) => ({
225
+ scanner: scanner,
226
+ total: stats.tp + stats.fp,
227
+ tp: stats.tp,
228
+ fp: stats.fp,
229
+ fpRate: (stats.tp + stats.fp) > 0 ? stats.fp / (stats.tp + stats.fp) : 0,
230
+ }));
231
+ // Top FP reasons
232
+ const topFPReasons = Object.entries(db.stats.byReason)
233
+ .sort(([, a], [, b]) => b - a)
234
+ .slice(0, 5)
235
+ .map(([reason, count]) => ({
236
+ reason: reason,
237
+ count,
238
+ percentage: db.stats.fpCount > 0 ? (count / db.stats.fpCount) * 100 : 0,
239
+ }));
240
+ // Recent feedback (last 10)
241
+ const recentFeedback = [...db.entries]
242
+ .sort((a, b) => new Date(b.submittedAt).getTime() - new Date(a.submittedAt).getTime())
243
+ .slice(0, 10);
244
+ return {
245
+ overview: {
246
+ totalFeedback,
247
+ tpCount: db.stats.tpCount,
248
+ fpCount: db.stats.fpCount,
249
+ overallFPRate,
250
+ },
251
+ byScanner,
252
+ topFPReasons,
253
+ recentFeedback,
254
+ suppressionSuggestions,
255
+ };
256
+ }
257
+ /**
258
+ * Clear old feedback entries
259
+ */
260
+ export async function pruneOldFeedback(projectPath, maxAgeDays = 90) {
261
+ const db = await loadFeedbackDatabase(projectPath);
262
+ const cutoffDate = new Date();
263
+ cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
264
+ const originalCount = db.entries.length;
265
+ db.entries = db.entries.filter((entry) => {
266
+ return new Date(entry.submittedAt) >= cutoffDate;
267
+ });
268
+ const removedCount = originalCount - db.entries.length;
269
+ if (removedCount > 0) {
270
+ // Recalculate stats
271
+ db.stats = {
272
+ totalFeedback: db.entries.length,
273
+ tpCount: db.entries.filter((e) => e.verdict === "tp").length,
274
+ fpCount: db.entries.filter((e) => e.verdict === "fp").length,
275
+ byScanner: {},
276
+ byReason: {},
277
+ };
278
+ for (const entry of db.entries) {
279
+ if (!db.stats.byScanner[entry.scanner]) {
280
+ db.stats.byScanner[entry.scanner] = { tp: 0, fp: 0 };
281
+ }
282
+ db.stats.byScanner[entry.scanner][entry.verdict]++;
283
+ if (entry.verdict === "fp" && entry.reason) {
284
+ db.stats.byReason[entry.reason] = (db.stats.byReason[entry.reason] || 0) + 1;
285
+ }
286
+ }
287
+ await saveFeedbackDatabase(projectPath, db);
288
+ logger.info("fp_feedback.pruned", { removed: removedCount, remaining: db.entries.length });
289
+ }
290
+ return removedCount;
291
+ }
292
+ //# sourceMappingURL=fp-feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fp-feedback.js","sourceRoot":"","sources":["../../src/scanners/fp-feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGrC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAgBtC;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,WAAW,EAAE,uDAAuD;IACpE,qBAAqB,EAAE,yDAAyD;IAChF,qBAAqB,EAAE,wDAAwD;IAC/E,aAAa,EAAE,2DAA2D;IAC1E,aAAa,EAAE,mDAAmD;IAClE,gBAAgB,EAAE,8CAA8C;IAChE,cAAc,EAAE,0CAA0C;IAC1D,eAAe,EAAE,8CAA8C;IAC/D,OAAO,EAAE,mCAAmC;CAC7C,CAAC;AA8DF;;GAEG;AACH,SAAS,mBAAmB,CAAC,WAAmB;IAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB;IACzB,OAAO,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IAC5D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,WAAW;YACX,OAAO,EAAE,EAAE;YACX,KAAK,EAAE;gBACL,aAAa,EAAE,CAAC;gBAChB,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;gBACV,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,EAAE;aACb;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,EAAoB;IAEpB,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAElD,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAEhE,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAmB,EACnB,OAA6B,EAC7B,OAAoB,EACpB,OAKC;IAED,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEnD,wBAAwB;IACxB,MAAM,KAAK,GAAkB;QAC3B,EAAE,EAAE,kBAAkB,EAAE;QACxB,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE;QACjF,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO;QACP,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS;QACtD,OAAO,EAAE,OAAO,EAAE,OAAO;QACzB,WAAW,EAAE,OAAO,EAAE,WAAW;QACjC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,QAAQ,EAAE,OAAO,EAAE,QAAQ;KAC5B,CAAC;IAEF,kBAAkB;IAClB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEvB,eAAe;IACf,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IACzB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAE/C,MAAM,oBAAoB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAE5C,6BAA6B;IAC7B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,iBAAiB,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACzF,CAAC;SAAM,CAAC;QACN,MAAM,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE;QACnC,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO;QACP,MAAM,EAAE,OAAO,EAAE,MAAM;KACxB,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB,EACnB,OAAoB,EACpB,MAAc,EACd,IAAY,EACZ,IAAa;IAEb,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC;QACjD,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC;QAC5C,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAE9D,OAAO,cAAc,IAAI,WAAW,IAAI,WAAW,IAAI,WAAW,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,OAAoB,EACpB,MAAc;IAEd,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CACtB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAChE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,WAAmB,EACnB,OAAoB,EACpB,MAAc,EACd,IAAY,EACZ,IAAa;IAEb,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvF,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAmB,EACnB,OAGC;IASD,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,GAAG,CAAC;IAC5C,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,CAAC,CAAC;IAClD,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEnD,iCAAiC;IACjC,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEtD,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,WAAW,GAOZ,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,UAAU,EAAE,CAAC;QACxC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;QAElE,IAAI,OAAO,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;QACjE,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAExC,IAAI,MAAM,GAAG,SAAS,EAAE,CAAC;YACvB,SAAS;QACX,CAAC;QAED,wBAAwB;QACxB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;QACjD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC3C,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;QAED,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;aACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAE7B,uBAAuB;QACvB,IAAI,UAAyC,CAAC;QAC9C,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;YAClB,UAAU,GAAG,SAAS,CAAC;QACzB,CAAC;aAAM,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;YACzB,UAAU,GAAG,QAAQ,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,MAAM,CAAC;QACtB,CAAC;QAED,WAAW,CAAC,IAAI,CAAC;YACf,OAAO;YACP,MAAM;YACN,MAAM;YACN,UAAU,EAAE,OAAO,CAAC,MAAM;YAC1B,UAAU;YACV,aAAa;SACd,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAC7B,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,WAAmB;IAsB9D,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,sBAAsB,GAAG,MAAM,yBAAyB,CAAC,WAAW,CAAC,CAAC;IAE5E,MAAM,aAAa,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC;IAC7C,MAAM,aAAa,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/E,aAAa;IACb,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9E,OAAO,EAAE,OAAsB;QAC/B,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE;QAC1B,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC,CAAC,CAAC;IAEJ,iBAAiB;IACjB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;SACnD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;SAC7B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,MAAM,EAAE,MAAkB;QAC1B,KAAK;QACL,UAAU,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;KACxE,CAAC,CAAC,CAAC;IAEN,4BAA4B;IAC5B,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;SACnC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;SACrF,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhB,OAAO;QACL,QAAQ,EAAE;YACR,aAAa;YACb,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO;YACzB,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO;YACzB,aAAa;SACd;QACD,SAAS;QACT,YAAY;QACZ,cAAc;QACd,sBAAsB;KACvB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAmB,EACnB,aAAqB,EAAE;IAEvB,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;IAEtD,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAExC,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACvC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAEvD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,oBAAoB;QACpB,EAAE,CAAC,KAAK,GAAG;YACT,aAAa,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM;YAChC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,MAAM;YAC5D,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,MAAM;YAC5D,SAAS,EAAE,EAAE;YACb,QAAQ,EAAE,EAAE;SACb,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YACvD,CAAC;YACD,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAEnD,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC3C,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;QAED,MAAM,oBAAoB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAE5C,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * False Positive Filter
3
+ *
4
+ * Context-aware filtering to reduce false positives from scanners.
5
+ * Analyzes code context, semantic patterns, and historical data.
6
+ *
7
+ * @module scanners/fp-filter
8
+ */
9
+ import type { DeterministicFinding } from "./types.js";
10
+ /**
11
+ * Context information for filtering decisions
12
+ */
13
+ export interface FPFilterContext {
14
+ /** File-level context */
15
+ codeContext: {
16
+ isTestFile: boolean;
17
+ isGeneratedCode: boolean;
18
+ isThirdPartyVendored: boolean;
19
+ isMockFile: boolean;
20
+ isFixtureFile: boolean;
21
+ isExampleFile: boolean;
22
+ };
23
+ /** Semantic context from code analysis */
24
+ semanticContext: {
25
+ hasValidation: boolean;
26
+ hasEncoding: boolean;
27
+ isSanitized: boolean;
28
+ isConstant: boolean;
29
+ isEnvironmentVariable: boolean;
30
+ };
31
+ /** Historical FP data */
32
+ historicalContext: {
33
+ previousFPs: string[];
34
+ ruleAccuracy: number;
35
+ suppressions: string[];
36
+ };
37
+ }
38
+ /**
39
+ * Result of filtering decision
40
+ */
41
+ export interface FilterResult {
42
+ /** Whether to filter out this finding */
43
+ filter: boolean;
44
+ /** Reason for filtering (if filtered) */
45
+ reason?: string;
46
+ /** Confidence in the filtering decision (0-100) */
47
+ confidence: number;
48
+ /** Suggested action */
49
+ suggestion?: "suppress" | "review" | "confirm";
50
+ }
51
+ /**
52
+ * Analyze file path to determine code context
53
+ */
54
+ export declare function analyzeFilePath(filePath: string): FPFilterContext["codeContext"];
55
+ /**
56
+ * Analyze code content for semantic context
57
+ */
58
+ export declare function analyzeCodeContext(projectPath: string, finding: DeterministicFinding): Promise<FPFilterContext["semanticContext"]>;
59
+ /**
60
+ * Determine if a finding should be filtered as a false positive
61
+ */
62
+ export declare function shouldFilter(finding: DeterministicFinding, context: FPFilterContext): FilterResult;
63
+ /**
64
+ * Filter findings and return filtered results with reasons
65
+ */
66
+ export declare function filterFindings(projectPath: string, findings: DeterministicFinding[], options?: {
67
+ historicalData?: Map<string, {
68
+ fpRate: number;
69
+ suppressions: string[];
70
+ }>;
71
+ minConfidence?: number;
72
+ }): Promise<{
73
+ filtered: DeterministicFinding[];
74
+ removed: Array<{
75
+ finding: DeterministicFinding;
76
+ reason: string;
77
+ confidence: number;
78
+ }>;
79
+ stats: {
80
+ total: number;
81
+ kept: number;
82
+ filtered: number;
83
+ byReason: Record<string, number>;
84
+ };
85
+ }>;
86
+ /**
87
+ * Get suggested suppressions for a finding
88
+ */
89
+ export declare function getSuppressSuggestion(finding: DeterministicFinding, context: FPFilterContext): {
90
+ suppressionComment: string;
91
+ inlineSuppress: string;
92
+ fileSuppress: string;
93
+ };
94
+ //# sourceMappingURL=fp-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fp-filter.d.ts","sourceRoot":"","sources":["../../src/scanners/fp-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAe,MAAM,YAAY,CAAC;AAGpE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yBAAyB;IACzB,WAAW,EAAE;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,eAAe,EAAE,OAAO,CAAC;QACzB,oBAAoB,EAAE,OAAO,CAAC;QAC9B,UAAU,EAAE,OAAO,CAAC;QACpB,aAAa,EAAE,OAAO,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IAEF,0CAA0C;IAC1C,eAAe,EAAE;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,qBAAqB,EAAE,OAAO,CAAC;KAChC,CAAC;IAEF,yBAAyB;IACzB,iBAAiB,EAAE;QACjB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAC;IAEhB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IAEnB,uBAAuB;IACvB,UAAU,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;CAChD;AAqGD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAAC,aAAa,CAAC,CAYhF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC,CAmF7C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,eAAe,GACvB,YAAY,CAyGd;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,oBAAoB,EAAE,EAChC,OAAO,CAAC,EAAE;IACR,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA,OAAO,CAAC;IACT,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,oBAAoB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtF,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;CACH,CAAC,CAwDD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,eAAe,GACvB;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,CAmCA"}