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 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
- - [ ] Custom rule definitions (YAML)
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"}
@@ -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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"}