coverme-scanner 1.0.19 → 1.0.21
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.
- package/.claude/commands/coverme.md +21 -5
- package/dist/prompts/cross-validator.md +44 -0
- package/dist/prompts/mitigation-validator.md +68 -0
- package/dist/prompts/orchestration.md +125 -33
- package/dist/prompts/security-scanner.md +63 -2
- package/package.json +1 -1
- package/src/prompts/cross-validator.md +44 -0
- package/src/prompts/mitigation-validator.md +68 -0
- package/src/prompts/orchestration.md +125 -33
- package/src/prompts/security-scanner.md +63 -2
|
@@ -4,7 +4,15 @@ The most comprehensive AI-powered code scanner. 10 specialized agents + 3 valida
|
|
|
4
4
|
|
|
5
5
|
$ARGUMENTS
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## CRITICAL INSTRUCTIONS - READ FIRST!
|
|
8
|
+
|
|
9
|
+
1. **DO NOT ASK ANY QUESTIONS** - Run the entire scan autonomously from start to finish
|
|
10
|
+
2. **DO NOT STOP FOR CONFIRMATION** - Just keep going through all phases
|
|
11
|
+
3. **DO NOT ASK ABOUT FILE CHANGES** - Automatically update/overwrite scan.json
|
|
12
|
+
4. **DO NOT ASK TO OPEN REPORT** - Just open it automatically at the end
|
|
13
|
+
5. **COMPLETE EVERYTHING IN ONE GO** - All 5 phases without interruption
|
|
14
|
+
|
|
15
|
+
Execute ALL phases automatically. Do NOT stop until the HTML report is open.
|
|
8
16
|
|
|
9
17
|
---
|
|
10
18
|
|
|
@@ -471,7 +479,9 @@ Combine all results:
|
|
|
471
479
|
|
|
472
480
|
## Phase 4: Generate Report
|
|
473
481
|
|
|
474
|
-
|
|
482
|
+
**DO NOT ASK - JUST OVERWRITE THE FILE!**
|
|
483
|
+
|
|
484
|
+
Update `.coverme/scan.json` with the scan results. Overwrite any existing content without asking:
|
|
475
485
|
|
|
476
486
|
- **projectName**: from package.json or folder name
|
|
477
487
|
- **scanDate**: today's date
|
|
@@ -480,13 +490,15 @@ Update the existing `.coverme/scan.json` file with the scan results. The file al
|
|
|
480
490
|
- **scanDuration**: time taken in ms
|
|
481
491
|
- **agentCount**: 7
|
|
482
492
|
|
|
483
|
-
Use the
|
|
493
|
+
Use the Write tool to overwrite `.coverme/scan.json` with the results. Do not ask for confirmation.
|
|
484
494
|
|
|
485
495
|
---
|
|
486
496
|
|
|
487
497
|
## Phase 5: Generate HTML Report
|
|
488
498
|
|
|
489
|
-
|
|
499
|
+
**DO NOT ASK - JUST RUN THE COMMANDS!**
|
|
500
|
+
|
|
501
|
+
Generate the HTML report and open it automatically:
|
|
490
502
|
```bash
|
|
491
503
|
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
|
|
492
504
|
npx coverme-scanner report .coverme/scan.json -f html -o ".coverme/report_$TIMESTAMP.html"
|
|
@@ -494,8 +506,12 @@ cp .coverme/scan.json ".coverme/scan_$TIMESTAMP.json"
|
|
|
494
506
|
open ".coverme/report_$TIMESTAMP.html"
|
|
495
507
|
```
|
|
496
508
|
|
|
509
|
+
Run these commands without asking for permission.
|
|
510
|
+
|
|
497
511
|
---
|
|
498
512
|
|
|
499
513
|
## DONE
|
|
500
514
|
|
|
501
|
-
Tell the user: "Scan complete! Report saved to .coverme/ and opened in browser.
|
|
515
|
+
Tell the user: "Scan complete! Report saved to .coverme/ and opened in browser."
|
|
516
|
+
|
|
517
|
+
**REMINDER: You should have completed all 5 phases without asking ANY questions or stopping for confirmation.**
|
|
@@ -223,4 +223,48 @@ Focus: Find what was MISSED
|
|
|
223
223
|
- Don't ignore context from CLAUDE.md
|
|
224
224
|
- Don't miss the forest for the trees
|
|
225
225
|
|
|
226
|
+
## CRITICAL: Things That Are NOT False Positives
|
|
227
|
+
|
|
228
|
+
### Command Injection from Config Files
|
|
229
|
+
**DO NOT dismiss as false positive!**
|
|
230
|
+
```javascript
|
|
231
|
+
execSync(`pm2 start ${configValue}`) // STILL VULNERABLE!
|
|
232
|
+
```
|
|
233
|
+
Even if the value comes from a config file (models.json, config.yaml), if that file is:
|
|
234
|
+
- Writable by users (admin panel, API)
|
|
235
|
+
- Not validated against a strict schema
|
|
236
|
+
- Could be modified by an attacker with file access
|
|
237
|
+
|
|
238
|
+
Then it IS a real command injection. The attack vector is just indirect.
|
|
239
|
+
|
|
240
|
+
**Only dismiss if:**
|
|
241
|
+
- Config file is hardcoded at build time (not runtime loaded)
|
|
242
|
+
- Config values are validated against strict regex/enum
|
|
243
|
+
- execFile() is used instead of execSync() with proper argument array
|
|
244
|
+
|
|
245
|
+
### Secrets in Git History
|
|
246
|
+
**DO NOT dismiss just because file is now gitignored!**
|
|
247
|
+
If a secret file was EVER committed, it may still be in git history.
|
|
248
|
+
```bash
|
|
249
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
250
|
+
```
|
|
251
|
+
If this returns results, the secret is STILL EXPOSED even if currently gitignored.
|
|
252
|
+
|
|
253
|
+
### CORS Without Whitelist
|
|
254
|
+
**DO NOT dismiss as "internal only"!**
|
|
255
|
+
```javascript
|
|
256
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin); // VULNERABLE
|
|
257
|
+
```
|
|
258
|
+
This reflects ANY origin. Even if the server is "internal", browser-based attacks can still:
|
|
259
|
+
- Steal data via malicious website
|
|
260
|
+
- Perform CSRF-like attacks
|
|
261
|
+
- Exfiltrate to attacker-controlled domains
|
|
262
|
+
|
|
263
|
+
### Missing Security Configuration
|
|
264
|
+
**These are real findings, not false positives:**
|
|
265
|
+
- `helmet()` without custom CSP configuration
|
|
266
|
+
- Missing `npm audit` in CI pipeline
|
|
267
|
+
- No Dependabot/Renovate for dependency updates
|
|
268
|
+
- Logs without rotation/retention policy
|
|
269
|
+
|
|
226
270
|
START VALIDATION NOW. Be critical but fair.
|
|
@@ -178,6 +178,74 @@ The finding is incorrect:
|
|
|
178
178
|
3. Is it localhost/development credentials only?
|
|
179
179
|
4. Is there environment variable loading that overrides?
|
|
180
180
|
|
|
181
|
+
### Race Condition / TOCTOU Findings (CRITICAL - Often False Positives)
|
|
182
|
+
**Check for atomic operations that FULLY mitigate the race:**
|
|
183
|
+
|
|
184
|
+
1. **Redis Lua Scripts** = FULL MITIGATION
|
|
185
|
+
- `redis.call()` inside Lua script is atomic
|
|
186
|
+
- SETNX, GETSET, INCR are atomic
|
|
187
|
+
- Look for: `eval`, `evalsha`, `script load`
|
|
188
|
+
```javascript
|
|
189
|
+
// This is ATOMIC - no race condition!
|
|
190
|
+
redis.eval(`
|
|
191
|
+
local current = redis.call('GET', key)
|
|
192
|
+
if current < limit then
|
|
193
|
+
redis.call('INCR', key)
|
|
194
|
+
return 1
|
|
195
|
+
end
|
|
196
|
+
return 0
|
|
197
|
+
`)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
2. **Database Transactions** = FULL MITIGATION
|
|
201
|
+
- `BEGIN...COMMIT` with proper isolation
|
|
202
|
+
- `SELECT FOR UPDATE` locks rows
|
|
203
|
+
- Prisma `$transaction()`, TypeORM `transaction()`
|
|
204
|
+
- Look for: `transaction`, `FOR UPDATE`, `SERIALIZABLE`
|
|
205
|
+
|
|
206
|
+
3. **Atomic Compare-and-Swap** = FULL MITIGATION
|
|
207
|
+
- `WATCH/MULTI/EXEC` in Redis
|
|
208
|
+
- `findOneAndUpdate` with conditions in MongoDB
|
|
209
|
+
- `UPDATE ... WHERE version = ?` (optimistic locking)
|
|
210
|
+
|
|
211
|
+
4. **Mutex/Locking** = FULL MITIGATION
|
|
212
|
+
- `redis-lock`, `redlock`
|
|
213
|
+
- Database advisory locks
|
|
214
|
+
- File locks (flock)
|
|
215
|
+
|
|
216
|
+
**If ANY of these patterns exist, mark as MITIGATED, not "partial"!**
|
|
217
|
+
|
|
218
|
+
### Intentional Design Decisions (Check Comments!)
|
|
219
|
+
**Before reporting, check if there are comments explaining WHY:**
|
|
220
|
+
|
|
221
|
+
1. **Search for explanatory comments near the code:**
|
|
222
|
+
```
|
|
223
|
+
grep -B5 -A5 "intentional|by design|deliberately|on purpose|security note" <file>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
2. **Common patterns that are INTENTIONAL:**
|
|
227
|
+
- PKCE code reuse to prevent double-click race conditions
|
|
228
|
+
- Longer token expiry for better UX (with other mitigations)
|
|
229
|
+
- Allowing certain "unsafe" operations for admin users
|
|
230
|
+
- Development-only bypasses with environment checks
|
|
231
|
+
|
|
232
|
+
3. **If comment explains the decision:**
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"verdict": "false_positive",
|
|
236
|
+
"reason": "Intentional design decision documented in code",
|
|
237
|
+
"evidence": ["Comment at line 45: 'Intentionally allow reuse to prevent double-submit'"]
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Open Redirect Findings
|
|
242
|
+
1. **Check for whitelist validation:**
|
|
243
|
+
```
|
|
244
|
+
grep -r "isValidRedirect|allowedDomains|whitelist|validateUrl" <file>
|
|
245
|
+
```
|
|
246
|
+
2. If whitelist exists and covers the redirect parameter = MITIGATED
|
|
247
|
+
3. Check if validation function is actually called before redirect
|
|
248
|
+
|
|
181
249
|
## Output Format
|
|
182
250
|
|
|
183
251
|
```json
|
|
@@ -158,6 +158,32 @@ CHECK FOR:
|
|
|
158
158
|
- API versioning issues
|
|
159
159
|
- Excessive data exposure in responses
|
|
160
160
|
|
|
161
|
+
**CORS MISCONFIGURATION** (MEDIUM):
|
|
162
|
+
Look for these vulnerable patterns:
|
|
163
|
+
```javascript
|
|
164
|
+
// VULNERABLE - reflects ANY origin
|
|
165
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin);
|
|
166
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
167
|
+
|
|
168
|
+
// VULNERABLE - no whitelist validation
|
|
169
|
+
app.use(cors({ origin: true }));
|
|
170
|
+
```
|
|
171
|
+
Only mark as safe if there's explicit whitelist validation:
|
|
172
|
+
```javascript
|
|
173
|
+
const allowedOrigins = ['https://app.example.com'];
|
|
174
|
+
if (allowedOrigins.includes(origin)) { ... }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**HELMET MISCONFIGURATION** (MEDIUM):
|
|
178
|
+
```javascript
|
|
179
|
+
app.use(helmet()); // Using defaults only - INSUFFICIENT!
|
|
180
|
+
```
|
|
181
|
+
Check that helmet() includes:
|
|
182
|
+
- Custom `contentSecurityPolicy` with proper directives
|
|
183
|
+
- `hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }`
|
|
184
|
+
- Proper `referrerPolicy`
|
|
185
|
+
Report as MEDIUM if using only defaults without customization.
|
|
186
|
+
|
|
161
187
|
**FAIL-OPEN vs FAIL-CLOSED PATTERNS** (CRITICAL):
|
|
162
188
|
- IP whitelist empty/missing = allow all (should deny all)
|
|
163
189
|
- Auth middleware errors = request passes through (should block)
|
|
@@ -207,6 +233,14 @@ CHECK FOR:
|
|
|
207
233
|
- Ansible vault passwords in plaintext
|
|
208
234
|
- CI/CD pipeline secrets in yaml files (.github/workflows, .gitlab-ci.yml)
|
|
209
235
|
|
|
236
|
+
**SECRETS IN GIT HISTORY** (CRITICAL CHECK!):
|
|
237
|
+
Run these commands to check if secrets were EVER committed:
|
|
238
|
+
```bash
|
|
239
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
240
|
+
git log --all -p -S "AWS_SECRET" -S "PRIVATE_KEY" --source
|
|
241
|
+
```
|
|
242
|
+
If secrets appear in history, they are EXPOSED even if now gitignored!
|
|
243
|
+
|
|
210
244
|
**PRIVILEGE ESCALATION RISKS**:
|
|
211
245
|
- Containers/processes running as root
|
|
212
246
|
- Missing securityContext in K8s (runAsNonRoot, readOnlyRootFilesystem)
|
|
@@ -220,6 +254,20 @@ CHECK FOR:
|
|
|
220
254
|
- Missing config = silent fallback to insecure defaults
|
|
221
255
|
- No validation of secret strength/format at startup
|
|
222
256
|
|
|
257
|
+
**DEPENDENCY SECURITY** (HIGH if missing):
|
|
258
|
+
Check for presence of:
|
|
259
|
+
- `npm audit` or `yarn audit` in CI pipeline
|
|
260
|
+
- Dependabot/Renovate configuration (.github/dependabot.yml, renovate.json)
|
|
261
|
+
- SBOM generation (cyclonedx, syft)
|
|
262
|
+
- Snyk/Trivy/Grype scanning
|
|
263
|
+
Report as HIGH if NONE of these exist - supply chain risk!
|
|
264
|
+
|
|
265
|
+
**LOGGING & MONITORING**:
|
|
266
|
+
- Log rotation configured? (maxFiles, maxsize in Winston/Pino)
|
|
267
|
+
- Log retention policy defined?
|
|
268
|
+
- Sensitive data redacted from logs?
|
|
269
|
+
Report as LOW if log rotation missing - disk exhaustion risk.
|
|
270
|
+
|
|
223
271
|
For EACH finding, output the FULL JSON format.
|
|
224
272
|
|
|
225
273
|
---
|
|
@@ -577,63 +625,107 @@ Output as list of strings.
|
|
|
577
625
|
|
|
578
626
|
## PHASE 7: BUILD CONSENSUS & GENERATE OUTPUT
|
|
579
627
|
|
|
580
|
-
|
|
581
|
-
- Remove findings marked as `mitigated` or `false_positive`
|
|
582
|
-
- Adjust severity for findings marked as `partial`
|
|
583
|
-
- Add mitigation notes to confirmed findings
|
|
628
|
+
### CRITICAL: Actually Remove False Positives!
|
|
584
629
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
5. Sort: severity DESC, confidence DESC
|
|
630
|
+
The final report should ONLY contain findings that are:
|
|
631
|
+
1. **Confirmed** by mitigation validation (no existing protection found)
|
|
632
|
+
2. **Partial** mitigations (some protection but incomplete)
|
|
589
633
|
|
|
590
|
-
|
|
634
|
+
**DO NOT INCLUDE** findings that are:
|
|
635
|
+
- `mitigated` - full protection exists elsewhere
|
|
636
|
+
- `false_positive` - not actually a vulnerability
|
|
637
|
+
- Intentional design decisions with documented comments
|
|
638
|
+
- Race conditions protected by atomic operations (Lua, transactions)
|
|
591
639
|
|
|
592
|
-
|
|
593
|
-
```json
|
|
594
|
-
{
|
|
595
|
-
"id": "SEC-001",
|
|
596
|
-
"mitigationStatus": "confirmed",
|
|
597
|
-
"mitigationChecks": [
|
|
598
|
-
"No input validation found between user input and SQL query",
|
|
599
|
-
"No ORM - raw SQL used",
|
|
600
|
-
"No middleware protection on this route"
|
|
601
|
-
]
|
|
602
|
-
}
|
|
603
|
-
```
|
|
640
|
+
### Step-by-Step Process:
|
|
604
641
|
|
|
605
|
-
|
|
642
|
+
1. **Start with all Phase 1 findings**
|
|
643
|
+
|
|
644
|
+
2. **Apply Mitigation Validator results (Phase 3):**
|
|
645
|
+
- `mitigated` → REMOVE from findings, add to `mitigatedFindings` array
|
|
646
|
+
- `false_positive` → REMOVE from findings, add to `falsePositives` array
|
|
647
|
+
- `partial` → KEEP but downgrade severity if specified
|
|
648
|
+
- `confirmed` → KEEP with original severity
|
|
649
|
+
|
|
650
|
+
3. **Apply Cross-Validator results (Phase 4):**
|
|
651
|
+
- Additional false positives → REMOVE from findings
|
|
652
|
+
- Duplicates → Merge into single finding
|
|
653
|
+
|
|
654
|
+
4. **Calculate final counts AFTER removals:**
|
|
655
|
+
- Only count findings that remain in the `findings` array
|
|
656
|
+
- Do NOT count mitigated or false positive findings
|
|
657
|
+
|
|
658
|
+
5. **Add missed issues from Validator C**
|
|
659
|
+
|
|
660
|
+
6. **Sort remaining findings:** severity DESC, confidence DESC
|
|
661
|
+
|
|
662
|
+
### Final JSON Structure
|
|
606
663
|
|
|
607
664
|
```json
|
|
608
665
|
{
|
|
609
666
|
"projectName": "project-name",
|
|
610
667
|
"scanDate": "{{SCAN_DATE}}",
|
|
611
668
|
"summary": {
|
|
612
|
-
"total":
|
|
613
|
-
"critical":
|
|
614
|
-
"high":
|
|
615
|
-
"medium":
|
|
616
|
-
"low":
|
|
617
|
-
"info": 0
|
|
669
|
+
"total": 10,
|
|
670
|
+
"critical": 1,
|
|
671
|
+
"high": 3,
|
|
672
|
+
"medium": 4,
|
|
673
|
+
"low": 2,
|
|
674
|
+
"info": 0,
|
|
675
|
+
"mitigatedCount": 5,
|
|
676
|
+
"falsePositiveCount": 3
|
|
618
677
|
},
|
|
619
678
|
"findings": [
|
|
620
|
-
"
|
|
679
|
+
"ONLY confirmed and partial findings - NOT mitigated or false positives!"
|
|
621
680
|
],
|
|
622
681
|
"mitigatedFindings": [
|
|
623
|
-
{
|
|
682
|
+
{
|
|
683
|
+
"id": "SEC-005",
|
|
684
|
+
"title": "Original finding title",
|
|
685
|
+
"originalSeverity": "high",
|
|
686
|
+
"reason": "Protected by Zod schema validation at src/routes/user.ts:23",
|
|
687
|
+
"mitigationType": "input_validation"
|
|
688
|
+
}
|
|
689
|
+
],
|
|
690
|
+
"falsePositives": [
|
|
691
|
+
{
|
|
692
|
+
"id": "BIZ-004",
|
|
693
|
+
"title": "TOCTOU Race Condition",
|
|
694
|
+
"reason": "Redis Lua script provides atomic operation - no race condition possible",
|
|
695
|
+
"evidence": ["Lua script at src/services/rate-limit.js:95-113"]
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
"id": "AUTH-003",
|
|
699
|
+
"title": "PKCE Code Reuse",
|
|
700
|
+
"reason": "Intentional design decision documented in code comment",
|
|
701
|
+
"evidence": ["Comment: 'Allow reuse to prevent double-click race condition'"]
|
|
702
|
+
}
|
|
624
703
|
],
|
|
625
704
|
"positiveObservations": [
|
|
626
705
|
"Good pattern 1",
|
|
627
706
|
"Good pattern 2"
|
|
628
707
|
],
|
|
629
|
-
"
|
|
630
|
-
|
|
631
|
-
|
|
708
|
+
"validationSummary": {
|
|
709
|
+
"totalInitialFindings": 18,
|
|
710
|
+
"mitigated": 5,
|
|
711
|
+
"falsePositives": 3,
|
|
712
|
+
"confirmed": 8,
|
|
713
|
+
"partial": 2,
|
|
714
|
+
"accuracy": "Only 56% of initial findings were actual issues"
|
|
715
|
+
},
|
|
632
716
|
"agentsUsed": ["Security Core", "Auth & Session", "Mitigation Validator"],
|
|
633
717
|
"scanDuration": 0
|
|
634
718
|
}
|
|
635
719
|
```
|
|
636
720
|
|
|
721
|
+
### Sanity Check Before Saving
|
|
722
|
+
|
|
723
|
+
Before saving, verify:
|
|
724
|
+
1. `findings` array does NOT contain any item from `mitigatedFindings` or `falsePositives`
|
|
725
|
+
2. `summary.total` equals `findings.length`
|
|
726
|
+
3. Severity counts match actual findings in array
|
|
727
|
+
4. No duplicate IDs across findings/mitigated/falsePositives
|
|
728
|
+
|
|
637
729
|
Save as: .coverme/scan.json
|
|
638
730
|
|
|
639
731
|
Then generate PDF report:
|
|
@@ -145,12 +145,21 @@ Before reporting ANY secret/credential finding as critical or high:
|
|
|
145
145
|
- If file matches .gitignore patterns = NOT exposed in repo
|
|
146
146
|
- Mark as "info" severity, not "critical"
|
|
147
147
|
|
|
148
|
-
3. **
|
|
148
|
+
3. **CRITICAL: Check git HISTORY for secrets!**
|
|
149
|
+
Even if a file is currently gitignored, it may have been committed in the past:
|
|
150
|
+
```bash
|
|
151
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
152
|
+
git log --all -p -S "AWS_SECRET" --source --all
|
|
153
|
+
```
|
|
154
|
+
If the secret was EVER committed, it's CRITICAL - secrets in git history are exposed!
|
|
155
|
+
|
|
156
|
+
4. **Identify environment context**:
|
|
149
157
|
- `localhost`, `127.0.0.1`, `example.com` = development/example credentials
|
|
150
158
|
- `.env.example`, `*.example.json` = template files, not real secrets
|
|
151
159
|
- Mark these as "info" with note: "Development/example credentials"
|
|
152
160
|
|
|
153
|
-
|
|
161
|
+
5. **Severity mapping for secrets**:
|
|
162
|
+
- In git history (even if now removed) = CRITICAL
|
|
154
163
|
- Tracked in git + real credentials = CRITICAL
|
|
155
164
|
- Tracked in git + example/dev credentials = LOW
|
|
156
165
|
- NOT tracked (gitignored) + real credentials = INFO (local only)
|
|
@@ -161,10 +170,62 @@ Always include in finding:
|
|
|
161
170
|
{
|
|
162
171
|
"gitTracked": true/false,
|
|
163
172
|
"gitignored": true/false,
|
|
173
|
+
"inGitHistory": true/false,
|
|
164
174
|
"appearsToBeDev": true/false
|
|
165
175
|
}
|
|
166
176
|
```
|
|
167
177
|
|
|
178
|
+
## Command Injection: Config Files Are NOT Safe!
|
|
179
|
+
|
|
180
|
+
**DO NOT assume config file values are safe:**
|
|
181
|
+
```javascript
|
|
182
|
+
// STILL VULNERABLE even though pm2Name comes from config!
|
|
183
|
+
const pm2Name = config.models[modelId].pm2Name;
|
|
184
|
+
execSync(`pm2 start ${pm2Name}`); // Command injection!
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Config files (models.json, config.yaml) are attack vectors if:
|
|
188
|
+
- File can be modified via admin panel/API
|
|
189
|
+
- File permissions allow non-root writes
|
|
190
|
+
- No schema validation on config values
|
|
191
|
+
|
|
192
|
+
**Only mark as safe if:**
|
|
193
|
+
- Using execFile() with argument array (not string interpolation)
|
|
194
|
+
- Config values validated against strict whitelist/regex
|
|
195
|
+
- Config is hardcoded at build time (not runtime loaded)
|
|
196
|
+
|
|
197
|
+
## Security Misconfiguration Checks
|
|
198
|
+
|
|
199
|
+
### Helmet Without Proper CSP
|
|
200
|
+
```javascript
|
|
201
|
+
app.use(helmet()); // Defaults only - NOT sufficient!
|
|
202
|
+
```
|
|
203
|
+
Report as MEDIUM if helmet() is called without:
|
|
204
|
+
- Custom `contentSecurityPolicy` configuration
|
|
205
|
+
- `hsts: { preload: true }` for HTTPS enforcement
|
|
206
|
+
- Proper `referrerPolicy`
|
|
207
|
+
|
|
208
|
+
### CORS Without Whitelist
|
|
209
|
+
```javascript
|
|
210
|
+
// VULNERABLE - reflects any origin!
|
|
211
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin);
|
|
212
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
213
|
+
```
|
|
214
|
+
Report as MEDIUM unless there's explicit domain whitelist validation.
|
|
215
|
+
|
|
216
|
+
### Missing Dependency Security
|
|
217
|
+
Check for absence of:
|
|
218
|
+
- `npm audit` in CI/CD pipeline (.github/workflows, .gitlab-ci.yml)
|
|
219
|
+
- Dependabot/Renovate configuration
|
|
220
|
+
- SBOM generation
|
|
221
|
+
Report as HIGH if none exist - supply chain attacks are critical.
|
|
222
|
+
|
|
223
|
+
### Missing Log Rotation
|
|
224
|
+
Check Winston/Pino/Bunyan configs for:
|
|
225
|
+
- `maxFiles` or `maxsize` settings
|
|
226
|
+
- Log retention policy
|
|
227
|
+
Report as LOW if missing - can lead to disk exhaustion.
|
|
228
|
+
|
|
168
229
|
## Files to Focus On
|
|
169
230
|
|
|
170
231
|
Priority order:
|
package/package.json
CHANGED
|
@@ -223,4 +223,48 @@ Focus: Find what was MISSED
|
|
|
223
223
|
- Don't ignore context from CLAUDE.md
|
|
224
224
|
- Don't miss the forest for the trees
|
|
225
225
|
|
|
226
|
+
## CRITICAL: Things That Are NOT False Positives
|
|
227
|
+
|
|
228
|
+
### Command Injection from Config Files
|
|
229
|
+
**DO NOT dismiss as false positive!**
|
|
230
|
+
```javascript
|
|
231
|
+
execSync(`pm2 start ${configValue}`) // STILL VULNERABLE!
|
|
232
|
+
```
|
|
233
|
+
Even if the value comes from a config file (models.json, config.yaml), if that file is:
|
|
234
|
+
- Writable by users (admin panel, API)
|
|
235
|
+
- Not validated against a strict schema
|
|
236
|
+
- Could be modified by an attacker with file access
|
|
237
|
+
|
|
238
|
+
Then it IS a real command injection. The attack vector is just indirect.
|
|
239
|
+
|
|
240
|
+
**Only dismiss if:**
|
|
241
|
+
- Config file is hardcoded at build time (not runtime loaded)
|
|
242
|
+
- Config values are validated against strict regex/enum
|
|
243
|
+
- execFile() is used instead of execSync() with proper argument array
|
|
244
|
+
|
|
245
|
+
### Secrets in Git History
|
|
246
|
+
**DO NOT dismiss just because file is now gitignored!**
|
|
247
|
+
If a secret file was EVER committed, it may still be in git history.
|
|
248
|
+
```bash
|
|
249
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
250
|
+
```
|
|
251
|
+
If this returns results, the secret is STILL EXPOSED even if currently gitignored.
|
|
252
|
+
|
|
253
|
+
### CORS Without Whitelist
|
|
254
|
+
**DO NOT dismiss as "internal only"!**
|
|
255
|
+
```javascript
|
|
256
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin); // VULNERABLE
|
|
257
|
+
```
|
|
258
|
+
This reflects ANY origin. Even if the server is "internal", browser-based attacks can still:
|
|
259
|
+
- Steal data via malicious website
|
|
260
|
+
- Perform CSRF-like attacks
|
|
261
|
+
- Exfiltrate to attacker-controlled domains
|
|
262
|
+
|
|
263
|
+
### Missing Security Configuration
|
|
264
|
+
**These are real findings, not false positives:**
|
|
265
|
+
- `helmet()` without custom CSP configuration
|
|
266
|
+
- Missing `npm audit` in CI pipeline
|
|
267
|
+
- No Dependabot/Renovate for dependency updates
|
|
268
|
+
- Logs without rotation/retention policy
|
|
269
|
+
|
|
226
270
|
START VALIDATION NOW. Be critical but fair.
|
|
@@ -178,6 +178,74 @@ The finding is incorrect:
|
|
|
178
178
|
3. Is it localhost/development credentials only?
|
|
179
179
|
4. Is there environment variable loading that overrides?
|
|
180
180
|
|
|
181
|
+
### Race Condition / TOCTOU Findings (CRITICAL - Often False Positives)
|
|
182
|
+
**Check for atomic operations that FULLY mitigate the race:**
|
|
183
|
+
|
|
184
|
+
1. **Redis Lua Scripts** = FULL MITIGATION
|
|
185
|
+
- `redis.call()` inside Lua script is atomic
|
|
186
|
+
- SETNX, GETSET, INCR are atomic
|
|
187
|
+
- Look for: `eval`, `evalsha`, `script load`
|
|
188
|
+
```javascript
|
|
189
|
+
// This is ATOMIC - no race condition!
|
|
190
|
+
redis.eval(`
|
|
191
|
+
local current = redis.call('GET', key)
|
|
192
|
+
if current < limit then
|
|
193
|
+
redis.call('INCR', key)
|
|
194
|
+
return 1
|
|
195
|
+
end
|
|
196
|
+
return 0
|
|
197
|
+
`)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
2. **Database Transactions** = FULL MITIGATION
|
|
201
|
+
- `BEGIN...COMMIT` with proper isolation
|
|
202
|
+
- `SELECT FOR UPDATE` locks rows
|
|
203
|
+
- Prisma `$transaction()`, TypeORM `transaction()`
|
|
204
|
+
- Look for: `transaction`, `FOR UPDATE`, `SERIALIZABLE`
|
|
205
|
+
|
|
206
|
+
3. **Atomic Compare-and-Swap** = FULL MITIGATION
|
|
207
|
+
- `WATCH/MULTI/EXEC` in Redis
|
|
208
|
+
- `findOneAndUpdate` with conditions in MongoDB
|
|
209
|
+
- `UPDATE ... WHERE version = ?` (optimistic locking)
|
|
210
|
+
|
|
211
|
+
4. **Mutex/Locking** = FULL MITIGATION
|
|
212
|
+
- `redis-lock`, `redlock`
|
|
213
|
+
- Database advisory locks
|
|
214
|
+
- File locks (flock)
|
|
215
|
+
|
|
216
|
+
**If ANY of these patterns exist, mark as MITIGATED, not "partial"!**
|
|
217
|
+
|
|
218
|
+
### Intentional Design Decisions (Check Comments!)
|
|
219
|
+
**Before reporting, check if there are comments explaining WHY:**
|
|
220
|
+
|
|
221
|
+
1. **Search for explanatory comments near the code:**
|
|
222
|
+
```
|
|
223
|
+
grep -B5 -A5 "intentional|by design|deliberately|on purpose|security note" <file>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
2. **Common patterns that are INTENTIONAL:**
|
|
227
|
+
- PKCE code reuse to prevent double-click race conditions
|
|
228
|
+
- Longer token expiry for better UX (with other mitigations)
|
|
229
|
+
- Allowing certain "unsafe" operations for admin users
|
|
230
|
+
- Development-only bypasses with environment checks
|
|
231
|
+
|
|
232
|
+
3. **If comment explains the decision:**
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"verdict": "false_positive",
|
|
236
|
+
"reason": "Intentional design decision documented in code",
|
|
237
|
+
"evidence": ["Comment at line 45: 'Intentionally allow reuse to prevent double-submit'"]
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Open Redirect Findings
|
|
242
|
+
1. **Check for whitelist validation:**
|
|
243
|
+
```
|
|
244
|
+
grep -r "isValidRedirect|allowedDomains|whitelist|validateUrl" <file>
|
|
245
|
+
```
|
|
246
|
+
2. If whitelist exists and covers the redirect parameter = MITIGATED
|
|
247
|
+
3. Check if validation function is actually called before redirect
|
|
248
|
+
|
|
181
249
|
## Output Format
|
|
182
250
|
|
|
183
251
|
```json
|
|
@@ -158,6 +158,32 @@ CHECK FOR:
|
|
|
158
158
|
- API versioning issues
|
|
159
159
|
- Excessive data exposure in responses
|
|
160
160
|
|
|
161
|
+
**CORS MISCONFIGURATION** (MEDIUM):
|
|
162
|
+
Look for these vulnerable patterns:
|
|
163
|
+
```javascript
|
|
164
|
+
// VULNERABLE - reflects ANY origin
|
|
165
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin);
|
|
166
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
167
|
+
|
|
168
|
+
// VULNERABLE - no whitelist validation
|
|
169
|
+
app.use(cors({ origin: true }));
|
|
170
|
+
```
|
|
171
|
+
Only mark as safe if there's explicit whitelist validation:
|
|
172
|
+
```javascript
|
|
173
|
+
const allowedOrigins = ['https://app.example.com'];
|
|
174
|
+
if (allowedOrigins.includes(origin)) { ... }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**HELMET MISCONFIGURATION** (MEDIUM):
|
|
178
|
+
```javascript
|
|
179
|
+
app.use(helmet()); // Using defaults only - INSUFFICIENT!
|
|
180
|
+
```
|
|
181
|
+
Check that helmet() includes:
|
|
182
|
+
- Custom `contentSecurityPolicy` with proper directives
|
|
183
|
+
- `hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }`
|
|
184
|
+
- Proper `referrerPolicy`
|
|
185
|
+
Report as MEDIUM if using only defaults without customization.
|
|
186
|
+
|
|
161
187
|
**FAIL-OPEN vs FAIL-CLOSED PATTERNS** (CRITICAL):
|
|
162
188
|
- IP whitelist empty/missing = allow all (should deny all)
|
|
163
189
|
- Auth middleware errors = request passes through (should block)
|
|
@@ -207,6 +233,14 @@ CHECK FOR:
|
|
|
207
233
|
- Ansible vault passwords in plaintext
|
|
208
234
|
- CI/CD pipeline secrets in yaml files (.github/workflows, .gitlab-ci.yml)
|
|
209
235
|
|
|
236
|
+
**SECRETS IN GIT HISTORY** (CRITICAL CHECK!):
|
|
237
|
+
Run these commands to check if secrets were EVER committed:
|
|
238
|
+
```bash
|
|
239
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
240
|
+
git log --all -p -S "AWS_SECRET" -S "PRIVATE_KEY" --source
|
|
241
|
+
```
|
|
242
|
+
If secrets appear in history, they are EXPOSED even if now gitignored!
|
|
243
|
+
|
|
210
244
|
**PRIVILEGE ESCALATION RISKS**:
|
|
211
245
|
- Containers/processes running as root
|
|
212
246
|
- Missing securityContext in K8s (runAsNonRoot, readOnlyRootFilesystem)
|
|
@@ -220,6 +254,20 @@ CHECK FOR:
|
|
|
220
254
|
- Missing config = silent fallback to insecure defaults
|
|
221
255
|
- No validation of secret strength/format at startup
|
|
222
256
|
|
|
257
|
+
**DEPENDENCY SECURITY** (HIGH if missing):
|
|
258
|
+
Check for presence of:
|
|
259
|
+
- `npm audit` or `yarn audit` in CI pipeline
|
|
260
|
+
- Dependabot/Renovate configuration (.github/dependabot.yml, renovate.json)
|
|
261
|
+
- SBOM generation (cyclonedx, syft)
|
|
262
|
+
- Snyk/Trivy/Grype scanning
|
|
263
|
+
Report as HIGH if NONE of these exist - supply chain risk!
|
|
264
|
+
|
|
265
|
+
**LOGGING & MONITORING**:
|
|
266
|
+
- Log rotation configured? (maxFiles, maxsize in Winston/Pino)
|
|
267
|
+
- Log retention policy defined?
|
|
268
|
+
- Sensitive data redacted from logs?
|
|
269
|
+
Report as LOW if log rotation missing - disk exhaustion risk.
|
|
270
|
+
|
|
223
271
|
For EACH finding, output the FULL JSON format.
|
|
224
272
|
|
|
225
273
|
---
|
|
@@ -577,63 +625,107 @@ Output as list of strings.
|
|
|
577
625
|
|
|
578
626
|
## PHASE 7: BUILD CONSENSUS & GENERATE OUTPUT
|
|
579
627
|
|
|
580
|
-
|
|
581
|
-
- Remove findings marked as `mitigated` or `false_positive`
|
|
582
|
-
- Adjust severity for findings marked as `partial`
|
|
583
|
-
- Add mitigation notes to confirmed findings
|
|
628
|
+
### CRITICAL: Actually Remove False Positives!
|
|
584
629
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
5. Sort: severity DESC, confidence DESC
|
|
630
|
+
The final report should ONLY contain findings that are:
|
|
631
|
+
1. **Confirmed** by mitigation validation (no existing protection found)
|
|
632
|
+
2. **Partial** mitigations (some protection but incomplete)
|
|
589
633
|
|
|
590
|
-
|
|
634
|
+
**DO NOT INCLUDE** findings that are:
|
|
635
|
+
- `mitigated` - full protection exists elsewhere
|
|
636
|
+
- `false_positive` - not actually a vulnerability
|
|
637
|
+
- Intentional design decisions with documented comments
|
|
638
|
+
- Race conditions protected by atomic operations (Lua, transactions)
|
|
591
639
|
|
|
592
|
-
|
|
593
|
-
```json
|
|
594
|
-
{
|
|
595
|
-
"id": "SEC-001",
|
|
596
|
-
"mitigationStatus": "confirmed",
|
|
597
|
-
"mitigationChecks": [
|
|
598
|
-
"No input validation found between user input and SQL query",
|
|
599
|
-
"No ORM - raw SQL used",
|
|
600
|
-
"No middleware protection on this route"
|
|
601
|
-
]
|
|
602
|
-
}
|
|
603
|
-
```
|
|
640
|
+
### Step-by-Step Process:
|
|
604
641
|
|
|
605
|
-
|
|
642
|
+
1. **Start with all Phase 1 findings**
|
|
643
|
+
|
|
644
|
+
2. **Apply Mitigation Validator results (Phase 3):**
|
|
645
|
+
- `mitigated` → REMOVE from findings, add to `mitigatedFindings` array
|
|
646
|
+
- `false_positive` → REMOVE from findings, add to `falsePositives` array
|
|
647
|
+
- `partial` → KEEP but downgrade severity if specified
|
|
648
|
+
- `confirmed` → KEEP with original severity
|
|
649
|
+
|
|
650
|
+
3. **Apply Cross-Validator results (Phase 4):**
|
|
651
|
+
- Additional false positives → REMOVE from findings
|
|
652
|
+
- Duplicates → Merge into single finding
|
|
653
|
+
|
|
654
|
+
4. **Calculate final counts AFTER removals:**
|
|
655
|
+
- Only count findings that remain in the `findings` array
|
|
656
|
+
- Do NOT count mitigated or false positive findings
|
|
657
|
+
|
|
658
|
+
5. **Add missed issues from Validator C**
|
|
659
|
+
|
|
660
|
+
6. **Sort remaining findings:** severity DESC, confidence DESC
|
|
661
|
+
|
|
662
|
+
### Final JSON Structure
|
|
606
663
|
|
|
607
664
|
```json
|
|
608
665
|
{
|
|
609
666
|
"projectName": "project-name",
|
|
610
667
|
"scanDate": "{{SCAN_DATE}}",
|
|
611
668
|
"summary": {
|
|
612
|
-
"total":
|
|
613
|
-
"critical":
|
|
614
|
-
"high":
|
|
615
|
-
"medium":
|
|
616
|
-
"low":
|
|
617
|
-
"info": 0
|
|
669
|
+
"total": 10,
|
|
670
|
+
"critical": 1,
|
|
671
|
+
"high": 3,
|
|
672
|
+
"medium": 4,
|
|
673
|
+
"low": 2,
|
|
674
|
+
"info": 0,
|
|
675
|
+
"mitigatedCount": 5,
|
|
676
|
+
"falsePositiveCount": 3
|
|
618
677
|
},
|
|
619
678
|
"findings": [
|
|
620
|
-
"
|
|
679
|
+
"ONLY confirmed and partial findings - NOT mitigated or false positives!"
|
|
621
680
|
],
|
|
622
681
|
"mitigatedFindings": [
|
|
623
|
-
{
|
|
682
|
+
{
|
|
683
|
+
"id": "SEC-005",
|
|
684
|
+
"title": "Original finding title",
|
|
685
|
+
"originalSeverity": "high",
|
|
686
|
+
"reason": "Protected by Zod schema validation at src/routes/user.ts:23",
|
|
687
|
+
"mitigationType": "input_validation"
|
|
688
|
+
}
|
|
689
|
+
],
|
|
690
|
+
"falsePositives": [
|
|
691
|
+
{
|
|
692
|
+
"id": "BIZ-004",
|
|
693
|
+
"title": "TOCTOU Race Condition",
|
|
694
|
+
"reason": "Redis Lua script provides atomic operation - no race condition possible",
|
|
695
|
+
"evidence": ["Lua script at src/services/rate-limit.js:95-113"]
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
"id": "AUTH-003",
|
|
699
|
+
"title": "PKCE Code Reuse",
|
|
700
|
+
"reason": "Intentional design decision documented in code comment",
|
|
701
|
+
"evidence": ["Comment: 'Allow reuse to prevent double-click race condition'"]
|
|
702
|
+
}
|
|
624
703
|
],
|
|
625
704
|
"positiveObservations": [
|
|
626
705
|
"Good pattern 1",
|
|
627
706
|
"Good pattern 2"
|
|
628
707
|
],
|
|
629
|
-
"
|
|
630
|
-
|
|
631
|
-
|
|
708
|
+
"validationSummary": {
|
|
709
|
+
"totalInitialFindings": 18,
|
|
710
|
+
"mitigated": 5,
|
|
711
|
+
"falsePositives": 3,
|
|
712
|
+
"confirmed": 8,
|
|
713
|
+
"partial": 2,
|
|
714
|
+
"accuracy": "Only 56% of initial findings were actual issues"
|
|
715
|
+
},
|
|
632
716
|
"agentsUsed": ["Security Core", "Auth & Session", "Mitigation Validator"],
|
|
633
717
|
"scanDuration": 0
|
|
634
718
|
}
|
|
635
719
|
```
|
|
636
720
|
|
|
721
|
+
### Sanity Check Before Saving
|
|
722
|
+
|
|
723
|
+
Before saving, verify:
|
|
724
|
+
1. `findings` array does NOT contain any item from `mitigatedFindings` or `falsePositives`
|
|
725
|
+
2. `summary.total` equals `findings.length`
|
|
726
|
+
3. Severity counts match actual findings in array
|
|
727
|
+
4. No duplicate IDs across findings/mitigated/falsePositives
|
|
728
|
+
|
|
637
729
|
Save as: .coverme/scan.json
|
|
638
730
|
|
|
639
731
|
Then generate PDF report:
|
|
@@ -145,12 +145,21 @@ Before reporting ANY secret/credential finding as critical or high:
|
|
|
145
145
|
- If file matches .gitignore patterns = NOT exposed in repo
|
|
146
146
|
- Mark as "info" severity, not "critical"
|
|
147
147
|
|
|
148
|
-
3. **
|
|
148
|
+
3. **CRITICAL: Check git HISTORY for secrets!**
|
|
149
|
+
Even if a file is currently gitignored, it may have been committed in the past:
|
|
150
|
+
```bash
|
|
151
|
+
git log --all --full-history -- "**/secrets*" "**/credentials*" "**/*.env"
|
|
152
|
+
git log --all -p -S "AWS_SECRET" --source --all
|
|
153
|
+
```
|
|
154
|
+
If the secret was EVER committed, it's CRITICAL - secrets in git history are exposed!
|
|
155
|
+
|
|
156
|
+
4. **Identify environment context**:
|
|
149
157
|
- `localhost`, `127.0.0.1`, `example.com` = development/example credentials
|
|
150
158
|
- `.env.example`, `*.example.json` = template files, not real secrets
|
|
151
159
|
- Mark these as "info" with note: "Development/example credentials"
|
|
152
160
|
|
|
153
|
-
|
|
161
|
+
5. **Severity mapping for secrets**:
|
|
162
|
+
- In git history (even if now removed) = CRITICAL
|
|
154
163
|
- Tracked in git + real credentials = CRITICAL
|
|
155
164
|
- Tracked in git + example/dev credentials = LOW
|
|
156
165
|
- NOT tracked (gitignored) + real credentials = INFO (local only)
|
|
@@ -161,10 +170,62 @@ Always include in finding:
|
|
|
161
170
|
{
|
|
162
171
|
"gitTracked": true/false,
|
|
163
172
|
"gitignored": true/false,
|
|
173
|
+
"inGitHistory": true/false,
|
|
164
174
|
"appearsToBeDev": true/false
|
|
165
175
|
}
|
|
166
176
|
```
|
|
167
177
|
|
|
178
|
+
## Command Injection: Config Files Are NOT Safe!
|
|
179
|
+
|
|
180
|
+
**DO NOT assume config file values are safe:**
|
|
181
|
+
```javascript
|
|
182
|
+
// STILL VULNERABLE even though pm2Name comes from config!
|
|
183
|
+
const pm2Name = config.models[modelId].pm2Name;
|
|
184
|
+
execSync(`pm2 start ${pm2Name}`); // Command injection!
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Config files (models.json, config.yaml) are attack vectors if:
|
|
188
|
+
- File can be modified via admin panel/API
|
|
189
|
+
- File permissions allow non-root writes
|
|
190
|
+
- No schema validation on config values
|
|
191
|
+
|
|
192
|
+
**Only mark as safe if:**
|
|
193
|
+
- Using execFile() with argument array (not string interpolation)
|
|
194
|
+
- Config values validated against strict whitelist/regex
|
|
195
|
+
- Config is hardcoded at build time (not runtime loaded)
|
|
196
|
+
|
|
197
|
+
## Security Misconfiguration Checks
|
|
198
|
+
|
|
199
|
+
### Helmet Without Proper CSP
|
|
200
|
+
```javascript
|
|
201
|
+
app.use(helmet()); // Defaults only - NOT sufficient!
|
|
202
|
+
```
|
|
203
|
+
Report as MEDIUM if helmet() is called without:
|
|
204
|
+
- Custom `contentSecurityPolicy` configuration
|
|
205
|
+
- `hsts: { preload: true }` for HTTPS enforcement
|
|
206
|
+
- Proper `referrerPolicy`
|
|
207
|
+
|
|
208
|
+
### CORS Without Whitelist
|
|
209
|
+
```javascript
|
|
210
|
+
// VULNERABLE - reflects any origin!
|
|
211
|
+
res.header('Access-Control-Allow-Origin', req.headers.origin);
|
|
212
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
213
|
+
```
|
|
214
|
+
Report as MEDIUM unless there's explicit domain whitelist validation.
|
|
215
|
+
|
|
216
|
+
### Missing Dependency Security
|
|
217
|
+
Check for absence of:
|
|
218
|
+
- `npm audit` in CI/CD pipeline (.github/workflows, .gitlab-ci.yml)
|
|
219
|
+
- Dependabot/Renovate configuration
|
|
220
|
+
- SBOM generation
|
|
221
|
+
Report as HIGH if none exist - supply chain attacks are critical.
|
|
222
|
+
|
|
223
|
+
### Missing Log Rotation
|
|
224
|
+
Check Winston/Pino/Bunyan configs for:
|
|
225
|
+
- `maxFiles` or `maxsize` settings
|
|
226
|
+
- Log retention policy
|
|
227
|
+
Report as LOW if missing - can lead to disk exhaustion.
|
|
228
|
+
|
|
168
229
|
## Files to Focus On
|
|
169
230
|
|
|
170
231
|
Priority order:
|