verification-layer 0.11.0 → 0.13.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/README.md +124 -5
- package/dist/acknowledgments.d.ts +22 -0
- package/dist/acknowledgments.d.ts.map +1 -0
- package/dist/acknowledgments.js +97 -0
- package/dist/acknowledgments.js.map +1 -0
- package/dist/baseline.d.ts +48 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +86 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cli.js +221 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js +14 -5
- package/dist/reporters/index.js.map +1 -1
- package/dist/rules/scanner.d.ts.map +1 -1
- package/dist/rules/scanner.js +2 -0
- package/dist/rules/scanner.js.map +1 -1
- package/dist/rules/schema.d.ts +24 -0
- package/dist/rules/schema.d.ts.map +1 -1
- package/dist/rules/schema.js +4 -0
- package/dist/rules/schema.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +44 -1
- package/dist/scan.js.map +1 -1
- package/dist/semantic-analysis.d.ts +19 -0
- package/dist/semantic-analysis.d.ts.map +1 -0
- package/dist/semantic-analysis.js +220 -0
- package/dist/semantic-analysis.js.map +1 -0
- package/dist/suppression.d.ts +14 -0
- package/dist/suppression.d.ts.map +1 -0
- package/dist/suppression.js +110 -0
- package/dist/suppression.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -19,6 +19,9 @@ vlayer is a CLI tool that scans your codebase for HIPAA compliance issues. It's
|
|
|
19
19
|
- Generate professional audit reports (HTML, PDF, JSON)
|
|
20
20
|
- Detect your tech stack and provide tailored recommendations
|
|
21
21
|
- Create cryptographic audit trails for compliance documentation
|
|
22
|
+
- **Professional suppression system** with inline comments and justifications
|
|
23
|
+
- **Baseline support** to focus on new findings while tracking existing issues
|
|
24
|
+
- **Confidence levels** for progressive strictness adoption
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
@@ -44,6 +47,95 @@ node dist/cli.js audit /path/to/project --generate-report
|
|
|
44
47
|
|
|
45
48
|
---
|
|
46
49
|
|
|
50
|
+
## 🆕 IDE & Developer Experience
|
|
51
|
+
|
|
52
|
+
### VS Code Extension
|
|
53
|
+
|
|
54
|
+
Get real-time HIPAA compliance feedback directly in your editor:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Install from the vscode-extension directory
|
|
58
|
+
cd vscode-extension
|
|
59
|
+
npm install
|
|
60
|
+
npm run compile
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Features:**
|
|
64
|
+
- ✅ Real-time scanning on file save
|
|
65
|
+
- ✅ Inline diagnostics with severity markers
|
|
66
|
+
- ✅ Hover tooltips with HIPAA references and recommendations
|
|
67
|
+
- ✅ Quick-fix actions for auto-remediation
|
|
68
|
+
- ✅ Status bar compliance score
|
|
69
|
+
- ✅ Commands: "VLayer: Scan Current File", "VLayer: Scan Workspace"
|
|
70
|
+
|
|
71
|
+
**Configuration:**
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"vlayer.enableAutoScan": true,
|
|
75
|
+
"vlayer.minConfidence": "low",
|
|
76
|
+
"vlayer.showStatusBar": true,
|
|
77
|
+
"vlayer.configPath": ".vlayerrc.json"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Watch Mode
|
|
82
|
+
|
|
83
|
+
Continuous monitoring with real-time feedback:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Watch a directory for changes
|
|
87
|
+
node dist/cli.js watch ./src
|
|
88
|
+
|
|
89
|
+
# Watch with specific categories
|
|
90
|
+
node dist/cli.js watch ./src --categories phi-exposure encryption
|
|
91
|
+
|
|
92
|
+
# Watch with custom config
|
|
93
|
+
node dist/cli.js watch ./src --config .vlayerrc.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Features:**
|
|
97
|
+
- 🔍 Automatic scan on file save/create
|
|
98
|
+
- 🎨 Colored terminal output by severity
|
|
99
|
+
- 📊 Diff tracking (new findings vs. previous scan)
|
|
100
|
+
- 🚨 Alerts for new critical/high severity findings
|
|
101
|
+
- ⚡ Smart file filtering (excludes node_modules, dist, etc.)
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Suppression & Baseline
|
|
106
|
+
|
|
107
|
+
### Inline Suppressions
|
|
108
|
+
|
|
109
|
+
Suppress specific findings with inline comments (justification required):
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// vlayer-ignore phi-ssn-hardcoded -- Test data for unit tests
|
|
113
|
+
const testSSN = "123-45-6789";
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Baseline for Existing Codebases
|
|
117
|
+
|
|
118
|
+
Generate a baseline to track existing findings without blocking progress:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Generate baseline from current state
|
|
122
|
+
node dist/cli.js baseline . -o .vlayer-baseline.json
|
|
123
|
+
|
|
124
|
+
# Scan with baseline (only NEW findings cause failures)
|
|
125
|
+
node dist/cli.js scan . --baseline .vlayer-baseline.json
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Confidence Levels
|
|
129
|
+
|
|
130
|
+
Filter findings by confidence level for progressive adoption:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Only fail on high-confidence findings
|
|
134
|
+
node dist/cli.js scan . --min-confidence high
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
47
139
|
## Features
|
|
48
140
|
|
|
49
141
|
### 1. Vulnerability Detection
|
|
@@ -264,11 +356,22 @@ vlayer scan <path> -f markdown -o report.md # Markdown report
|
|
|
264
356
|
vlayer scan <path> -c phi-exposure encryption # Specific categories
|
|
265
357
|
vlayer scan <path> --fix # Auto-fix issues
|
|
266
358
|
|
|
359
|
+
# Watch mode
|
|
360
|
+
vlayer watch <path> # Watch for changes
|
|
361
|
+
vlayer watch <path> -c phi-exposure # Watch specific categories
|
|
362
|
+
vlayer watch <path> --config .vlayerrc.json # Watch with custom config
|
|
363
|
+
vlayer watch <path> --min-confidence high # Watch with confidence filter
|
|
364
|
+
|
|
267
365
|
# Audit commands
|
|
268
366
|
vlayer audit <path> --summary # View audit summary
|
|
269
367
|
vlayer audit <path> --generate-report # Generate PDF
|
|
270
368
|
vlayer audit <path> --generate-report --text # Generate text instead
|
|
271
369
|
vlayer audit <path> --generate-report --org "Company" --auditor "Name"
|
|
370
|
+
|
|
371
|
+
# Baseline commands
|
|
372
|
+
vlayer baseline <path> # Generate baseline
|
|
373
|
+
vlayer baseline <path> -o custom.json # Custom output path
|
|
374
|
+
vlayer scan <path> --baseline .vlayer-baseline.json # Scan with baseline
|
|
272
375
|
```
|
|
273
376
|
|
|
274
377
|
**Exit codes:**
|
|
@@ -295,13 +398,29 @@ Each finding maps to specific HIPAA regulations:
|
|
|
295
398
|
|
|
296
399
|
## Roadmap
|
|
297
400
|
|
|
401
|
+
### Recently Completed ✅
|
|
402
|
+
- [x] **Phase 3A: IDE & Developer Experience**
|
|
403
|
+
- [x] VS Code Extension v2.0 with real-time scanning
|
|
404
|
+
- [x] Watch mode for continuous monitoring
|
|
405
|
+
- [x] Inline diagnostics with hover tooltips
|
|
406
|
+
- [x] Quick-fix actions and status bar integration
|
|
407
|
+
- [x] **Phase 2B: Enhanced Custom Rules**
|
|
408
|
+
- [x] Semantic awareness for custom rules
|
|
409
|
+
- [x] Pattern-aware context detection
|
|
410
|
+
- [x] Confidence level controls
|
|
411
|
+
- [x] **Phase 2A: Semantic Context Analysis**
|
|
412
|
+
- [x] AST-based semantic analysis
|
|
413
|
+
- [x] Context-aware confidence levels
|
|
414
|
+
- [x] Test file detection
|
|
415
|
+
- [x] **Phase 1B: Reusable GitHub Action**
|
|
416
|
+
- [x] GitHub Action for CI/CD integration
|
|
417
|
+
- [x] Enhanced npm package
|
|
418
|
+
- [x] Baseline and suppression systems
|
|
419
|
+
|
|
298
420
|
### Coming Soon
|
|
299
|
-
- [x] GitHub Action for CI/CD integration
|
|
300
|
-
- [x] Automated npm releases with semantic-release
|
|
301
|
-
- [x] Dependabot for dependency updates
|
|
302
|
-
- [ ] VS Code extension with inline warnings
|
|
303
421
|
- [ ] Slack/Teams notifications for new findings
|
|
304
|
-
- [ ]
|
|
422
|
+
- [ ] Web dashboard with trend tracking
|
|
423
|
+
- [ ] API for programmatic access
|
|
305
424
|
|
|
306
425
|
### Planned
|
|
307
426
|
- [ ] HITRUST CSF mapping
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Finding, VlayerConfig } from './types.js';
|
|
2
|
+
export interface AcknowledgmentMatch {
|
|
3
|
+
acknowledged: boolean;
|
|
4
|
+
reason?: string;
|
|
5
|
+
acknowledgedBy?: string;
|
|
6
|
+
acknowledgedAt?: string;
|
|
7
|
+
ticketUrl?: string;
|
|
8
|
+
expired?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check if a finding matches any acknowledged finding pattern in the config
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkAcknowledgment(finding: Finding, config: VlayerConfig): AcknowledgmentMatch;
|
|
14
|
+
/**
|
|
15
|
+
* Apply acknowledgments to all findings
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyAcknowledgments(findings: Finding[], config: VlayerConfig): Finding[];
|
|
18
|
+
/**
|
|
19
|
+
* Validate acknowledged finding configuration
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateAcknowledgedFinding(ack: any, index: number): string[];
|
|
22
|
+
//# sourceMappingURL=acknowledgments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acknowledgments.d.ts","sourceRoot":"","sources":["../src/acknowledgments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CA4CrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EAAE,EACnB,MAAM,EAAE,YAAY,GACnB,OAAO,EAAE,CAoBX;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,MAAM,GACZ,MAAM,EAAE,CAiCV"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { minimatch } from 'minimatch';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a finding matches any acknowledged finding pattern in the config
|
|
4
|
+
*/
|
|
5
|
+
export function checkAcknowledgment(finding, config) {
|
|
6
|
+
if (!config.acknowledgedFindings || config.acknowledgedFindings.length === 0) {
|
|
7
|
+
return { acknowledged: false };
|
|
8
|
+
}
|
|
9
|
+
for (const ack of config.acknowledgedFindings) {
|
|
10
|
+
// Check if file path matches the pattern
|
|
11
|
+
if (!minimatch(finding.file, ack.pattern)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
// Check if finding ID matches (if specified)
|
|
15
|
+
if (ack.id) {
|
|
16
|
+
const idPattern = new RegExp(ack.id.replace(/\*/g, '.*'));
|
|
17
|
+
if (!idPattern.test(finding.id)) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Check if category matches (if specified)
|
|
22
|
+
if (ack.category && ack.category !== finding.category) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Check if severity matches (if specified)
|
|
26
|
+
if (ack.severity && ack.severity !== finding.severity) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
// Check if acknowledgment has expired
|
|
30
|
+
const expired = ack.expiresAt ? new Date(ack.expiresAt) < new Date() : false;
|
|
31
|
+
// All criteria matched
|
|
32
|
+
return {
|
|
33
|
+
acknowledged: true,
|
|
34
|
+
reason: ack.reason,
|
|
35
|
+
acknowledgedBy: ack.acknowledgedBy,
|
|
36
|
+
acknowledgedAt: ack.acknowledgedAt,
|
|
37
|
+
ticketUrl: ack.ticketUrl,
|
|
38
|
+
expired,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return { acknowledged: false };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Apply acknowledgments to all findings
|
|
45
|
+
*/
|
|
46
|
+
export function applyAcknowledgments(findings, config) {
|
|
47
|
+
return findings.map(finding => {
|
|
48
|
+
const ack = checkAcknowledgment(finding, config);
|
|
49
|
+
if (ack.acknowledged) {
|
|
50
|
+
return {
|
|
51
|
+
...finding,
|
|
52
|
+
acknowledged: true,
|
|
53
|
+
acknowledgment: {
|
|
54
|
+
reason: ack.reason,
|
|
55
|
+
acknowledgedBy: ack.acknowledgedBy,
|
|
56
|
+
acknowledgedAt: ack.acknowledgedAt,
|
|
57
|
+
ticketUrl: ack.ticketUrl,
|
|
58
|
+
expired: ack.expired,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return finding;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validate acknowledged finding configuration
|
|
67
|
+
*/
|
|
68
|
+
export function validateAcknowledgedFinding(ack, index) {
|
|
69
|
+
const errors = [];
|
|
70
|
+
if (!ack.pattern || typeof ack.pattern !== 'string') {
|
|
71
|
+
errors.push(`acknowledgedFindings[${index}]: 'pattern' is required and must be a string`);
|
|
72
|
+
}
|
|
73
|
+
if (!ack.reason || typeof ack.reason !== 'string') {
|
|
74
|
+
errors.push(`acknowledgedFindings[${index}]: 'reason' is required and must be a string`);
|
|
75
|
+
}
|
|
76
|
+
if (!ack.acknowledgedBy || typeof ack.acknowledgedBy !== 'string') {
|
|
77
|
+
errors.push(`acknowledgedFindings[${index}]: 'acknowledgedBy' is required and must be a string`);
|
|
78
|
+
}
|
|
79
|
+
if (!ack.acknowledgedAt || typeof ack.acknowledgedAt !== 'string') {
|
|
80
|
+
errors.push(`acknowledgedFindings[${index}]: 'acknowledgedAt' is required and must be a string`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Validate ISO 8601 date format
|
|
84
|
+
const date = new Date(ack.acknowledgedAt);
|
|
85
|
+
if (isNaN(date.getTime())) {
|
|
86
|
+
errors.push(`acknowledgedFindings[${index}]: 'acknowledgedAt' must be a valid ISO 8601 date`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (ack.expiresAt) {
|
|
90
|
+
const date = new Date(ack.expiresAt);
|
|
91
|
+
if (isNaN(date.getTime())) {
|
|
92
|
+
errors.push(`acknowledgedFindings[${index}]: 'expiresAt' must be a valid ISO 8601 date`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return errors;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=acknowledgments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acknowledgments.js","sourceRoot":"","sources":["../src/acknowledgments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Finding } from './types.js';
|
|
2
|
+
export interface BaselineEntry {
|
|
3
|
+
hash: string;
|
|
4
|
+
id: string;
|
|
5
|
+
file: string;
|
|
6
|
+
line?: number;
|
|
7
|
+
title: string;
|
|
8
|
+
severity: string;
|
|
9
|
+
category: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Baseline {
|
|
12
|
+
version: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
findings: BaselineEntry[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate a stable hash for a finding
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateFindingHash(finding: Finding): string;
|
|
20
|
+
/**
|
|
21
|
+
* Create a baseline entry from a finding
|
|
22
|
+
*/
|
|
23
|
+
export declare function createBaselineEntry(finding: Finding): BaselineEntry;
|
|
24
|
+
/**
|
|
25
|
+
* Load baseline from file
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadBaseline(path: string): Promise<Baseline | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Save baseline to file
|
|
30
|
+
*/
|
|
31
|
+
export declare function saveBaseline(path: string, findings: Finding[]): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a finding exists in the baseline
|
|
34
|
+
*/
|
|
35
|
+
export declare function isInBaseline(finding: Finding, baseline: Baseline): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Apply baseline to findings
|
|
38
|
+
*/
|
|
39
|
+
export declare function applyBaseline(findings: Finding[], baseline: Baseline | null): Finding[];
|
|
40
|
+
/**
|
|
41
|
+
* Get statistics about baseline application
|
|
42
|
+
*/
|
|
43
|
+
export declare function getBaselineStats(findings: Finding[]): {
|
|
44
|
+
total: number;
|
|
45
|
+
baseline: number;
|
|
46
|
+
new: number;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAK5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAUnE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAQzE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQnF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAG1E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO,EAAE,CAcvF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CASA"}
|
package/dist/baseline.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
/**
|
|
4
|
+
* Generate a stable hash for a finding
|
|
5
|
+
*/
|
|
6
|
+
export function generateFindingHash(finding) {
|
|
7
|
+
// Use file path, line number, finding ID, and title to create a stable hash
|
|
8
|
+
// This allows the same issue at the same location to be matched
|
|
9
|
+
const key = `${finding.file}:${finding.line || 0}:${finding.id}:${finding.title}`;
|
|
10
|
+
return createHash('sha256').update(key).digest('hex').substring(0, 16);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a baseline entry from a finding
|
|
14
|
+
*/
|
|
15
|
+
export function createBaselineEntry(finding) {
|
|
16
|
+
return {
|
|
17
|
+
hash: generateFindingHash(finding),
|
|
18
|
+
id: finding.id,
|
|
19
|
+
file: finding.file,
|
|
20
|
+
line: finding.line,
|
|
21
|
+
title: finding.title,
|
|
22
|
+
severity: finding.severity,
|
|
23
|
+
category: finding.category,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load baseline from file
|
|
28
|
+
*/
|
|
29
|
+
export async function loadBaseline(path) {
|
|
30
|
+
try {
|
|
31
|
+
const content = await readFile(path, 'utf-8');
|
|
32
|
+
const baseline = JSON.parse(content);
|
|
33
|
+
return baseline;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Save baseline to file
|
|
41
|
+
*/
|
|
42
|
+
export async function saveBaseline(path, findings) {
|
|
43
|
+
const baseline = {
|
|
44
|
+
version: '1.0',
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
findings: findings.map(createBaselineEntry),
|
|
47
|
+
};
|
|
48
|
+
await writeFile(path, JSON.stringify(baseline, null, 2), 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a finding exists in the baseline
|
|
52
|
+
*/
|
|
53
|
+
export function isInBaseline(finding, baseline) {
|
|
54
|
+
const hash = generateFindingHash(finding);
|
|
55
|
+
return baseline.findings.some(entry => entry.hash === hash);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Apply baseline to findings
|
|
59
|
+
*/
|
|
60
|
+
export function applyBaseline(findings, baseline) {
|
|
61
|
+
if (!baseline) {
|
|
62
|
+
return findings;
|
|
63
|
+
}
|
|
64
|
+
return findings.map(finding => {
|
|
65
|
+
if (isInBaseline(finding, baseline)) {
|
|
66
|
+
return {
|
|
67
|
+
...finding,
|
|
68
|
+
isBaseline: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return finding;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get statistics about baseline application
|
|
76
|
+
*/
|
|
77
|
+
export function getBaselineStats(findings) {
|
|
78
|
+
const baseline = findings.filter(f => f.isBaseline).length;
|
|
79
|
+
const total = findings.length;
|
|
80
|
+
return {
|
|
81
|
+
total,
|
|
82
|
+
baseline,
|
|
83
|
+
new: total - baseline,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
|