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,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-Based Access Control (RBAC) Scanner
|
|
3
|
+
* Detects missing authorization checks and HIPAA minimum necessary violations
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import { ALL_RBAC_PATTERNS } from './patterns.js';
|
|
7
|
+
export const rbacScanner = {
|
|
8
|
+
name: 'Role-Based Access Control Scanner',
|
|
9
|
+
category: 'access-control',
|
|
10
|
+
async scan(files, options) {
|
|
11
|
+
const findings = [];
|
|
12
|
+
// Filter to code files
|
|
13
|
+
const codeFiles = files.filter((f) => /\.(js|ts|jsx|tsx|sql|prisma)$/i.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
|
+
// Determine if this is a client-side file for RBAC-002
|
|
19
|
+
const isClientFile = isClientSideFile(file, content);
|
|
20
|
+
for (const pattern of ALL_RBAC_PATTERNS) {
|
|
21
|
+
// Special handling for RBAC-002 (only scan client-side files)
|
|
22
|
+
if (pattern.id === 'RBAC-002' && !isClientFile) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Special handling for RBAC-001 (check authorization in surrounding context)
|
|
26
|
+
if (pattern.id === 'RBAC-001') {
|
|
27
|
+
await scanPHIAccessWithoutAuthz(file, content, lines, pattern, findings);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Standard pattern matching for RBAC-002 and RBAC-003
|
|
31
|
+
for (let i = 0; i < lines.length; i++) {
|
|
32
|
+
const line = lines[i];
|
|
33
|
+
const lineNumber = i + 1;
|
|
34
|
+
// Skip comments
|
|
35
|
+
if (/^\s*(?:\/\/|#|\/\*|\*)/.test(line))
|
|
36
|
+
continue;
|
|
37
|
+
// Check if line matches violation pattern
|
|
38
|
+
const matched = pattern.patterns.some((p) => p.test(line));
|
|
39
|
+
if (!matched)
|
|
40
|
+
continue;
|
|
41
|
+
// Check if negative patterns indicate compliance
|
|
42
|
+
const isCompliant = pattern.negativePatterns?.some((p) => {
|
|
43
|
+
// For RBAC-002, check if in server-side context
|
|
44
|
+
if (pattern.id === 'RBAC-002') {
|
|
45
|
+
return p.test(file) || p.test(content);
|
|
46
|
+
}
|
|
47
|
+
// For RBAC-003, check current line and surrounding lines
|
|
48
|
+
const context = lines.slice(Math.max(0, i - 2), i + 3).join('\n');
|
|
49
|
+
return p.test(context);
|
|
50
|
+
});
|
|
51
|
+
if (isCompliant)
|
|
52
|
+
continue;
|
|
53
|
+
// Create finding
|
|
54
|
+
findings.push({
|
|
55
|
+
id: pattern.id,
|
|
56
|
+
category: 'access-control',
|
|
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
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// Skip files that can't be read
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return findings;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Determine if a file is client-side code
|
|
78
|
+
*/
|
|
79
|
+
function isClientSideFile(file, content) {
|
|
80
|
+
// Client-side indicators
|
|
81
|
+
const clientPatterns = [
|
|
82
|
+
/\/(?:components?|pages?|app)\//i,
|
|
83
|
+
/\.client\./i,
|
|
84
|
+
/use client/i,
|
|
85
|
+
/useState|useEffect|useContext/i,
|
|
86
|
+
/window\./i,
|
|
87
|
+
/document\./i,
|
|
88
|
+
];
|
|
89
|
+
// Server-side indicators (take precedence)
|
|
90
|
+
const serverPatterns = [
|
|
91
|
+
/\/api\//i,
|
|
92
|
+
/\.server\./i,
|
|
93
|
+
/getServerSideProps/i,
|
|
94
|
+
/getStaticProps/i,
|
|
95
|
+
/use server/i,
|
|
96
|
+
];
|
|
97
|
+
// If file has server indicators, it's server-side
|
|
98
|
+
if (serverPatterns.some((p) => p.test(file) || p.test(content))) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
// If file has client indicators, it's client-side
|
|
102
|
+
if (clientPatterns.some((p) => p.test(file) || p.test(content))) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
// Default: treat as client-side if in common web directories
|
|
106
|
+
return /\/(?:src|components?|pages?|app|views?)\//i.test(file);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Scan for PHI data access without authorization checks
|
|
110
|
+
*/
|
|
111
|
+
async function scanPHIAccessWithoutAuthz(file, content, lines, pattern, findings) {
|
|
112
|
+
for (let i = 0; i < lines.length; i++) {
|
|
113
|
+
const line = lines[i];
|
|
114
|
+
const lineNumber = i + 1;
|
|
115
|
+
// Skip comments
|
|
116
|
+
if (/^\s*(?:\/\/|#|\/\*|\*)/.test(line))
|
|
117
|
+
continue;
|
|
118
|
+
// Check if line contains PHI data access
|
|
119
|
+
const hasPHIAccess = pattern.patterns.some((p) => p.test(line));
|
|
120
|
+
if (!hasPHIAccess)
|
|
121
|
+
continue;
|
|
122
|
+
// Check surrounding context (10 lines before and 5 lines after) for authorization
|
|
123
|
+
const contextStart = Math.max(0, i - 10);
|
|
124
|
+
const contextEnd = Math.min(lines.length, i + 6);
|
|
125
|
+
const context = lines.slice(contextStart, contextEnd).join('\n');
|
|
126
|
+
// Check if authorization is present in context
|
|
127
|
+
const hasAuthorization = pattern.negativePatterns?.some((p) => p.test(context));
|
|
128
|
+
if (hasAuthorization)
|
|
129
|
+
continue;
|
|
130
|
+
// Create finding
|
|
131
|
+
findings.push({
|
|
132
|
+
id: pattern.id,
|
|
133
|
+
category: 'access-control',
|
|
134
|
+
severity: pattern.severity,
|
|
135
|
+
title: pattern.name,
|
|
136
|
+
description: `${pattern.description}\n\nCode: ${line.trim()}\n\nNo authorization check found in surrounding code.`,
|
|
137
|
+
file: file,
|
|
138
|
+
line: lineNumber,
|
|
139
|
+
recommendation: pattern.recommendation,
|
|
140
|
+
hipaaReference: pattern.hipaaReference,
|
|
141
|
+
confidence: 'high',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scanners/rbac/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/scanners/rbac/index.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Role-Based Access Control Scanner
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { rbacScanner } from './index.js';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
describe('RBAC Scanner', () => {
|
|
10
|
+
let tempDir = '';
|
|
11
|
+
let testFiles = [];
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rbac-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('RBAC-001: PHI Access Without Authorization', () => {
|
|
43
|
+
it('should detect SQL query to patients table without role check', async () => {
|
|
44
|
+
const file = await createTestFile('api.ts', `
|
|
45
|
+
export async function getPatients(req: Request) {
|
|
46
|
+
const result = await db.query(
|
|
47
|
+
'SELECT * FROM patients WHERE active = true'
|
|
48
|
+
);
|
|
49
|
+
return result.rows;
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
53
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
54
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
55
|
+
expect(rbacFindings[0].severity).toBe('high');
|
|
56
|
+
expect(rbacFindings[0].hipaaReference).toContain('164.312(a)(1)');
|
|
57
|
+
});
|
|
58
|
+
it('should detect ORM query to health_records without permission check', async () => {
|
|
59
|
+
const file = await createTestFile('records.ts', `
|
|
60
|
+
async function getMedicalRecords(patientId: string) {
|
|
61
|
+
const records = await MedicalRecord.findAll({
|
|
62
|
+
where: { patientId }
|
|
63
|
+
});
|
|
64
|
+
return records;
|
|
65
|
+
}
|
|
66
|
+
`);
|
|
67
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
68
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
69
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
70
|
+
});
|
|
71
|
+
it('should detect Supabase query to prescriptions without authorization', async () => {
|
|
72
|
+
const file = await createTestFile('prescriptions.ts', `
|
|
73
|
+
export async function getPrescriptions(userId: string) {
|
|
74
|
+
const { data } = await supabase
|
|
75
|
+
.from('prescriptions')
|
|
76
|
+
.select('*')
|
|
77
|
+
.eq('user_id', userId);
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
`);
|
|
81
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
82
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
83
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
84
|
+
});
|
|
85
|
+
it('should detect Prisma query to patients without role verification', async () => {
|
|
86
|
+
const file = await createTestFile('patient-service.ts', `
|
|
87
|
+
async function listPatients() {
|
|
88
|
+
const patients = await prisma.patient.findMany({
|
|
89
|
+
take: 100
|
|
90
|
+
});
|
|
91
|
+
return patients;
|
|
92
|
+
}
|
|
93
|
+
`);
|
|
94
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
95
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
96
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
97
|
+
});
|
|
98
|
+
it('should not flag query with role verification', async () => {
|
|
99
|
+
const file = await createTestFile('secure-api.ts', `
|
|
100
|
+
export async function getPatients(req: Request, user: User) {
|
|
101
|
+
if (!hasPermission(user, 'read:patients')) {
|
|
102
|
+
throw new Error('Unauthorized');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const result = await db.query(
|
|
106
|
+
'SELECT * FROM patients WHERE active = true'
|
|
107
|
+
);
|
|
108
|
+
return result.rows;
|
|
109
|
+
}
|
|
110
|
+
`);
|
|
111
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
112
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
113
|
+
expect(rbacFindings.length).toBe(0);
|
|
114
|
+
});
|
|
115
|
+
it('should not flag query with isAdmin check', async () => {
|
|
116
|
+
const file = await createTestFile('admin-route.ts', `
|
|
117
|
+
async function getMedicalRecords(user: User) {
|
|
118
|
+
if (!user.isAdmin) {
|
|
119
|
+
throw new ForbiddenError();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const records = await MedicalRecord.findAll();
|
|
123
|
+
return records;
|
|
124
|
+
}
|
|
125
|
+
`);
|
|
126
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
127
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
128
|
+
expect(rbacFindings.length).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
it('should not flag query with canAccess verification', async () => {
|
|
131
|
+
const file = await createTestFile('authorization.ts', `
|
|
132
|
+
export async function getDiagnosis(diagnosisId: string, userId: string) {
|
|
133
|
+
const canAccess = await checkAccess(userId, 'diagnosis', diagnosisId);
|
|
134
|
+
if (!canAccess) return null;
|
|
135
|
+
|
|
136
|
+
const diagnosis = await db.query(
|
|
137
|
+
'SELECT * FROM diagnosis WHERE id = $1',
|
|
138
|
+
[diagnosisId]
|
|
139
|
+
);
|
|
140
|
+
return diagnosis;
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
144
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-001');
|
|
145
|
+
expect(rbacFindings.length).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('RBAC-002: Service Role in Client-Side Code', () => {
|
|
149
|
+
it('should detect service_role in client component', async () => {
|
|
150
|
+
const file = await createTestFile('components/Dashboard.tsx', `
|
|
151
|
+
'use client';
|
|
152
|
+
|
|
153
|
+
import { createClient } from '@supabase/supabase-js';
|
|
154
|
+
|
|
155
|
+
const supabase = createClient(
|
|
156
|
+
'https://myproject.supabase.co',
|
|
157
|
+
'eyJhbGci...service_role_key_here'
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
export function Dashboard() {
|
|
161
|
+
return <div>Dashboard</div>;
|
|
162
|
+
}
|
|
163
|
+
`);
|
|
164
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
165
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
166
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
167
|
+
expect(rbacFindings[0].severity).toBe('critical');
|
|
168
|
+
});
|
|
169
|
+
it('should detect serviceRole in pages directory', async () => {
|
|
170
|
+
const file = await createTestFile('pages/admin.tsx', `
|
|
171
|
+
const serviceRole = 'secret_service_role_key';
|
|
172
|
+
|
|
173
|
+
export default function AdminPage() {
|
|
174
|
+
return <div>Admin</div>;
|
|
175
|
+
}
|
|
176
|
+
`);
|
|
177
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
178
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
179
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
180
|
+
});
|
|
181
|
+
it('should detect isAdmin default to true', async () => {
|
|
182
|
+
const file = await createTestFile('components/UserProfile.tsx', `
|
|
183
|
+
export function UserProfile() {
|
|
184
|
+
const [isAdmin, setIsAdmin] = useState(true);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div>
|
|
188
|
+
{isAdmin && <AdminPanel />}
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
`);
|
|
193
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
194
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
195
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
196
|
+
});
|
|
197
|
+
it('should detect role default to admin', async () => {
|
|
198
|
+
const file = await createTestFile('app/layout.tsx', `
|
|
199
|
+
const user = {
|
|
200
|
+
email: 'user@example.com',
|
|
201
|
+
role: 'admin'
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default function Layout() {
|
|
205
|
+
return <div />;
|
|
206
|
+
}
|
|
207
|
+
`);
|
|
208
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
209
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
210
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
211
|
+
});
|
|
212
|
+
it('should detect always-admin condition', async () => {
|
|
213
|
+
const file = await createTestFile('components/Auth.tsx', `
|
|
214
|
+
function checkAdmin() {
|
|
215
|
+
const isAdmin = true;
|
|
216
|
+
if (isAdmin) {
|
|
217
|
+
return <AdminDashboard />;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
`);
|
|
221
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
222
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
223
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
224
|
+
});
|
|
225
|
+
it('should not flag service_role in API route', async () => {
|
|
226
|
+
const file = await createTestFile('api/admin.ts', `
|
|
227
|
+
import { createClient } from '@supabase/supabase-js';
|
|
228
|
+
|
|
229
|
+
const supabase = createClient(
|
|
230
|
+
process.env.SUPABASE_URL,
|
|
231
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
export async function GET(req: Request) {
|
|
235
|
+
const data = await supabase.from('users').select('*');
|
|
236
|
+
return Response.json(data);
|
|
237
|
+
}
|
|
238
|
+
`);
|
|
239
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
240
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
241
|
+
expect(rbacFindings.length).toBe(0);
|
|
242
|
+
});
|
|
243
|
+
it('should not flag service_role in .server file', async () => {
|
|
244
|
+
const file = await createTestFile('lib/supabase.server.ts', `
|
|
245
|
+
export const serviceRole = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
246
|
+
`);
|
|
247
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
248
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
249
|
+
expect(rbacFindings.length).toBe(0);
|
|
250
|
+
});
|
|
251
|
+
it('should not flag isAdmin in test files', async () => {
|
|
252
|
+
const file = await createTestFile('components/Auth.test.tsx', `
|
|
253
|
+
describe('Auth', () => {
|
|
254
|
+
it('should grant admin access', () => {
|
|
255
|
+
const isAdmin = true;
|
|
256
|
+
expect(isAdmin).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
`);
|
|
260
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
261
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-002');
|
|
262
|
+
expect(rbacFindings.length).toBe(0);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
describe('RBAC-003: SELECT * on PHI Tables', () => {
|
|
266
|
+
it('should detect SELECT * FROM patients', async () => {
|
|
267
|
+
const file = await createTestFile('queries.ts', `
|
|
268
|
+
async function getAllPatients() {
|
|
269
|
+
const result = await db.query('SELECT * FROM patients');
|
|
270
|
+
return result.rows;
|
|
271
|
+
}
|
|
272
|
+
`);
|
|
273
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
274
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
275
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
276
|
+
expect(rbacFindings[0].severity).toBe('medium');
|
|
277
|
+
expect(rbacFindings[0].hipaaReference).toContain('164.502(b)');
|
|
278
|
+
});
|
|
279
|
+
it('should detect SELECT * FROM medical_records', async () => {
|
|
280
|
+
const file = await createTestFile('medical.ts', `
|
|
281
|
+
const query = \`
|
|
282
|
+
SELECT * FROM medical_records
|
|
283
|
+
WHERE patient_id = ?
|
|
284
|
+
\`;
|
|
285
|
+
`);
|
|
286
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
287
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
288
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
289
|
+
});
|
|
290
|
+
it('should detect .select("*") on prescriptions', async () => {
|
|
291
|
+
const file = await createTestFile('prescriptions-api.ts', `
|
|
292
|
+
export async function getPrescriptions(patientId: string) {
|
|
293
|
+
const { data } = await supabase
|
|
294
|
+
.from('prescriptions')
|
|
295
|
+
.select('*')
|
|
296
|
+
.eq('patient_id', patientId);
|
|
297
|
+
return data;
|
|
298
|
+
}
|
|
299
|
+
`);
|
|
300
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
301
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
302
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
303
|
+
});
|
|
304
|
+
it('should detect .select(*) without quotes', async () => {
|
|
305
|
+
const file = await createTestFile('diagnosis.ts', `
|
|
306
|
+
const diagnoses = await db.from('diagnosis').select(*);
|
|
307
|
+
`);
|
|
308
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
309
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
310
|
+
expect(rbacFindings.length).toBeGreaterThan(0);
|
|
311
|
+
});
|
|
312
|
+
it('should not flag SELECT with specific fields', async () => {
|
|
313
|
+
const file = await createTestFile('minimal-query.ts', `
|
|
314
|
+
async function getPatientNames() {
|
|
315
|
+
const result = await db.query(
|
|
316
|
+
'SELECT id, name, dob FROM patients WHERE active = true'
|
|
317
|
+
);
|
|
318
|
+
return result.rows;
|
|
319
|
+
}
|
|
320
|
+
`);
|
|
321
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
322
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
323
|
+
expect(rbacFindings.length).toBe(0);
|
|
324
|
+
});
|
|
325
|
+
it('should not flag .select() with specific fields', async () => {
|
|
326
|
+
const file = await createTestFile('specific-fields.ts', `
|
|
327
|
+
const { data } = await supabase
|
|
328
|
+
.from('prescriptions')
|
|
329
|
+
.select('id, medication_name, dosage, patient_id')
|
|
330
|
+
.eq('patient_id', patientId);
|
|
331
|
+
`);
|
|
332
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
333
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
334
|
+
expect(rbacFindings.length).toBe(0);
|
|
335
|
+
});
|
|
336
|
+
it('should not flag SELECT * on non-PHI tables', async () => {
|
|
337
|
+
const file = await createTestFile('settings.ts', `
|
|
338
|
+
const settings = await db.query('SELECT * FROM app_settings');
|
|
339
|
+
`);
|
|
340
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
341
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
342
|
+
expect(rbacFindings.length).toBe(0);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
describe('General Scanner Behavior', () => {
|
|
346
|
+
it('should only scan code files', async () => {
|
|
347
|
+
await createTestFile('README.md', 'SELECT * FROM patients');
|
|
348
|
+
await createTestFile('data.json', '{"query": "SELECT * FROM patients"}');
|
|
349
|
+
await createTestFile('code.ts', 'SELECT * FROM patients');
|
|
350
|
+
const findings = await rbacScanner.scan(testFiles, scanOptions);
|
|
351
|
+
// Should only find findings in .ts file
|
|
352
|
+
const mdFindings = findings.filter((f) => f.file.endsWith('.md'));
|
|
353
|
+
const jsonFindings = findings.filter((f) => f.file.endsWith('.json'));
|
|
354
|
+
expect(mdFindings.length).toBe(0);
|
|
355
|
+
expect(jsonFindings.length).toBe(0);
|
|
356
|
+
});
|
|
357
|
+
it('should skip comment lines', async () => {
|
|
358
|
+
const file = await createTestFile('commented.ts', `
|
|
359
|
+
// SELECT * FROM patients - this is how you would do it
|
|
360
|
+
/*
|
|
361
|
+
* const result = await db.query('SELECT * FROM patients');
|
|
362
|
+
*/
|
|
363
|
+
const validCode = true;
|
|
364
|
+
`);
|
|
365
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
366
|
+
const rbacFindings = findings.filter((f) => f.id === 'RBAC-003');
|
|
367
|
+
expect(rbacFindings.length).toBe(0);
|
|
368
|
+
});
|
|
369
|
+
it('should include confidence scores', async () => {
|
|
370
|
+
const file = await createTestFile('test.ts', `
|
|
371
|
+
const patients = await db.query('SELECT * FROM patients');
|
|
372
|
+
`);
|
|
373
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
374
|
+
for (const finding of findings) {
|
|
375
|
+
expect(finding.confidence).toBeDefined();
|
|
376
|
+
expect(['high', 'medium', 'low']).toContain(finding.confidence);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
it('should include proper HIPAA references', async () => {
|
|
380
|
+
const file = await createTestFile('multi-violation.ts', `
|
|
381
|
+
// RBAC-001 violation
|
|
382
|
+
const records = await MedicalRecord.findAll();
|
|
383
|
+
|
|
384
|
+
// RBAC-003 violation
|
|
385
|
+
const query = 'SELECT * FROM diagnoses';
|
|
386
|
+
`);
|
|
387
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
388
|
+
for (const finding of findings) {
|
|
389
|
+
expect(finding.hipaaReference).toBeDefined();
|
|
390
|
+
expect(finding.hipaaReference).toMatch(/164\./);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
it('should detect multiple violations in same file', async () => {
|
|
394
|
+
const file = await createTestFile('components/BadComponent.tsx', `
|
|
395
|
+
'use client';
|
|
396
|
+
|
|
397
|
+
// RBAC-002: service_role in client
|
|
398
|
+
const SERVICE_ROLE_KEY = 'service_role_secret';
|
|
399
|
+
|
|
400
|
+
// RBAC-002: admin default
|
|
401
|
+
const [isAdmin, setIsAdmin] = useState(true);
|
|
402
|
+
|
|
403
|
+
async function loadData() {
|
|
404
|
+
// RBAC-001: No authorization check
|
|
405
|
+
const patients = await db.from('patients').select('*');
|
|
406
|
+
|
|
407
|
+
// RBAC-003: SELECT *
|
|
408
|
+
return patients;
|
|
409
|
+
}
|
|
410
|
+
`);
|
|
411
|
+
const findings = await rbacScanner.scan([file], scanOptions);
|
|
412
|
+
expect(findings.length).toBeGreaterThan(2);
|
|
413
|
+
const rbac001 = findings.filter((f) => f.id === 'RBAC-001');
|
|
414
|
+
const rbac002 = findings.filter((f) => f.id === 'RBAC-002');
|
|
415
|
+
const rbac003 = findings.filter((f) => f.id === 'RBAC-003');
|
|
416
|
+
expect(rbac001.length).toBeGreaterThan(0);
|
|
417
|
+
expect(rbac002.length).toBeGreaterThan(0);
|
|
418
|
+
expect(rbac003.length).toBeGreaterThan(0);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../src/scanners/rbac/index.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-Based Access Control (RBAC) Detection Patterns
|
|
3
|
+
* Enforces proper authorization and minimum necessary principle per HIPAA
|
|
4
|
+
*/
|
|
5
|
+
export interface RBACPattern {
|
|
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
|
+
* RBAC-001: PHI Data Access Without Role/Permission Verification
|
|
18
|
+
* Detects database queries to PHI tables without authorization checks
|
|
19
|
+
*/
|
|
20
|
+
export declare const PHI_ACCESS_NO_AUTHZ: RBACPattern;
|
|
21
|
+
/**
|
|
22
|
+
* RBAC-002: Service Role Keys in Client-Side Code
|
|
23
|
+
* Detects privileged keys exposed to client, admin defaults, or always-admin conditions
|
|
24
|
+
*/
|
|
25
|
+
export declare const SERVICE_ROLE_CLIENT_SIDE: RBACPattern;
|
|
26
|
+
/**
|
|
27
|
+
* RBAC-003: SELECT * on PHI Tables (Violates Minimum Necessary)
|
|
28
|
+
* Detects SELECT * queries that retrieve all columns from PHI tables
|
|
29
|
+
*/
|
|
30
|
+
export declare const SELECT_ALL_PHI: RBACPattern;
|
|
31
|
+
export declare const ALL_RBAC_PATTERNS: RBACPattern[];
|
|
32
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/scanners/rbac/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,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,mBAAmB,EAAE,WAoCjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,WA2CtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,WAmC5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAI1C,CAAC"}
|