verification-layer 0.20.0 → 0.22.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 +251 -615
- package/dist/cli.js +542 -0
- package/dist/cli.js.map +1 -1
- package/dist/marketplace/index.d.ts +8 -0
- package/dist/marketplace/index.d.ts.map +1 -0
- package/dist/marketplace/index.js +7 -0
- package/dist/marketplace/index.js.map +1 -0
- package/dist/marketplace/installer.d.ts +62 -0
- package/dist/marketplace/installer.d.ts.map +1 -0
- package/dist/marketplace/installer.js +254 -0
- package/dist/marketplace/installer.js.map +1 -0
- package/dist/marketplace/registry.d.ts +52 -0
- package/dist/marketplace/registry.d.ts.map +1 -0
- package/dist/marketplace/registry.js +759 -0
- package/dist/marketplace/registry.js.map +1 -0
- package/dist/marketplace/types.d.ts +123 -0
- package/dist/marketplace/types.d.ts.map +1 -0
- package/dist/marketplace/types.js +6 -0
- package/dist/marketplace/types.js.map +1 -0
- package/dist/reporters/audit-report.d.ts.map +1 -1
- package/dist/reporters/audit-report.js +180 -0
- package/dist/reporters/audit-report.js.map +1 -1
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js +2612 -5
- package/dist/reporters/index.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +15 -1
- package/dist/scan.js.map +1 -1
- package/dist/scanners/api-security/index.d.ts +7 -0
- package/dist/scanners/api-security/index.d.ts.map +1 -0
- package/dist/scanners/api-security/index.js +139 -0
- package/dist/scanners/api-security/index.js.map +1 -0
- package/dist/scanners/api-security/index.test.d.ts +5 -0
- package/dist/scanners/api-security/index.test.d.ts.map +1 -0
- package/dist/scanners/api-security/index.test.js +360 -0
- package/dist/scanners/api-security/index.test.js.map +1 -0
- package/dist/scanners/api-security/patterns.d.ts +32 -0
- package/dist/scanners/api-security/patterns.d.ts.map +1 -0
- package/dist/scanners/api-security/patterns.js +159 -0
- package/dist/scanners/api-security/patterns.js.map +1 -0
- package/dist/scanners/authentication/index.d.ts +7 -0
- package/dist/scanners/authentication/index.d.ts.map +1 -0
- package/dist/scanners/authentication/index.js +107 -0
- package/dist/scanners/authentication/index.js.map +1 -0
- package/dist/scanners/authentication/index.test.d.ts +5 -0
- package/dist/scanners/authentication/index.test.d.ts.map +1 -0
- package/dist/scanners/authentication/index.test.js +379 -0
- package/dist/scanners/authentication/index.test.js.map +1 -0
- package/dist/scanners/authentication/patterns.d.ts +32 -0
- package/dist/scanners/authentication/patterns.d.ts.map +1 -0
- package/dist/scanners/authentication/patterns.js +133 -0
- package/dist/scanners/authentication/patterns.js.map +1 -0
- package/dist/scanners/configuration/index.d.ts +8 -0
- package/dist/scanners/configuration/index.d.ts.map +1 -0
- package/dist/scanners/configuration/index.js +87 -0
- package/dist/scanners/configuration/index.js.map +1 -0
- package/dist/scanners/configuration/index.test.d.ts +5 -0
- package/dist/scanners/configuration/index.test.d.ts.map +1 -0
- package/dist/scanners/configuration/index.test.js +344 -0
- package/dist/scanners/configuration/index.test.js.map +1 -0
- package/dist/scanners/configuration/patterns.d.ts +32 -0
- package/dist/scanners/configuration/patterns.d.ts.map +1 -0
- package/dist/scanners/configuration/patterns.js +146 -0
- package/dist/scanners/configuration/patterns.js.map +1 -0
- package/dist/scanners/credentials/index.d.ts +7 -0
- package/dist/scanners/credentials/index.d.ts.map +1 -0
- package/dist/scanners/credentials/index.js +129 -0
- package/dist/scanners/credentials/index.js.map +1 -0
- package/dist/scanners/credentials/index.test.d.ts +5 -0
- package/dist/scanners/credentials/index.test.d.ts.map +1 -0
- package/dist/scanners/credentials/index.test.js +395 -0
- package/dist/scanners/credentials/index.test.js.map +1 -0
- package/dist/scanners/credentials/patterns.d.ts +32 -0
- package/dist/scanners/credentials/patterns.d.ts.map +1 -0
- package/dist/scanners/credentials/patterns.js +140 -0
- package/dist/scanners/credentials/patterns.js.map +1 -0
- package/dist/scanners/errors/index.d.ts +8 -0
- package/dist/scanners/errors/index.d.ts.map +1 -0
- package/dist/scanners/errors/index.js +78 -0
- package/dist/scanners/errors/index.js.map +1 -0
- package/dist/scanners/errors/index.test.d.ts +5 -0
- package/dist/scanners/errors/index.test.d.ts.map +1 -0
- package/dist/scanners/errors/index.test.js +330 -0
- package/dist/scanners/errors/index.test.js.map +1 -0
- package/dist/scanners/errors/patterns.d.ts +27 -0
- package/dist/scanners/errors/patterns.d.ts.map +1 -0
- package/dist/scanners/errors/patterns.js +97 -0
- package/dist/scanners/errors/patterns.js.map +1 -0
- package/dist/scanners/hipaa2026/index.d.ts +8 -0
- package/dist/scanners/hipaa2026/index.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/index.js +345 -0
- package/dist/scanners/hipaa2026/index.js.map +1 -0
- package/dist/scanners/hipaa2026/index.test.d.ts +5 -0
- package/dist/scanners/hipaa2026/index.test.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/index.test.js +332 -0
- package/dist/scanners/hipaa2026/index.test.js.map +1 -0
- package/dist/scanners/hipaa2026/patterns.d.ts +57 -0
- package/dist/scanners/hipaa2026/patterns.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/patterns.js +268 -0
- package/dist/scanners/hipaa2026/patterns.js.map +1 -0
- package/dist/scanners/operational/index.d.ts +7 -0
- package/dist/scanners/operational/index.d.ts.map +1 -0
- package/dist/scanners/operational/index.js +171 -0
- package/dist/scanners/operational/index.js.map +1 -0
- package/dist/scanners/operational/index.test.d.ts +5 -0
- package/dist/scanners/operational/index.test.d.ts.map +1 -0
- package/dist/scanners/operational/index.test.js +406 -0
- package/dist/scanners/operational/index.test.js.map +1 -0
- package/dist/scanners/operational/patterns.d.ts +33 -0
- package/dist/scanners/operational/patterns.d.ts.map +1 -0
- package/dist/scanners/operational/patterns.js +151 -0
- package/dist/scanners/operational/patterns.js.map +1 -0
- package/dist/scanners/rbac/index.d.ts +7 -0
- package/dist/scanners/rbac/index.d.ts.map +1 -0
- package/dist/scanners/rbac/index.js +145 -0
- package/dist/scanners/rbac/index.js.map +1 -0
- package/dist/scanners/rbac/index.test.d.ts +5 -0
- package/dist/scanners/rbac/index.test.d.ts.map +1 -0
- package/dist/scanners/rbac/index.test.js +422 -0
- package/dist/scanners/rbac/index.test.js.map +1 -0
- package/dist/scanners/rbac/patterns.d.ts +32 -0
- package/dist/scanners/rbac/patterns.d.ts.map +1 -0
- package/dist/scanners/rbac/patterns.js +124 -0
- package/dist/scanners/rbac/patterns.js.map +1 -0
- package/dist/scanners/revocation/index.d.ts +8 -0
- package/dist/scanners/revocation/index.d.ts.map +1 -0
- package/dist/scanners/revocation/index.js +83 -0
- package/dist/scanners/revocation/index.js.map +1 -0
- package/dist/scanners/revocation/index.test.d.ts +5 -0
- package/dist/scanners/revocation/index.test.d.ts.map +1 -0
- package/dist/scanners/revocation/index.test.js +332 -0
- package/dist/scanners/revocation/index.test.js.map +1 -0
- package/dist/scanners/revocation/patterns.d.ts +27 -0
- package/dist/scanners/revocation/patterns.d.ts.map +1 -0
- package/dist/scanners/revocation/patterns.js +109 -0
- package/dist/scanners/revocation/patterns.js.map +1 -0
- package/dist/scanners/sanitization/index.d.ts +8 -0
- package/dist/scanners/sanitization/index.d.ts.map +1 -0
- package/dist/scanners/sanitization/index.js +98 -0
- package/dist/scanners/sanitization/index.js.map +1 -0
- package/dist/scanners/sanitization/index.test.d.ts +5 -0
- package/dist/scanners/sanitization/index.test.d.ts.map +1 -0
- package/dist/scanners/sanitization/index.test.js +370 -0
- package/dist/scanners/sanitization/index.test.js.map +1 -0
- package/dist/scanners/sanitization/patterns.d.ts +27 -0
- package/dist/scanners/sanitization/patterns.d.ts.map +1 -0
- package/dist/scanners/sanitization/patterns.js +117 -0
- package/dist/scanners/sanitization/patterns.js.map +1 -0
- package/dist/training/certificate.d.ts +26 -0
- package/dist/training/certificate.d.ts.map +1 -0
- package/dist/training/certificate.js +92 -0
- package/dist/training/certificate.js.map +1 -0
- package/dist/training/index.d.ts +3 -0
- package/dist/training/index.d.ts.map +1 -0
- package/dist/training/index.js +243 -0
- package/dist/training/index.js.map +1 -0
- package/dist/training/modules.d.ts +13 -0
- package/dist/training/modules.d.ts.map +1 -0
- package/dist/training/modules.js +608 -0
- package/dist/training/modules.js.map +1 -0
- package/dist/training/questions.d.ts +9 -0
- package/dist/training/questions.d.ts.map +1 -0
- package/dist/training/questions.js +505 -0
- package/dist/training/questions.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/npm-audit.d.ts +6 -0
- package/dist/utils/npm-audit.d.ts.map +1 -0
- package/dist/utils/npm-audit.js +95 -0
- package/dist/utils/npm-audit.js.map +1 -0
- package/dist/utils/scan-history.d.ts +59 -0
- package/dist/utils/scan-history.d.ts.map +1 -0
- package/dist/utils/scan-history.js +170 -0
- package/dist/utils/scan-history.js.map +1 -0
- package/package.json +4 -1
- package/templates/baa-verification-letter.md +105 -0
- package/templates/irp.md +545 -0
- package/templates/notice-of-privacy-practices.md +491 -0
- package/templates/physical-safeguards-checklist.md +247 -0
- package/templates/security-officer-designation.md +237 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Security Scanner
|
|
3
|
+
* Detects unsafe error responses and PHI in error logs
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import { ALL_ERROR_PATTERNS } from './patterns.js';
|
|
7
|
+
export const errorsScanner = {
|
|
8
|
+
name: 'Error Handling Security Scanner',
|
|
9
|
+
category: 'audit-logging',
|
|
10
|
+
async scan(files, options) {
|
|
11
|
+
const findings = [];
|
|
12
|
+
// Filter to code files
|
|
13
|
+
const codeFiles = files.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f));
|
|
14
|
+
for (const file of codeFiles) {
|
|
15
|
+
try {
|
|
16
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
17
|
+
const lines = content.split('\n');
|
|
18
|
+
// Check if this is a test file (check filename)
|
|
19
|
+
const isTestFile = /\.(?:test|spec)\.[jt]sx?$/.test(file);
|
|
20
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21
|
+
const line = lines[i];
|
|
22
|
+
const lineNumber = i + 1;
|
|
23
|
+
// Skip empty lines
|
|
24
|
+
if (/^\s*$/.test(line))
|
|
25
|
+
continue;
|
|
26
|
+
// Scan each pattern
|
|
27
|
+
for (const pattern of ALL_ERROR_PATTERNS) {
|
|
28
|
+
// Check if line matches any pattern
|
|
29
|
+
const matched = pattern.patterns.some((regex) => regex.test(line));
|
|
30
|
+
if (!matched)
|
|
31
|
+
continue;
|
|
32
|
+
// For ERROR-002, skip test files
|
|
33
|
+
if (pattern.id === 'ERROR-002' && isTestFile)
|
|
34
|
+
continue;
|
|
35
|
+
// Get surrounding context (5 lines before and after)
|
|
36
|
+
const contextStart = Math.max(0, i - 5);
|
|
37
|
+
const contextEnd = Math.min(lines.length, i + 6);
|
|
38
|
+
const context = lines.slice(contextStart, contextEnd).join('\n');
|
|
39
|
+
// Check negative patterns (safe usage indicators)
|
|
40
|
+
// For ERROR-001, only check development env in context, rest in current line
|
|
41
|
+
// For ERROR-002, check redaction/masking in context, test file in filename
|
|
42
|
+
const isSafe = pattern.negativePatterns?.some((regex) => {
|
|
43
|
+
const patternStr = regex.source;
|
|
44
|
+
// Check development environment and redaction/masking in context
|
|
45
|
+
if (/NODE_ENV|isDevelopment|redact|mask|sanitize|obfuscate/i.test(patternStr)) {
|
|
46
|
+
return regex.test(context);
|
|
47
|
+
}
|
|
48
|
+
// All other patterns check current line only
|
|
49
|
+
return regex.test(line);
|
|
50
|
+
});
|
|
51
|
+
if (isSafe)
|
|
52
|
+
continue;
|
|
53
|
+
// Create finding
|
|
54
|
+
const finding = {
|
|
55
|
+
id: pattern.id,
|
|
56
|
+
category: pattern.category,
|
|
57
|
+
severity: pattern.severity,
|
|
58
|
+
title: pattern.name,
|
|
59
|
+
description: `${pattern.description}\n\nCode: ${line.trim()}`,
|
|
60
|
+
file: file,
|
|
61
|
+
line: lineNumber,
|
|
62
|
+
recommendation: pattern.recommendation,
|
|
63
|
+
hipaaReference: pattern.hipaaReference,
|
|
64
|
+
confidence: 'high',
|
|
65
|
+
};
|
|
66
|
+
findings.push(finding);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// Skip files that can't be read
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return findings;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
export default errorsScanner;
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scanners/errors/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/scanners/errors/index.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Security Scanner Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { errorsScanner } from './index.js';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
describe('Error Handling Security Scanner', () => {
|
|
10
|
+
let tempDir = '';
|
|
11
|
+
let testFiles = [];
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'errors-test-'));
|
|
14
|
+
});
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
// Cleanup
|
|
17
|
+
for (const file of testFiles) {
|
|
18
|
+
try {
|
|
19
|
+
await fs.unlink(file);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Ignore
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore
|
|
30
|
+
}
|
|
31
|
+
testFiles = [];
|
|
32
|
+
});
|
|
33
|
+
async function createTestFile(filename, content) {
|
|
34
|
+
const filePath = path.join(tempDir, filename);
|
|
35
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
36
|
+
testFiles.push(filePath);
|
|
37
|
+
return filePath;
|
|
38
|
+
}
|
|
39
|
+
const scanOptions = {
|
|
40
|
+
path: tempDir,
|
|
41
|
+
};
|
|
42
|
+
describe('ERROR-001: Unsanitized Error Details Sent to User', () => {
|
|
43
|
+
it('should detect error.stack in res.send', async () => {
|
|
44
|
+
const file = await createTestFile('error-stack.ts', `
|
|
45
|
+
// VIOLATION ERROR-001: Sending error.stack to user
|
|
46
|
+
app.get('/api/data', (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
processData();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
res.send(error.stack);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
`);
|
|
54
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
55
|
+
expect(findings.length).toBeGreaterThan(0);
|
|
56
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('should detect error.stack in res.json', async () => {
|
|
59
|
+
const file = await createTestFile('error-json.ts', `
|
|
60
|
+
// VIOLATION ERROR-001: JSON with error.stack
|
|
61
|
+
export async function handler(req, res) {
|
|
62
|
+
try {
|
|
63
|
+
await fetchData();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
res.json({ error: err.stack });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`);
|
|
69
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
70
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
it('should detect error.message in res.send', async () => {
|
|
73
|
+
const file = await createTestFile('error-message.ts', `
|
|
74
|
+
// VIOLATION ERROR-001: Sending error.message
|
|
75
|
+
catch (error) {
|
|
76
|
+
res.send(error.message);
|
|
77
|
+
}
|
|
78
|
+
`);
|
|
79
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
80
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
it('should detect error object sent directly', async () => {
|
|
83
|
+
const file = await createTestFile('error-direct.ts', `
|
|
84
|
+
// VIOLATION ERROR-001: Sending full error object
|
|
85
|
+
catch (err) {
|
|
86
|
+
return res.json(err);
|
|
87
|
+
}
|
|
88
|
+
`);
|
|
89
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
90
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
it('should detect error in catch block response', async () => {
|
|
93
|
+
const file = await createTestFile('catch-error.ts', `
|
|
94
|
+
// VIOLATION ERROR-001: Catch block sends error
|
|
95
|
+
try {
|
|
96
|
+
doSomething();
|
|
97
|
+
} catch (error) { res.send(error); }
|
|
98
|
+
`);
|
|
99
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
100
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it('should detect next(error) pattern', async () => {
|
|
103
|
+
const file = await createTestFile('next-error.ts', `
|
|
104
|
+
// VIOLATION ERROR-001: Passing error to next
|
|
105
|
+
catch (error) {
|
|
106
|
+
next(error);
|
|
107
|
+
}
|
|
108
|
+
`);
|
|
109
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
110
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('should NOT flag sanitized errors', async () => {
|
|
113
|
+
const file = await createTestFile('sanitized.ts', `
|
|
114
|
+
// SECURE: Sanitized error message
|
|
115
|
+
catch (error) {
|
|
116
|
+
const sanitizedError = sanitizeError(error);
|
|
117
|
+
res.send(sanitizedError);
|
|
118
|
+
}
|
|
119
|
+
`);
|
|
120
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
121
|
+
const error001 = findings.filter((f) => f.id === 'ERROR-001');
|
|
122
|
+
expect(error001.length).toBe(0);
|
|
123
|
+
});
|
|
124
|
+
it('should NOT flag generic error messages', async () => {
|
|
125
|
+
const file = await createTestFile('generic.ts', `
|
|
126
|
+
// SECURE: Generic error message
|
|
127
|
+
catch (error) {
|
|
128
|
+
res.status(500).json({ error: "An error occurred" });
|
|
129
|
+
}
|
|
130
|
+
`);
|
|
131
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
132
|
+
const error001 = findings.filter((f) => f.id === 'ERROR-001');
|
|
133
|
+
expect(error001.length).toBe(0);
|
|
134
|
+
});
|
|
135
|
+
it('should NOT flag console logging (not sent to user)', async () => {
|
|
136
|
+
const file = await createTestFile('console.ts', `
|
|
137
|
+
// SECURE: Logging error.stack (not sending to user)
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error(error.stack);
|
|
140
|
+
res.status(500).json({ error: "Internal server error" });
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
144
|
+
const error001 = findings.filter((f) => f.id === 'ERROR-001');
|
|
145
|
+
expect(error001.length).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
it('should NOT flag development environment checks', async () => {
|
|
148
|
+
const file = await createTestFile('dev-env.ts', `
|
|
149
|
+
// SECURE: Development mode only
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (process.env.NODE_ENV === 'development') {
|
|
152
|
+
res.send(error.stack);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`);
|
|
156
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
157
|
+
const error001 = findings.filter((f) => f.id === 'ERROR-001');
|
|
158
|
+
expect(error001.length).toBe(0);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('ERROR-002: PHI in Error Logs or Thrown Errors', () => {
|
|
162
|
+
it('should detect patient data in console.log', async () => {
|
|
163
|
+
const file = await createTestFile('console-patient.ts', `
|
|
164
|
+
// VIOLATION ERROR-002: PHI in console.log
|
|
165
|
+
function processPatient(patient) {
|
|
166
|
+
console.log('Processing patient:', patient);
|
|
167
|
+
}
|
|
168
|
+
`);
|
|
169
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
170
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
it('should detect SSN in console.error', async () => {
|
|
173
|
+
const file = await createTestFile('console-ssn.ts', `
|
|
174
|
+
// VIOLATION ERROR-002: SSN in error log
|
|
175
|
+
if (!validSSN(ssn)) {
|
|
176
|
+
console.error('Invalid SSN:', ssn);
|
|
177
|
+
}
|
|
178
|
+
`);
|
|
179
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
180
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
it('should detect DOB in logger.warn', async () => {
|
|
183
|
+
const file = await createTestFile('logger-dob.ts', `
|
|
184
|
+
// VIOLATION ERROR-002: DOB in logger
|
|
185
|
+
logger.warn('Missing DOB for patient:', { dob: patient.dob });
|
|
186
|
+
`);
|
|
187
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
188
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
it('should detect MRN in throw Error', async () => {
|
|
191
|
+
const file = await createTestFile('throw-mrn.ts', `
|
|
192
|
+
// VIOLATION ERROR-002: MRN in thrown error
|
|
193
|
+
if (!patient) {
|
|
194
|
+
throw new Error('Patient not found with MRN: ' + mrn);
|
|
195
|
+
}
|
|
196
|
+
`);
|
|
197
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
198
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
199
|
+
});
|
|
200
|
+
it('should detect diagnosis in logger.error', async () => {
|
|
201
|
+
const file = await createTestFile('logger-diagnosis.ts', `
|
|
202
|
+
// VIOLATION ERROR-002: Diagnosis in error log
|
|
203
|
+
logger.error('Failed to save diagnosis:', diagnosis);
|
|
204
|
+
`);
|
|
205
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
206
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
it('should detect medication in console.warn', async () => {
|
|
209
|
+
const file = await createTestFile('console-medication.ts', `
|
|
210
|
+
// VIOLATION ERROR-002: Medication in warning
|
|
211
|
+
console.warn('Discontinued medication:', medication);
|
|
212
|
+
`);
|
|
213
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
214
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
it('should detect health_record in logger', async () => {
|
|
217
|
+
const file = await createTestFile('logger-health.ts', `
|
|
218
|
+
// VIOLATION ERROR-002: Health record in log
|
|
219
|
+
logger.info('Updating health_record:', health_record);
|
|
220
|
+
`);
|
|
221
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
222
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
it('should detect patientData in console', async () => {
|
|
225
|
+
const file = await createTestFile('patient-data.ts', `
|
|
226
|
+
// VIOLATION ERROR-002: Patient data object
|
|
227
|
+
console.debug('Patient data:', patientData);
|
|
228
|
+
`);
|
|
229
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
230
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
it('should NOT flag redacted PHI', async () => {
|
|
233
|
+
const file = await createTestFile('redacted.ts', `
|
|
234
|
+
// SECURE: Redacted patient data
|
|
235
|
+
logger.error('Error processing patient:', {
|
|
236
|
+
patientId: redact(patient.id)
|
|
237
|
+
});
|
|
238
|
+
`);
|
|
239
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
240
|
+
const error002 = findings.filter((f) => f.id === 'ERROR-002');
|
|
241
|
+
expect(error002.length).toBe(0);
|
|
242
|
+
});
|
|
243
|
+
it('should NOT flag masked data', async () => {
|
|
244
|
+
const file = await createTestFile('masked.ts', `
|
|
245
|
+
// SECURE: Masked SSN
|
|
246
|
+
console.log('SSN:', maskSsn(patient.ssn));
|
|
247
|
+
`);
|
|
248
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
249
|
+
const error002 = findings.filter((f) => f.id === 'ERROR-002');
|
|
250
|
+
expect(error002.length).toBe(0);
|
|
251
|
+
});
|
|
252
|
+
it('should NOT flag generic patient messages', async () => {
|
|
253
|
+
const file = await createTestFile('generic-message.ts', `
|
|
254
|
+
// SECURE: Generic message without PHI
|
|
255
|
+
if (!patient) {
|
|
256
|
+
throw new Error("Patient not found");
|
|
257
|
+
}
|
|
258
|
+
`);
|
|
259
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
260
|
+
const error002 = findings.filter((f) => f.id === 'ERROR-002');
|
|
261
|
+
expect(error002.length).toBe(0);
|
|
262
|
+
});
|
|
263
|
+
it('should NOT flag patient ID only', async () => {
|
|
264
|
+
const file = await createTestFile('patient-id.ts', `
|
|
265
|
+
// SECURE: Patient ID is allowed (not full PHI)
|
|
266
|
+
logger.error('Error for patient_id:', patient_id);
|
|
267
|
+
`);
|
|
268
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
269
|
+
const error002 = findings.filter((f) => f.id === 'ERROR-002');
|
|
270
|
+
expect(error002.length).toBe(0);
|
|
271
|
+
});
|
|
272
|
+
it('should NOT flag test files', async () => {
|
|
273
|
+
const file = await createTestFile('patient.test.ts', `
|
|
274
|
+
// SECURE: Test file
|
|
275
|
+
describe('patient tests', () => {
|
|
276
|
+
it('should process patient', () => {
|
|
277
|
+
console.log(patient);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
`);
|
|
281
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
282
|
+
const error002 = findings.filter((f) => f.id === 'ERROR-002');
|
|
283
|
+
expect(error002.length).toBe(0);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe('Combined violations', () => {
|
|
287
|
+
it('should detect multiple ERROR-001 and ERROR-002 violations in same file', async () => {
|
|
288
|
+
const file = await createTestFile('combined.ts', `
|
|
289
|
+
// Multiple violations
|
|
290
|
+
export async function handler(req, res) {
|
|
291
|
+
try {
|
|
292
|
+
const patient = await getPatient(req.params.id);
|
|
293
|
+
console.log('Processing patient:', patient); // ERROR-002
|
|
294
|
+
processHealthRecord(patient);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
logger.error('Patient error:', patient); // ERROR-002
|
|
297
|
+
res.send(error.stack); // ERROR-001
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`);
|
|
301
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
302
|
+
expect(findings.some((f) => f.id === 'ERROR-001')).toBe(true);
|
|
303
|
+
expect(findings.some((f) => f.id === 'ERROR-002')).toBe(true);
|
|
304
|
+
expect(findings.length).toBeGreaterThanOrEqual(2);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
it('should provide correct HIPAA references', async () => {
|
|
308
|
+
const file = await createTestFile('hipaa-refs.ts', `
|
|
309
|
+
res.send(error.stack); // ERROR-001
|
|
310
|
+
console.log(patient); // ERROR-002
|
|
311
|
+
`);
|
|
312
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
313
|
+
const error001 = findings.find((f) => f.id === 'ERROR-001');
|
|
314
|
+
const error002 = findings.find((f) => f.id === 'ERROR-002');
|
|
315
|
+
expect(error001?.hipaaReference).toContain('164.312(b)');
|
|
316
|
+
expect(error002?.hipaaReference).toContain('164.312(c)');
|
|
317
|
+
});
|
|
318
|
+
it('should have correct severity levels', async () => {
|
|
319
|
+
const file = await createTestFile('severity.ts', `
|
|
320
|
+
res.json(error); // ERROR-001 (high)
|
|
321
|
+
console.log(patient.ssn); // ERROR-002 (critical)
|
|
322
|
+
`);
|
|
323
|
+
const findings = await errorsScanner.scan([file], scanOptions);
|
|
324
|
+
const error001 = findings.find((f) => f.id === 'ERROR-001');
|
|
325
|
+
const error002 = findings.find((f) => f.id === 'ERROR-002');
|
|
326
|
+
expect(error001?.severity).toBe('high');
|
|
327
|
+
expect(error002?.severity).toBe('critical');
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../src/scanners/errors/index.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Security Detection Patterns
|
|
3
|
+
* Detects unsafe error responses and PHI in error logs
|
|
4
|
+
*/
|
|
5
|
+
export interface ErrorPattern {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
severity: 'critical' | 'high' | 'medium';
|
|
10
|
+
hipaaReference: string;
|
|
11
|
+
patterns: RegExp[];
|
|
12
|
+
negativePatterns?: RegExp[];
|
|
13
|
+
recommendation: string;
|
|
14
|
+
category: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* ERROR-001: Unsanitized Error Details Sent to User
|
|
18
|
+
* Detects error.stack or error.message sent directly in responses
|
|
19
|
+
*/
|
|
20
|
+
export declare const UNSANITIZED_ERROR_RESPONSE: ErrorPattern;
|
|
21
|
+
/**
|
|
22
|
+
* ERROR-002: PHI in Error Logs/Throws
|
|
23
|
+
* Detects PHI data in console.log, logger, or throw Error
|
|
24
|
+
*/
|
|
25
|
+
export declare const PHI_IN_ERROR_LOGS: ErrorPattern;
|
|
26
|
+
export declare const ALL_ERROR_PATTERNS: ErrorPattern[];
|
|
27
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/scanners/errors/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,YAkDxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAiD/B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,YAAY,EAG5C,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Security Detection Patterns
|
|
3
|
+
* Detects unsafe error responses and PHI in error logs
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* ERROR-001: Unsanitized Error Details Sent to User
|
|
7
|
+
* Detects error.stack or error.message sent directly in responses
|
|
8
|
+
*/
|
|
9
|
+
export const UNSANITIZED_ERROR_RESPONSE = {
|
|
10
|
+
id: 'ERROR-001',
|
|
11
|
+
name: 'Unsanitized Error Details Sent to User',
|
|
12
|
+
description: 'Response sends error.stack or error.message directly to user without sanitization, potentially exposing sensitive system information',
|
|
13
|
+
severity: 'high',
|
|
14
|
+
hipaaReference: '45 CFR §164.312(b) - Audit Controls',
|
|
15
|
+
patterns: [
|
|
16
|
+
// res.send/json with error.stack
|
|
17
|
+
/res\.(?:send|json)\s*\([^)]*err(?:or)?\.stack/i,
|
|
18
|
+
// res.send/json with error.message
|
|
19
|
+
/res\.(?:send|json)\s*\([^)]*err(?:or)?\.message/i,
|
|
20
|
+
// res.send/json with error object directly (just the variable)
|
|
21
|
+
/res\.(?:send|json)\s*\(\s*err(?:or)?\s*\)/i,
|
|
22
|
+
// return error object directly
|
|
23
|
+
/return.*?res\.(?:send|json)\s*\(\s*err(?:or)?\s*\)/i,
|
|
24
|
+
// Response with error details
|
|
25
|
+
/response\.(?:send|json)\s*\([^)]*err(?:or)?\.(?:stack|message)/i,
|
|
26
|
+
// Next.js/Express error handlers
|
|
27
|
+
/next\s*\(\s*err(?:or)?\s*\)/i,
|
|
28
|
+
// throw error with stack
|
|
29
|
+
/throw.*?err(?:or)?\.stack/i,
|
|
30
|
+
],
|
|
31
|
+
negativePatterns: [
|
|
32
|
+
// Sanitized error messages
|
|
33
|
+
/sanitize/i,
|
|
34
|
+
/safe.*?error/i,
|
|
35
|
+
/filterError/i,
|
|
36
|
+
// Generic user-friendly messages
|
|
37
|
+
/['"](?:An error occurred|Internal server error|Something went wrong)/i,
|
|
38
|
+
// Logging (not sending to user)
|
|
39
|
+
/console\./i,
|
|
40
|
+
/logger\./i,
|
|
41
|
+
/log\(/i,
|
|
42
|
+
// Development environment checks
|
|
43
|
+
/process\.env\.NODE_ENV\s*===?\s*['"]development['"]/i,
|
|
44
|
+
/isDevelopment/i,
|
|
45
|
+
],
|
|
46
|
+
recommendation: 'Never send error.stack or error.message directly to users. Use generic error messages for production. Example: res.status(500).json({ error: "An error occurred" }). Log detailed errors server-side only.',
|
|
47
|
+
category: 'audit-logging',
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* ERROR-002: PHI in Error Logs/Throws
|
|
51
|
+
* Detects PHI data in console.log, logger, or throw Error
|
|
52
|
+
*/
|
|
53
|
+
export const PHI_IN_ERROR_LOGS = {
|
|
54
|
+
id: 'ERROR-002',
|
|
55
|
+
name: 'PHI Data in Error Logs or Thrown Errors',
|
|
56
|
+
description: 'Protected Health Information (patient, ssn, dob, mrn, diagnosis, medication, healthRecord) exposed in console logs, logger, or thrown errors',
|
|
57
|
+
severity: 'critical',
|
|
58
|
+
hipaaReference: '45 CFR §164.312(c) - Integrity Controls',
|
|
59
|
+
patterns: [
|
|
60
|
+
// console.* with PHI
|
|
61
|
+
/console\.(?:log|error|warn|info|debug)\s*\([^)]*(?:patient|ssn|dob|mrn|diagnosis|medication|health[-_]?record)/i,
|
|
62
|
+
// logger.* with PHI
|
|
63
|
+
/logger\.(?:error|warn|info|debug|log)\s*\([^)]*(?:patient|ssn|dob|mrn|diagnosis|medication|health[-_]?record)/i,
|
|
64
|
+
// throw Error with PHI
|
|
65
|
+
/throw\s+(?:new\s+)?Error\s*\([^)]*(?:patient|ssn|dob|mrn|diagnosis|medication|health[-_]?record)/i,
|
|
66
|
+
// log.* with PHI (Winston, Bunyan, etc.)
|
|
67
|
+
/log\.(?:error|warn|info|debug)\s*\([^)]*(?:patient|ssn|dob|mrn|diagnosis|medication|health[-_]?record)/i,
|
|
68
|
+
// console with patient data object
|
|
69
|
+
/console\.[a-z]+\s*\([^)]*patient(?:Data|Info|Record|Object)/i,
|
|
70
|
+
// logger with health record
|
|
71
|
+
/logger\.[a-z]+\s*\([^)]*health[-_]?record/i,
|
|
72
|
+
],
|
|
73
|
+
negativePatterns: [
|
|
74
|
+
// Redacted or masked PHI
|
|
75
|
+
/redact/i,
|
|
76
|
+
/mask/i,
|
|
77
|
+
/sanitize/i,
|
|
78
|
+
/obfuscate/i,
|
|
79
|
+
// Generic messages without actual data
|
|
80
|
+
/['"]Patient not found['"]/i,
|
|
81
|
+
/['"]Invalid patient ID['"]/i,
|
|
82
|
+
/['"]Health record/i,
|
|
83
|
+
// Patient ID only (not full PHI)
|
|
84
|
+
/patient[-_]?id\b/i,
|
|
85
|
+
// Test files
|
|
86
|
+
/\.test\./i,
|
|
87
|
+
/\.spec\./i,
|
|
88
|
+
/describe\(/i,
|
|
89
|
+
],
|
|
90
|
+
recommendation: 'Never log PHI in error messages. Redact sensitive data before logging. Example: logger.error("Error processing patient", { patientId: redact(patient.id) }). Use patient IDs only, never full PHI.',
|
|
91
|
+
category: 'phi-exposure',
|
|
92
|
+
};
|
|
93
|
+
export const ALL_ERROR_PATTERNS = [
|
|
94
|
+
UNSANITIZED_ERROR_RESPONSE,
|
|
95
|
+
PHI_IN_ERROR_LOGS,
|
|
96
|
+
];
|
|
97
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/scanners/errors/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAiB;IACtD,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,wCAAwC;IAC9C,WAAW,EACT,sIAAsI;IACxI,QAAQ,EAAE,MAAM;IAChB,cAAc,EAAE,qCAAqC;IACrD,QAAQ,EAAE;QACR,iCAAiC;QACjC,gDAAgD;QAEhD,mCAAmC;QACnC,kDAAkD;QAElD,+DAA+D;QAC/D,4CAA4C;QAE5C,+BAA+B;QAC/B,qDAAqD;QAErD,8BAA8B;QAC9B,iEAAiE;QAEjE,iCAAiC;QACjC,8BAA8B;QAE9B,yBAAyB;QACzB,4BAA4B;KAC7B;IACD,gBAAgB,EAAE;QAChB,2BAA2B;QAC3B,WAAW;QACX,eAAe;QACf,cAAc;QAEd,iCAAiC;QACjC,uEAAuE;QAEvE,gCAAgC;QAChC,YAAY;QACZ,WAAW;QACX,QAAQ;QAER,iCAAiC;QACjC,sDAAsD;QACtD,gBAAgB;KACjB;IACD,cAAc,EACZ,4MAA4M;IAC9M,QAAQ,EAAE,eAAe;CAC1B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAiB;IAC7C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,yCAAyC;IAC/C,WAAW,EACT,8IAA8I;IAChJ,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,yCAAyC;IACzD,QAAQ,EAAE;QACR,qBAAqB;QACrB,iHAAiH;QAEjH,oBAAoB;QACpB,gHAAgH;QAEhH,uBAAuB;QACvB,mGAAmG;QAEnG,yCAAyC;QACzC,yGAAyG;QAEzG,mCAAmC;QACnC,8DAA8D;QAE9D,4BAA4B;QAC5B,4CAA4C;KAC7C;IACD,gBAAgB,EAAE;QAChB,yBAAyB;QACzB,SAAS;QACT,OAAO;QACP,WAAW;QACX,YAAY;QAEZ,uCAAuC;QACvC,4BAA4B;QAC5B,6BAA6B;QAC7B,oBAAoB;QAEpB,iCAAiC;QACjC,mBAAmB;QAEnB,aAAa;QACb,WAAW;QACX,WAAW;QACX,aAAa;KACd;IACD,cAAc,EACZ,oMAAoM;IACtM,QAAQ,EAAE,cAAc;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAmB;IAChD,0BAA0B;IAC1B,iBAAiB;CAClB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HIPAA 2026 Security Rule Scanner
|
|
3
|
+
* Implements detection for 15 technical requirements (all now "required")
|
|
4
|
+
* Expected enforcement: May 2026
|
|
5
|
+
*/
|
|
6
|
+
import type { Scanner } from '../../types.js';
|
|
7
|
+
export declare const hipaa2026Scanner: Scanner;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scanners/hipaa2026/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,gBAAgB,CAAC;AAoRpE,eAAO,MAAM,gBAAgB,EAAE,OAyI9B,CAAC"}
|