verification-layer 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +251 -615
  2. package/dist/cli.js +542 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/marketplace/index.d.ts +8 -0
  5. package/dist/marketplace/index.d.ts.map +1 -0
  6. package/dist/marketplace/index.js +7 -0
  7. package/dist/marketplace/index.js.map +1 -0
  8. package/dist/marketplace/installer.d.ts +62 -0
  9. package/dist/marketplace/installer.d.ts.map +1 -0
  10. package/dist/marketplace/installer.js +254 -0
  11. package/dist/marketplace/installer.js.map +1 -0
  12. package/dist/marketplace/registry.d.ts +52 -0
  13. package/dist/marketplace/registry.d.ts.map +1 -0
  14. package/dist/marketplace/registry.js +759 -0
  15. package/dist/marketplace/registry.js.map +1 -0
  16. package/dist/marketplace/types.d.ts +123 -0
  17. package/dist/marketplace/types.d.ts.map +1 -0
  18. package/dist/marketplace/types.js +6 -0
  19. package/dist/marketplace/types.js.map +1 -0
  20. package/dist/reporters/audit-report.d.ts.map +1 -1
  21. package/dist/reporters/audit-report.js +180 -0
  22. package/dist/reporters/audit-report.js.map +1 -1
  23. package/dist/reporters/index.d.ts.map +1 -1
  24. package/dist/reporters/index.js +2612 -5
  25. package/dist/reporters/index.js.map +1 -1
  26. package/dist/scan.d.ts.map +1 -1
  27. package/dist/scan.js +15 -1
  28. package/dist/scan.js.map +1 -1
  29. package/dist/scanners/api-security/index.d.ts +7 -0
  30. package/dist/scanners/api-security/index.d.ts.map +1 -0
  31. package/dist/scanners/api-security/index.js +139 -0
  32. package/dist/scanners/api-security/index.js.map +1 -0
  33. package/dist/scanners/api-security/index.test.d.ts +5 -0
  34. package/dist/scanners/api-security/index.test.d.ts.map +1 -0
  35. package/dist/scanners/api-security/index.test.js +360 -0
  36. package/dist/scanners/api-security/index.test.js.map +1 -0
  37. package/dist/scanners/api-security/patterns.d.ts +32 -0
  38. package/dist/scanners/api-security/patterns.d.ts.map +1 -0
  39. package/dist/scanners/api-security/patterns.js +159 -0
  40. package/dist/scanners/api-security/patterns.js.map +1 -0
  41. package/dist/scanners/authentication/index.d.ts +7 -0
  42. package/dist/scanners/authentication/index.d.ts.map +1 -0
  43. package/dist/scanners/authentication/index.js +107 -0
  44. package/dist/scanners/authentication/index.js.map +1 -0
  45. package/dist/scanners/authentication/index.test.d.ts +5 -0
  46. package/dist/scanners/authentication/index.test.d.ts.map +1 -0
  47. package/dist/scanners/authentication/index.test.js +379 -0
  48. package/dist/scanners/authentication/index.test.js.map +1 -0
  49. package/dist/scanners/authentication/patterns.d.ts +32 -0
  50. package/dist/scanners/authentication/patterns.d.ts.map +1 -0
  51. package/dist/scanners/authentication/patterns.js +133 -0
  52. package/dist/scanners/authentication/patterns.js.map +1 -0
  53. package/dist/scanners/configuration/index.d.ts +8 -0
  54. package/dist/scanners/configuration/index.d.ts.map +1 -0
  55. package/dist/scanners/configuration/index.js +87 -0
  56. package/dist/scanners/configuration/index.js.map +1 -0
  57. package/dist/scanners/configuration/index.test.d.ts +5 -0
  58. package/dist/scanners/configuration/index.test.d.ts.map +1 -0
  59. package/dist/scanners/configuration/index.test.js +344 -0
  60. package/dist/scanners/configuration/index.test.js.map +1 -0
  61. package/dist/scanners/configuration/patterns.d.ts +32 -0
  62. package/dist/scanners/configuration/patterns.d.ts.map +1 -0
  63. package/dist/scanners/configuration/patterns.js +146 -0
  64. package/dist/scanners/configuration/patterns.js.map +1 -0
  65. package/dist/scanners/credentials/index.d.ts +7 -0
  66. package/dist/scanners/credentials/index.d.ts.map +1 -0
  67. package/dist/scanners/credentials/index.js +129 -0
  68. package/dist/scanners/credentials/index.js.map +1 -0
  69. package/dist/scanners/credentials/index.test.d.ts +5 -0
  70. package/dist/scanners/credentials/index.test.d.ts.map +1 -0
  71. package/dist/scanners/credentials/index.test.js +395 -0
  72. package/dist/scanners/credentials/index.test.js.map +1 -0
  73. package/dist/scanners/credentials/patterns.d.ts +32 -0
  74. package/dist/scanners/credentials/patterns.d.ts.map +1 -0
  75. package/dist/scanners/credentials/patterns.js +140 -0
  76. package/dist/scanners/credentials/patterns.js.map +1 -0
  77. package/dist/scanners/errors/index.d.ts +8 -0
  78. package/dist/scanners/errors/index.d.ts.map +1 -0
  79. package/dist/scanners/errors/index.js +78 -0
  80. package/dist/scanners/errors/index.js.map +1 -0
  81. package/dist/scanners/errors/index.test.d.ts +5 -0
  82. package/dist/scanners/errors/index.test.d.ts.map +1 -0
  83. package/dist/scanners/errors/index.test.js +330 -0
  84. package/dist/scanners/errors/index.test.js.map +1 -0
  85. package/dist/scanners/errors/patterns.d.ts +27 -0
  86. package/dist/scanners/errors/patterns.d.ts.map +1 -0
  87. package/dist/scanners/errors/patterns.js +97 -0
  88. package/dist/scanners/errors/patterns.js.map +1 -0
  89. package/dist/scanners/hipaa2026/index.d.ts +8 -0
  90. package/dist/scanners/hipaa2026/index.d.ts.map +1 -0
  91. package/dist/scanners/hipaa2026/index.js +345 -0
  92. package/dist/scanners/hipaa2026/index.js.map +1 -0
  93. package/dist/scanners/hipaa2026/index.test.d.ts +5 -0
  94. package/dist/scanners/hipaa2026/index.test.d.ts.map +1 -0
  95. package/dist/scanners/hipaa2026/index.test.js +332 -0
  96. package/dist/scanners/hipaa2026/index.test.js.map +1 -0
  97. package/dist/scanners/hipaa2026/patterns.d.ts +57 -0
  98. package/dist/scanners/hipaa2026/patterns.d.ts.map +1 -0
  99. package/dist/scanners/hipaa2026/patterns.js +268 -0
  100. package/dist/scanners/hipaa2026/patterns.js.map +1 -0
  101. package/dist/scanners/operational/index.d.ts +7 -0
  102. package/dist/scanners/operational/index.d.ts.map +1 -0
  103. package/dist/scanners/operational/index.js +171 -0
  104. package/dist/scanners/operational/index.js.map +1 -0
  105. package/dist/scanners/operational/index.test.d.ts +5 -0
  106. package/dist/scanners/operational/index.test.d.ts.map +1 -0
  107. package/dist/scanners/operational/index.test.js +406 -0
  108. package/dist/scanners/operational/index.test.js.map +1 -0
  109. package/dist/scanners/operational/patterns.d.ts +33 -0
  110. package/dist/scanners/operational/patterns.d.ts.map +1 -0
  111. package/dist/scanners/operational/patterns.js +151 -0
  112. package/dist/scanners/operational/patterns.js.map +1 -0
  113. package/dist/scanners/rbac/index.d.ts +7 -0
  114. package/dist/scanners/rbac/index.d.ts.map +1 -0
  115. package/dist/scanners/rbac/index.js +145 -0
  116. package/dist/scanners/rbac/index.js.map +1 -0
  117. package/dist/scanners/rbac/index.test.d.ts +5 -0
  118. package/dist/scanners/rbac/index.test.d.ts.map +1 -0
  119. package/dist/scanners/rbac/index.test.js +422 -0
  120. package/dist/scanners/rbac/index.test.js.map +1 -0
  121. package/dist/scanners/rbac/patterns.d.ts +32 -0
  122. package/dist/scanners/rbac/patterns.d.ts.map +1 -0
  123. package/dist/scanners/rbac/patterns.js +124 -0
  124. package/dist/scanners/rbac/patterns.js.map +1 -0
  125. package/dist/scanners/revocation/index.d.ts +8 -0
  126. package/dist/scanners/revocation/index.d.ts.map +1 -0
  127. package/dist/scanners/revocation/index.js +83 -0
  128. package/dist/scanners/revocation/index.js.map +1 -0
  129. package/dist/scanners/revocation/index.test.d.ts +5 -0
  130. package/dist/scanners/revocation/index.test.d.ts.map +1 -0
  131. package/dist/scanners/revocation/index.test.js +332 -0
  132. package/dist/scanners/revocation/index.test.js.map +1 -0
  133. package/dist/scanners/revocation/patterns.d.ts +27 -0
  134. package/dist/scanners/revocation/patterns.d.ts.map +1 -0
  135. package/dist/scanners/revocation/patterns.js +109 -0
  136. package/dist/scanners/revocation/patterns.js.map +1 -0
  137. package/dist/scanners/sanitization/index.d.ts +8 -0
  138. package/dist/scanners/sanitization/index.d.ts.map +1 -0
  139. package/dist/scanners/sanitization/index.js +98 -0
  140. package/dist/scanners/sanitization/index.js.map +1 -0
  141. package/dist/scanners/sanitization/index.test.d.ts +5 -0
  142. package/dist/scanners/sanitization/index.test.d.ts.map +1 -0
  143. package/dist/scanners/sanitization/index.test.js +370 -0
  144. package/dist/scanners/sanitization/index.test.js.map +1 -0
  145. package/dist/scanners/sanitization/patterns.d.ts +27 -0
  146. package/dist/scanners/sanitization/patterns.d.ts.map +1 -0
  147. package/dist/scanners/sanitization/patterns.js +117 -0
  148. package/dist/scanners/sanitization/patterns.js.map +1 -0
  149. package/dist/training/certificate.d.ts +26 -0
  150. package/dist/training/certificate.d.ts.map +1 -0
  151. package/dist/training/certificate.js +92 -0
  152. package/dist/training/certificate.js.map +1 -0
  153. package/dist/training/index.d.ts +3 -0
  154. package/dist/training/index.d.ts.map +1 -0
  155. package/dist/training/index.js +243 -0
  156. package/dist/training/index.js.map +1 -0
  157. package/dist/training/modules.d.ts +13 -0
  158. package/dist/training/modules.d.ts.map +1 -0
  159. package/dist/training/modules.js +608 -0
  160. package/dist/training/modules.js.map +1 -0
  161. package/dist/training/questions.d.ts +9 -0
  162. package/dist/training/questions.d.ts.map +1 -0
  163. package/dist/training/questions.js +505 -0
  164. package/dist/training/questions.js.map +1 -0
  165. package/dist/types.d.ts +45 -0
  166. package/dist/types.d.ts.map +1 -1
  167. package/dist/utils/npm-audit.d.ts +6 -0
  168. package/dist/utils/npm-audit.d.ts.map +1 -0
  169. package/dist/utils/npm-audit.js +95 -0
  170. package/dist/utils/npm-audit.js.map +1 -0
  171. package/dist/utils/scan-history.d.ts +59 -0
  172. package/dist/utils/scan-history.d.ts.map +1 -0
  173. package/dist/utils/scan-history.js +170 -0
  174. package/dist/utils/scan-history.js.map +1 -0
  175. package/package.json +4 -1
  176. package/templates/baa-verification-letter.md +105 -0
  177. package/templates/irp.md +545 -0
  178. package/templates/notice-of-privacy-practices.md +491 -0
  179. package/templates/physical-safeguards-checklist.md +247 -0
  180. package/templates/security-officer-designation.md +237 -0
@@ -0,0 +1,608 @@
1
+ export const trainingModules = [
2
+ {
3
+ id: 1,
4
+ title: 'What is PHI/ePHI?',
5
+ description: 'Understanding Protected Health Information and the 18 HIPAA identifiers',
6
+ content: [
7
+ '📋 Protected Health Information (PHI) is any information that:',
8
+ ' 1. Identifies an individual (or could be used to identify them)',
9
+ ' 2. Relates to their past, present, or future health condition, healthcare, or payment',
10
+ '',
11
+ '🔐 The 18 HIPAA Identifiers:',
12
+ ' 1. Names 10. Medical record numbers (MRN)',
13
+ ' 2. Geographic data 11. Health plan numbers',
14
+ ' 3. Dates (except year) 12. Account numbers',
15
+ ' 4. Phone numbers 13. Certificate/license numbers',
16
+ ' 5. Fax numbers 14. Vehicle IDs',
17
+ ' 6. Email addresses 15. Device IDs/serial numbers',
18
+ ' 7. SSN 16. URLs',
19
+ ' 8. Medical record # 17. IP addresses',
20
+ ' 9. Health plan # 18. Biometric data (fingerprints, photos)',
21
+ '',
22
+ '⚡ ePHI = PHI in ELECTRONIC form (stored, transmitted, or processed digitally)',
23
+ '',
24
+ '⚠️ KEY POINT: Name + Medical Condition = PHI',
25
+ ' "John Smith has diabetes" = PHI',
26
+ ' "Patient 12345 has diabetes" = NOT PHI (de-identified)',
27
+ ],
28
+ },
29
+ {
30
+ id: 2,
31
+ title: 'The HIPAA Security Rule',
32
+ description: 'Understanding security safeguards and compliance requirements',
33
+ content: [
34
+ '🛡️ The HIPAA Security Rule has 3 types of safeguards:',
35
+ '',
36
+ '1️⃣ ADMINISTRATIVE SAFEGUARDS (§164.308)',
37
+ ' - Security management process (risk analysis, incident response)',
38
+ ' - Workforce security (authorization, supervision, termination)',
39
+ ' - Training and awareness',
40
+ ' - Contingency planning (backup, disaster recovery)',
41
+ '',
42
+ '2️⃣ PHYSICAL SAFEGUARDS (§164.310)',
43
+ ' - Facility access controls',
44
+ ' - Workstation security',
45
+ ' - Device and media controls',
46
+ '',
47
+ '3️⃣ TECHNICAL SAFEGUARDS (§164.312)',
48
+ ' - Access control (unique IDs, emergency access, auto logoff, encryption)',
49
+ ' - Audit controls',
50
+ ' - Integrity controls',
51
+ ' - Transmission security',
52
+ '',
53
+ '📜 Required vs Addressable:',
54
+ ' - REQUIRED: Must implement',
55
+ ' - ADDRESSABLE: Must implement OR document why not applicable + alternative',
56
+ ' - ⚠️ 2024 NPRM proposes eliminating this distinction (all become required)',
57
+ '',
58
+ '💰 Penalties for Non-Compliance:',
59
+ ' - Tier 1 (unknowing): $100-$50,000 per violation',
60
+ ' - Tier 2 (reasonable cause): $1,000-$50,000 per violation',
61
+ ' - Tier 3 (willful neglect, corrected): $10,000-$50,000 per violation',
62
+ ' - Tier 4 (willful neglect, not corrected): $50,000 per violation',
63
+ ' - Annual maximum: $1.5 million per violation type',
64
+ '',
65
+ '⏱️ Breach notification timeline: 60 days to notify affected individuals',
66
+ ],
67
+ },
68
+ {
69
+ id: 3,
70
+ title: 'Access Control in Code',
71
+ description: 'Implementing proper access controls for ePHI',
72
+ content: [
73
+ '🔑 Key Access Control Requirements:',
74
+ '',
75
+ '1. UNIQUE USER IDs (§164.312(a)(2)(i)) - REQUIRED',
76
+ ' - Each person must have their own login',
77
+ ' - NO shared accounts (no "admin", "developer", "test")',
78
+ ' - Track who accessed what PHI',
79
+ '',
80
+ '2. AUTOMATIC LOGOFF (§164.312(a)(2)(iii)) - ADDRESSABLE',
81
+ ' - Session timeout ≤ 15 minutes of inactivity',
82
+ ' - Apply to ALL systems with ePHI access',
83
+ '',
84
+ '3. ROLE-BASED ACCESS (Minimum Necessary principle)',
85
+ ' - Users only see PHI needed for their job',
86
+ ' - Developers should NOT have production PHI access',
87
+ ' - Use roles like: admin, clinician, billing, readonly',
88
+ '',
89
+ '4. ACCESS REVIEWS',
90
+ ' - Review user access quarterly',
91
+ ' - Remove access immediately on termination',
92
+ ],
93
+ codeExamples: [
94
+ {
95
+ wrong: `// ❌ WRONG: Shared credentials
96
+ const DB_USER = "admin";
97
+ const DB_PASS = "admin123";
98
+
99
+ // ❌ WRONG: No session timeout
100
+ app.use(session({
101
+ cookie: { maxAge: null } // Never expires!
102
+ }));
103
+
104
+ // ❌ WRONG: Everyone can access everything
105
+ if (user.isAuthenticated()) {
106
+ return allPatientRecords;
107
+ }`,
108
+ right: `// ✅ RIGHT: Individual user authentication
109
+ const user = await authenticateUser(username, password);
110
+ const userId = user.id; // Track individual access
111
+
112
+ // ✅ RIGHT: 15-minute session timeout
113
+ app.use(session({
114
+ cookie: { maxAge: 15 * 60 * 1000 }, // 15 minutes
115
+ rolling: true // Reset on activity
116
+ }));
117
+
118
+ // ✅ RIGHT: Role-based access
119
+ if (user.hasRole('CLINICIAN')) {
120
+ return user.getAssignedPatients();
121
+ } else if (user.hasRole('BILLING')) {
122
+ return user.getBillingRecords(); // No medical data
123
+ }`,
124
+ explanation: 'Always use individual user accounts, implement session timeouts ≤15 minutes, and restrict access based on roles and minimum necessary principle.',
125
+ },
126
+ ],
127
+ },
128
+ {
129
+ id: 4,
130
+ title: 'Encryption',
131
+ description: 'Encryption requirements for ePHI at rest and in transit',
132
+ content: [
133
+ '🔐 Encryption Requirements:',
134
+ '',
135
+ '1️⃣ ENCRYPTION AT REST (§164.312(a)(2)(iv)) - ADDRESSABLE',
136
+ ' - Use AES-256 (industry standard)',
137
+ ' - Encrypt databases, backups, file storage',
138
+ ' - NEVER use MD5, SHA1, DES, or 3DES for encryption',
139
+ ' - Note: MD5/SHA1 are OK for checksums, NOT for secrets',
140
+ '',
141
+ '2️⃣ ENCRYPTION IN TRANSIT (§164.312(e)(1)) - ADDRESSABLE',
142
+ ' - TLS 1.2 or higher (TLS 1.3 preferred)',
143
+ ' - HTTPS for all web traffic',
144
+ ' - Use HSTS headers (Strict-Transport-Security)',
145
+ ' - No mixed content (HTTP + HTTPS)',
146
+ '',
147
+ '3️⃣ KEY MANAGEMENT',
148
+ ' - NEVER hardcode encryption keys in source code',
149
+ ' - Use environment variables or secret managers (AWS KMS, Vault, etc.)',
150
+ ' - Rotate keys periodically',
151
+ ' - Store keys separately from encrypted data',
152
+ '',
153
+ '⚠️ Safe Harbor: If ePHI is encrypted using NIST standards, breach notification may not be required (must still report to OCR)',
154
+ ],
155
+ codeExamples: [
156
+ {
157
+ wrong: `// ❌ WRONG: Weak encryption
158
+ const crypto = require('crypto');
159
+ const key = 'hardcoded-key-12345'; // BAD!
160
+ const cipher = crypto.createCipher('des', key); // DES is weak!
161
+
162
+ // ❌ WRONG: No TLS
163
+ app.listen(80); // HTTP only
164
+
165
+ // ❌ WRONG: MD5 for passwords
166
+ const hash = crypto.createHash('md5')
167
+ .update(password).digest('hex');`,
168
+ right: `// ✅ RIGHT: Strong encryption
169
+ const crypto = require('crypto');
170
+ const key = process.env.ENCRYPTION_KEY; // From env
171
+ const algorithm = 'aes-256-gcm'; // AES-256
172
+ const cipher = crypto.createCipheriv(algorithm, key, iv);
173
+
174
+ // ✅ RIGHT: TLS 1.2+ with HSTS
175
+ app.use((req, res, next) => {
176
+ res.setHeader('Strict-Transport-Security',
177
+ 'max-age=31536000; includeSubDomains');
178
+ next();
179
+ });
180
+ https.createServer(tlsOptions, app).listen(443);
181
+
182
+ // ✅ RIGHT: bcrypt for passwords
183
+ const bcrypt = require('bcrypt');
184
+ const hash = await bcrypt.hash(password, 10);`,
185
+ explanation: 'Use AES-256 for encryption, TLS 1.2+ for transport, never hardcode keys, and use bcrypt/argon2 for password hashing.',
186
+ },
187
+ ],
188
+ },
189
+ {
190
+ id: 5,
191
+ title: 'Authentication & MFA',
192
+ description: 'Multi-factor authentication and secure authentication practices',
193
+ content: [
194
+ '🔒 Authentication Best Practices:',
195
+ '',
196
+ '1️⃣ WHY PASSWORDS ALONE AREN\'T ENOUGH',
197
+ ' - Passwords can be stolen (phishing, data breaches, keyloggers)',
198
+ ' - 81% of breaches involve weak or stolen passwords',
199
+ ' - MFA reduces account compromise by 99.9% (Microsoft study)',
200
+ '',
201
+ '2️⃣ MFA IMPLEMENTATION',
202
+ ' - TOTP (Time-based One-Time Password) - Google Authenticator, Authy',
203
+ ' - SMS (less secure but better than nothing)',
204
+ ' - Hardware tokens (YubiKey, etc.)',
205
+ ' - Passkeys/WebAuthn (most secure, emerging standard)',
206
+ '',
207
+ '3️⃣ MFA FOR ePHI ACCESS',
208
+ ' - REQUIRED for remote access to ePHI systems',
209
+ ' - Recommended for all ePHI access',
210
+ ' - No MFA bypass in code (even for "trusted" IPs)',
211
+ '',
212
+ '4️⃣ PASSWORD POLICIES',
213
+ ' - Minimum 12 characters (or passphrase)',
214
+ ' - Require complexity OR length (not both)',
215
+ ' - No password expiration (NIST no longer recommends)',
216
+ ' - Check against known breach databases (HaveIBeenPwned)',
217
+ ],
218
+ codeExamples: [
219
+ {
220
+ wrong: `// ❌ WRONG: MFA bypass
221
+ if (req.ip === '192.168.1.100' || req.headers['x-admin']) {
222
+ return loginWithoutMFA(user);
223
+ }
224
+
225
+ // ❌ WRONG: Storing MFA secret in plain text
226
+ user.mfaSecret = totpSecret; // Unencrypted!
227
+
228
+ // ❌ WRONG: No rate limiting on auth
229
+ app.post('/login', async (req, res) => {
230
+ const user = await authenticate(req.body);
231
+ // Allows unlimited login attempts
232
+ });`,
233
+ right: `// ✅ RIGHT: Always require MFA
234
+ const user = await authenticate(username, password);
235
+ if (!user.mfaVerified) {
236
+ return requireMFAChallenge(user);
237
+ }
238
+
239
+ // ✅ RIGHT: Encrypt MFA secrets
240
+ user.mfaSecret = encrypt(totpSecret, encryptionKey);
241
+
242
+ // ✅ RIGHT: Rate limiting on auth endpoints
243
+ const rateLimit = require('express-rate-limit');
244
+ const loginLimiter = rateLimit({
245
+ windowMs: 15 * 60 * 1000, // 15 minutes
246
+ max: 5, // 5 attempts
247
+ message: 'Too many login attempts'
248
+ });
249
+ app.post('/login', loginLimiter, authenticateHandler);`,
250
+ explanation: 'Never bypass MFA, encrypt MFA secrets, implement rate limiting on authentication endpoints, and use strong MFA methods (TOTP, hardware tokens, passkeys).',
251
+ },
252
+ ],
253
+ },
254
+ {
255
+ id: 6,
256
+ title: 'Audit Logging',
257
+ description: 'What to log, what not to log, and retention requirements',
258
+ content: [
259
+ '📝 Audit Logging Requirements (§164.312(b)):',
260
+ '',
261
+ '1️⃣ WHAT TO LOG (the 5 W\'s):',
262
+ ' - WHO: User ID, role, IP address',
263
+ ' - WHAT: Action performed (read, write, delete, export)',
264
+ ' - WHEN: Timestamp (with timezone)',
265
+ ' - WHERE: Resource accessed (patient ID, record ID)',
266
+ ' - OUTCOME: Success or failure (with error codes)',
267
+ '',
268
+ '2️⃣ WHAT NOT TO LOG:',
269
+ ' - ❌ PHI in log messages (no names, SSNs, diagnoses)',
270
+ ' - ❌ Passwords or auth tokens',
271
+ ' - ❌ Encryption keys',
272
+ ' - ✅ Use patient IDs, record IDs, or de-identified references',
273
+ '',
274
+ '3️⃣ LOG RETENTION:',
275
+ ' - Minimum 6 years (HIPAA requirement)',
276
+ ' - Longer if required by state law',
277
+ ' - Protect logs from tampering (write-once, centralized logging)',
278
+ '',
279
+ '4️⃣ WHAT TO AUDIT LOG:',
280
+ ' - All ePHI access (read, write, update, delete)',
281
+ ' - Authentication events (login, logout, failed attempts)',
282
+ ' - Authorization changes (role assignments, permission changes)',
283
+ ' - Configuration changes (security settings)',
284
+ ' - Administrative actions (user creation, deletion)',
285
+ ],
286
+ codeExamples: [
287
+ {
288
+ wrong: `// ❌ WRONG: PHI in logs
289
+ logger.info(\`User accessed patient: John Smith, DOB: 1985-03-15\`);
290
+
291
+ // ❌ WRONG: Passwords in logs
292
+ logger.error(\`Login failed for user: admin, password: \${password}\`);
293
+
294
+ // ❌ WRONG: No structured logging
295
+ console.log('Someone did something');`,
296
+ right: `// ✅ RIGHT: Log patient IDs, not names
297
+ logger.info('ePHI access', {
298
+ userId: user.id,
299
+ action: 'READ',
300
+ resourceType: 'PATIENT',
301
+ resourceId: 'PT-12345', // Patient ID, not name
302
+ timestamp: new Date().toISOString(),
303
+ ipAddress: req.ip,
304
+ outcome: 'SUCCESS'
305
+ });
306
+
307
+ // ✅ RIGHT: Never log sensitive data
308
+ logger.error('Authentication failed', {
309
+ userId: username, // OK to log username
310
+ reason: 'INVALID_CREDENTIALS',
311
+ ipAddress: req.ip
312
+ // NO password logged
313
+ });
314
+
315
+ // ✅ RIGHT: Structured logging with context
316
+ const auditLog = {
317
+ timestamp: new Date().toISOString(),
318
+ userId: user.id,
319
+ action: 'EXPORT',
320
+ resourceType: 'MEDICAL_RECORD',
321
+ resourceId: recordId,
322
+ outcome: 'SUCCESS',
323
+ details: { format: 'PDF', pageCount: 5 }
324
+ };
325
+ logger.info('Audit', auditLog);`,
326
+ explanation: 'Log the 5 W\'s (who, what, when, where, outcome) but NEVER log PHI, passwords, or keys. Use patient IDs instead of names. Retain logs for 6+ years.',
327
+ },
328
+ ],
329
+ },
330
+ {
331
+ id: 7,
332
+ title: 'Input Validation & Injection Prevention',
333
+ description: 'Preventing SQL injection, XSS, and other attacks on PHI systems',
334
+ content: [
335
+ '🛡️ Input Validation for PHI Systems:',
336
+ '',
337
+ '1️⃣ SQL INJECTION',
338
+ ' - NEVER concatenate user input into SQL queries',
339
+ ' - Always use parameterized queries / prepared statements',
340
+ ' - Extra critical with PHI (can expose all patient records)',
341
+ ' - Use ORMs (Sequelize, TypeORM) with proper escaping',
342
+ '',
343
+ '2️⃣ CROSS-SITE SCRIPTING (XSS)',
344
+ ' - Sanitize ALL user input before rendering',
345
+ ' - Use Content-Security-Policy headers',
346
+ ' - Escape HTML entities in PHI display',
347
+ ' - Critical in patient portals and healthcare apps',
348
+ '',
349
+ '3️⃣ FILE UPLOAD SECURITY',
350
+ ' - Validate file types (whitelist, not blacklist)',
351
+ ' - Scan uploads for malware',
352
+ ' - Store uploads outside web root',
353
+ ' - Limit file sizes',
354
+ ' - Medical records, insurance cards, etc. need protection',
355
+ '',
356
+ '4️⃣ API INPUT VALIDATION',
357
+ ' - Validate data types, ranges, formats',
358
+ ' - Reject unexpected fields',
359
+ ' - Use schema validation (Zod, Joi, JSON Schema)',
360
+ ],
361
+ codeExamples: [
362
+ {
363
+ wrong: `// ❌ WRONG: SQL injection vulnerability
364
+ const patientId = req.query.id;
365
+ const sql = \`SELECT * FROM patients WHERE id = '\${patientId}'\`;
366
+ db.query(sql); // Attacker can inject: ' OR '1'='1
367
+
368
+ // ❌ WRONG: XSS vulnerability
369
+ res.send(\`<h1>Patient: \${req.query.name}</h1>\`);
370
+ // Attacker can inject: <script>steal()</script>
371
+
372
+ // ❌ WRONG: No file validation
373
+ app.post('/upload', (req, res) => {
374
+ const file = req.files.medicalRecord;
375
+ file.mv('./uploads/' + file.name); // Any file type!
376
+ });`,
377
+ right: `// ✅ RIGHT: Parameterized query
378
+ const patientId = req.query.id;
379
+ const sql = 'SELECT * FROM patients WHERE id = ?';
380
+ db.query(sql, [patientId]); // Safe from injection
381
+
382
+ // ✅ RIGHT: HTML escaping
383
+ const escapeHtml = require('escape-html');
384
+ res.send(\`<h1>Patient: \${escapeHtml(req.query.name)}</h1>\`);
385
+
386
+ // ✅ RIGHT: File validation
387
+ const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
388
+ app.post('/upload', (req, res) => {
389
+ const file = req.files.medicalRecord;
390
+ if (!allowedTypes.includes(file.mimetype)) {
391
+ return res.status(400).send('Invalid file type');
392
+ }
393
+ if (file.size > 10 * 1024 * 1024) { // 10MB limit
394
+ return res.status(400).send('File too large');
395
+ }
396
+ // Scan for malware, store securely
397
+ });`,
398
+ explanation: 'Always use parameterized queries, escape HTML output, validate file uploads, and use schema validation. Input validation is critical for PHI protection.',
399
+ },
400
+ ],
401
+ },
402
+ {
403
+ id: 8,
404
+ title: 'Error Handling',
405
+ description: 'Secure error handling that doesn\'t expose PHI or system details',
406
+ content: [
407
+ '⚠️ Secure Error Handling:',
408
+ '',
409
+ '1️⃣ NEVER EXPOSE STACK TRACES',
410
+ ' - Stack traces reveal code structure, file paths, dependencies',
411
+ ' - Attackers use this info to plan attacks',
412
+ ' - Only show stack traces in development, never production',
413
+ '',
414
+ '2️⃣ NEVER INCLUDE PHI IN ERROR MESSAGES',
415
+ ' - ❌ "Patient John Smith not found"',
416
+ ' - ✅ "Patient record not found" (use patient ID in logs only)',
417
+ ' - ❌ "SSN 123-45-6789 is invalid"',
418
+ ' - ✅ "Invalid identifier format"',
419
+ '',
420
+ '3️⃣ SANITIZE ERROR RESPONSES',
421
+ ' - Generic messages to users',
422
+ ' - Detailed errors in server logs (without PHI)',
423
+ ' - Use error codes, not detailed descriptions',
424
+ '',
425
+ '4️⃣ DATABASE ERRORS',
426
+ ' - Don\'t expose table names, column names, or query details',
427
+ ' - Map database errors to generic messages',
428
+ ' - Log full errors server-side for debugging',
429
+ ],
430
+ codeExamples: [
431
+ {
432
+ wrong: `// ❌ WRONG: Exposing stack traces
433
+ app.use((err, req, res, next) => {
434
+ res.status(500).send(err.stack); // Shows file paths!
435
+ });
436
+
437
+ // ❌ WRONG: PHI in error messages
438
+ if (!patient) {
439
+ throw new Error(\`Patient \${patientName} not found\`);
440
+ }
441
+
442
+ // ❌ WRONG: Database details exposed
443
+ catch (err) {
444
+ res.status(500).send(err.message);
445
+ // "Column 'ssn' doesn't exist in table 'patients'"
446
+ }`,
447
+ right: `// ✅ RIGHT: Generic errors to client, detailed logs
448
+ app.use((err, req, res, next) => {
449
+ logger.error('Error occurred', {
450
+ error: err.message,
451
+ stack: err.stack,
452
+ userId: req.user?.id,
453
+ path: req.path
454
+ });
455
+
456
+ res.status(500).json({
457
+ error: 'An error occurred',
458
+ code: 'INTERNAL_ERROR',
459
+ requestId: req.id // For support lookup
460
+ });
461
+ });
462
+
463
+ // ✅ RIGHT: Use IDs, not PHI
464
+ if (!patient) {
465
+ logger.warn('Patient not found', { patientId });
466
+ throw new Error('Record not found'); // No PHI
467
+ }
468
+
469
+ // ✅ RIGHT: Map database errors
470
+ catch (err) {
471
+ logger.error('Database error', { error: err.message });
472
+ res.status(500).json({
473
+ error: 'Database operation failed',
474
+ code: 'DB_ERROR'
475
+ });
476
+ }`,
477
+ explanation: 'Never expose stack traces, PHI, or system details in error messages. Use generic errors for users, detailed logs (without PHI) for debugging.',
478
+ },
479
+ ],
480
+ },
481
+ {
482
+ id: 9,
483
+ title: 'Secure API Design',
484
+ description: 'API security best practices for ePHI systems',
485
+ content: [
486
+ '🔌 API Security for ePHI:',
487
+ '',
488
+ '1️⃣ PHI NEVER IN URL PARAMETERS',
489
+ ' - URLs are logged in browsers, proxies, load balancers',
490
+ ' - ❌ GET /patients?ssn=123-45-6789',
491
+ ' - ✅ POST /patients/search with SSN in request body',
492
+ ' - ✅ GET /patients/PT-12345 (use patient IDs, not PHI)',
493
+ '',
494
+ '2️⃣ RATE LIMITING',
495
+ ' - Prevent brute force attacks on auth endpoints',
496
+ ' - Prevent data scraping / mass PHI extraction',
497
+ ' - Example: 100 requests/minute per IP, 1000/hour per user',
498
+ '',
499
+ '3️⃣ CORS CONFIGURATION',
500
+ ' - Whitelist specific origins, not "*"',
501
+ ' - Use credentials: true only with specific origins',
502
+ ' - Critical for patient portals and SaaS apps',
503
+ '',
504
+ '4️⃣ JWT SECURITY',
505
+ ' - Short expiration (15-30 minutes)',
506
+ ' - Use refresh tokens for longer sessions',
507
+ ' - Implement token revocation (blacklist)',
508
+ ' - Don\'t store PHI in JWT payload (only user ID, roles)',
509
+ ' - Use strong signing algorithm (RS256, not HS256)',
510
+ ],
511
+ codeExamples: [
512
+ {
513
+ wrong: `// ❌ WRONG: PHI in URL
514
+ app.get('/patient/:ssn', (req, res) => {
515
+ const patient = getPatientBySSN(req.params.ssn);
516
+ });
517
+
518
+ // ❌ WRONG: No rate limiting
519
+ app.post('/login', loginHandler);
520
+
521
+ // ❌ WRONG: Permissive CORS
522
+ app.use(cors({ origin: '*', credentials: true }));
523
+
524
+ // ❌ WRONG: Long-lived JWT with PHI
525
+ const token = jwt.sign({
526
+ userId: user.id,
527
+ ssn: user.ssn, // PHI in token!
528
+ diagnosis: user.diagnosis
529
+ }, secret, { expiresIn: '30d' }); // Too long!`,
530
+ right: `// ✅ RIGHT: Use patient IDs in URL
531
+ app.get('/patients/:patientId', (req, res) => {
532
+ const patient = getPatient(req.params.patientId);
533
+ });
534
+
535
+ // ✅ RIGHT: Rate limiting
536
+ const rateLimit = require('express-rate-limit');
537
+ const apiLimiter = rateLimit({
538
+ windowMs: 15 * 60 * 1000, // 15 minutes
539
+ max: 100 // limit each IP to 100 requests per windowMs
540
+ });
541
+ app.use('/api/', apiLimiter);
542
+
543
+ // ✅ RIGHT: Strict CORS
544
+ app.use(cors({
545
+ origin: 'https://patient-portal.example.com',
546
+ credentials: true,
547
+ methods: ['GET', 'POST', 'PUT', 'DELETE']
548
+ }));
549
+
550
+ // ✅ RIGHT: Short JWT, no PHI
551
+ const token = jwt.sign({
552
+ userId: user.id,
553
+ roles: user.roles
554
+ // NO PHI in token
555
+ }, secret, {
556
+ expiresIn: '15m',
557
+ algorithm: 'RS256'
558
+ });`,
559
+ explanation: 'Never put PHI in URLs, implement rate limiting, configure CORS strictly, use short-lived JWTs without PHI, and implement token revocation.',
560
+ },
561
+ ],
562
+ },
563
+ {
564
+ id: 10,
565
+ title: 'Incident Response for Developers',
566
+ description: 'What to do when a security incident occurs',
567
+ content: [
568
+ '🚨 Incident Response for Developers:',
569
+ '',
570
+ '1️⃣ WHAT QUALIFIES AS A SECURITY INCIDENT?',
571
+ ' - Unauthorized access to ePHI (successful or attempted)',
572
+ ' - Malware/ransomware on systems with ePHI',
573
+ ' - Lost/stolen device containing ePHI',
574
+ ' - Accidental ePHI disclosure (wrong email, public exposure)',
575
+ ' - PHI found in logs, error messages, or URLs',
576
+ ' - Misconfigured database/S3 bucket exposing PHI',
577
+ '',
578
+ '2️⃣ WHAT TO DO IMMEDIATELY:',
579
+ ' - 🛑 STOP: Don\'t delete anything (logs, databases, files)',
580
+ ' - 📞 NOTIFY: Contact your Security Officer / Incident Response Team',
581
+ ' - 📸 PRESERVE: Take screenshots, save logs, document timeline',
582
+ ' - 🔒 CONTAIN: Isolate affected systems (if safe to do so)',
583
+ ' - ⏰ DOCUMENT: Write down what happened, when, and who was involved',
584
+ '',
585
+ '3️⃣ WHAT NOT TO DO:',
586
+ ' - ❌ Don\'t hide it or "fix it quietly"',
587
+ ' - ❌ Don\'t delete logs or evidence',
588
+ ' - ❌ Don\'t communicate with press/public',
589
+ ' - ❌ Don\'t assume it\'s "not a big deal"',
590
+ '',
591
+ '4️⃣ NOTIFICATION TIMELINE:',
592
+ ' - Business Associate → Covered Entity: Within 24 hours of discovery',
593
+ ' - Covered Entity → Affected Individuals: Within 60 days',
594
+ ' - If 500+ affected: Notify HHS and media within 60 days',
595
+ '',
596
+ '5️⃣ EVIDENCE PRESERVATION:',
597
+ ' - Keep all logs (even if they contain evidence of the breach)',
598
+ ' - Document: What happened? When? How was it discovered?',
599
+ ' - List: What PHI was involved? How many individuals?',
600
+ ' - Screenshot: Error messages, exposed data, system configs',
601
+ ' - Isolate: Affected systems (but don\'t turn off/wipe)',
602
+ '',
603
+ '⚖️ REMEMBER: Hiding a breach is worse than the breach itself!',
604
+ ' Federal law protects whistleblowers who report violations.',
605
+ ],
606
+ },
607
+ ];
608
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../../src/training/modules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface Question {
2
+ moduleId: number;
3
+ question: string;
4
+ options: string[];
5
+ correctAnswer: number;
6
+ explanation: string;
7
+ }
8
+ export declare const questions: Question[];
9
+ //# sourceMappingURL=questions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.d.ts","sourceRoot":"","sources":["../../src/training/questions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAyiB/B,CAAC"}