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.
- package/CHANGELOG.md +122 -7
- package/README.md +58 -1
- package/dist/__tests__/autofix/branch-manager.test.d.ts +2 -0
- package/dist/__tests__/autofix/branch-manager.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/branch-manager.test.js +60 -0
- package/dist/__tests__/autofix/branch-manager.test.js.map +1 -0
- package/dist/__tests__/autofix/commit-generator.test.d.ts +2 -0
- package/dist/__tests__/autofix/commit-generator.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/commit-generator.test.js +147 -0
- package/dist/__tests__/autofix/commit-generator.test.js.map +1 -0
- package/dist/__tests__/autofix/constitution.test.d.ts +9 -0
- package/dist/__tests__/autofix/constitution.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/constitution.test.js +421 -0
- package/dist/__tests__/autofix/constitution.test.js.map +1 -0
- package/dist/__tests__/autofix/pr-generator.test.d.ts +2 -0
- package/dist/__tests__/autofix/pr-generator.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/pr-generator.test.js +152 -0
- package/dist/__tests__/autofix/pr-generator.test.js.map +1 -0
- package/dist/__tests__/property-test-helpers.d.ts +87 -0
- package/dist/__tests__/property-test-helpers.d.ts.map +1 -0
- package/dist/__tests__/property-test-helpers.js +136 -0
- package/dist/__tests__/property-test-helpers.js.map +1 -0
- package/dist/__tests__/scanners/dast/index.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/index.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/index.test.js +183 -0
- package/dist/__tests__/scanners/dast/index.test.js.map +1 -0
- package/dist/__tests__/scanners/dast/nuclei.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/nuclei.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/nuclei.test.js +166 -0
- package/dist/__tests__/scanners/dast/nuclei.test.js.map +1 -0
- package/dist/__tests__/scanners/dast/zap.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/zap.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/zap.test.js +158 -0
- package/dist/__tests__/scanners/dast/zap.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-feedback.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-feedback.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-feedback.test.js +202 -0
- package/dist/__tests__/scanners/fp-feedback.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-filter.property.test.d.ts +9 -0
- package/dist/__tests__/scanners/fp-filter.property.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-filter.property.test.js +253 -0
- package/dist/__tests__/scanners/fp-filter.property.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-filter.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-filter.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-filter.test.js +234 -0
- package/dist/__tests__/scanners/fp-filter.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-tracker.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-tracker.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-tracker.test.js +262 -0
- package/dist/__tests__/scanners/fp-tracker.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts +10 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js +238 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js +55 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/index.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/index.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/index.test.js +165 -0
- package/dist/__tests__/scanners/logic/index.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/types.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/types.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/types.test.js +85 -0
- package/dist/__tests__/scanners/logic/types.test.js.map +1 -0
- package/dist/action/pr-comment.test.js +4 -0
- package/dist/action/pr-comment.test.js.map +1 -1
- package/dist/action/sarif-upload.test.js +4 -0
- package/dist/action/sarif-upload.test.js.map +1 -1
- package/dist/autofix/branch-manager.d.ts +115 -0
- package/dist/autofix/branch-manager.d.ts.map +1 -0
- package/dist/autofix/branch-manager.js +308 -0
- package/dist/autofix/branch-manager.js.map +1 -0
- package/dist/autofix/commit-generator.d.ts +55 -0
- package/dist/autofix/commit-generator.d.ts.map +1 -0
- package/dist/autofix/commit-generator.js +277 -0
- package/dist/autofix/commit-generator.js.map +1 -0
- package/dist/autofix/constitution.d.ts +77 -0
- package/dist/autofix/constitution.d.ts.map +1 -0
- package/dist/autofix/constitution.js +261 -0
- package/dist/autofix/constitution.js.map +1 -0
- package/dist/autofix/constitution.schema.d.ts +441 -0
- package/dist/autofix/constitution.schema.d.ts.map +1 -0
- package/dist/autofix/constitution.schema.js +144 -0
- package/dist/autofix/constitution.schema.js.map +1 -0
- package/dist/autofix/index.d.ts +13 -0
- package/dist/autofix/index.d.ts.map +1 -0
- package/dist/autofix/index.js +15 -0
- package/dist/autofix/index.js.map +1 -0
- package/dist/autofix/pr-generator.d.ts +57 -0
- package/dist/autofix/pr-generator.d.ts.map +1 -0
- package/dist/autofix/pr-generator.js +597 -0
- package/dist/autofix/pr-generator.js.map +1 -0
- package/dist/autofix/types.d.ts +151 -0
- package/dist/autofix/types.d.ts.map +1 -0
- package/dist/autofix/types.js +22 -0
- package/dist/autofix/types.js.map +1 -0
- package/dist/eval/fixtures.d.ts +20 -0
- package/dist/eval/fixtures.d.ts.map +1 -1
- package/dist/eval/fixtures.js +430 -0
- package/dist/eval/fixtures.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -1
- package/dist/index.js.map +1 -1
- package/dist/scanners/cache.d.ts.map +1 -1
- package/dist/scanners/cache.js +4 -0
- package/dist/scanners/cache.js.map +1 -1
- package/dist/scanners/dast/index.d.ts +39 -0
- package/dist/scanners/dast/index.d.ts.map +1 -0
- package/dist/scanners/dast/index.js +259 -0
- package/dist/scanners/dast/index.js.map +1 -0
- package/dist/scanners/dast/nuclei.d.ts +26 -0
- package/dist/scanners/dast/nuclei.d.ts.map +1 -0
- package/dist/scanners/dast/nuclei.js +354 -0
- package/dist/scanners/dast/nuclei.js.map +1 -0
- package/dist/scanners/dast/types.d.ts +306 -0
- package/dist/scanners/dast/types.d.ts.map +1 -0
- package/dist/scanners/dast/types.js +52 -0
- package/dist/scanners/dast/types.js.map +1 -0
- package/dist/scanners/dast/zap.d.ts +26 -0
- package/dist/scanners/dast/zap.d.ts.map +1 -0
- package/dist/scanners/dast/zap.js +453 -0
- package/dist/scanners/dast/zap.js.map +1 -0
- package/dist/scanners/fp-feedback.d.ts +140 -0
- package/dist/scanners/fp-feedback.d.ts.map +1 -0
- package/dist/scanners/fp-feedback.js +292 -0
- package/dist/scanners/fp-feedback.js.map +1 -0
- package/dist/scanners/fp-filter.d.ts +94 -0
- package/dist/scanners/fp-filter.d.ts.map +1 -0
- package/dist/scanners/fp-filter.js +397 -0
- package/dist/scanners/fp-filter.js.map +1 -0
- package/dist/scanners/fp-tracker.d.ts +125 -0
- package/dist/scanners/fp-tracker.d.ts.map +1 -0
- package/dist/scanners/fp-tracker.js +330 -0
- package/dist/scanners/fp-tracker.js.map +1 -0
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +56 -0
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/index.test.js +6 -6
- package/dist/scanners/index.test.js.map +1 -1
- package/dist/scanners/logic/auth-flow-analyzer.d.ts +18 -0
- package/dist/scanners/logic/auth-flow-analyzer.d.ts.map +1 -0
- package/dist/scanners/logic/auth-flow-analyzer.js +384 -0
- package/dist/scanners/logic/auth-flow-analyzer.js.map +1 -0
- package/dist/scanners/logic/endpoint-analyzer.d.ts +29 -0
- package/dist/scanners/logic/endpoint-analyzer.d.ts.map +1 -0
- package/dist/scanners/logic/endpoint-analyzer.js +528 -0
- package/dist/scanners/logic/endpoint-analyzer.js.map +1 -0
- package/dist/scanners/logic/index.d.ts +41 -0
- package/dist/scanners/logic/index.d.ts.map +1 -0
- package/dist/scanners/logic/index.js +268 -0
- package/dist/scanners/logic/index.js.map +1 -0
- package/dist/scanners/logic/types.d.ts +254 -0
- package/dist/scanners/logic/types.d.ts.map +1 -0
- package/dist/scanners/logic/types.js +142 -0
- package/dist/scanners/logic/types.js.map +1 -0
- package/dist/scanners/types.d.ts +1 -1
- package/dist/scanners/types.d.ts.map +1 -1
- package/dist/scanners/types.js +4 -0
- package/dist/scanners/types.js.map +1 -1
- package/dist/telemetry/usage.d.ts +1 -1
- package/dist/telemetry/usage.d.ts.map +1 -1
- package/dist/telemetry/usage.js +14 -6
- package/dist/telemetry/usage.js.map +1 -1
- 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":"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"}
|
|
@@ -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"}
|