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,772 @@
1
+ // =============================================================================
2
+ // SESSION MANAGEMENT GUIDES
3
+ // =============================================================================
4
+ const sessionGuides = {
5
+ nextjs: [
6
+ {
7
+ title: 'Use Server Components for PHI',
8
+ description: 'Keep PHI in server components to prevent client exposure. Never pass PHI as props to client components.',
9
+ language: 'tsx',
10
+ code: `// app/patients/[id]/page.tsx (Server Component)
11
+ import { getPatient } from '@/lib/db';
12
+
13
+ export default async function PatientPage({ params }: { params: { id: string } }) {
14
+ const patient = await getPatient(params.id);
15
+
16
+ // PHI stays on server, only send necessary display data
17
+ return (
18
+ <PatientView
19
+ name={patient.name}
20
+ // Don't pass: ssn, fullRecord, etc.
21
+ />
22
+ );
23
+ }`,
24
+ },
25
+ {
26
+ title: 'Secure API Routes with Middleware',
27
+ description: 'Protect all PHI-related API routes with authentication middleware.',
28
+ language: 'typescript',
29
+ code: `// middleware.ts
30
+ import { NextResponse } from 'next/server';
31
+ import type { NextRequest } from 'next/server';
32
+
33
+ export function middleware(request: NextRequest) {
34
+ const session = request.cookies.get('session');
35
+
36
+ if (request.nextUrl.pathname.startsWith('/api/patients')) {
37
+ if (!session) {
38
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
39
+ }
40
+ // Log PHI access for audit trail
41
+ console.log(\`[AUDIT] PHI access: \${request.nextUrl.pathname}\`);
42
+ }
43
+
44
+ return NextResponse.next();
45
+ }
46
+
47
+ export const config = {
48
+ matcher: ['/api/patients/:path*', '/dashboard/:path*']
49
+ };`,
50
+ },
51
+ ],
52
+ express: [
53
+ {
54
+ title: 'Use express-session with Redis',
55
+ description: 'Store sessions in Redis for HIPAA-compliant session management with automatic expiration.',
56
+ language: 'typescript',
57
+ code: `import session from 'express-session';
58
+ import RedisStore from 'connect-redis';
59
+ import { createClient } from 'redis';
60
+
61
+ const redisClient = createClient({ url: process.env.REDIS_URL });
62
+ await redisClient.connect();
63
+
64
+ app.use(session({
65
+ store: new RedisStore({ client: redisClient }),
66
+ secret: process.env.SESSION_SECRET!,
67
+ resave: false,
68
+ saveUninitialized: false,
69
+ cookie: {
70
+ secure: true, // HTTPS only
71
+ httpOnly: true, // No JS access
72
+ maxAge: 15 * 60 * 1000, // 15 min (HIPAA recommendation)
73
+ sameSite: 'strict'
74
+ }
75
+ }));`,
76
+ },
77
+ {
78
+ title: 'PHI Access Logging Middleware',
79
+ description: 'Log all PHI access for HIPAA audit requirements.',
80
+ language: 'typescript',
81
+ code: `import { Request, Response, NextFunction } from 'express';
82
+
83
+ const phiAuditMiddleware = (req: Request, res: Response, next: NextFunction) => {
84
+ if (req.path.includes('/patient') || req.path.includes('/phi')) {
85
+ const auditLog = {
86
+ timestamp: new Date().toISOString(),
87
+ userId: req.session?.userId,
88
+ action: req.method,
89
+ resource: req.path,
90
+ ip: req.ip,
91
+ };
92
+ // Send to audit logging service
93
+ auditLogger.log(auditLog);
94
+ }
95
+ next();
96
+ };
97
+
98
+ app.use('/api', phiAuditMiddleware);`,
99
+ },
100
+ ],
101
+ react: [],
102
+ vue: [],
103
+ nuxt: [],
104
+ angular: [],
105
+ fastify: [],
106
+ nestjs: [],
107
+ koa: [],
108
+ hono: [],
109
+ unknown: [],
110
+ };
111
+ // =============================================================================
112
+ // DATABASE SECURITY GUIDES
113
+ // =============================================================================
114
+ const databaseGuides = {
115
+ supabase: [
116
+ {
117
+ title: 'Enable Row Level Security (RLS)',
118
+ description: 'Supabase RLS ensures users can only access their authorized data at the database level.',
119
+ language: 'sql',
120
+ code: `-- Enable RLS on patients table
121
+ ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
122
+
123
+ -- Policy: Users can only see patients they're assigned to
124
+ CREATE POLICY "Users can view assigned patients" ON patients
125
+ FOR SELECT
126
+ USING (
127
+ auth.uid() IN (
128
+ SELECT user_id FROM patient_assignments
129
+ WHERE patient_id = patients.id
130
+ )
131
+ );
132
+
133
+ -- Policy: Only admins can insert patients
134
+ CREATE POLICY "Admins can insert patients" ON patients
135
+ FOR INSERT
136
+ WITH CHECK (
137
+ EXISTS (
138
+ SELECT 1 FROM user_roles
139
+ WHERE user_id = auth.uid() AND role = 'admin'
140
+ )
141
+ );
142
+
143
+ -- Audit logging via trigger
144
+ CREATE OR REPLACE FUNCTION log_phi_access()
145
+ RETURNS TRIGGER AS $$
146
+ BEGIN
147
+ INSERT INTO audit_log (user_id, action, table_name, record_id, timestamp)
148
+ VALUES (auth.uid(), TG_OP, TG_TABLE_NAME, NEW.id, NOW());
149
+ RETURN NEW;
150
+ END;
151
+ $$ LANGUAGE plpgsql;
152
+
153
+ CREATE TRIGGER patients_audit
154
+ AFTER INSERT OR UPDATE OR DELETE ON patients
155
+ FOR EACH ROW EXECUTE FUNCTION log_phi_access();`,
156
+ },
157
+ {
158
+ title: 'Use Supabase Server Client',
159
+ description: 'Always use server-side Supabase client for PHI operations to prevent exposing service role key.',
160
+ language: 'typescript',
161
+ code: `// lib/supabase/server.ts
162
+ import { createServerClient } from '@supabase/ssr';
163
+ import { cookies } from 'next/headers';
164
+
165
+ export async function createClient() {
166
+ const cookieStore = await cookies();
167
+
168
+ return createServerClient(
169
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
170
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
171
+ {
172
+ cookies: {
173
+ getAll() {
174
+ return cookieStore.getAll();
175
+ },
176
+ setAll(cookiesToSet) {
177
+ cookiesToSet.forEach(({ name, value, options }) =>
178
+ cookieStore.set(name, value, options)
179
+ );
180
+ },
181
+ },
182
+ }
183
+ );
184
+ }
185
+
186
+ // Usage in Server Component
187
+ const supabase = await createClient();
188
+ const { data: patients } = await supabase
189
+ .from('patients')
190
+ .select('id, name, dob') // Only select needed columns
191
+ .limit(50);`,
192
+ },
193
+ ],
194
+ firebase: [
195
+ {
196
+ title: 'Firestore Security Rules',
197
+ description: 'Configure Firestore rules to enforce HIPAA access controls.',
198
+ language: 'javascript',
199
+ code: `// firestore.rules
200
+ rules_version = '2';
201
+ service cloud.firestore {
202
+ match /databases/{database}/documents {
203
+ // Helper function to check user role
204
+ function hasRole(role) {
205
+ return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == role;
206
+ }
207
+
208
+ // Patients collection - strict access
209
+ match /patients/{patientId} {
210
+ // Only authenticated healthcare providers can read
211
+ allow read: if request.auth != null
212
+ && hasRole('healthcare_provider')
213
+ && resource.data.assignedProviders.hasAny([request.auth.uid]);
214
+
215
+ // Only admins can create/update
216
+ allow write: if request.auth != null && hasRole('admin');
217
+
218
+ // Never allow delete - use soft delete
219
+ allow delete: if false;
220
+ }
221
+
222
+ // Audit log - append only
223
+ match /audit_log/{logId} {
224
+ allow create: if request.auth != null;
225
+ allow read: if hasRole('compliance_officer');
226
+ allow update, delete: if false;
227
+ }
228
+ }
229
+ }`,
230
+ },
231
+ {
232
+ title: 'Firebase Admin SDK for PHI',
233
+ description: 'Use Admin SDK server-side for PHI operations with proper audit logging.',
234
+ language: 'typescript',
235
+ code: `// lib/firebase-admin.ts
236
+ import { initializeApp, cert, getApps } from 'firebase-admin/app';
237
+ import { getFirestore } from 'firebase-admin/firestore';
238
+
239
+ const app = getApps().length === 0
240
+ ? initializeApp({ credential: cert(JSON.parse(process.env.FIREBASE_ADMIN_KEY!)) })
241
+ : getApps()[0];
242
+
243
+ const db = getFirestore(app);
244
+
245
+ export async function getPatient(patientId: string, userId: string) {
246
+ // Log access attempt
247
+ await db.collection('audit_log').add({
248
+ userId,
249
+ action: 'READ',
250
+ resource: \`patients/\${patientId}\`,
251
+ timestamp: new Date(),
252
+ });
253
+
254
+ const doc = await db.collection('patients').doc(patientId).get();
255
+ return doc.data();
256
+ }`,
257
+ },
258
+ ],
259
+ postgresql: [
260
+ {
261
+ title: 'Parameterized Queries',
262
+ description: 'Always use parameterized queries to prevent SQL injection.',
263
+ language: 'typescript',
264
+ code: `import { Pool } from 'pg';
265
+
266
+ const pool = new Pool({
267
+ connectionString: process.env.DATABASE_URL,
268
+ ssl: { rejectUnauthorized: true } // Enforce TLS
269
+ });
270
+
271
+ // GOOD: Parameterized query
272
+ async function getPatient(patientId: string) {
273
+ const result = await pool.query(
274
+ 'SELECT id, name, dob FROM patients WHERE id = $1',
275
+ [patientId]
276
+ );
277
+ return result.rows[0];
278
+ }
279
+
280
+ // GOOD: With audit logging
281
+ async function getPatientWithAudit(patientId: string, userId: string) {
282
+ const client = await pool.connect();
283
+ try {
284
+ await client.query('BEGIN');
285
+
286
+ // Log access
287
+ await client.query(
288
+ 'INSERT INTO audit_log (user_id, action, resource, timestamp) VALUES ($1, $2, $3, NOW())',
289
+ [userId, 'READ', \`patient:\${patientId}\`]
290
+ );
291
+
292
+ // Fetch data
293
+ const result = await client.query(
294
+ 'SELECT id, name, dob FROM patients WHERE id = $1',
295
+ [patientId]
296
+ );
297
+
298
+ await client.query('COMMIT');
299
+ return result.rows[0];
300
+ } catch (e) {
301
+ await client.query('ROLLBACK');
302
+ throw e;
303
+ } finally {
304
+ client.release();
305
+ }
306
+ }`,
307
+ },
308
+ ],
309
+ mysql: [
310
+ {
311
+ title: 'Prepared Statements with mysql2',
312
+ description: 'Use prepared statements to prevent SQL injection.',
313
+ language: 'typescript',
314
+ code: `import mysql from 'mysql2/promise';
315
+
316
+ const pool = mysql.createPool({
317
+ uri: process.env.DATABASE_URL,
318
+ ssl: { rejectUnauthorized: true }
319
+ });
320
+
321
+ async function getPatient(patientId: string) {
322
+ const [rows] = await pool.execute(
323
+ 'SELECT id, name, dob FROM patients WHERE id = ?',
324
+ [patientId]
325
+ );
326
+ return rows[0];
327
+ }`,
328
+ },
329
+ ],
330
+ mongodb: [
331
+ {
332
+ title: 'MongoDB Query Sanitization',
333
+ description: 'Sanitize inputs and use projection to limit PHI exposure.',
334
+ language: 'typescript',
335
+ code: `import { MongoClient, ObjectId } from 'mongodb';
336
+
337
+ const client = new MongoClient(process.env.MONGODB_URI!, {
338
+ tls: true,
339
+ tlsAllowInvalidCertificates: false
340
+ });
341
+
342
+ async function getPatient(patientId: string) {
343
+ const db = client.db('healthcare');
344
+
345
+ // Validate ObjectId format
346
+ if (!ObjectId.isValid(patientId)) {
347
+ throw new Error('Invalid patient ID');
348
+ }
349
+
350
+ // Use projection to limit returned fields
351
+ return db.collection('patients').findOne(
352
+ { _id: new ObjectId(patientId) },
353
+ { projection: { ssn: 0, fullRecords: 0 } } // Exclude sensitive fields
354
+ );
355
+ }`,
356
+ },
357
+ ],
358
+ prisma: [
359
+ {
360
+ title: 'Prisma with Field-Level Selection',
361
+ description: 'Use Prisma select to limit PHI exposure.',
362
+ language: 'typescript',
363
+ code: `import { PrismaClient } from '@prisma/client';
364
+
365
+ const prisma = new PrismaClient({
366
+ log: ['query', 'error'], // Enable query logging for audit
367
+ });
368
+
369
+ async function getPatient(patientId: string) {
370
+ return prisma.patient.findUnique({
371
+ where: { id: patientId },
372
+ select: {
373
+ id: true,
374
+ name: true,
375
+ dob: true,
376
+ // Explicitly exclude: ssn, medicalRecords, etc.
377
+ }
378
+ });
379
+ }
380
+
381
+ // With audit middleware
382
+ prisma.$use(async (params, next) => {
383
+ if (params.model === 'Patient') {
384
+ await prisma.auditLog.create({
385
+ data: {
386
+ action: params.action,
387
+ model: params.model,
388
+ timestamp: new Date(),
389
+ }
390
+ });
391
+ }
392
+ return next(params);
393
+ });`,
394
+ },
395
+ ],
396
+ drizzle: [
397
+ {
398
+ title: 'Drizzle ORM Secure Queries',
399
+ description: 'Use Drizzle with explicit column selection.',
400
+ language: 'typescript',
401
+ code: `import { drizzle } from 'drizzle-orm/node-postgres';
402
+ import { eq } from 'drizzle-orm';
403
+ import { patients } from './schema';
404
+
405
+ const db = drizzle(pool);
406
+
407
+ async function getPatient(patientId: string) {
408
+ return db
409
+ .select({
410
+ id: patients.id,
411
+ name: patients.name,
412
+ dob: patients.dob,
413
+ // Don't select: ssn, medicalHistory
414
+ })
415
+ .from(patients)
416
+ .where(eq(patients.id, patientId));
417
+ }`,
418
+ },
419
+ ],
420
+ unknown: [],
421
+ };
422
+ // =============================================================================
423
+ // AUTH GUIDES
424
+ // =============================================================================
425
+ const authGuides = {
426
+ nextauth: [
427
+ {
428
+ title: 'NextAuth.js Session Configuration',
429
+ description: 'Configure NextAuth with secure session settings for HIPAA compliance.',
430
+ language: 'typescript',
431
+ code: `// auth.ts
432
+ import NextAuth from 'next-auth';
433
+ import { authConfig } from './auth.config';
434
+
435
+ export const { auth, signIn, signOut } = NextAuth({
436
+ ...authConfig,
437
+ session: {
438
+ strategy: 'jwt',
439
+ maxAge: 15 * 60, // 15 minutes (HIPAA recommendation)
440
+ },
441
+ callbacks: {
442
+ async session({ session, token }) {
443
+ // Add user role to session
444
+ session.user.role = token.role;
445
+ session.user.id = token.sub;
446
+ return session;
447
+ },
448
+ async jwt({ token, user }) {
449
+ if (user) {
450
+ token.role = user.role;
451
+ }
452
+ return token;
453
+ },
454
+ },
455
+ events: {
456
+ async signIn({ user }) {
457
+ // Audit log
458
+ await logAuditEvent('SIGN_IN', user.id);
459
+ },
460
+ async signOut({ token }) {
461
+ await logAuditEvent('SIGN_OUT', token.sub);
462
+ },
463
+ },
464
+ });`,
465
+ },
466
+ ],
467
+ 'supabase-auth': [
468
+ {
469
+ title: 'Supabase Auth with Session Refresh',
470
+ description: 'Configure Supabase Auth with automatic session refresh and timeout.',
471
+ language: 'typescript',
472
+ code: `// lib/supabase/middleware.ts
473
+ import { createServerClient } from '@supabase/ssr';
474
+ import { NextResponse, type NextRequest } from 'next/server';
475
+
476
+ export async function updateSession(request: NextRequest) {
477
+ let response = NextResponse.next({ request });
478
+
479
+ const supabase = createServerClient(
480
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
481
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
482
+ {
483
+ cookies: {
484
+ getAll() {
485
+ return request.cookies.getAll();
486
+ },
487
+ setAll(cookiesToSet) {
488
+ cookiesToSet.forEach(({ name, value, options }) => {
489
+ response.cookies.set(name, value, {
490
+ ...options,
491
+ httpOnly: true,
492
+ secure: true,
493
+ sameSite: 'lax',
494
+ maxAge: 60 * 15, // 15 min session
495
+ });
496
+ });
497
+ },
498
+ },
499
+ }
500
+ );
501
+
502
+ const { data: { user } } = await supabase.auth.getUser();
503
+
504
+ // Protect PHI routes
505
+ if (request.nextUrl.pathname.startsWith('/patients') && !user) {
506
+ return NextResponse.redirect(new URL('/login', request.url));
507
+ }
508
+
509
+ return response;
510
+ }`,
511
+ },
512
+ ],
513
+ 'firebase-auth': [
514
+ {
515
+ title: 'Firebase Auth Session Management',
516
+ description: 'Implement secure session cookies with Firebase Auth.',
517
+ language: 'typescript',
518
+ code: `// app/api/session/route.ts
519
+ import { auth } from 'firebase-admin';
520
+ import { cookies } from 'next/headers';
521
+
522
+ export async function POST(request: Request) {
523
+ const { idToken } = await request.json();
524
+
525
+ // Verify and create session cookie (15 min)
526
+ const expiresIn = 15 * 60 * 1000;
527
+ const sessionCookie = await auth().createSessionCookie(idToken, { expiresIn });
528
+
529
+ const cookieStore = await cookies();
530
+ cookieStore.set('session', sessionCookie, {
531
+ maxAge: expiresIn / 1000,
532
+ httpOnly: true,
533
+ secure: true,
534
+ sameSite: 'strict',
535
+ });
536
+
537
+ return Response.json({ status: 'success' });
538
+ }
539
+
540
+ // Verify session in middleware
541
+ export async function verifySession(sessionCookie: string) {
542
+ try {
543
+ const decoded = await auth().verifySessionCookie(sessionCookie, true);
544
+ return decoded;
545
+ } catch {
546
+ return null;
547
+ }
548
+ }`,
549
+ },
550
+ ],
551
+ auth0: [
552
+ {
553
+ title: 'Auth0 Session Configuration',
554
+ description: 'Configure Auth0 with HIPAA-compliant session settings.',
555
+ language: 'typescript',
556
+ code: `// lib/auth0.ts
557
+ import { initAuth0 } from '@auth0/nextjs-auth0';
558
+
559
+ export default initAuth0({
560
+ session: {
561
+ rollingDuration: 15 * 60, // 15 min inactivity timeout
562
+ absoluteDuration: 8 * 60 * 60, // 8 hour max
563
+ cookie: {
564
+ httpOnly: true,
565
+ secure: true,
566
+ sameSite: 'lax',
567
+ },
568
+ },
569
+ authorizationParams: {
570
+ scope: 'openid profile email',
571
+ audience: process.env.AUTH0_AUDIENCE,
572
+ },
573
+ });
574
+
575
+ // Protect API route
576
+ import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';
577
+
578
+ export const GET = withApiAuthRequired(async (req) => {
579
+ const session = await getSession();
580
+ // Log PHI access
581
+ await auditLog('PHI_ACCESS', session?.user.sub);
582
+ // ... handle request
583
+ });`,
584
+ },
585
+ ],
586
+ clerk: [
587
+ {
588
+ title: 'Clerk Auth Protection',
589
+ description: 'Use Clerk middleware for route protection.',
590
+ language: 'typescript',
591
+ code: `// middleware.ts
592
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
593
+
594
+ const isProtectedRoute = createRouteMatcher([
595
+ '/patients(.*)',
596
+ '/dashboard(.*)',
597
+ '/api/phi(.*)',
598
+ ]);
599
+
600
+ export default clerkMiddleware(async (auth, req) => {
601
+ if (isProtectedRoute(req)) {
602
+ await auth.protect();
603
+ }
604
+ });
605
+
606
+ // In component
607
+ import { auth } from '@clerk/nextjs/server';
608
+
609
+ export default async function PatientsPage() {
610
+ const { userId } = await auth();
611
+ if (!userId) redirect('/sign-in');
612
+
613
+ // Fetch with audit
614
+ const patients = await getPatients(userId);
615
+ return <PatientList patients={patients} />;
616
+ }`,
617
+ },
618
+ ],
619
+ lucia: [],
620
+ passport: [
621
+ {
622
+ title: 'Passport.js with Session Store',
623
+ description: 'Configure Passport with secure session management.',
624
+ language: 'typescript',
625
+ code: `import passport from 'passport';
626
+ import session from 'express-session';
627
+ import RedisStore from 'connect-redis';
628
+
629
+ app.use(session({
630
+ store: new RedisStore({ client: redisClient }),
631
+ secret: process.env.SESSION_SECRET!,
632
+ resave: false,
633
+ saveUninitialized: false,
634
+ cookie: {
635
+ secure: true,
636
+ httpOnly: true,
637
+ maxAge: 15 * 60 * 1000, // 15 min
638
+ }
639
+ }));
640
+
641
+ app.use(passport.initialize());
642
+ app.use(passport.session());
643
+
644
+ // Protect PHI routes
645
+ const requireAuth = (req, res, next) => {
646
+ if (!req.isAuthenticated()) {
647
+ return res.status(401).json({ error: 'Unauthorized' });
648
+ }
649
+ // Log PHI access
650
+ auditLogger.log({ userId: req.user.id, action: 'PHI_ACCESS', path: req.path });
651
+ next();
652
+ };
653
+
654
+ app.use('/api/patients', requireAuth);`,
655
+ },
656
+ ],
657
+ unknown: [],
658
+ };
659
+ // =============================================================================
660
+ // GENERAL HIPAA GUIDES
661
+ // =============================================================================
662
+ const generalGuides = [
663
+ {
664
+ title: 'Environment Variables for Secrets',
665
+ description: 'Never hardcode credentials. Use environment variables with proper validation.',
666
+ language: 'typescript',
667
+ code: `// lib/env.ts
668
+ import { z } from 'zod';
669
+
670
+ const envSchema = z.object({
671
+ DATABASE_URL: z.string().url(),
672
+ SESSION_SECRET: z.string().min(32),
673
+ API_KEY: z.string().min(20),
674
+ });
675
+
676
+ export const env = envSchema.parse(process.env);
677
+
678
+ // Usage
679
+ import { env } from '@/lib/env';
680
+ const db = new Pool({ connectionString: env.DATABASE_URL });`,
681
+ },
682
+ {
683
+ title: 'Structured Audit Logging',
684
+ description: 'Implement comprehensive audit logging for HIPAA compliance.',
685
+ language: 'typescript',
686
+ code: `// lib/audit.ts
687
+ interface AuditEvent {
688
+ timestamp: string;
689
+ userId: string;
690
+ action: 'CREATE' | 'READ' | 'UPDATE' | 'DELETE' | 'LOGIN' | 'LOGOUT';
691
+ resource: string;
692
+ resourceId?: string;
693
+ ip?: string;
694
+ userAgent?: string;
695
+ success: boolean;
696
+ details?: Record<string, unknown>;
697
+ }
698
+
699
+ export async function logAudit(event: Omit<AuditEvent, 'timestamp'>) {
700
+ const auditEvent: AuditEvent = {
701
+ ...event,
702
+ timestamp: new Date().toISOString(),
703
+ };
704
+
705
+ // Store in append-only audit table
706
+ await db.query(
707
+ 'INSERT INTO audit_log (data) VALUES ($1)',
708
+ [JSON.stringify(auditEvent)]
709
+ );
710
+
711
+ // Also send to external SIEM if configured
712
+ if (process.env.SIEM_ENDPOINT) {
713
+ await fetch(process.env.SIEM_ENDPOINT, {
714
+ method: 'POST',
715
+ body: JSON.stringify(auditEvent),
716
+ });
717
+ }
718
+ }`,
719
+ },
720
+ ];
721
+ // =============================================================================
722
+ // MAIN EXPORT
723
+ // =============================================================================
724
+ export function getStackSpecificGuides(stack) {
725
+ return {
726
+ session: sessionGuides[stack.framework] || [],
727
+ database: databaseGuides[stack.database] || [],
728
+ auth: authGuides[stack.auth] || [],
729
+ general: generalGuides,
730
+ };
731
+ }
732
+ export function getStackSummary(stack) {
733
+ const recommendations = [];
734
+ // Framework-specific
735
+ if (stack.framework === 'nextjs') {
736
+ recommendations.push('Use Server Components for PHI data to prevent client exposure');
737
+ recommendations.push('Implement middleware.ts for route protection');
738
+ }
739
+ else if (stack.framework === 'express') {
740
+ recommendations.push('Use express-session with Redis for HIPAA-compliant sessions');
741
+ recommendations.push('Implement PHI access logging middleware');
742
+ }
743
+ // Database-specific
744
+ if (stack.database === 'supabase') {
745
+ recommendations.push('Enable Row Level Security (RLS) on all tables containing PHI');
746
+ recommendations.push('Use Supabase server client for PHI operations');
747
+ recommendations.push('Configure database triggers for audit logging');
748
+ }
749
+ else if (stack.database === 'firebase') {
750
+ recommendations.push('Configure Firestore Security Rules for PHI access control');
751
+ recommendations.push('Use Firebase Admin SDK server-side for PHI operations');
752
+ }
753
+ else if (stack.database === 'postgresql' || stack.database === 'mysql') {
754
+ recommendations.push('Always use parameterized queries to prevent SQL injection');
755
+ recommendations.push('Enable TLS for database connections');
756
+ }
757
+ // Auth-specific
758
+ if (stack.auth === 'supabase-auth') {
759
+ recommendations.push('Configure 15-minute session timeout for HIPAA compliance');
760
+ recommendations.push('Use Supabase Auth middleware for route protection');
761
+ }
762
+ else if (stack.auth === 'nextauth') {
763
+ recommendations.push('Set JWT maxAge to 15 minutes for HIPAA compliance');
764
+ recommendations.push('Implement signIn/signOut audit events');
765
+ }
766
+ // General
767
+ recommendations.push('Never log PHI to console in production');
768
+ recommendations.push('Use environment variables for all credentials');
769
+ recommendations.push('Implement structured audit logging for all PHI access');
770
+ return recommendations;
771
+ }
772
+ //# sourceMappingURL=stack-guides.js.map