verification-layer 0.21.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.
Files changed (157) hide show
  1. package/README.md +251 -615
  2. package/dist/cli.js +283 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/reporters/audit-report.d.ts.map +1 -1
  5. package/dist/reporters/audit-report.js +180 -0
  6. package/dist/reporters/audit-report.js.map +1 -1
  7. package/dist/reporters/index.d.ts.map +1 -1
  8. package/dist/reporters/index.js +2612 -5
  9. package/dist/reporters/index.js.map +1 -1
  10. package/dist/scan.d.ts.map +1 -1
  11. package/dist/scan.js +14 -1
  12. package/dist/scan.js.map +1 -1
  13. package/dist/scanners/api-security/index.d.ts +7 -0
  14. package/dist/scanners/api-security/index.d.ts.map +1 -0
  15. package/dist/scanners/api-security/index.js +139 -0
  16. package/dist/scanners/api-security/index.js.map +1 -0
  17. package/dist/scanners/api-security/index.test.d.ts +5 -0
  18. package/dist/scanners/api-security/index.test.d.ts.map +1 -0
  19. package/dist/scanners/api-security/index.test.js +360 -0
  20. package/dist/scanners/api-security/index.test.js.map +1 -0
  21. package/dist/scanners/api-security/patterns.d.ts +32 -0
  22. package/dist/scanners/api-security/patterns.d.ts.map +1 -0
  23. package/dist/scanners/api-security/patterns.js +159 -0
  24. package/dist/scanners/api-security/patterns.js.map +1 -0
  25. package/dist/scanners/authentication/index.d.ts +7 -0
  26. package/dist/scanners/authentication/index.d.ts.map +1 -0
  27. package/dist/scanners/authentication/index.js +107 -0
  28. package/dist/scanners/authentication/index.js.map +1 -0
  29. package/dist/scanners/authentication/index.test.d.ts +5 -0
  30. package/dist/scanners/authentication/index.test.d.ts.map +1 -0
  31. package/dist/scanners/authentication/index.test.js +379 -0
  32. package/dist/scanners/authentication/index.test.js.map +1 -0
  33. package/dist/scanners/authentication/patterns.d.ts +32 -0
  34. package/dist/scanners/authentication/patterns.d.ts.map +1 -0
  35. package/dist/scanners/authentication/patterns.js +133 -0
  36. package/dist/scanners/authentication/patterns.js.map +1 -0
  37. package/dist/scanners/configuration/index.d.ts +8 -0
  38. package/dist/scanners/configuration/index.d.ts.map +1 -0
  39. package/dist/scanners/configuration/index.js +87 -0
  40. package/dist/scanners/configuration/index.js.map +1 -0
  41. package/dist/scanners/configuration/index.test.d.ts +5 -0
  42. package/dist/scanners/configuration/index.test.d.ts.map +1 -0
  43. package/dist/scanners/configuration/index.test.js +344 -0
  44. package/dist/scanners/configuration/index.test.js.map +1 -0
  45. package/dist/scanners/configuration/patterns.d.ts +32 -0
  46. package/dist/scanners/configuration/patterns.d.ts.map +1 -0
  47. package/dist/scanners/configuration/patterns.js +146 -0
  48. package/dist/scanners/configuration/patterns.js.map +1 -0
  49. package/dist/scanners/credentials/index.d.ts +7 -0
  50. package/dist/scanners/credentials/index.d.ts.map +1 -0
  51. package/dist/scanners/credentials/index.js +129 -0
  52. package/dist/scanners/credentials/index.js.map +1 -0
  53. package/dist/scanners/credentials/index.test.d.ts +5 -0
  54. package/dist/scanners/credentials/index.test.d.ts.map +1 -0
  55. package/dist/scanners/credentials/index.test.js +395 -0
  56. package/dist/scanners/credentials/index.test.js.map +1 -0
  57. package/dist/scanners/credentials/patterns.d.ts +32 -0
  58. package/dist/scanners/credentials/patterns.d.ts.map +1 -0
  59. package/dist/scanners/credentials/patterns.js +140 -0
  60. package/dist/scanners/credentials/patterns.js.map +1 -0
  61. package/dist/scanners/errors/index.d.ts +8 -0
  62. package/dist/scanners/errors/index.d.ts.map +1 -0
  63. package/dist/scanners/errors/index.js +78 -0
  64. package/dist/scanners/errors/index.js.map +1 -0
  65. package/dist/scanners/errors/index.test.d.ts +5 -0
  66. package/dist/scanners/errors/index.test.d.ts.map +1 -0
  67. package/dist/scanners/errors/index.test.js +330 -0
  68. package/dist/scanners/errors/index.test.js.map +1 -0
  69. package/dist/scanners/errors/patterns.d.ts +27 -0
  70. package/dist/scanners/errors/patterns.d.ts.map +1 -0
  71. package/dist/scanners/errors/patterns.js +97 -0
  72. package/dist/scanners/errors/patterns.js.map +1 -0
  73. package/dist/scanners/hipaa2026/index.d.ts.map +1 -1
  74. package/dist/scanners/hipaa2026/index.js +49 -17
  75. package/dist/scanners/hipaa2026/index.js.map +1 -1
  76. package/dist/scanners/hipaa2026/index.test.js +26 -15
  77. package/dist/scanners/hipaa2026/index.test.js.map +1 -1
  78. package/dist/scanners/operational/index.d.ts +7 -0
  79. package/dist/scanners/operational/index.d.ts.map +1 -0
  80. package/dist/scanners/operational/index.js +171 -0
  81. package/dist/scanners/operational/index.js.map +1 -0
  82. package/dist/scanners/operational/index.test.d.ts +5 -0
  83. package/dist/scanners/operational/index.test.d.ts.map +1 -0
  84. package/dist/scanners/operational/index.test.js +406 -0
  85. package/dist/scanners/operational/index.test.js.map +1 -0
  86. package/dist/scanners/operational/patterns.d.ts +33 -0
  87. package/dist/scanners/operational/patterns.d.ts.map +1 -0
  88. package/dist/scanners/operational/patterns.js +151 -0
  89. package/dist/scanners/operational/patterns.js.map +1 -0
  90. package/dist/scanners/rbac/index.d.ts +7 -0
  91. package/dist/scanners/rbac/index.d.ts.map +1 -0
  92. package/dist/scanners/rbac/index.js +145 -0
  93. package/dist/scanners/rbac/index.js.map +1 -0
  94. package/dist/scanners/rbac/index.test.d.ts +5 -0
  95. package/dist/scanners/rbac/index.test.d.ts.map +1 -0
  96. package/dist/scanners/rbac/index.test.js +422 -0
  97. package/dist/scanners/rbac/index.test.js.map +1 -0
  98. package/dist/scanners/rbac/patterns.d.ts +32 -0
  99. package/dist/scanners/rbac/patterns.d.ts.map +1 -0
  100. package/dist/scanners/rbac/patterns.js +124 -0
  101. package/dist/scanners/rbac/patterns.js.map +1 -0
  102. package/dist/scanners/revocation/index.d.ts +8 -0
  103. package/dist/scanners/revocation/index.d.ts.map +1 -0
  104. package/dist/scanners/revocation/index.js +83 -0
  105. package/dist/scanners/revocation/index.js.map +1 -0
  106. package/dist/scanners/revocation/index.test.d.ts +5 -0
  107. package/dist/scanners/revocation/index.test.d.ts.map +1 -0
  108. package/dist/scanners/revocation/index.test.js +332 -0
  109. package/dist/scanners/revocation/index.test.js.map +1 -0
  110. package/dist/scanners/revocation/patterns.d.ts +27 -0
  111. package/dist/scanners/revocation/patterns.d.ts.map +1 -0
  112. package/dist/scanners/revocation/patterns.js +109 -0
  113. package/dist/scanners/revocation/patterns.js.map +1 -0
  114. package/dist/scanners/sanitization/index.d.ts +8 -0
  115. package/dist/scanners/sanitization/index.d.ts.map +1 -0
  116. package/dist/scanners/sanitization/index.js +98 -0
  117. package/dist/scanners/sanitization/index.js.map +1 -0
  118. package/dist/scanners/sanitization/index.test.d.ts +5 -0
  119. package/dist/scanners/sanitization/index.test.d.ts.map +1 -0
  120. package/dist/scanners/sanitization/index.test.js +370 -0
  121. package/dist/scanners/sanitization/index.test.js.map +1 -0
  122. package/dist/scanners/sanitization/patterns.d.ts +27 -0
  123. package/dist/scanners/sanitization/patterns.d.ts.map +1 -0
  124. package/dist/scanners/sanitization/patterns.js +117 -0
  125. package/dist/scanners/sanitization/patterns.js.map +1 -0
  126. package/dist/training/certificate.d.ts +26 -0
  127. package/dist/training/certificate.d.ts.map +1 -0
  128. package/dist/training/certificate.js +92 -0
  129. package/dist/training/certificate.js.map +1 -0
  130. package/dist/training/index.d.ts +3 -0
  131. package/dist/training/index.d.ts.map +1 -0
  132. package/dist/training/index.js +243 -0
  133. package/dist/training/index.js.map +1 -0
  134. package/dist/training/modules.d.ts +13 -0
  135. package/dist/training/modules.d.ts.map +1 -0
  136. package/dist/training/modules.js +608 -0
  137. package/dist/training/modules.js.map +1 -0
  138. package/dist/training/questions.d.ts +9 -0
  139. package/dist/training/questions.d.ts.map +1 -0
  140. package/dist/training/questions.js +505 -0
  141. package/dist/training/questions.js.map +1 -0
  142. package/dist/types.d.ts +45 -0
  143. package/dist/types.d.ts.map +1 -1
  144. package/dist/utils/npm-audit.d.ts +6 -0
  145. package/dist/utils/npm-audit.d.ts.map +1 -0
  146. package/dist/utils/npm-audit.js +95 -0
  147. package/dist/utils/npm-audit.js.map +1 -0
  148. package/dist/utils/scan-history.d.ts +59 -0
  149. package/dist/utils/scan-history.d.ts.map +1 -0
  150. package/dist/utils/scan-history.js +170 -0
  151. package/dist/utils/scan-history.js.map +1 -0
  152. package/package.json +4 -1
  153. package/templates/baa-verification-letter.md +105 -0
  154. package/templates/irp.md +545 -0
  155. package/templates/notice-of-privacy-practices.md +491 -0
  156. package/templates/physical-safeguards-checklist.md +247 -0
  157. 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,5 @@
1
+ /**
2
+ * Tests for Role-Based Access Control Scanner
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=index.test.d.ts.map
@@ -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"}