@softspark/ai-toolkit 4.20.0 → 4.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.
@@ -48,7 +48,7 @@ Scan a codebase for HIPAA compliance issues using pattern-matching heuristics. D
48
48
  Execute the Python scanner with the user's arguments:
49
49
 
50
50
  ```bash
51
- python3 "$(dirname "$0")/../app/skills/hipaa-validate/scripts/hipaa_scan.py" [path] [--mode developer|compliance] [--severity high|warn] [--keywords term1,term2] [--output json]
51
+ python3 ${CLAUDE_SKILL_DIR}/scripts/hipaa_scan.py [path] [--mode developer|compliance] [--severity high|warn] [--keywords term1,term2] [--output json]
52
52
  ```
53
53
 
54
54
  The script handles all scanning logic deterministically:
@@ -65,6 +65,9 @@ If `--output json` is used, the script outputs structured JSON suitable for CI p
65
65
 
66
66
  ### Step 2: Interpret and Enrich Results
67
67
 
68
+ Read [reference/scanner-categories.md](reference/scanner-categories.md) once before
69
+ starting — you cannot judge a heuristic finding without the pattern that produced it.
70
+
68
71
  For each finding from the script output:
69
72
 
70
73
  1. **Read the flagged file and line** to understand the actual code context
@@ -74,226 +77,27 @@ For each finding from the script output:
74
77
 
75
78
  ### Scanner Reference
76
79
 
77
- The script implements the following scan categories. This reference is provided so you can explain findings to the user and verify edge cases.
78
-
79
- **Modes:**
80
- - `developer` (default): Categories 1, 3, 4, 7, 8 — definitive regex matches only, low false-positive rate
81
- - `compliance`: All 8 categories includes heuristic checks (Cat 2, 5, 6)
82
-
83
- **Default keywords**: `patient`, `diagnosis`, `medication`, `clinical`, `healthcare`, `medical`, `fhir`, `hl7`, `hipaa`, `phi`, `protected.health`, `health-record`, `health-plan`, `health-insurance`
84
-
85
- > **Note**: Bare `health` is deliberately excluded it matches infrastructure health checks in nearly every codebase.
86
-
87
- **Built-in exclusions**: Binary files, lock files, vendored directories (`node_modules/`, `vendor/`, `.git/`, `dist/`, `build/`, `out/`, `.next/`). Test directories (`test/`, `tests/`, `__tests__/`, `spec/`, `fixtures/`, `mocks/`) are excluded for Category 4 only.
88
-
89
- Categories 1 and 2 scan the full project. Categories 3–8 scan only PHI-adjacent files.
90
-
91
- ---
92
-
93
- #### Category 1: PHI in Logs/Console Output
94
-
95
- Scan the full project for log/print statements that reference PHI keywords.
96
-
97
- | Pattern | Severity | Language | Description |
98
- |---------|----------|----------|-------------|
99
- | `console\.log\(.*patient` | HIGH | JS/TS | Patient data in console |
100
- | `console\.\w+\(.*req\.body` | WARN | JS/TS | Raw request body may contain PHI |
101
- | `JSON\.stringify\(.*patient` | WARN | JS/TS | Full patient object serialization |
102
- | `print\(.*\b(patient\|ssn\|social.security)` | HIGH | Python | PHI in print statements |
103
- | `(logging\|logger\|pprint)\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | Python | PHI in logger/named logger/pprint output |
104
- | `print\(.*request\.(data\|json\|form\|POST\|body)` | WARN | Python | Raw request body may contain PHI (Django/Flask/FastAPI) |
105
- | `(logging\|logger)\.\w+\(.*request\.(data\|json\|form\|POST\|body)` | WARN | Python | Raw request body in logger |
106
- | `\brepr\(.*\b(patient\|ssn\|mrn)` | WARN | Python | repr() may expose PHI fields |
107
- | `\bvars\(.*\b(patient\|ssn\|mrn)` | WARN | Python | vars() dumps all PHI fields |
108
- | `fmt\.Print.*\b(patient\|ssn\|mrn)` | HIGH | Go | PHI in fmt output |
109
- | `log\.\w+\(.*\b(patient\|ssn\|mrn)` | HIGH | Go/Any | PHI in log calls |
110
- | `System\.out\.print.*\b(patient\|ssn\|mrn)` | HIGH | Java | PHI in stdout |
111
- | `logger\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | Java/Any | PHI fields in logger |
112
- | `puts.*\b(patient\|ssn\|mrn)` | HIGH | Ruby | PHI in puts |
113
- | `Rails\.logger.*\b(patient\|ssn\|mrn)` | HIGH | Ruby | PHI in Rails logger |
114
- | `Console\.Write.*\b(patient\|ssn\|mrn)` | HIGH | C# | PHI in Console output |
115
- | `_logger\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | C# | PHI in ILogger calls |
116
-
117
- > **Language coverage note**: JS/TS and Python patterns are the most comprehensive. Go, Ruby, and Java have baseline coverage for common log patterns. Contributions for additional language-specific patterns are welcome.
118
-
119
- **Minimum Necessary violations** (§164.502(b)):
120
-
121
- | Pattern | Severity | Language | Description |
122
- |---------|----------|----------|-------------|
123
- | `res\.(json\|send)\(.*patient` without field projection | WARN | JS/TS | Full patient object in API response |
124
- | `return.*patient` in route handler without field selection | WARN | Any | May expose unnecessary PHI fields |
125
- | `SELECT\s+\*.*FROM.*(patient\|member\|enrollee)` | WARN | SQL | SELECT * on PHI tables violates minimum necessary |
126
- | `JSON\.stringify\(.*patient` | WARN | JS/TS | Full patient object serialization |
127
- | `json\.dumps\(.*patient` | WARN | Python | Full patient object serialization |
128
- | `JsonConvert\.Serialize.*patient` | WARN | C# | Full patient object serialization |
129
-
130
- ---
131
-
132
- #### Category 2: Missing Audit Logging
133
-
134
- *Compliance mode only. Heuristic — flags potential gaps, not definitive findings.*
135
-
136
- > **Developer mode**: This category is skipped. Run with `--mode compliance` to include audit gap checks.
137
-
138
- Scan the full project for files that handle PHI data operations but lack audit-related keywords.
139
-
140
- **PHI route file definition**: A file qualifies if it contains BOTH:
141
- 1. A healthcare keyword from Step 0 (`patient`, `diagnosis`, `medication`, etc.)
142
- 2. A data operation pattern: `router`, `app.get`, `app.post`, `app.put`, `app.delete`, `@RequestMapping`, `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `Model.find`, `Model.save`, `Model.update`, `db.query`, `db.execute`, `cursor.execute`, `repository.`, `findBy`, `save(`, `delete(`, `@app.route`, `@blueprint.route`, `@api_view`, `ViewSet`, `APIView`, `\bsession.(query|add|execute|delete|merge)\b` (word-anchored — SQLAlchemy only, avoids matching Express `req.session.save`)
143
-
144
- Files with a healthcare keyword but no data operation pattern are excluded.
145
-
146
- **Audit keywords** (co-occurrence check): `audit`, `AuditEvent`, `auditLog`, `logAccess`, `logEvent`, `createAuditEntry`, `recordAccess`, `ActivityLog`, `trail`, `writeAudit`
147
-
148
- | Pattern | Severity | Description |
149
- |---------|----------|-------------|
150
- | PHI route file without any audit keywords in same file | HIGH | §164.312(b) — POTENTIAL audit gap: verify audit controls exist in call chain |
151
- | CRUD operations on patient resources without audit keywords in same file | HIGH | POTENTIAL gap: all PHI access must be logged |
152
- | Admin operations without audit trail reference | WARN | POTENTIAL gap: administrative actions need recording |
153
- | Bulk data operations (`export`, `download`, `bulk`, `batch`) on PHI resources without audit keywords in same file | HIGH | POTENTIAL gap: mass PHI access must be tracked |
154
-
155
- > **Note**: This category uses co-occurrence heuristics — checking whether PHI route keywords and audit keywords appear in the same file. False positives are expected when audit logging is handled by middleware or a separate call chain. Use `.hipaaignore` to suppress confirmed false positives.
156
-
157
- See: [reference/hipaa-rules.md](reference/hipaa-rules.md) §164.312(b) for audit control requirements.
158
-
159
- ---
160
-
161
- #### Category 3: Unencrypted PHI Transmission
162
-
163
- *Context-gated: scans PHI-adjacent files only.*
164
-
165
- | Pattern | Severity | Language | Description |
166
- |---------|----------|----------|-------------|
167
- | `http://` in API calls (not `localhost`/`127.0.0.1`) | HIGH | Any | §164.312(e)(1) requires encryption in transit |
168
- | Missing TLS/SSL config in database connections | HIGH | Any | Database connections must be encrypted |
169
- | `rejectUnauthorized:\s*false` | HIGH | Any | TLS verification disabled |
170
- | `ws://` (WebSocket without TLS) | WARN | Any | Unencrypted WebSocket may carry PHI |
171
- | `verify\s*=\s*False` | HIGH | Python | TLS verification disabled (requests/httpx) |
172
- | `InsecureRequestWarning` | WARN | Python | TLS warning suppressed |
173
- | `[,(]\s*ssl\s*=\s*False\b` | HIGH | Python | SSL disabled in connector call (anchored to arg position to avoid matching `is_ssl_enabled = False`) |
174
- | `ssl\.CERT_NONE` | HIGH | Python | TLS certificate verification disabled (anchored to `ssl.` module) |
175
- | `check_hostname\s*=\s*False` | HIGH | Python | TLS hostname verification disabled |
176
- | `urllib3\.disable_warnings` | WARN | Python | TLS warnings suppressed (urllib3) |
177
- | `SECURE_SSL_REDIRECT\s*=\s*False` | WARN | Python | Django HTTPS redirect disabled (commonly False in dev settings — verify production config) |
178
- | `NODE_TLS_REJECT_UNAUTHORIZED.*0` | HIGH | JS/TS | TLS rejection disabled globally |
179
-
180
- See: [reference/hipaa-rules.md](reference/hipaa-rules.md) §164.312(e)(1) for transmission security requirements.
181
-
182
- ---
183
-
184
- #### Category 4: Hardcoded PHI/Test Data
185
-
186
- *Context-gated: scans PHI-adjacent files only.*
187
-
188
- **Built-in test directory exclusions**: Skip files in `test/`, `tests/`, `__tests__/`, `spec/`, `fixtures/`, `mocks/`, `__mocks__/`, `testdata/`, `test-data/` — test fixtures legitimately contain synthetic PHI.
189
-
190
- | Pattern | Severity | Description |
191
- |---------|----------|-------------|
192
- | `\d{3}-\d{2}-\d{4}` in PHI-adjacent source files | HIGH | Hardcoded SSNs |
193
- | MRN patterns near healthcare keywords | HIGH | Medical record numbers in code |
194
- | `\b\d{5}(-\d{4})?\b` near `zip\|postal\|address` keywords | WARN | ZIP codes in healthcare context (§164.514(b)(2)(i)(B)) |
195
- | Real-looking patient names in seed/fixture data | WARN | Use synthetic data generators |
196
- | Date of birth + name co-occurrence in same file | WARN | Combined identifiers = PHI |
197
- | Phone/email/IP regex matches in PHI-adjacent files | WARN | HIPAA identifiers in healthcare context |
198
- | `\d{3}[\s.-]?\d{3}[\s.-]?\d{4}` near `phone` keyword | WARN | Phone numbers in healthcare context |
199
-
200
- See: [reference/phi-identifiers.md](reference/phi-identifiers.md) for the full list of 18 HIPAA identifiers and detection patterns.
201
-
202
- ---
203
-
204
- #### Category 5: Access Control Gaps
205
-
206
- *Context-gated: scans PHI-adjacent files only. Heuristic — flags potential gaps.*
207
-
208
- **Auth keywords** (co-occurrence check): `auth`, `authenticate`, `requireAuth`, `isAuthenticated`, `protect`, `guard`, `Authorize`, `login_required`, `Permission`, `permission_required`, `LoginRequiredMixin`, `PermissionRequiredMixin`, `IsAuthenticated`, `Depends`, `Security`
209
-
210
- **Data operation patterns** (Python frameworks): `@app.route`, `@blueprint.route`, `@api_view`, `ViewSet`, `APIView`, `cursor.execute`, `\bsession.(query|execute)\b` (word-anchored — SQLAlchemy only)
211
-
212
- | Pattern | Severity | Language | Description |
213
- |---------|----------|----------|-------------|
214
- | PHI route file without any auth keywords in same file | WARN | Any | POTENTIAL access control gap — verify auth middleware covers these routes (§164.312(d)) |
215
- | `Access-Control-Allow-Origin:\s*\*` or `origin:\s*(true\|\*)` in PHI-adjacent files | HIGH | Any | Unrestricted cross-origin access to PHI endpoints |
216
- | Routes marked `public`, `noAuth`, `anonymous` exposing PHI keywords | HIGH | Any | PHI must require authentication |
217
- | `permission_classes\s*=.*AllowAny` | WARN | Python | DRF AllowAny — verify no PHI exposed |
218
-
219
- > **Note**: Auth middleware is commonly applied at router-level or app-level. The co-occurrence heuristic checks the same file only. False positives expected when auth is configured globally. Use `.hipaaignore` to suppress.
220
-
221
- See: [reference/hipaa-rules.md](reference/hipaa-rules.md) §164.312(a)(1) and §164.312(d) for access control and authentication requirements.
222
-
223
- ---
224
-
225
- #### Category 6: Missing BAA References
226
-
227
- *Compliance mode only. Context-gated: scans PHI-adjacent files only.*
228
-
229
- > **Developer mode**: This category is skipped. Run with `--mode compliance` to include BAA checks.
230
-
231
- Instead of per-finding rows, emit a single **BAA Verification Checklist** in the compliance report:
232
-
233
- 1. Grep PHI-adjacent files for HTTP client calls (`fetch(`, `axios.`, `requests.`, `http.Get`, `HttpClient`, `RestTemplate`, `urllib`). Extract external domains.
234
- 2. Grep for cloud storage calls (`S3`, `GCS`, `BlobStorage`, `putObject`, `upload`). Note each service.
235
- 3. Grep for cloud database connections (`mongodb+srv://`, `postgres://`, `mysql://`, `firestore`, `dynamodb`, `CosmosClient`, `MongoClient`, connection strings with cloud hostnames). Note each service.
236
- 4. Grep for message queue / event streaming services (`SQS`, `SNS`, `RabbitMQ`, `redis://`, `kafka`, `EventBridge`, `PubSub`). Note each service.
237
- 5. Grep for CDN references (`CloudFront`, `Cloudflare`, `Akamai`, `Fastly`, `cdn.`) serving PHI-adjacent paths. Note each service.
238
- 6. Grep for observability / logging SDKs (`datadog`, `splunk`, `newrelic`, `sentry`, `logstash`, `elasticsearch`, `bugsnag`, `rollbar`). Note each SDK.
239
- 7. Grep for analytics SDK calls (`analytics.`, `gtag`, `mixpanel`, `segment`, `amplitude`, `posthog`). Note each SDK.
240
- 8. Read `.hipaa-config` at project root if it exists. Suppress vendors listed under `covered_vendors`.
241
- 9. Emit one checklist row per unverified domain/service.
242
-
243
- **`.hipaa-config` format** (suppress known-covered vendors):
244
- ```json
245
- {
246
- "covered_vendors": ["aws", "twilio", "sendgrid", "stripe"]
247
- }
248
- ```
249
-
250
- **Output format for compliance mode**:
251
- ```
252
- ### BAA Verification Checklist
253
- | Service/Domain | Pattern Detected | BAA Status |
254
- |----------------|-----------------|------------|
255
- | AWS S3 | `putObject` in src/storage/patient-files.ts | ✓ covered (covered_vendors) |
256
- | sendgrid.com | `axios.post` in src/notifications/email.ts | ⚠️ verify BAA exists |
257
- | analytics.google.com | `gtag` in src/components/Dashboard.tsx | ❌ verify no PHI flows here |
258
- ```
259
-
260
- > **Note**: This is a documentation checklist, not a legal review. Items marked ⚠️ or ❌ require human verification, not code changes.
261
-
262
- ---
263
-
264
- #### Category 7: Encryption at Rest
265
-
266
- *Context-gated: scans PHI-adjacent files only. Developer mode.*
267
-
268
- Detects PHI storage patterns without encryption references. Per §164.312(a)(2)(iv), ePHI must be encrypted when stored.
269
-
270
- | Pattern | Severity | Language | Description |
271
- |---------|----------|----------|-------------|
272
- | `encrypt\s*[:=]\s*false` in database config files | HIGH | Any | §164.312(a)(2)(iv) — Encryption explicitly disabled |
273
- | File write operations (`writeFile`, `fs.write`, `open(.*w`, `File.Create`) in PHI-adjacent code without encryption references | WARN | Any | PHI written to disk may lack encryption at rest |
274
- | Database connection without `ssl`, `encrypt`, or `tls` keywords in PHI-adjacent config | WARN | Any | Database storing PHI should enforce encrypted connections |
275
- | `localStorage.setItem` or `sessionStorage.setItem` with PHI keywords | HIGH | JS/TS | Browser storage is unencrypted — PHI must not be stored client-side without encryption |
276
- | `SharedPreferences` or `UserDefaults` with PHI keywords | HIGH | Java/Any | Mobile local storage is unencrypted by default |
277
- | `pickle\.(dump\|dumps)\(` with PHI keywords | HIGH | Python | pickle serialization is unencrypted — PHI must be encrypted at rest |
278
- | `shelve\.open\(` with PHI keywords | HIGH | Python | shelve storage is unencrypted — PHI must be encrypted at rest |
279
-
280
- See: [reference/hipaa-rules.md](reference/hipaa-rules.md) §164.312(a)(2)(iv) for encryption requirements.
281
-
282
- ---
283
-
284
- #### Category 8: PHI Temp File Exposure
285
-
286
- *Context-gated: scans PHI-adjacent files only. Developer mode.*
287
-
288
- Detects temporary file creation in PHI-adjacent code without secure deletion. Per §164.310(d)(2)(iii), media containing PHI must be sanitized before reuse or disposal.
289
-
290
- | Pattern | Severity | Description |
291
- |---------|----------|-------------|
292
- | `/tmp/` or `tempfile\.` or `os\.tmpdir\(\)` or `Path\.GetTempPath` in PHI-adjacent code | WARN | §164.310(d)(2)(iii) — Temp files with PHI must be securely deleted |
293
- | `mktemp` or `NamedTemporaryFile` or `createTempFile` near PHI keywords | WARN | Verify temp files are cleaned up after use |
294
- | Cache directory writes (`cache/`, `.cache`, `Cache.set`) with PHI keywords | WARN | Cached PHI must be encrypted or purged on schedule |
295
-
296
- See: [reference/hipaa-rules.md](reference/hipaa-rules.md) §164.310(d)(2)(iii) for disposal requirements.
80
+ The eight scan categories are implemented in `scripts/hipaa_scan.py`. The pattern
81
+ tables, severities, per-language coverage and rule citations live in
82
+ [reference/scanner-categories.md](reference/scanner-categories.md).
83
+
84
+ Read that file once, in full, before enriching findings in Step 2 it is what lets
85
+ you explain *why* a line matched and judge whether a heuristic hit is a false
86
+ positive. The scan itself does not need it; the script already holds the patterns.
87
+
88
+ | # | Category | Scope | Mode |
89
+ |---|----------|-------|------|
90
+ | 1 | PHI in logs / console output (+ minimum-necessary violations) | full project | developer |
91
+ | 2 | Missing audit logging | full project | compliance only, heuristic |
92
+ | 3 | Unencrypted transmission | PHI-adjacent | developer |
93
+ | 4 | Hardcoded PHI test data | PHI-adjacent | developer |
94
+ | 5 | Access control gaps | PHI-adjacent | compliance only, heuristic |
95
+ | 6 | Missing BAA references | PHI-adjacent | compliance only, heuristic |
96
+ | 7 | Encryption at rest | PHI-adjacent | developer |
97
+ | 8 | PHI temp file exposure | PHI-adjacent | developer |
98
+
99
+ Categories 1 and 2 scan the full project; categories 3–8 scan only PHI-adjacent
100
+ files. Compliance mode adds the heuristic categories 2, 5 and 6 to the developer set.
297
101
 
298
102
  ### Step 3: Compile and Report
299
103
 
@@ -0,0 +1,224 @@
1
+ # HIPAA Scanner Categories
2
+
3
+ ## Scanner Reference
4
+
5
+ The script implements the following scan categories. This reference is provided so you can explain findings to the user and verify edge cases.
6
+
7
+ **Modes:**
8
+ - `developer` (default): Categories 1, 3, 4, 7, 8 — definitive regex matches only, low false-positive rate
9
+ - `compliance`: All 8 categories — includes heuristic checks (Cat 2, 5, 6)
10
+
11
+ **Default keywords**: `patient`, `diagnosis`, `medication`, `clinical`, `healthcare`, `medical`, `fhir`, `hl7`, `hipaa`, `phi`, `protected.health`, `health-record`, `health-plan`, `health-insurance`
12
+
13
+ > **Note**: Bare `health` is deliberately excluded — it matches infrastructure health checks in nearly every codebase.
14
+
15
+ **Built-in exclusions**: Binary files, lock files, vendored directories (`node_modules/`, `vendor/`, `.git/`, `dist/`, `build/`, `out/`, `.next/`). Test directories (`test/`, `tests/`, `__tests__/`, `spec/`, `fixtures/`, `mocks/`) are excluded for Category 4 only.
16
+
17
+ Categories 1 and 2 scan the full project. Categories 3–8 scan only PHI-adjacent files.
18
+
19
+ ---
20
+
21
+ ### Category 1: PHI in Logs/Console Output
22
+
23
+ Scan the full project for log/print statements that reference PHI keywords.
24
+
25
+ | Pattern | Severity | Language | Description |
26
+ |---------|----------|----------|-------------|
27
+ | `console\.log\(.*patient` | HIGH | JS/TS | Patient data in console |
28
+ | `console\.\w+\(.*req\.body` | WARN | JS/TS | Raw request body may contain PHI |
29
+ | `JSON\.stringify\(.*patient` | WARN | JS/TS | Full patient object serialization |
30
+ | `print\(.*\b(patient\|ssn\|social.security)` | HIGH | Python | PHI in print statements |
31
+ | `(logging\|logger\|pprint)\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | Python | PHI in logger/named logger/pprint output |
32
+ | `print\(.*request\.(data\|json\|form\|POST\|body)` | WARN | Python | Raw request body may contain PHI (Django/Flask/FastAPI) |
33
+ | `(logging\|logger)\.\w+\(.*request\.(data\|json\|form\|POST\|body)` | WARN | Python | Raw request body in logger |
34
+ | `\brepr\(.*\b(patient\|ssn\|mrn)` | WARN | Python | repr() may expose PHI fields |
35
+ | `\bvars\(.*\b(patient\|ssn\|mrn)` | WARN | Python | vars() dumps all PHI fields |
36
+ | `fmt\.Print.*\b(patient\|ssn\|mrn)` | HIGH | Go | PHI in fmt output |
37
+ | `log\.\w+\(.*\b(patient\|ssn\|mrn)` | HIGH | Go/Any | PHI in log calls |
38
+ | `System\.out\.print.*\b(patient\|ssn\|mrn)` | HIGH | Java | PHI in stdout |
39
+ | `logger\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | Java/Any | PHI fields in logger |
40
+ | `puts.*\b(patient\|ssn\|mrn)` | HIGH | Ruby | PHI in puts |
41
+ | `Rails\.logger.*\b(patient\|ssn\|mrn)` | HIGH | Ruby | PHI in Rails logger |
42
+ | `Console\.Write.*\b(patient\|ssn\|mrn)` | HIGH | C# | PHI in Console output |
43
+ | `_logger\.\w+\(.*\b(patient\|ssn\|mrn\|dob)` | HIGH | C# | PHI in ILogger calls |
44
+
45
+ > **Language coverage note**: JS/TS and Python patterns are the most comprehensive. Go, Ruby, and Java have baseline coverage for common log patterns. Contributions for additional language-specific patterns are welcome.
46
+
47
+ **Minimum Necessary violations** (§164.502(b)):
48
+
49
+ | Pattern | Severity | Language | Description |
50
+ |---------|----------|----------|-------------|
51
+ | `res\.(json\|send)\(.*patient` without field projection | WARN | JS/TS | Full patient object in API response |
52
+ | `return.*patient` in route handler without field selection | WARN | Any | May expose unnecessary PHI fields |
53
+ | `SELECT\s+\*.*FROM.*(patient\|member\|enrollee)` | WARN | SQL | SELECT * on PHI tables violates minimum necessary |
54
+ | `JSON\.stringify\(.*patient` | WARN | JS/TS | Full patient object serialization |
55
+ | `json\.dumps\(.*patient` | WARN | Python | Full patient object serialization |
56
+ | `JsonConvert\.Serialize.*patient` | WARN | C# | Full patient object serialization |
57
+
58
+ ---
59
+
60
+ ### Category 2: Missing Audit Logging
61
+
62
+ *Compliance mode only. Heuristic — flags potential gaps, not definitive findings.*
63
+
64
+ > **Developer mode**: This category is skipped. Run with `--mode compliance` to include audit gap checks.
65
+
66
+ Scan the full project for files that handle PHI data operations but lack audit-related keywords.
67
+
68
+ **PHI route file definition**: A file qualifies if it contains BOTH:
69
+ 1. A healthcare keyword from Step 0 (`patient`, `diagnosis`, `medication`, etc.)
70
+ 2. A data operation pattern: `router`, `app.get`, `app.post`, `app.put`, `app.delete`, `@RequestMapping`, `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `Model.find`, `Model.save`, `Model.update`, `db.query`, `db.execute`, `cursor.execute`, `repository.`, `findBy`, `save(`, `delete(`, `@app.route`, `@blueprint.route`, `@api_view`, `ViewSet`, `APIView`, `\bsession.(query|add|execute|delete|merge)\b` (word-anchored — SQLAlchemy only, avoids matching Express `req.session.save`)
71
+
72
+ Files with a healthcare keyword but no data operation pattern are excluded.
73
+
74
+ **Audit keywords** (co-occurrence check): `audit`, `AuditEvent`, `auditLog`, `logAccess`, `logEvent`, `createAuditEntry`, `recordAccess`, `ActivityLog`, `trail`, `writeAudit`
75
+
76
+ | Pattern | Severity | Description |
77
+ |---------|----------|-------------|
78
+ | PHI route file without any audit keywords in same file | HIGH | §164.312(b) — POTENTIAL audit gap: verify audit controls exist in call chain |
79
+ | CRUD operations on patient resources without audit keywords in same file | HIGH | POTENTIAL gap: all PHI access must be logged |
80
+ | Admin operations without audit trail reference | WARN | POTENTIAL gap: administrative actions need recording |
81
+ | Bulk data operations (`export`, `download`, `bulk`, `batch`) on PHI resources without audit keywords in same file | HIGH | POTENTIAL gap: mass PHI access must be tracked |
82
+
83
+ > **Note**: This category uses co-occurrence heuristics — checking whether PHI route keywords and audit keywords appear in the same file. False positives are expected when audit logging is handled by middleware or a separate call chain. Use `.hipaaignore` to suppress confirmed false positives.
84
+
85
+ See: [hipaa-rules.md](hipaa-rules.md) §164.312(b) for audit control requirements.
86
+
87
+ ---
88
+
89
+ ### Category 3: Unencrypted PHI Transmission
90
+
91
+ *Context-gated: scans PHI-adjacent files only.*
92
+
93
+ | Pattern | Severity | Language | Description |
94
+ |---------|----------|----------|-------------|
95
+ | `http://` in API calls (not `localhost`/`127.0.0.1`) | HIGH | Any | §164.312(e)(1) requires encryption in transit |
96
+ | Missing TLS/SSL config in database connections | HIGH | Any | Database connections must be encrypted |
97
+ | `rejectUnauthorized:\s*false` | HIGH | Any | TLS verification disabled |
98
+ | `ws://` (WebSocket without TLS) | WARN | Any | Unencrypted WebSocket may carry PHI |
99
+ | `verify\s*=\s*False` | HIGH | Python | TLS verification disabled (requests/httpx) |
100
+ | `InsecureRequestWarning` | WARN | Python | TLS warning suppressed |
101
+ | `[,(]\s*ssl\s*=\s*False\b` | HIGH | Python | SSL disabled in connector call (anchored to arg position to avoid matching `is_ssl_enabled = False`) |
102
+ | `ssl\.CERT_NONE` | HIGH | Python | TLS certificate verification disabled (anchored to `ssl.` module) |
103
+ | `check_hostname\s*=\s*False` | HIGH | Python | TLS hostname verification disabled |
104
+ | `urllib3\.disable_warnings` | WARN | Python | TLS warnings suppressed (urllib3) |
105
+ | `SECURE_SSL_REDIRECT\s*=\s*False` | WARN | Python | Django HTTPS redirect disabled (commonly False in dev settings — verify production config) |
106
+ | `NODE_TLS_REJECT_UNAUTHORIZED.*0` | HIGH | JS/TS | TLS rejection disabled globally |
107
+
108
+ See: [hipaa-rules.md](hipaa-rules.md) §164.312(e)(1) for transmission security requirements.
109
+
110
+ ---
111
+
112
+ ### Category 4: Hardcoded PHI/Test Data
113
+
114
+ *Context-gated: scans PHI-adjacent files only.*
115
+
116
+ **Built-in test directory exclusions**: Skip files in `test/`, `tests/`, `__tests__/`, `spec/`, `fixtures/`, `mocks/`, `__mocks__/`, `testdata/`, `test-data/` — test fixtures legitimately contain synthetic PHI.
117
+
118
+ | Pattern | Severity | Description |
119
+ |---------|----------|-------------|
120
+ | `\d{3}-\d{2}-\d{4}` in PHI-adjacent source files | HIGH | Hardcoded SSNs |
121
+ | MRN patterns near healthcare keywords | HIGH | Medical record numbers in code |
122
+ | `\b\d{5}(-\d{4})?\b` near `zip\|postal\|address` keywords | WARN | ZIP codes in healthcare context (§164.514(b)(2)(i)(B)) |
123
+ | Real-looking patient names in seed/fixture data | WARN | Use synthetic data generators |
124
+ | Date of birth + name co-occurrence in same file | WARN | Combined identifiers = PHI |
125
+ | Phone/email/IP regex matches in PHI-adjacent files | WARN | HIPAA identifiers in healthcare context |
126
+ | `\d{3}[\s.-]?\d{3}[\s.-]?\d{4}` near `phone` keyword | WARN | Phone numbers in healthcare context |
127
+
128
+ See: [phi-identifiers.md](phi-identifiers.md) for the full list of 18 HIPAA identifiers and detection patterns.
129
+
130
+ ---
131
+
132
+ ### Category 5: Access Control Gaps
133
+
134
+ *Context-gated: scans PHI-adjacent files only. Heuristic — flags potential gaps.*
135
+
136
+ **Auth keywords** (co-occurrence check): `auth`, `authenticate`, `requireAuth`, `isAuthenticated`, `protect`, `guard`, `Authorize`, `login_required`, `Permission`, `permission_required`, `LoginRequiredMixin`, `PermissionRequiredMixin`, `IsAuthenticated`, `Depends`, `Security`
137
+
138
+ **Data operation patterns** (Python frameworks): `@app.route`, `@blueprint.route`, `@api_view`, `ViewSet`, `APIView`, `cursor.execute`, `\bsession.(query|execute)\b` (word-anchored — SQLAlchemy only)
139
+
140
+ | Pattern | Severity | Language | Description |
141
+ |---------|----------|----------|-------------|
142
+ | PHI route file without any auth keywords in same file | WARN | Any | POTENTIAL access control gap — verify auth middleware covers these routes (§164.312(d)) |
143
+ | `Access-Control-Allow-Origin:\s*\*` or `origin:\s*(true\|\*)` in PHI-adjacent files | HIGH | Any | Unrestricted cross-origin access to PHI endpoints |
144
+ | Routes marked `public`, `noAuth`, `anonymous` exposing PHI keywords | HIGH | Any | PHI must require authentication |
145
+ | `permission_classes\s*=.*AllowAny` | WARN | Python | DRF AllowAny — verify no PHI exposed |
146
+
147
+ > **Note**: Auth middleware is commonly applied at router-level or app-level. The co-occurrence heuristic checks the same file only. False positives expected when auth is configured globally. Use `.hipaaignore` to suppress.
148
+
149
+ See: [hipaa-rules.md](hipaa-rules.md) §164.312(a)(1) and §164.312(d) for access control and authentication requirements.
150
+
151
+ ---
152
+
153
+ ### Category 6: Missing BAA References
154
+
155
+ *Compliance mode only. Context-gated: scans PHI-adjacent files only.*
156
+
157
+ > **Developer mode**: This category is skipped. Run with `--mode compliance` to include BAA checks.
158
+
159
+ Instead of per-finding rows, emit a single **BAA Verification Checklist** in the compliance report:
160
+
161
+ 1. Grep PHI-adjacent files for HTTP client calls (`fetch(`, `axios.`, `requests.`, `http.Get`, `HttpClient`, `RestTemplate`, `urllib`). Extract external domains.
162
+ 2. Grep for cloud storage calls (`S3`, `GCS`, `BlobStorage`, `putObject`, `upload`). Note each service.
163
+ 3. Grep for cloud database connections (`mongodb+srv://`, `postgres://`, `mysql://`, `firestore`, `dynamodb`, `CosmosClient`, `MongoClient`, connection strings with cloud hostnames). Note each service.
164
+ 4. Grep for message queue / event streaming services (`SQS`, `SNS`, `RabbitMQ`, `redis://`, `kafka`, `EventBridge`, `PubSub`). Note each service.
165
+ 5. Grep for CDN references (`CloudFront`, `Cloudflare`, `Akamai`, `Fastly`, `cdn.`) serving PHI-adjacent paths. Note each service.
166
+ 6. Grep for observability / logging SDKs (`datadog`, `splunk`, `newrelic`, `sentry`, `logstash`, `elasticsearch`, `bugsnag`, `rollbar`). Note each SDK.
167
+ 7. Grep for analytics SDK calls (`analytics.`, `gtag`, `mixpanel`, `segment`, `amplitude`, `posthog`). Note each SDK.
168
+ 8. Read `.hipaa-config` at project root if it exists. Suppress vendors listed under `covered_vendors`.
169
+ 9. Emit one checklist row per unverified domain/service.
170
+
171
+ **`.hipaa-config` format** (suppress known-covered vendors):
172
+ ```json
173
+ {
174
+ "covered_vendors": ["aws", "twilio", "sendgrid", "stripe"]
175
+ }
176
+ ```
177
+
178
+ **Output format for compliance mode**:
179
+ ```
180
+ ### BAA Verification Checklist
181
+ | Service/Domain | Pattern Detected | BAA Status |
182
+ |----------------|-----------------|------------|
183
+ | AWS S3 | `putObject` in src/storage/patient-files.ts | ✓ covered (covered_vendors) |
184
+ | sendgrid.com | `axios.post` in src/notifications/email.ts | ⚠️ verify BAA exists |
185
+ | analytics.google.com | `gtag` in src/components/Dashboard.tsx | ❌ verify no PHI flows here |
186
+ ```
187
+
188
+ > **Note**: This is a documentation checklist, not a legal review. Items marked ⚠️ or ❌ require human verification, not code changes.
189
+
190
+ ---
191
+
192
+ ### Category 7: Encryption at Rest
193
+
194
+ *Context-gated: scans PHI-adjacent files only. Developer mode.*
195
+
196
+ Detects PHI storage patterns without encryption references. Per §164.312(a)(2)(iv), ePHI must be encrypted when stored.
197
+
198
+ | Pattern | Severity | Language | Description |
199
+ |---------|----------|----------|-------------|
200
+ | `encrypt\s*[:=]\s*false` in database config files | HIGH | Any | §164.312(a)(2)(iv) — Encryption explicitly disabled |
201
+ | File write operations (`writeFile`, `fs.write`, `open(.*w`, `File.Create`) in PHI-adjacent code without encryption references | WARN | Any | PHI written to disk may lack encryption at rest |
202
+ | Database connection without `ssl`, `encrypt`, or `tls` keywords in PHI-adjacent config | WARN | Any | Database storing PHI should enforce encrypted connections |
203
+ | `localStorage.setItem` or `sessionStorage.setItem` with PHI keywords | HIGH | JS/TS | Browser storage is unencrypted — PHI must not be stored client-side without encryption |
204
+ | `SharedPreferences` or `UserDefaults` with PHI keywords | HIGH | Java/Any | Mobile local storage is unencrypted by default |
205
+ | `pickle\.(dump\|dumps)\(` with PHI keywords | HIGH | Python | pickle serialization is unencrypted — PHI must be encrypted at rest |
206
+ | `shelve\.open\(` with PHI keywords | HIGH | Python | shelve storage is unencrypted — PHI must be encrypted at rest |
207
+
208
+ See: [hipaa-rules.md](hipaa-rules.md) §164.312(a)(2)(iv) for encryption requirements.
209
+
210
+ ---
211
+
212
+ ### Category 8: PHI Temp File Exposure
213
+
214
+ *Context-gated: scans PHI-adjacent files only. Developer mode.*
215
+
216
+ Detects temporary file creation in PHI-adjacent code without secure deletion. Per §164.310(d)(2)(iii), media containing PHI must be sanitized before reuse or disposal.
217
+
218
+ | Pattern | Severity | Description |
219
+ |---------|----------|-------------|
220
+ | `/tmp/` or `tempfile\.` or `os\.tmpdir\(\)` or `Path\.GetTempPath` in PHI-adjacent code | WARN | §164.310(d)(2)(iii) — Temp files with PHI must be securely deleted |
221
+ | `mktemp` or `NamedTemporaryFile` or `createTempFile` near PHI keywords | WARN | Verify temp files are cleaned up after use |
222
+ | Cache directory writes (`cache/`, `.cache`, `Cache.set`) with PHI keywords | WARN | Cached PHI must be encrypted or purged on schedule |
223
+
224
+ See: [hipaa-rules.md](hipaa-rules.md) §164.310(d)(2)(iii) for disposal requirements.
@@ -28,7 +28,7 @@ Create a GitHub pull request.
28
28
  Generate a structured PR summary from the commit history before writing the PR description:
29
29
 
30
30
  ```bash
31
- python3 "$(dirname "$0")/scripts/pr-summary.py" [base_branch]
31
+ python3 ${CLAUDE_SKILL_DIR}/scripts/pr-summary.py [base_branch]
32
32
  # Default base branch: main
33
33
  # Example: python3 scripts/pr-summary.py develop
34
34
  ```
@@ -19,6 +19,23 @@ Reviews code changes for quality and issues.
19
19
 
20
20
  - Changes: !`git diff --stat main...HEAD 2>/dev/null || git diff --cached --stat 2>/dev/null || echo "no changes detected"`
21
21
 
22
+ ## Signal Collection (never stop at the first red)
23
+
24
+ Collect every failing signal up front, then review the diff in full **anyway**:
25
+
26
+ | Signal | How to read it |
27
+ |--------|----------------|
28
+ | Merge conflict with base | `gh pr view --json mergeable,mergeStateStatus` or `git merge-tree` |
29
+ | Failing CI checks | `gh pr checks` or the platform equivalent |
30
+ | Lint / typecheck failure | the project's own commands |
31
+
32
+ Each failing signal becomes a `blocker` finding. **None of them ends the run.**
33
+
34
+ A review that aborts on the first red signal spends the whole cycle repeating what
35
+ the tracker already displayed, while the finding that would have told the author
36
+ something new never gets written. One invocation produces the most complete picture
37
+ of the change that it can.
38
+
22
39
  ## Automated Diff Analysis
23
40
 
24
41
  Before starting manual review, run the diff analyzer script to get a structured risk assessment:
@@ -58,8 +75,8 @@ Each reviewer should report findings independently. Do NOT modify files.
58
75
 
59
76
  After all reviewers complete:
60
77
  1. Synthesize findings into unified Code Review Report
61
- 2. Prioritize by severity (Critical > Major > Minor)
62
- 3. Issue verdict: APPROVE / REQUEST_CHANGES / NEEDS_DISCUSSION
78
+ 2. Prioritize by severity (blocker > major > minor > nit)
79
+ 3. Issue verdict per the verdict rule below — not by impression
63
80
 
64
81
  > **When to use**: PRs with >5 files changed, cross-module changes, security-sensitive code.
65
82
  > **READ-ONLY**: No teammate should modify files during review.
@@ -101,12 +118,15 @@ After all reviewers complete:
101
118
  - [ ] A08: Integrity checks on deserialized data, CI/CD pipeline safety
102
119
  - [ ] A09: Security-relevant events logged (without PII)
103
120
  - [ ] A10: External URL handling validates scheme/host (SSRF prevention)
121
+ - [ ] Cross-scope replay: can an identifier from one tenant/user/org be replayed in another?
122
+ - [ ] Fails closed wherever the path affects security, money, or data retention
104
123
 
105
124
  ### API / Contract Changes
106
125
  - [ ] Backward compatibility preserved (no silent breaking changes)
107
126
  - [ ] API versioning updated if contract changed
108
127
  - [ ] Schema validation on request/response
109
128
  - [ ] Error responses follow project convention
129
+ - [ ] Wire-level contracts checked, not just code signatures: HTTP routes, webhook payloads, event/queue schemas
110
130
 
111
131
  ### Concurrency / Async
112
132
  - [ ] Shared mutable state protected (locks, atomics, channels)
@@ -131,6 +151,26 @@ After all reviewers complete:
131
151
  - [ ] Edge cases covered
132
152
  - [ ] Mocks appropriate
133
153
 
154
+ ## Severity & Verdict
155
+
156
+ | Tier | Meaning | Merge impact |
157
+ |------|---------|--------------|
158
+ | `blocker` | Causes damage: data loss, security hole, money, corruption | Blocks merge, no exceptions |
159
+ | `major` | Real defect that will bite in production | Blocks merge unless waived in writing |
160
+ | `minor` | Should be fixed, not worth blocking on | Does not block |
161
+ | `nit` | Polish, taste, style | Does not block |
162
+
163
+ **Verdict rule** — apply it mechanically, do not negotiate with yourself:
164
+
165
+ - any `blocker` → `REQUEST_CHANGES`
166
+ - any `major` without a documented waiver (who waived it, why, what the follow-up is) → `REQUEST_CHANGES`
167
+ - only `minor` / `nit` → `APPROVE`
168
+ - the change cannot be classified from the diff → `NEEDS_DISCUSSION`, and state what would resolve it
169
+
170
+ Severity describes impact, confidence describes certainty — they are independent
171
+ axes. A finding with confidence < 6 is reported at the tier its evidence supports
172
+ and is never promoted to `blocker` on suspicion alone.
173
+
134
174
  ## Output Format
135
175
 
136
176
  ```markdown
@@ -145,9 +185,9 @@ After all reviewers complete:
145
185
 
146
186
  ### Findings
147
187
 
148
- #### Critical
188
+ #### Blocker
149
189
  - **[file:line]**: [issue]
150
- - Severity: critical | Confidence: [1-10]
190
+ - Severity: blocker | Confidence: [1-10]
151
191
  - Evidence: [specific code reference and reasoning]
152
192
  - Suggested fix: [code]
153
193
 
@@ -181,6 +221,11 @@ After all reviewers complete:
181
221
 
182
222
  ### Verdict
183
223
  [APPROVE / REQUEST_CHANGES / NEEDS_DISCUSSION]
224
+
225
+ State which clause of the verdict rule produced it, e.g.
226
+ "REQUEST_CHANGES — 1 blocker (auth.ts:88)" or
227
+ "APPROVE — 2 minor, 1 nit, no blocker or major".
228
+ Waived majors must name the waiver and the follow-up.
184
229
  ```
185
230
 
186
231
  ## Common Rationalizations