verification-layer 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +345 -0
  3. package/dist/audit/evidence.d.ts +25 -0
  4. package/dist/audit/evidence.d.ts.map +1 -0
  5. package/dist/audit/evidence.js +70 -0
  6. package/dist/audit/evidence.js.map +1 -0
  7. package/dist/audit/index.d.ts +54 -0
  8. package/dist/audit/index.d.ts.map +1 -0
  9. package/dist/audit/index.js +159 -0
  10. package/dist/audit/index.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +199 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +7 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +77 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/fixer/index.d.ts +11 -0
  20. package/dist/fixer/index.d.ts.map +1 -0
  21. package/dist/fixer/index.js +171 -0
  22. package/dist/fixer/index.js.map +1 -0
  23. package/dist/fixer/strategies.d.ts +3 -0
  24. package/dist/fixer/strategies.d.ts.map +1 -0
  25. package/dist/fixer/strategies.js +199 -0
  26. package/dist/fixer/strategies.js.map +1 -0
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/reporters/audit-report.d.ts +13 -0
  32. package/dist/reporters/audit-report.d.ts.map +1 -0
  33. package/dist/reporters/audit-report.js +526 -0
  34. package/dist/reporters/audit-report.js.map +1 -0
  35. package/dist/reporters/fix-report.d.ts +3 -0
  36. package/dist/reporters/fix-report.d.ts.map +1 -0
  37. package/dist/reporters/fix-report.js +70 -0
  38. package/dist/reporters/fix-report.js.map +1 -0
  39. package/dist/reporters/index.d.ts +3 -0
  40. package/dist/reporters/index.d.ts.map +1 -0
  41. package/dist/reporters/index.js +425 -0
  42. package/dist/reporters/index.js.map +1 -0
  43. package/dist/reporters/remediation-guides.d.ts +25 -0
  44. package/dist/reporters/remediation-guides.d.ts.map +1 -0
  45. package/dist/reporters/remediation-guides.js +636 -0
  46. package/dist/reporters/remediation-guides.js.map +1 -0
  47. package/dist/scan.d.ts +3 -0
  48. package/dist/scan.d.ts.map +1 -0
  49. package/dist/scan.js +96 -0
  50. package/dist/scan.js.map +1 -0
  51. package/dist/scanners/access/index.d.ts +3 -0
  52. package/dist/scanners/access/index.d.ts.map +1 -0
  53. package/dist/scanners/access/index.js +102 -0
  54. package/dist/scanners/access/index.js.map +1 -0
  55. package/dist/scanners/audit/index.d.ts +3 -0
  56. package/dist/scanners/audit/index.d.ts.map +1 -0
  57. package/dist/scanners/audit/index.js +94 -0
  58. package/dist/scanners/audit/index.js.map +1 -0
  59. package/dist/scanners/encryption/index.d.ts +3 -0
  60. package/dist/scanners/encryption/index.d.ts.map +1 -0
  61. package/dist/scanners/encryption/index.js +86 -0
  62. package/dist/scanners/encryption/index.js.map +1 -0
  63. package/dist/scanners/phi/index.d.ts +3 -0
  64. package/dist/scanners/phi/index.d.ts.map +1 -0
  65. package/dist/scanners/phi/index.js +47 -0
  66. package/dist/scanners/phi/index.js.map +1 -0
  67. package/dist/scanners/phi/patterns.d.ts +13 -0
  68. package/dist/scanners/phi/patterns.d.ts.map +1 -0
  69. package/dist/scanners/phi/patterns.js +242 -0
  70. package/dist/scanners/phi/patterns.js.map +1 -0
  71. package/dist/scanners/retention/index.d.ts +3 -0
  72. package/dist/scanners/retention/index.d.ts.map +1 -0
  73. package/dist/scanners/retention/index.js +102 -0
  74. package/dist/scanners/retention/index.js.map +1 -0
  75. package/dist/scanners/security/index.d.ts +3 -0
  76. package/dist/scanners/security/index.d.ts.map +1 -0
  77. package/dist/scanners/security/index.js +280 -0
  78. package/dist/scanners/security/index.js.map +1 -0
  79. package/dist/stack-detector/index.d.ts +26 -0
  80. package/dist/stack-detector/index.d.ts.map +1 -0
  81. package/dist/stack-detector/index.js +317 -0
  82. package/dist/stack-detector/index.js.map +1 -0
  83. package/dist/stack-detector/stack-guides.d.ts +16 -0
  84. package/dist/stack-detector/stack-guides.d.ts.map +1 -0
  85. package/dist/stack-detector/stack-guides.js +772 -0
  86. package/dist/stack-detector/stack-guides.js.map +1 -0
  87. package/dist/types.d.ts +143 -0
  88. package/dist/types.d.ts.map +1 -0
  89. package/dist/types.js +2 -0
  90. package/dist/types.js.map +1 -0
  91. package/dist/utils/context.d.ts +3 -0
  92. package/dist/utils/context.d.ts.map +1 -0
  93. package/dist/utils/context.js +14 -0
  94. package/dist/utils/context.js.map +1 -0
  95. package/package.json +76 -0
@@ -0,0 +1,636 @@
1
+ export const REMEDIATION_GUIDES = [
2
+ // === PHI Exposure Guides ===
3
+ {
4
+ id: 'dob-exposure',
5
+ matchPatterns: ['Date of birth exposure', 'dob-exposed'],
6
+ hipaaImpact: "Date of birth is one of the 18 HIPAA identifiers that makes health information \"Protected Health Information\" (PHI).\nExposing DOB in code, logs, or unencrypted storage violates the HIPAA Privacy Rule (§164.502) and Security Rule (§164.312).\nBreaches involving DOB can result in fines of $100-$50,000 per violation, with annual maximums of $1.5 million per violation category.",
7
+ options: [
8
+ {
9
+ title: 'Option 1: Encrypt at Rest with AES-256',
10
+ description: 'Encrypt DOB before storing in database. Use AES-256-GCM for HIPAA-compliant encryption.',
11
+ language: 'typescript',
12
+ code: `import crypto from 'crypto';
13
+
14
+ // Encryption configuration (store key in secure vault like AWS KMS)
15
+ const ENCRYPTION_KEY = process.env.PHI_ENCRYPTION_KEY!; // 32 bytes for AES-256
16
+ const ALGORITHM = 'aes-256-gcm';
17
+
18
+ interface EncryptedData {
19
+ encrypted: string;
20
+ iv: string;
21
+ tag: string;
22
+ }
23
+
24
+ function encryptPHI(plaintext: string): EncryptedData {
25
+ const iv = crypto.randomBytes(16);
26
+ const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, 'hex'), iv);
27
+
28
+ let encrypted = cipher.update(plaintext, 'utf8', 'hex');
29
+ encrypted += cipher.final('hex');
30
+
31
+ return {
32
+ encrypted,
33
+ iv: iv.toString('hex'),
34
+ tag: cipher.getAuthTag().toString('hex')
35
+ };
36
+ }
37
+
38
+ // Usage
39
+ const patient = {
40
+ id: 'patient-123',
41
+ dateOfBirth: encryptPHI('1990-05-15'), // Store encrypted
42
+ };`
43
+ },
44
+ {
45
+ title: 'Option 2: Use Environment Variables for Test Data',
46
+ description: 'For development/test environments, load sample PHI from environment variables instead of hardcoding.',
47
+ language: 'typescript',
48
+ code: `// .env.development (add to .gitignore!)
49
+ // SAMPLE_DOB=1990-01-15
50
+
51
+ // config/sample-data.ts
52
+ export const samplePatient = {
53
+ firstName: process.env.SAMPLE_FIRST_NAME || 'Test',
54
+ lastName: process.env.SAMPLE_LAST_NAME || 'Patient',
55
+ dateOfBirth: process.env.SAMPLE_DOB || '2000-01-01',
56
+ };
57
+
58
+ // For CI/CD, use secrets management
59
+ // GitHub Actions: secrets.SAMPLE_DOB
60
+ // AWS: AWS Secrets Manager`
61
+ },
62
+ {
63
+ title: 'Option 3: Use Tokenization',
64
+ description: 'Replace sensitive data with non-sensitive tokens that map to real values in a secure vault.',
65
+ language: 'typescript',
66
+ code: `import { v4 as uuidv4 } from 'uuid';
67
+
68
+ class PHITokenVault {
69
+ private vault: Map<string, string> = new Map();
70
+
71
+ tokenize(sensitiveData: string): string {
72
+ const token = 'PHI_' + uuidv4();
73
+ this.vault.set(token, sensitiveData);
74
+ return token;
75
+ }
76
+
77
+ detokenize(token: string): string | null {
78
+ return this.vault.get(token) || null;
79
+ }
80
+ }
81
+
82
+ // Usage
83
+ const vault = new PHITokenVault();
84
+ const patient = {
85
+ id: 'patient-123',
86
+ dobToken: vault.tokenize('1990-05-15'),
87
+ };`
88
+ }
89
+ ],
90
+ documentation: [
91
+ { title: 'HIPAA Security Rule - Encryption Standards', url: 'https://www.hhs.gov/hipaa/for-professionals/security/guidance/index.html' },
92
+ { title: 'NIST Cryptographic Standards', url: 'https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines' },
93
+ { title: 'Node.js Crypto Documentation', url: 'https://nodejs.org/api/crypto.html' }
94
+ ]
95
+ },
96
+ {
97
+ id: 'phi-console-log',
98
+ matchPatterns: ['PHI data in console output', 'Patient name in console', 'phi-console-log', 'Patient object serialized'],
99
+ hipaaImpact: "Logging PHI to console or log files creates unauthorized copies of protected health information.\nConsole logs may be captured by monitoring tools, stored in plain text, or visible to unauthorized personnel.\nThis violates HIPAA's minimum necessary standard (§164.502(b)) and access controls (§164.312(a)(1)).",
100
+ options: [
101
+ {
102
+ title: 'Option 1: Use Structured Logging with PHI Redaction',
103
+ description: 'Implement a logging wrapper that automatically redacts PHI fields.',
104
+ language: 'typescript',
105
+ code: `import pino from 'pino';
106
+
107
+ // PHI fields to redact
108
+ const PHI_FIELDS = [
109
+ 'ssn', 'socialSecurityNumber', 'dateOfBirth', 'dob',
110
+ 'mrn', 'medicalRecordNumber', 'diagnosis', 'medication'
111
+ ];
112
+
113
+ const logger = pino({
114
+ redact: {
115
+ paths: PHI_FIELDS.concat(PHI_FIELDS.map(f => '*.' + f)),
116
+ censor: '[PHI REDACTED]'
117
+ },
118
+ level: process.env.LOG_LEVEL || 'info'
119
+ });
120
+
121
+ // Usage - PHI is automatically redacted
122
+ logger.info({ patient: { id: '123', ssn: '123-45-6789' } }, 'Patient loaded');
123
+ // Output: {"patient":{"id":"123","ssn":"[PHI REDACTED]"}}`
124
+ },
125
+ {
126
+ title: 'Option 2: Create a Safe Logging Wrapper',
127
+ description: 'Create utility functions that strip PHI before logging.',
128
+ language: 'typescript',
129
+ code: `interface SafePatient {
130
+ id: string;
131
+ hasInsurance: boolean;
132
+ appointmentCount: number;
133
+ }
134
+
135
+ function toSafeLog<T extends { id: string }>(patient: T): SafePatient {
136
+ return {
137
+ id: patient.id,
138
+ hasInsurance: 'insurance' in patient,
139
+ appointmentCount: 'appointments' in patient
140
+ ? (patient as any).appointments?.length || 0
141
+ : 0
142
+ };
143
+ }
144
+
145
+ // Usage
146
+ function processPatient(patient: Patient) {
147
+ console.log('Processing patient:', toSafeLog(patient));
148
+ // Logs: { id: '123', hasInsurance: true, appointmentCount: 3 }
149
+ }`
150
+ }
151
+ ],
152
+ documentation: [
153
+ { title: 'Pino Logger - Redaction', url: 'https://getpino.io/#/docs/redaction' },
154
+ { title: 'Winston Logger', url: 'https://github.com/winstonjs/winston' },
155
+ { title: 'OWASP Logging Cheat Sheet', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html' }
156
+ ]
157
+ },
158
+ {
159
+ id: 'phi-storage',
160
+ matchPatterns: ['PHI stored in localStorage', 'PHI stored in sessionStorage', 'PHI stored in cookies', 'phi-localstorage'],
161
+ hipaaImpact: "Browser storage (localStorage, sessionStorage, cookies) is not encrypted and can be accessed by any JavaScript on the page.\nThis creates severe XSS risks where attackers could steal PHI. Browser storage also persists across sessions.\nThis violates HIPAA encryption requirements (§164.312(a)(2)(iv)) and access controls.",
162
+ options: [
163
+ {
164
+ title: 'Option 1: Use Server-Side Sessions Only',
165
+ description: 'Store only a session ID in cookies, keep PHI server-side.',
166
+ language: 'typescript',
167
+ code: `import session from 'express-session';
168
+ import RedisStore from 'connect-redis';
169
+ import { createClient } from 'redis';
170
+
171
+ const redisClient = createClient({ url: process.env.REDIS_URL });
172
+
173
+ app.use(session({
174
+ store: new RedisStore({ client: redisClient }),
175
+ secret: process.env.SESSION_SECRET!,
176
+ resave: false,
177
+ saveUninitialized: false,
178
+ cookie: {
179
+ secure: true, // HTTPS only
180
+ httpOnly: true, // No JavaScript access
181
+ sameSite: 'strict',
182
+ maxAge: 15 * 60 * 1000 // 15 minutes
183
+ }
184
+ }));
185
+
186
+ // Store PHI in session, not browser
187
+ app.get('/api/patient/:id', async (req, res) => {
188
+ const patient = await getPatient(req.params.id);
189
+ req.session.currentPatient = patient; // Server-side only
190
+ res.json({ id: patient.id }); // Only send ID to client
191
+ });`
192
+ }
193
+ ],
194
+ documentation: [
195
+ { title: 'OWASP Session Management', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html' },
196
+ { title: 'Express Session', url: 'https://github.com/expressjs/session' }
197
+ ]
198
+ },
199
+ // === Audit Logging Guides ===
200
+ {
201
+ id: 'no-audit-logging',
202
+ matchPatterns: ['No audit logging framework detected', 'audit-framework-missing'],
203
+ hipaaImpact: "HIPAA requires covered entities to implement audit controls that record and examine activity in systems containing PHI (§164.312(b)).\nWithout audit logging, you cannot detect unauthorized access, investigate breaches, or demonstrate compliance during audits.\nMissing audit logs can result in automatic assumption of breach during investigations.",
204
+ options: [
205
+ {
206
+ title: 'Option 1: Implement Winston with HIPAA-Compliant Configuration',
207
+ description: 'Set up Winston logger with file rotation, timestamps, and structured format.',
208
+ language: 'typescript',
209
+ code: `import winston from 'winston';
210
+ import DailyRotateFile from 'winston-daily-rotate-file';
211
+
212
+ const auditLogger = winston.createLogger({
213
+ level: 'info',
214
+ format: winston.format.combine(
215
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
216
+ winston.format.json()
217
+ ),
218
+ defaultMeta: { service: 'healthcare-app' },
219
+ transports: [
220
+ new DailyRotateFile({
221
+ filename: 'logs/audit-%DATE%.log',
222
+ datePattern: 'YYYY-MM-DD',
223
+ maxFiles: '2190d', // 6 years retention
224
+ zippedArchive: true,
225
+ })
226
+ ]
227
+ });
228
+
229
+ export function auditLog(action: string, details: {
230
+ userId: string;
231
+ patientId?: string;
232
+ resource: string;
233
+ success: boolean;
234
+ }) {
235
+ auditLogger.info({ action, ...details, timestamp: new Date().toISOString() });
236
+ }`
237
+ },
238
+ {
239
+ title: 'Option 2: Implement Pino with Async Logging',
240
+ description: 'Use Pino for high-performance async logging.',
241
+ language: 'typescript',
242
+ code: `import pino from 'pino';
243
+
244
+ const transport = pino.transport({
245
+ targets: [
246
+ {
247
+ target: 'pino/file',
248
+ options: { destination: './logs/audit.log' },
249
+ level: 'info'
250
+ }
251
+ ]
252
+ });
253
+
254
+ const logger = pino({
255
+ level: 'info',
256
+ timestamp: pino.stdTimeFunctions.isoTime,
257
+ redact: ['*.ssn', '*.dateOfBirth']
258
+ }, transport);
259
+
260
+ // Audit middleware
261
+ export function auditMiddleware(req: any, res: any, next: any) {
262
+ res.on('finish', () => {
263
+ logger.info({
264
+ type: 'http_request',
265
+ method: req.method,
266
+ path: req.path,
267
+ statusCode: res.statusCode,
268
+ userId: req.user?.id
269
+ });
270
+ });
271
+ next();
272
+ }`
273
+ }
274
+ ],
275
+ documentation: [
276
+ { title: 'Winston Documentation', url: 'https://github.com/winstonjs/winston' },
277
+ { title: 'Pino Documentation', url: 'https://getpino.io/' },
278
+ { title: 'HIPAA Audit Controls Guide', url: 'https://www.hhs.gov/hipaa/for-professionals/security/guidance/audit-controls/index.html' }
279
+ ]
280
+ },
281
+ {
282
+ id: 'phi-read-no-audit',
283
+ matchPatterns: ['PHI read operation may lack audit logging', 'phi-access-unlogged'],
284
+ hipaaImpact: "Every access to PHI must be logged per HIPAA's audit control requirements (§164.312(b)).\nWithout logging PHI access, you cannot detect unauthorized access, provide access reports to patients, or investigate breaches.",
285
+ options: [
286
+ {
287
+ title: 'Option 1: Create an Audited Repository Pattern',
288
+ description: 'Wrap data access with automatic audit logging.',
289
+ language: 'typescript',
290
+ code: `import { auditLog } from './audit-logger';
291
+
292
+ class AuditedPatientRepository {
293
+ constructor(private db: Database, private getContext: () => { userId: string }) {}
294
+
295
+ async findById(patientId: string): Promise<Patient | null> {
296
+ const ctx = this.getContext();
297
+ const patient = await this.db.patients.findUnique({ where: { id: patientId } });
298
+
299
+ auditLog('PHI_READ', {
300
+ userId: ctx.userId,
301
+ patientId,
302
+ resource: 'patient',
303
+ success: !!patient,
304
+ fieldsAccessed: patient ? Object.keys(patient) : []
305
+ });
306
+
307
+ return patient;
308
+ }
309
+ }`
310
+ },
311
+ {
312
+ title: 'Option 2: Use Prisma Middleware',
313
+ description: 'Add audit logging middleware to Prisma client.',
314
+ language: 'typescript',
315
+ code: `import { PrismaClient } from '@prisma/client';
316
+ import { auditLog } from './audit-logger';
317
+
318
+ const prisma = new PrismaClient();
319
+ const PHI_MODELS = ['Patient', 'MedicalRecord', 'Prescription'];
320
+
321
+ prisma.$use(async (params, next) => {
322
+ const { model, action, args } = params;
323
+
324
+ if (!model || !PHI_MODELS.includes(model)) {
325
+ return next(params);
326
+ }
327
+
328
+ const result = await next(params);
329
+
330
+ auditLog('PHI_' + action.toUpperCase(), {
331
+ userId: getCurrentUserId(),
332
+ resource: model.toLowerCase(),
333
+ patientId: args?.where?.id || result?.id,
334
+ success: true
335
+ });
336
+
337
+ return result;
338
+ });`
339
+ }
340
+ ],
341
+ documentation: [
342
+ { title: 'Prisma Middleware', url: 'https://www.prisma.io/docs/concepts/components/prisma-client/middleware' },
343
+ { title: 'HIPAA Audit Requirements', url: 'https://www.hhs.gov/hipaa/for-professionals/security/guidance/audit-controls/index.html' }
344
+ ]
345
+ },
346
+ {
347
+ id: 'data-deletion-no-audit',
348
+ matchPatterns: ['Data deletion without apparent logging', 'unlogged-delete'],
349
+ hipaaImpact: "PHI deletion must be logged to comply with HIPAA audit requirements and to prove proper data retention/destruction.\nHIPAA requires maintaining records of PHI dispositions for 6 years.",
350
+ options: [
351
+ {
352
+ title: 'Option 1: Soft Delete with Audit Trail',
353
+ description: 'Never hard delete PHI - use soft deletes with audit logging.',
354
+ language: 'typescript',
355
+ code: `async function softDeletePatient(
356
+ patientId: string,
357
+ userId: string,
358
+ reason: string
359
+ ): Promise<void> {
360
+ await prisma.$transaction(async (tx) => {
361
+ await tx.patient.update({
362
+ where: { id: patientId },
363
+ data: {
364
+ deletedAt: new Date(),
365
+ deletedBy: userId,
366
+ deletionReason: reason
367
+ }
368
+ });
369
+
370
+ await tx.phiAuditLog.create({
371
+ data: {
372
+ action: 'PHI_SOFT_DELETE',
373
+ resourceType: 'patient',
374
+ resourceId: patientId,
375
+ userId,
376
+ reason
377
+ }
378
+ });
379
+ });
380
+ }`
381
+ }
382
+ ],
383
+ documentation: [
384
+ { title: 'HIPAA Retention Requirements', url: 'https://www.hhs.gov/hipaa/for-professionals/faq/580/does-hipaa-require-covered-entities-to-keep-medical-records/index.html' }
385
+ ]
386
+ },
387
+ // === Security Guides ===
388
+ {
389
+ id: 'hardcoded-credentials',
390
+ matchPatterns: ['Hardcoded password', 'Hardcoded secret', 'API key exposed', 'hardcoded-password', 'hardcoded-secret', 'api-key-exposed'],
391
+ hipaaImpact: "Hardcoded credentials in source code violate HIPAA's access control requirements (§164.312(d)).\nCredentials in code can be exposed through version control, logs, or decompilation, creating unauthorized access paths to PHI.",
392
+ options: [
393
+ {
394
+ title: 'Option 1: Use Environment Variables',
395
+ description: 'Load credentials from environment variables with validation.',
396
+ language: 'typescript',
397
+ code: `import { z } from 'zod';
398
+
399
+ const envSchema = z.object({
400
+ DATABASE_URL: z.string().url(),
401
+ API_KEY: z.string().min(20),
402
+ JWT_SECRET: z.string().min(32),
403
+ });
404
+
405
+ function loadEnv() {
406
+ const result = envSchema.safeParse(process.env);
407
+ if (!result.success) {
408
+ console.error('Missing environment variables:', result.error.format());
409
+ process.exit(1);
410
+ }
411
+ return result.data;
412
+ }
413
+
414
+ export const env = loadEnv();`
415
+ },
416
+ {
417
+ title: 'Option 2: Use AWS Secrets Manager',
418
+ description: 'Store and rotate secrets using AWS Secrets Manager.',
419
+ language: 'typescript',
420
+ code: `import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
421
+
422
+ const client = new SecretsManagerClient({ region: process.env.AWS_REGION });
423
+
424
+ async function getSecrets(): Promise<{ apiKey: string; dbUrl: string }> {
425
+ const response = await client.send(
426
+ new GetSecretValueCommand({ SecretId: 'healthcare-app/production' })
427
+ );
428
+ return JSON.parse(response.SecretString!);
429
+ }
430
+
431
+ // Usage
432
+ const secrets = await getSecrets();
433
+ const db = new Database(secrets.dbUrl);`
434
+ }
435
+ ],
436
+ documentation: [
437
+ { title: 'AWS Secrets Manager', url: 'https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html' },
438
+ { title: 'dotenv for Node.js', url: 'https://github.com/motdotla/dotenv' },
439
+ { title: 'OWASP Secrets Management', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html' }
440
+ ]
441
+ },
442
+ {
443
+ id: 'sql-injection',
444
+ matchPatterns: ['SQL query string concatenation', 'SQL query with template literal', 'Database query with template', 'sql-injection'],
445
+ hipaaImpact: "SQL injection can allow attackers to extract entire databases containing PHI, modify medical records, or bypass authentication.\nA single SQL injection vulnerability can result in a massive PHI breach affecting thousands of patients.",
446
+ options: [
447
+ {
448
+ title: 'Option 1: Use Parameterized Queries',
449
+ description: 'Always use parameterized queries or prepared statements.',
450
+ language: 'typescript',
451
+ code: `// BAD - SQL Injection vulnerable
452
+ const user = await db.query(
453
+ 'SELECT * FROM users WHERE id = ' + userId
454
+ );
455
+
456
+ // GOOD - Parameterized query (PostgreSQL)
457
+ const user = await db.query(
458
+ 'SELECT * FROM users WHERE id = $1',
459
+ [userId]
460
+ );
461
+
462
+ // GOOD - Parameterized query (MySQL)
463
+ const user = await db.query(
464
+ 'SELECT * FROM users WHERE id = ?',
465
+ [userId]
466
+ );`
467
+ },
468
+ {
469
+ title: 'Option 2: Use an ORM (Prisma)',
470
+ description: 'ORMs automatically use parameterized queries.',
471
+ language: 'typescript',
472
+ code: `import { PrismaClient } from '@prisma/client';
473
+
474
+ const prisma = new PrismaClient();
475
+
476
+ // Prisma automatically parameterizes - SAFE
477
+ const user = await prisma.user.findUnique({
478
+ where: { id: userId }
479
+ });
480
+
481
+ const users = await prisma.user.findMany({
482
+ where: {
483
+ role: userRole,
484
+ createdAt: { gte: startDate }
485
+ }
486
+ });`
487
+ }
488
+ ],
489
+ documentation: [
490
+ { title: 'OWASP SQL Injection Prevention', url: 'https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html' },
491
+ { title: 'Prisma Documentation', url: 'https://www.prisma.io/docs/' }
492
+ ]
493
+ },
494
+ {
495
+ id: 'xss-innerhtml',
496
+ matchPatterns: ['Unsanitized innerHTML', 'dangerouslySetInnerHTML', 'innerHTML', 'document.write'],
497
+ hipaaImpact: "Cross-Site Scripting (XSS) through innerHTML can allow attackers to steal session cookies, capture PHI displayed on screen, or perform actions on behalf of authenticated users.",
498
+ options: [
499
+ {
500
+ title: 'Option 1: Use textContent Instead',
501
+ description: 'For text-only content, use textContent which is XSS-safe.',
502
+ language: 'typescript',
503
+ code: `// BAD - XSS vulnerable
504
+ element.innerHTML = userInput;
505
+
506
+ // GOOD - Safe for text content
507
+ element.textContent = userInput;
508
+
509
+ // React - Use normal JSX (auto-escaped)
510
+ function PatientName({ name }: { name: string }) {
511
+ return <span>{name}</span>; // Safe
512
+ }`
513
+ },
514
+ {
515
+ title: 'Option 2: Sanitize HTML with DOMPurify',
516
+ description: 'When HTML is required, sanitize with DOMPurify.',
517
+ language: 'typescript',
518
+ code: `import DOMPurify from 'dompurify';
519
+
520
+ const purifyConfig = {
521
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
522
+ ALLOWED_ATTR: []
523
+ };
524
+
525
+ // Sanitize before setting innerHTML
526
+ element.innerHTML = DOMPurify.sanitize(userHtml, purifyConfig);
527
+
528
+ // React with dangerouslySetInnerHTML
529
+ function RichText({ html }: { html: string }) {
530
+ const sanitized = DOMPurify.sanitize(html, purifyConfig);
531
+ return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
532
+ }`
533
+ }
534
+ ],
535
+ documentation: [
536
+ { title: 'DOMPurify', url: 'https://github.com/cure53/DOMPurify' },
537
+ { title: 'OWASP XSS Prevention', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html' }
538
+ ]
539
+ },
540
+ {
541
+ id: 'http-not-https',
542
+ matchPatterns: ['Unencrypted HTTP URL', 'http-url', 'HTTP endpoint'],
543
+ hipaaImpact: "HIPAA requires encryption of PHI in transit (§164.312(e)(1)).\nHTTP transmits data in plain text, allowing interception of PHI through network sniffing or man-in-the-middle attacks.",
544
+ options: [
545
+ {
546
+ title: 'Option 1: Convert to HTTPS',
547
+ description: 'Simply change http:// to https:// for external URLs.',
548
+ language: 'typescript',
549
+ code: `// BAD
550
+ const apiUrl = 'http://api.healthcare.com/patients';
551
+
552
+ // GOOD
553
+ const apiUrl = 'https://api.healthcare.com/patients';
554
+
555
+ // Better - use environment variables
556
+ const apiUrl = process.env.API_URL; // Set to https:// in env`
557
+ },
558
+ {
559
+ title: 'Option 2: Enforce HTTPS at Application Level',
560
+ description: 'Add middleware to redirect and enforce HTTPS.',
561
+ language: 'typescript',
562
+ code: `import helmet from 'helmet';
563
+
564
+ app.use(helmet({
565
+ hsts: {
566
+ maxAge: 31536000,
567
+ includeSubDomains: true,
568
+ preload: true
569
+ }
570
+ }));
571
+
572
+ // Redirect HTTP to HTTPS
573
+ app.use((req, res, next) => {
574
+ if (req.header('x-forwarded-proto') !== 'https' &&
575
+ process.env.NODE_ENV === 'production') {
576
+ return res.redirect('https://' + req.header('host') + req.url);
577
+ }
578
+ next();
579
+ });`
580
+ }
581
+ ],
582
+ documentation: [
583
+ { title: 'Helmet.js Security', url: 'https://helmetjs.github.io/' },
584
+ { title: 'HIPAA Transmission Security', url: 'https://www.hhs.gov/hipaa/for-professionals/security/guidance/transmission-security/index.html' }
585
+ ]
586
+ },
587
+ {
588
+ id: 'diagnosis-code-exposure',
589
+ matchPatterns: ['Diagnosis code in source', 'ICD-10', 'diagnosis-code'],
590
+ hipaaImpact: "ICD-10 diagnosis codes are considered PHI when associated with a patient.\nHardcoded diagnosis codes may indicate test data containing real patient information or improper handling of medical classifications.",
591
+ options: [
592
+ {
593
+ title: 'Option 1: Load from Secure Configuration',
594
+ description: 'Store diagnosis codes in encrypted configuration or database.',
595
+ language: 'typescript',
596
+ code: `// BAD - hardcoded diagnosis codes
597
+ const testPatient = {
598
+ diagnosis: 'F32.1', // Major depressive disorder
599
+ };
600
+
601
+ // GOOD - load from secure source
602
+ async function getDiagnosisCodes() {
603
+ return prisma.diagnosisCode.findMany({
604
+ select: { code: true, description: true }
605
+ });
606
+ }
607
+
608
+ // For test data, use obviously fake codes
609
+ const TEST_DIAGNOSIS = {
610
+ code: 'TEST-001',
611
+ description: 'Test Diagnosis - Not Real',
612
+ isTestData: true
613
+ };`
614
+ }
615
+ ],
616
+ documentation: [
617
+ { title: 'FHIR Terminology Service', url: 'https://www.hl7.org/fhir/terminology-service.html' },
618
+ { title: 'ICD-10 Code Set', url: 'https://www.cms.gov/medicare/coding-billing/icd-10-codes' }
619
+ ]
620
+ }
621
+ ];
622
+ /**
623
+ * Find the best matching remediation guide for a finding
624
+ */
625
+ export function getRemediationGuide(finding) {
626
+ for (const guide of REMEDIATION_GUIDES) {
627
+ for (const pattern of guide.matchPatterns) {
628
+ if (finding.title.toLowerCase().includes(pattern.toLowerCase()) ||
629
+ finding.id.toLowerCase().includes(pattern.toLowerCase())) {
630
+ return guide;
631
+ }
632
+ }
633
+ }
634
+ return null;
635
+ }
636
+ //# sourceMappingURL=remediation-guides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remediation-guides.js","sourceRoot":"","sources":["../../src/reporters/remediation-guides.ts"],"names":[],"mappings":"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"}
package/dist/scan.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { ScanOptions, ScanResult } from './types.js';
2
+ export declare function scan(options: ScanOptions): Promise<ScanResult>;
3
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAmD,MAAM,YAAY,CAAC;AAgC3G,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA+EpE"}