verification-layer 0.4.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/LICENSE +21 -0
- package/README.md +345 -0
- package/dist/audit/evidence.d.ts +25 -0
- package/dist/audit/evidence.d.ts.map +1 -0
- package/dist/audit/evidence.js +70 -0
- package/dist/audit/evidence.js.map +1 -0
- package/dist/audit/index.d.ts +54 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +159 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +199 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/fixer/index.d.ts +11 -0
- package/dist/fixer/index.d.ts.map +1 -0
- package/dist/fixer/index.js +171 -0
- package/dist/fixer/index.js.map +1 -0
- package/dist/fixer/strategies.d.ts +3 -0
- package/dist/fixer/strategies.d.ts.map +1 -0
- package/dist/fixer/strategies.js +199 -0
- package/dist/fixer/strategies.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/reporters/audit-report.d.ts +13 -0
- package/dist/reporters/audit-report.d.ts.map +1 -0
- package/dist/reporters/audit-report.js +526 -0
- package/dist/reporters/audit-report.js.map +1 -0
- package/dist/reporters/fix-report.d.ts +3 -0
- package/dist/reporters/fix-report.d.ts.map +1 -0
- package/dist/reporters/fix-report.js +70 -0
- package/dist/reporters/fix-report.js.map +1 -0
- package/dist/reporters/index.d.ts +3 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +425 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/reporters/remediation-guides.d.ts +25 -0
- package/dist/reporters/remediation-guides.d.ts.map +1 -0
- package/dist/reporters/remediation-guides.js +636 -0
- package/dist/reporters/remediation-guides.js.map +1 -0
- package/dist/scan.d.ts +3 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +96 -0
- package/dist/scan.js.map +1 -0
- package/dist/scanners/access/index.d.ts +3 -0
- package/dist/scanners/access/index.d.ts.map +1 -0
- package/dist/scanners/access/index.js +102 -0
- package/dist/scanners/access/index.js.map +1 -0
- package/dist/scanners/audit/index.d.ts +3 -0
- package/dist/scanners/audit/index.d.ts.map +1 -0
- package/dist/scanners/audit/index.js +94 -0
- package/dist/scanners/audit/index.js.map +1 -0
- package/dist/scanners/encryption/index.d.ts +3 -0
- package/dist/scanners/encryption/index.d.ts.map +1 -0
- package/dist/scanners/encryption/index.js +86 -0
- package/dist/scanners/encryption/index.js.map +1 -0
- package/dist/scanners/phi/index.d.ts +3 -0
- package/dist/scanners/phi/index.d.ts.map +1 -0
- package/dist/scanners/phi/index.js +47 -0
- package/dist/scanners/phi/index.js.map +1 -0
- package/dist/scanners/phi/patterns.d.ts +13 -0
- package/dist/scanners/phi/patterns.d.ts.map +1 -0
- package/dist/scanners/phi/patterns.js +242 -0
- package/dist/scanners/phi/patterns.js.map +1 -0
- package/dist/scanners/retention/index.d.ts +3 -0
- package/dist/scanners/retention/index.d.ts.map +1 -0
- package/dist/scanners/retention/index.js +102 -0
- package/dist/scanners/retention/index.js.map +1 -0
- package/dist/scanners/security/index.d.ts +3 -0
- package/dist/scanners/security/index.d.ts.map +1 -0
- package/dist/scanners/security/index.js +280 -0
- package/dist/scanners/security/index.js.map +1 -0
- package/dist/stack-detector/index.d.ts +26 -0
- package/dist/stack-detector/index.d.ts.map +1 -0
- package/dist/stack-detector/index.js +317 -0
- package/dist/stack-detector/index.js.map +1 -0
- package/dist/stack-detector/stack-guides.d.ts +16 -0
- package/dist/stack-detector/stack-guides.d.ts.map +1 -0
- package/dist/stack-detector/stack-guides.js +772 -0
- package/dist/stack-detector/stack-guides.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/context.d.ts +3 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +14 -0
- package/dist/utils/context.js.map +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon Franco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# vlayer - HIPAA Compliance Scanner
|
|
2
|
+
|
|
3
|
+
**Automated security scanning for healthcare applications.** Detect PHI exposure, fix vulnerabilities, and generate audit-ready compliance reports.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Francosimon53/verification-layer/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/verification-layer)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](package.json)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What is vlayer?
|
|
13
|
+
|
|
14
|
+
vlayer is a CLI tool that scans your codebase for HIPAA compliance issues. It's designed for healthcare startups and developers building applications that handle Protected Health Information (PHI).
|
|
15
|
+
|
|
16
|
+
**Key capabilities:**
|
|
17
|
+
- Scan for 50+ security vulnerabilities and PHI exposure patterns
|
|
18
|
+
- Auto-fix common issues with one command
|
|
19
|
+
- Generate professional audit reports (HTML, PDF, JSON)
|
|
20
|
+
- Detect your tech stack and provide tailored recommendations
|
|
21
|
+
- Create cryptographic audit trails for compliance documentation
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Install
|
|
29
|
+
npm install
|
|
30
|
+
npm run build
|
|
31
|
+
|
|
32
|
+
# Scan a project
|
|
33
|
+
node dist/cli.js scan /path/to/your/project
|
|
34
|
+
|
|
35
|
+
# Generate HTML report
|
|
36
|
+
node dist/cli.js scan /path/to/project -f html -o report.html
|
|
37
|
+
|
|
38
|
+
# Auto-fix issues
|
|
39
|
+
node dist/cli.js scan /path/to/project --fix
|
|
40
|
+
|
|
41
|
+
# Generate audit PDF
|
|
42
|
+
node dist/cli.js audit /path/to/project --generate-report
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
### 1. Vulnerability Detection
|
|
50
|
+
|
|
51
|
+
Scans for **50+ security patterns** across 5 HIPAA compliance categories:
|
|
52
|
+
|
|
53
|
+
| Category | What it detects |
|
|
54
|
+
|----------|-----------------|
|
|
55
|
+
| **PHI Exposure** | SSN/MRN in code, PHI in logs, localStorage, URLs |
|
|
56
|
+
| **Encryption** | Weak crypto (MD5, DES), disabled SSL/TLS, HTTP URLs |
|
|
57
|
+
| **Access Control** | SQL injection, XSS, CORS wildcards, hardcoded credentials |
|
|
58
|
+
| **Audit Logging** | Missing logging framework, unlogged PHI operations |
|
|
59
|
+
| **Data Retention** | Bulk deletes without audit, missing retention policies |
|
|
60
|
+
|
|
61
|
+
<details>
|
|
62
|
+
<summary><strong>View all detection patterns</strong></summary>
|
|
63
|
+
|
|
64
|
+
**PHI Exposure (18 patterns)**
|
|
65
|
+
- Social Security Numbers (XXX-XX-XXXX)
|
|
66
|
+
- Medical Record Numbers (MRN patterns)
|
|
67
|
+
- Date of Birth handling
|
|
68
|
+
- Diagnosis codes (ICD-10)
|
|
69
|
+
- PHI in console.log statements
|
|
70
|
+
- PHI in localStorage/sessionStorage
|
|
71
|
+
- Patient data in URLs
|
|
72
|
+
- Unencrypted patient contact info
|
|
73
|
+
|
|
74
|
+
**Security Vulnerabilities (20+ patterns)**
|
|
75
|
+
- Hardcoded passwords and secrets
|
|
76
|
+
- API keys (generic, Stripe, AWS)
|
|
77
|
+
- Database URIs with credentials
|
|
78
|
+
- SQL injection (template literals & concatenation)
|
|
79
|
+
- innerHTML without sanitization
|
|
80
|
+
- eval() and Function constructor
|
|
81
|
+
- dangerouslySetInnerHTML in React
|
|
82
|
+
|
|
83
|
+
**Infrastructure Issues**
|
|
84
|
+
- HTTP URLs for PHI transmission
|
|
85
|
+
- Disabled SSL/TLS verification
|
|
86
|
+
- CORS wildcard origins
|
|
87
|
+
- Sessions without expiration
|
|
88
|
+
- Missing authentication checks
|
|
89
|
+
|
|
90
|
+
</details>
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 2. Auto-Fix (`--fix`)
|
|
95
|
+
|
|
96
|
+
Automatically remediate common vulnerabilities:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
node dist/cli.js scan ./my-app --fix
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Issue | Auto-Fix Applied |
|
|
103
|
+
|-------|------------------|
|
|
104
|
+
| SQL injection | Convert to parameterized query `query('SELECT * FROM users WHERE id = ?', [id])` |
|
|
105
|
+
| Hardcoded password | Replace with `process.env.PASSWORD` |
|
|
106
|
+
| Hardcoded API key | Replace with `process.env.API_KEY` |
|
|
107
|
+
| HTTP URL | Upgrade to HTTPS |
|
|
108
|
+
| innerHTML | Replace with `textContent` |
|
|
109
|
+
| PHI in console.log | Comment out with review marker |
|
|
110
|
+
|
|
111
|
+
**Example output:**
|
|
112
|
+
```
|
|
113
|
+
✔ Scan complete. Found 29 issues.
|
|
114
|
+
✔ Applied 8 automatic fixes.
|
|
115
|
+
|
|
116
|
+
Changes by file:
|
|
117
|
+
src/api/users.ts
|
|
118
|
+
Line 45: SQL injection → parameterized query
|
|
119
|
+
Line 89: Hardcoded password → process.env.DB_PASSWORD
|
|
120
|
+
src/utils/logger.ts
|
|
121
|
+
Line 12: PHI in console.log → commented out
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 3. Stack Detection
|
|
127
|
+
|
|
128
|
+
vlayer automatically detects your tech stack and provides **personalized code examples**:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Stack detected:
|
|
132
|
+
Framework: Next.js
|
|
133
|
+
Database: Supabase
|
|
134
|
+
Auth: Supabase Auth
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Supported technologies:**
|
|
138
|
+
|
|
139
|
+
| Type | Detected |
|
|
140
|
+
|------|----------|
|
|
141
|
+
| Frameworks | Next.js, React, Vue, Nuxt, Angular, Express, Fastify, NestJS |
|
|
142
|
+
| Databases | Supabase, Firebase, PostgreSQL, MySQL, MongoDB, Prisma, Drizzle |
|
|
143
|
+
| Auth | NextAuth, Supabase Auth, Firebase Auth, Auth0, Clerk, Passport |
|
|
144
|
+
|
|
145
|
+
**Stack-specific recommendations include:**
|
|
146
|
+
|
|
147
|
+
- **Next.js + Supabase**: Server Components for PHI, Row Level Security (RLS), middleware protection
|
|
148
|
+
- **Express + PostgreSQL**: express-session with Redis, parameterized queries
|
|
149
|
+
- **React + Firebase**: Firestore Security Rules, Admin SDK for PHI
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### 4. Remediation Guides
|
|
154
|
+
|
|
155
|
+
Each finding includes a detailed remediation guide with:
|
|
156
|
+
|
|
157
|
+
- **HIPAA Impact**: Why this matters for compliance
|
|
158
|
+
- **Multiple fix options**: Different approaches with trade-offs
|
|
159
|
+
- **Code examples**: Copy-paste ready solutions
|
|
160
|
+
- **Documentation links**: Official resources
|
|
161
|
+
|
|
162
|
+
The guides are **personalized to your stack** - if you're using Supabase, you'll see Supabase-specific code examples, not generic SQL.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### 5. Audit Trail & PDF Reports
|
|
167
|
+
|
|
168
|
+
Generate compliance documentation with cryptographic verification:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Run scan with fixes (creates audit trail)
|
|
172
|
+
node dist/cli.js scan ./my-app --fix
|
|
173
|
+
|
|
174
|
+
# Generate PDF report
|
|
175
|
+
node dist/cli.js audit ./my-app --generate-report --org "Healthcare Inc" --auditor "Jane Smith"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Audit trail includes:**
|
|
179
|
+
|
|
180
|
+
| For Auto-Fixed Issues | For Manual Review Items |
|
|
181
|
+
|-----------------------|-------------------------|
|
|
182
|
+
| Code before & after | Status: "Pending human review" |
|
|
183
|
+
| SHA256 file hashes | Assigned responsible party |
|
|
184
|
+
| Timestamp of change | Suggested deadline by severity |
|
|
185
|
+
| HIPAA reference resolved | Full finding details |
|
|
186
|
+
|
|
187
|
+
**PDF Report sections:**
|
|
188
|
+
1. Cover Page - Project info, scan statistics
|
|
189
|
+
2. Executive Summary - Remediation rate, risk breakdown
|
|
190
|
+
3. Fix Evidence - Cryptographic proof of each change
|
|
191
|
+
4. Manual Reviews - Pending items with deadlines
|
|
192
|
+
5. Verification Page - Report hash, signature fields
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Report Examples
|
|
197
|
+
|
|
198
|
+
### HTML Report
|
|
199
|
+
|
|
200
|
+
The HTML report includes:
|
|
201
|
+
- Summary cards with severity counts
|
|
202
|
+
- Stack detection section with tailored recommendations
|
|
203
|
+
- Each finding with code context and line highlighting
|
|
204
|
+
- Expandable remediation guides with code examples
|
|
205
|
+
- Auto-fixable badge on issues that can be fixed automatically
|
|
206
|
+
|
|
207
|
+
### JSON Report
|
|
208
|
+
|
|
209
|
+
Machine-readable format for CI/CD integration:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"summary": {
|
|
214
|
+
"total": 29,
|
|
215
|
+
"critical": 8,
|
|
216
|
+
"high": 12,
|
|
217
|
+
"medium": 6,
|
|
218
|
+
"low": 3
|
|
219
|
+
},
|
|
220
|
+
"stack": {
|
|
221
|
+
"framework": "nextjs",
|
|
222
|
+
"database": "supabase",
|
|
223
|
+
"auth": "supabase-auth"
|
|
224
|
+
},
|
|
225
|
+
"findings": [...]
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Configuration
|
|
232
|
+
|
|
233
|
+
Create `.vlayerrc.json` in your project root:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"exclude": ["**/*.test.ts", "**/__mocks__/**"],
|
|
238
|
+
"ignorePaths": ["sample-data", "fixtures"],
|
|
239
|
+
"safeHttpDomains": ["my-internal-cdn.com"],
|
|
240
|
+
"contextLines": 3,
|
|
241
|
+
"categories": ["phi-exposure", "encryption", "access-control"]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
| Option | Description | Default |
|
|
246
|
+
|--------|-------------|---------|
|
|
247
|
+
| `exclude` | Glob patterns to skip | `[]` |
|
|
248
|
+
| `ignorePaths` | Path substrings to ignore | `[]` |
|
|
249
|
+
| `safeHttpDomains` | HTTP domains to allow (CDNs) | Built-in list |
|
|
250
|
+
| `contextLines` | Lines of context in reports | `2` |
|
|
251
|
+
| `categories` | Categories to scan | All |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## CLI Reference
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Basic scan
|
|
259
|
+
vlayer scan <path>
|
|
260
|
+
|
|
261
|
+
# Scan options
|
|
262
|
+
vlayer scan <path> -f html -o report.html # HTML report
|
|
263
|
+
vlayer scan <path> -f markdown -o report.md # Markdown report
|
|
264
|
+
vlayer scan <path> -c phi-exposure encryption # Specific categories
|
|
265
|
+
vlayer scan <path> --fix # Auto-fix issues
|
|
266
|
+
|
|
267
|
+
# Audit commands
|
|
268
|
+
vlayer audit <path> --summary # View audit summary
|
|
269
|
+
vlayer audit <path> --generate-report # Generate PDF
|
|
270
|
+
vlayer audit <path> --generate-report --text # Generate text instead
|
|
271
|
+
vlayer audit <path> --generate-report --org "Company" --auditor "Name"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Exit codes:**
|
|
275
|
+
- `0` - No critical issues
|
|
276
|
+
- `1` - Critical issues found (useful for CI/CD)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## HIPAA References
|
|
281
|
+
|
|
282
|
+
Each finding maps to specific HIPAA regulations:
|
|
283
|
+
|
|
284
|
+
| Reference | Requirement |
|
|
285
|
+
|-----------|-------------|
|
|
286
|
+
| §164.502, §164.514 | PHI disclosure and de-identification |
|
|
287
|
+
| §164.312(a)(1) | Access control mechanisms |
|
|
288
|
+
| §164.312(a)(2)(iv) | Encryption and decryption |
|
|
289
|
+
| §164.312(b) | Audit controls |
|
|
290
|
+
| §164.312(d) | Person or entity authentication |
|
|
291
|
+
| §164.312(e)(1) | Transmission security |
|
|
292
|
+
| §164.530(j) | Documentation retention (6 years) |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Roadmap
|
|
297
|
+
|
|
298
|
+
### Coming Soon
|
|
299
|
+
- [x] GitHub Action for CI/CD integration
|
|
300
|
+
- [ ] VS Code extension with inline warnings
|
|
301
|
+
- [ ] Slack/Teams notifications for new findings
|
|
302
|
+
- [ ] Custom rule definitions (YAML)
|
|
303
|
+
|
|
304
|
+
### Planned
|
|
305
|
+
- [ ] HITRUST CSF mapping
|
|
306
|
+
- [ ] SOC 2 compliance checks
|
|
307
|
+
- [ ] AWS/GCP/Azure infrastructure scanning
|
|
308
|
+
- [ ] Team dashboard with trend tracking
|
|
309
|
+
- [ ] Jira/Linear integration for issue tracking
|
|
310
|
+
|
|
311
|
+
### Future
|
|
312
|
+
- [ ] AI-powered fix suggestions
|
|
313
|
+
- [ ] Dependency vulnerability scanning
|
|
314
|
+
- [ ] Runtime PHI detection agent
|
|
315
|
+
- [ ] Compliance certification workflows
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Contributing
|
|
320
|
+
|
|
321
|
+
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Development
|
|
325
|
+
npm install
|
|
326
|
+
npm run dev # Watch mode
|
|
327
|
+
npm run test # Run tests
|
|
328
|
+
npm run lint # Lint code
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## License
|
|
334
|
+
|
|
335
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
<p align="center">
|
|
340
|
+
Built for healthcare developers who take compliance seriously.
|
|
341
|
+
<br>
|
|
342
|
+
<a href="https://github.com/Francosimon53/verification-layer/issues">Report Bug</a>
|
|
343
|
+
·
|
|
344
|
+
<a href="https://github.com/Francosimon53/verification-layer/issues">Request Feature</a>
|
|
345
|
+
</p>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuditEvidence, CodeSnapshot, Finding, FixType } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate SHA256 hash of file content
|
|
4
|
+
*/
|
|
5
|
+
export declare function hashContent(content: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Extract code snapshot with context lines
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractCodeSnapshot(lines: string[], lineNumber: number, contextSize?: number): CodeSnapshot;
|
|
10
|
+
/**
|
|
11
|
+
* Create audit evidence for a fix
|
|
12
|
+
*/
|
|
13
|
+
export declare function createEvidence(finding: Finding, filePath: string, contentBefore: string, contentAfter: string, lineNumber: number, fixType: FixType): Promise<AuditEvidence>;
|
|
14
|
+
/**
|
|
15
|
+
* Generate a hash for the entire audit trail (for verification)
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateAuditTrailHash(evidence: AuditEvidence[]): string;
|
|
18
|
+
/**
|
|
19
|
+
* Read file and compute hash
|
|
20
|
+
*/
|
|
21
|
+
export declare function getFileWithHash(filePath: string): Promise<{
|
|
22
|
+
content: string;
|
|
23
|
+
hash: string;
|
|
24
|
+
}>;
|
|
25
|
+
//# sourceMappingURL=evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/audit/evidence.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAe,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE9F;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,GAAE,MAAU,GACtB,YAAY,CAkBd;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CAoBxB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAKxE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAMlG"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
/**
|
|
4
|
+
* Generate SHA256 hash of file content
|
|
5
|
+
*/
|
|
6
|
+
export function hashContent(content) {
|
|
7
|
+
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract code snapshot with context lines
|
|
11
|
+
*/
|
|
12
|
+
export function extractCodeSnapshot(lines, lineNumber, contextSize = 3) {
|
|
13
|
+
const context = [];
|
|
14
|
+
const start = Math.max(0, lineNumber - contextSize);
|
|
15
|
+
const end = Math.min(lines.length - 1, lineNumber + contextSize);
|
|
16
|
+
for (let i = start; i <= end; i++) {
|
|
17
|
+
context.push({
|
|
18
|
+
lineNumber: i + 1,
|
|
19
|
+
content: lines[i] || '',
|
|
20
|
+
isMatch: i === lineNumber,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
content: lines[lineNumber] || '',
|
|
25
|
+
context,
|
|
26
|
+
lineNumber: lineNumber + 1,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create audit evidence for a fix
|
|
31
|
+
*/
|
|
32
|
+
export async function createEvidence(finding, filePath, contentBefore, contentAfter, lineNumber, fixType) {
|
|
33
|
+
const linesBefore = contentBefore.split('\n');
|
|
34
|
+
const linesAfter = contentAfter.split('\n');
|
|
35
|
+
const before = extractCodeSnapshot(linesBefore, lineNumber);
|
|
36
|
+
const after = extractCodeSnapshot(linesAfter, lineNumber);
|
|
37
|
+
return {
|
|
38
|
+
id: randomUUID(),
|
|
39
|
+
findingId: finding.id,
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
filePath,
|
|
42
|
+
before,
|
|
43
|
+
after,
|
|
44
|
+
fileHashBefore: hashContent(contentBefore),
|
|
45
|
+
fileHashAfter: hashContent(contentAfter),
|
|
46
|
+
hipaaReference: finding.hipaaReference || 'General HIPAA Security Rule',
|
|
47
|
+
fixType,
|
|
48
|
+
description: `Auto-fixed: ${finding.title}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generate a hash for the entire audit trail (for verification)
|
|
53
|
+
*/
|
|
54
|
+
export function generateAuditTrailHash(evidence) {
|
|
55
|
+
const evidenceStr = evidence
|
|
56
|
+
.map(e => `${e.id}|${e.fileHashBefore}|${e.fileHashAfter}|${e.timestamp}`)
|
|
57
|
+
.join('\n');
|
|
58
|
+
return hashContent(evidenceStr);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read file and compute hash
|
|
62
|
+
*/
|
|
63
|
+
export async function getFileWithHash(filePath) {
|
|
64
|
+
const content = await readFile(filePath, 'utf-8');
|
|
65
|
+
return {
|
|
66
|
+
content,
|
|
67
|
+
hash: hashContent(content),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/audit/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AuditTrail, AuditEvidence, ManualReviewItem, Finding, ManualReviewStatus } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a new audit trail
|
|
4
|
+
*/
|
|
5
|
+
export declare function createAuditTrail(projectPath: string): AuditTrail;
|
|
6
|
+
/**
|
|
7
|
+
* Add evidence to audit trail
|
|
8
|
+
*/
|
|
9
|
+
export declare function addEvidence(trail: AuditTrail, evidence: AuditEvidence): void;
|
|
10
|
+
/**
|
|
11
|
+
* Create a manual review item for findings that cannot be auto-fixed
|
|
12
|
+
*/
|
|
13
|
+
export declare function createManualReview(finding: Finding): ManualReviewItem;
|
|
14
|
+
/**
|
|
15
|
+
* Add manual review items for non-fixable findings
|
|
16
|
+
*/
|
|
17
|
+
export declare function addManualReviews(trail: AuditTrail, findings: Finding[]): void;
|
|
18
|
+
/**
|
|
19
|
+
* Update scan statistics
|
|
20
|
+
*/
|
|
21
|
+
export declare function updateScanStats(trail: AuditTrail, totalFindings: number, scannedFiles: number, scanDuration: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Finalize audit trail with hash
|
|
24
|
+
*/
|
|
25
|
+
export declare function finalizeAuditTrail(trail: AuditTrail): void;
|
|
26
|
+
/**
|
|
27
|
+
* Save audit trail to file
|
|
28
|
+
*/
|
|
29
|
+
export declare function saveAuditTrail(trail: AuditTrail, projectPath: string): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Load existing audit trail
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadAuditTrail(projectPath: string): Promise<AuditTrail | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Get audit trail file path
|
|
36
|
+
*/
|
|
37
|
+
export declare function getAuditTrailPath(projectPath: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Update manual review status
|
|
40
|
+
*/
|
|
41
|
+
export declare function updateManualReviewStatus(trail: AuditTrail, reviewId: string, status: ManualReviewStatus, assignedTo?: string, notes?: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get summary statistics from audit trail
|
|
44
|
+
*/
|
|
45
|
+
export declare function getAuditSummary(trail: AuditTrail): {
|
|
46
|
+
totalFindings: number;
|
|
47
|
+
autoFixed: number;
|
|
48
|
+
pendingManualReview: number;
|
|
49
|
+
reviewsByStatus: Record<string, number>;
|
|
50
|
+
reviewsBySeverity: Record<string, number>;
|
|
51
|
+
overdueCount: number;
|
|
52
|
+
reportHash: string | undefined;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAMrB;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAchE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAG5E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA4BrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAO7E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,IAAI,CAIN;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAE1D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY5F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CASpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,EAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAUT;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU;;;;;;;;EAwBhD"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
3
|
+
import { basename, join } from 'path';
|
|
4
|
+
import { generateAuditTrailHash } from './evidence.js';
|
|
5
|
+
const AUDIT_DIR = '.vlayer';
|
|
6
|
+
const AUDIT_FILE = 'audit-trail.json';
|
|
7
|
+
/**
|
|
8
|
+
* Create a new audit trail
|
|
9
|
+
*/
|
|
10
|
+
export function createAuditTrail(projectPath) {
|
|
11
|
+
return {
|
|
12
|
+
id: randomUUID(),
|
|
13
|
+
createdAt: new Date().toISOString(),
|
|
14
|
+
projectPath,
|
|
15
|
+
projectName: basename(projectPath),
|
|
16
|
+
scanDuration: 0,
|
|
17
|
+
scannedFiles: 0,
|
|
18
|
+
totalFindings: 0,
|
|
19
|
+
autoFixedCount: 0,
|
|
20
|
+
manualReviewCount: 0,
|
|
21
|
+
evidence: [],
|
|
22
|
+
manualReviews: [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Add evidence to audit trail
|
|
27
|
+
*/
|
|
28
|
+
export function addEvidence(trail, evidence) {
|
|
29
|
+
trail.evidence.push(evidence);
|
|
30
|
+
trail.autoFixedCount = trail.evidence.length;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a manual review item for findings that cannot be auto-fixed
|
|
34
|
+
*/
|
|
35
|
+
export function createManualReview(finding) {
|
|
36
|
+
const now = new Date();
|
|
37
|
+
const suggestedDeadline = new Date(now);
|
|
38
|
+
// Set deadline based on severity
|
|
39
|
+
switch (finding.severity) {
|
|
40
|
+
case 'critical':
|
|
41
|
+
suggestedDeadline.setDate(now.getDate() + 7); // 1 week
|
|
42
|
+
break;
|
|
43
|
+
case 'high':
|
|
44
|
+
suggestedDeadline.setDate(now.getDate() + 14); // 2 weeks
|
|
45
|
+
break;
|
|
46
|
+
case 'medium':
|
|
47
|
+
suggestedDeadline.setDate(now.getDate() + 30); // 1 month
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
suggestedDeadline.setDate(now.getDate() + 60); // 2 months
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
id: randomUUID(),
|
|
54
|
+
findingId: finding.id,
|
|
55
|
+
finding,
|
|
56
|
+
status: 'pending_review',
|
|
57
|
+
suggestedDeadline: suggestedDeadline.toISOString(),
|
|
58
|
+
createdAt: now.toISOString(),
|
|
59
|
+
updatedAt: now.toISOString(),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Add manual review items for non-fixable findings
|
|
64
|
+
*/
|
|
65
|
+
export function addManualReviews(trail, findings) {
|
|
66
|
+
const nonFixableFindings = findings.filter(f => !f.fixType);
|
|
67
|
+
for (const finding of nonFixableFindings) {
|
|
68
|
+
const review = createManualReview(finding);
|
|
69
|
+
trail.manualReviews.push(review);
|
|
70
|
+
}
|
|
71
|
+
trail.manualReviewCount = trail.manualReviews.length;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update scan statistics
|
|
75
|
+
*/
|
|
76
|
+
export function updateScanStats(trail, totalFindings, scannedFiles, scanDuration) {
|
|
77
|
+
trail.totalFindings = totalFindings;
|
|
78
|
+
trail.scannedFiles = scannedFiles;
|
|
79
|
+
trail.scanDuration = scanDuration;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Finalize audit trail with hash
|
|
83
|
+
*/
|
|
84
|
+
export function finalizeAuditTrail(trail) {
|
|
85
|
+
trail.reportHash = generateAuditTrailHash(trail.evidence);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Save audit trail to file
|
|
89
|
+
*/
|
|
90
|
+
export async function saveAuditTrail(trail, projectPath) {
|
|
91
|
+
const auditDir = join(projectPath, AUDIT_DIR);
|
|
92
|
+
const auditPath = join(auditDir, AUDIT_FILE);
|
|
93
|
+
try {
|
|
94
|
+
await mkdir(auditDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Directory may already exist
|
|
98
|
+
}
|
|
99
|
+
await writeFile(auditPath, JSON.stringify(trail, null, 2), 'utf-8');
|
|
100
|
+
return auditPath;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Load existing audit trail
|
|
104
|
+
*/
|
|
105
|
+
export async function loadAuditTrail(projectPath) {
|
|
106
|
+
const auditPath = join(projectPath, AUDIT_DIR, AUDIT_FILE);
|
|
107
|
+
try {
|
|
108
|
+
const content = await readFile(auditPath, 'utf-8');
|
|
109
|
+
return JSON.parse(content);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get audit trail file path
|
|
117
|
+
*/
|
|
118
|
+
export function getAuditTrailPath(projectPath) {
|
|
119
|
+
return join(projectPath, AUDIT_DIR, AUDIT_FILE);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Update manual review status
|
|
123
|
+
*/
|
|
124
|
+
export function updateManualReviewStatus(trail, reviewId, status, assignedTo, notes) {
|
|
125
|
+
const review = trail.manualReviews.find(r => r.id === reviewId);
|
|
126
|
+
if (!review)
|
|
127
|
+
return false;
|
|
128
|
+
review.status = status;
|
|
129
|
+
review.updatedAt = new Date().toISOString();
|
|
130
|
+
if (assignedTo)
|
|
131
|
+
review.assignedTo = assignedTo;
|
|
132
|
+
if (notes)
|
|
133
|
+
review.notes = notes;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get summary statistics from audit trail
|
|
138
|
+
*/
|
|
139
|
+
export function getAuditSummary(trail) {
|
|
140
|
+
const reviewsByStatus = trail.manualReviews.reduce((acc, review) => {
|
|
141
|
+
acc[review.status] = (acc[review.status] || 0) + 1;
|
|
142
|
+
return acc;
|
|
143
|
+
}, {});
|
|
144
|
+
const reviewsBySeverity = trail.manualReviews.reduce((acc, review) => {
|
|
145
|
+
acc[review.finding.severity] = (acc[review.finding.severity] || 0) + 1;
|
|
146
|
+
return acc;
|
|
147
|
+
}, {});
|
|
148
|
+
const overdueReviews = trail.manualReviews.filter(r => new Date(r.suggestedDeadline) < new Date() && r.status === 'pending_review');
|
|
149
|
+
return {
|
|
150
|
+
totalFindings: trail.totalFindings,
|
|
151
|
+
autoFixed: trail.autoFixedCount,
|
|
152
|
+
pendingManualReview: trail.manualReviewCount,
|
|
153
|
+
reviewsByStatus,
|
|
154
|
+
reviewsBySeverity,
|
|
155
|
+
overdueCount: overdueReviews.length,
|
|
156
|
+
reportHash: trail.reportHash,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=index.js.map
|