coverme-security-scanner 3.7.4 → 3.7.7
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/commands/coverme.md +276 -5
- package/dist/pdf/generator.js +60 -30
- package/package.json +3 -2
package/commands/coverme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Security Assessment
|
|
2
2
|
|
|
3
|
-
Run
|
|
3
|
+
Run an enterprise-grade security assessment combining 4 review types into a unified report. Execute automatically without asking questions.
|
|
4
4
|
|
|
5
5
|
## Step 1: Setup
|
|
6
6
|
```bash
|
|
@@ -8,12 +8,283 @@ mkdir -p .coverme
|
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
## Step 2: Run Security Scan
|
|
11
|
-
Use ONE Task agent with subagent_type="general-purpose" to perform
|
|
11
|
+
Use ONE Task agent with subagent_type="general-purpose" to perform a COMPREHENSIVE assessment:
|
|
12
12
|
|
|
13
|
-
prompt:
|
|
13
|
+
prompt: |
|
|
14
|
+
Perform a COMPREHENSIVE security assessment combining:
|
|
15
|
+
1. Security Review (STRIDE vulnerabilities)
|
|
16
|
+
2. Threat Model (DREAD scoring)
|
|
17
|
+
3. Code Quality Review (dead code, DRY violations)
|
|
18
|
+
4. Privacy Analysis (LINDDUN)
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
YOU MUST produce findings at the quality level of a professional security audit firm.
|
|
21
|
+
Target: 25-35 findings for a medium codebase, 40+ for large.
|
|
22
|
+
|
|
23
|
+
## PHASE 1: Codebase Metrics
|
|
24
|
+
|
|
25
|
+
First, gather precise metrics:
|
|
26
|
+
```
|
|
27
|
+
- Count total files: find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.tsx" -o -name "*.py" \) | wc -l
|
|
28
|
+
- Count lines: find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.tsx" -o -name "*.py" \) -exec wc -l {} + | tail -1
|
|
29
|
+
- List components (frontend/, backend/, api/, etc.)
|
|
30
|
+
- Map technology stack (React, Express, Postgres, etc.)
|
|
31
|
+
- Read .gitignore to know what's excluded
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## PHASE 2: Architecture Analysis
|
|
35
|
+
|
|
36
|
+
Map the system:
|
|
37
|
+
- Identify all components with their technology
|
|
38
|
+
- Define trust boundaries (TB1-Browser, TB2-BFF, TB3-API, TB4-Database, TB5-External)
|
|
39
|
+
- Document entry points with auth method
|
|
40
|
+
- List critical assets (keys, tokens, PII, credentials)
|
|
41
|
+
|
|
42
|
+
## PHASE 3: Security Vulnerabilities (STRIDE)
|
|
43
|
+
|
|
44
|
+
For EACH finding, you MUST include ALL of these:
|
|
45
|
+
|
|
46
|
+
**Required Fields:**
|
|
47
|
+
- **ID**: Use prefixes based on source:
|
|
48
|
+
- `T-FE-N`: Frontend threats
|
|
49
|
+
- `T-BE-N`: Backend threats
|
|
50
|
+
- `T-DB-N`: Database threats
|
|
51
|
+
- `T-ENC-N`: Encryption/enclave threats
|
|
52
|
+
- `CR-N`: Code review findings
|
|
53
|
+
- `SR-N`: Security review findings
|
|
54
|
+
- `QR-N`: Quality review items
|
|
55
|
+
|
|
56
|
+
- **Title**: Specific, not generic. Good: "Attestation Fallback Accepts Unverified Keys". Bad: "Security Issue"
|
|
57
|
+
|
|
58
|
+
- **File + Line Range**: Exact location with range. Example: `frontend/lib/e2e-encryption.ts:51-91`
|
|
59
|
+
|
|
60
|
+
- **Code References**: Include actual function/variable names inline:
|
|
61
|
+
- Good: "When unavailable, `fetchEnclaveInfo()` falls back and stores keys with `keysVerified: false`"
|
|
62
|
+
- Bad: "The function has a fallback"
|
|
63
|
+
|
|
64
|
+
- **DREAD Score**: Calculate each component (1-10):
|
|
65
|
+
- Damage: How bad if exploited?
|
|
66
|
+
- Reproducibility: How easy to reproduce?
|
|
67
|
+
- Exploitability: How easy to exploit?
|
|
68
|
+
- Affected Users: How many affected?
|
|
69
|
+
- Discoverability: How easy to find?
|
|
70
|
+
- Final: (D+R+E+A+D) / 5
|
|
71
|
+
|
|
72
|
+
- **Description**: 2-3 sentences explaining WHAT the code does wrong with inline `code references`
|
|
73
|
+
|
|
74
|
+
- **Recommendation**: Specific actionable fix, not generic advice
|
|
75
|
+
|
|
76
|
+
- **Status**: `open` | `partial` | `mitigated` | `accepted`
|
|
77
|
+
|
|
78
|
+
- **CWE**: Include CWE identifier (e.g., "CWE-295: Improper Certificate Validation")
|
|
79
|
+
|
|
80
|
+
- **relatedFindings**: Link to related findings when issues are connected
|
|
81
|
+
|
|
82
|
+
### Vulnerability Categories to Check:
|
|
83
|
+
|
|
84
|
+
**Silent Fallbacks (CRITICAL priority)**:
|
|
85
|
+
- try/catch blocks that swallow errors and continue execution
|
|
86
|
+
- `||` operators providing insecure defaults (e.g., `password || 'default123'`)
|
|
87
|
+
- Authentication that defaults to "allow" on error
|
|
88
|
+
- Crypto operations falling back to weaker algorithms
|
|
89
|
+
- if/else branches that silently skip security checks
|
|
90
|
+
- Graceful degradation that removes security controls
|
|
91
|
+
|
|
92
|
+
**Hardcoded Credentials**:
|
|
93
|
+
- Default passwords in code (not just comments)
|
|
94
|
+
- API keys in source (not from env vars)
|
|
95
|
+
- Fallback credentials like `amqp://user:pass123@localhost`
|
|
96
|
+
- Secrets in Helm values, Docker configs, CI files
|
|
97
|
+
|
|
98
|
+
**Input Validation Gaps**:
|
|
99
|
+
- User input used in SQL/shell/eval without sanitization
|
|
100
|
+
- Missing regex validation on IDs, names, paths
|
|
101
|
+
- Unbounded arrays that could cause DoS
|
|
102
|
+
- Path traversal vulnerabilities
|
|
103
|
+
|
|
104
|
+
**Error Information Leaks**:
|
|
105
|
+
- `error.message` or `error.stack` returned to clients
|
|
106
|
+
- Internal hostnames/IPs in error responses
|
|
107
|
+
- Database errors exposed to users
|
|
108
|
+
- Debug info in production responses
|
|
109
|
+
|
|
110
|
+
**Authentication/Authorization**:
|
|
111
|
+
- Missing auth on sensitive endpoints
|
|
112
|
+
- Token validation issues
|
|
113
|
+
- Session management flaws
|
|
114
|
+
- Privilege escalation paths
|
|
115
|
+
|
|
116
|
+
**Cryptographic Issues**:
|
|
117
|
+
- Weak algorithms (MD5, SHA1 for security)
|
|
118
|
+
- Hardcoded IVs or keys
|
|
119
|
+
- Missing signature verification
|
|
120
|
+
- Key management flaws
|
|
121
|
+
|
|
122
|
+
## PHASE 4: Code Quality Review
|
|
123
|
+
|
|
124
|
+
Find issues with LINE COUNTS:
|
|
125
|
+
|
|
126
|
+
**Dead Code (estimate removable lines)**:
|
|
127
|
+
- Functions defined but never called (trace call graph)
|
|
128
|
+
- Routes defined but not mounted
|
|
129
|
+
- Commented-out code blocks
|
|
130
|
+
- Example: "processMessage() - likely dead non-streaming path (~240 lines)"
|
|
131
|
+
|
|
132
|
+
**DRY Violations (estimate duplicated lines)**:
|
|
133
|
+
- Near-identical code blocks in multiple files
|
|
134
|
+
- Copy-pasted logic with minor variations
|
|
135
|
+
- Example: "CSV + Excel Handler process/prepareForStreaming nearly identical (~470 lines)"
|
|
136
|
+
|
|
137
|
+
**Deprecated Patterns**:
|
|
138
|
+
- Old API usage
|
|
139
|
+
- Legacy code paths
|
|
140
|
+
- Outdated dependencies
|
|
141
|
+
|
|
142
|
+
## PHASE 5: Privacy Analysis (LINDDUN)
|
|
143
|
+
|
|
144
|
+
Analyze for privacy threats:
|
|
145
|
+
- **L**inkability: Can actions be linked to users?
|
|
146
|
+
- **I**dentifiability: Can users be identified?
|
|
147
|
+
- **N**on-repudiation: Can users deny actions?
|
|
148
|
+
- **D**etectability: Can user presence be detected?
|
|
149
|
+
- **D**isclosure: Is data disclosed inappropriately?
|
|
150
|
+
- **U**nawareness: Are users unaware of data collection?
|
|
151
|
+
- **N**on-compliance: Are regulations violated?
|
|
152
|
+
|
|
153
|
+
## PHASE 6: Cross-Reference & Merge
|
|
154
|
+
|
|
155
|
+
**Merge duplicate findings**:
|
|
156
|
+
- When same issue found in multiple analyses, combine them
|
|
157
|
+
- Use format: "CR-02 / T-EKS-3: Hardcoded Tracker API Keys"
|
|
158
|
+
|
|
159
|
+
**Link related findings**:
|
|
160
|
+
- Add `relatedFindings: ["T-FE-2"]` when issues are connected
|
|
161
|
+
- Build attack chains showing how findings combine
|
|
162
|
+
|
|
163
|
+
## PHASE 7: Identify Resolved Issues
|
|
164
|
+
|
|
165
|
+
Look for issues that WERE present but are NOW fixed:
|
|
166
|
+
- Commented code showing old vulnerable patterns
|
|
167
|
+
- Security controls that are properly implemented
|
|
168
|
+
- Previous findings that have been addressed
|
|
169
|
+
|
|
170
|
+
Document these in `resolvedIssues` with:
|
|
171
|
+
- What was the original issue
|
|
172
|
+
- How it was resolved
|
|
173
|
+
- Current security status
|
|
174
|
+
|
|
175
|
+
## OUTPUT FORMAT
|
|
176
|
+
|
|
177
|
+
Create `.coverme/scan.json` with this EXACT structure:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"project": "<project-name>",
|
|
182
|
+
"date": "2026-02-19",
|
|
183
|
+
"branch": "main",
|
|
184
|
+
"scope": "<X> files, ~<Y> lines - Full platform assessment",
|
|
185
|
+
"components": ["Frontend (Next.js)", "Backend (Express)", "Database (Postgres)"],
|
|
186
|
+
"methodology": "STRIDE + DREAD-D + LINDDUN",
|
|
187
|
+
"reviewType": "Security + Threat Model + Code Quality + Privacy",
|
|
188
|
+
"author": "Claude Code",
|
|
189
|
+
|
|
190
|
+
"executiveSummary": "<3-4 paragraphs: 1) System overview 2) Security architecture 3) Key findings summary 4) Critical remaining issues>",
|
|
191
|
+
"overallRiskLevel": "low|medium|high|critical",
|
|
192
|
+
|
|
193
|
+
"summary": { "critical": 0, "high": 0, "medium": 0, "low": 0, "total": 0 },
|
|
194
|
+
|
|
195
|
+
"topPriorities": [
|
|
196
|
+
{ "finding": "T-FE-1", "severity": "high", "action": "Block attestation fallback" }
|
|
197
|
+
],
|
|
198
|
+
|
|
199
|
+
"architecture": {
|
|
200
|
+
"overview": "<1-2 sentences about system architecture>",
|
|
201
|
+
"components": [
|
|
202
|
+
{"name": "Frontend", "technology": "Next.js BFF", "description": "PKCE OAuth, session management"}
|
|
203
|
+
],
|
|
204
|
+
"trustBoundaries": [
|
|
205
|
+
{"id": "TB1", "boundary": "Browser", "trustLevel": "untrusted", "description": "User browser environment"},
|
|
206
|
+
{"id": "TB2", "boundary": "API Gateway", "trustLevel": "semi-trusted", "description": "Holds OAuth tokens"}
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
"findings": [
|
|
211
|
+
{
|
|
212
|
+
"id": "T-FE-1",
|
|
213
|
+
"title": "Attestation Fallback Accepts Unverified Keys",
|
|
214
|
+
"severity": "high",
|
|
215
|
+
"status": "open",
|
|
216
|
+
"file": "frontend/lib/e2e-encryption.ts",
|
|
217
|
+
"line": "51-91",
|
|
218
|
+
"dreadScore": 6.3,
|
|
219
|
+
"cwe": "CWE-295: Improper Certificate Validation",
|
|
220
|
+
"issue": "When the attestation bundle endpoint is unavailable, `fetchEnclaveInfo()` falls back to the legacy `/api/v1/enclave` endpoint and stores keys with `keysVerified: false`. The browser silently proceeds to encrypt messages with unverified public keys.",
|
|
221
|
+
"why": "This enables a man-in-the-middle attack by a compromised gateway. An attacker who controls the gateway can substitute their own keys and decrypt all messages.",
|
|
222
|
+
"fix": "Block the request or display a prominent security warning when attestation fails. Do not silently proceed with unverified keys. Consider: `if (!keysVerified) throw new AttestationError('Cannot proceed without verified keys')`",
|
|
223
|
+
"relatedFindings": ["T-FE-2", "CR-07"]
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
"threatModel": [
|
|
228
|
+
{"id": "T-FE-1", "severity": "high", "dread": 6.3, "status": "partial", "finding": "Attestation fallback accepts unverified keys"},
|
|
229
|
+
{"id": "T-ENC-2", "severity": "low", "dread": 2.0, "status": "mitigated", "finding": "Session key zeroization implemented"}
|
|
230
|
+
],
|
|
231
|
+
|
|
232
|
+
"qualityReview": {
|
|
233
|
+
"deadCode": [
|
|
234
|
+
{"type": "dead-code", "action": "DELETE", "file": "src/old-handler.js", "line": 1, "description": "processMessage() - likely dead non-streaming path (~240 lines)"}
|
|
235
|
+
],
|
|
236
|
+
"dryViolations": [
|
|
237
|
+
{"type": "dry-violation", "action": "MERGE", "file": "src/handlers/csv.js", "description": "CSV + Excel Handler process/prepareForStreaming nearly identical (~470 lines)", "relatedFiles": ["src/handlers/excel.js"]}
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
"positiveObservations": [
|
|
242
|
+
{"title": "Zero-Knowledge Architecture", "description": "API gateway genuinely never sees plaintext. Encrypted payloads flow through without decryption."},
|
|
243
|
+
{"title": "Atomic Credit Operations", "description": "Lua scripts for token burning prevent cross-pod race conditions."},
|
|
244
|
+
{"title": "Post-Quantum Cryptography", "description": "XWing (ML-KEM-768 + X25519) hybrid KEM with Ed25519 signing."}
|
|
245
|
+
],
|
|
246
|
+
|
|
247
|
+
"resolvedIssues": [
|
|
248
|
+
{"id": "RES-01", "title": "SQL Injection in DuckDB", "severity": "critical", "resolution": "Resolved: enable_external_access=false sandbox mode + SQL validation blocklist."},
|
|
249
|
+
{"id": "RES-02", "title": "Admin API Fail-Open", "severity": "high", "resolution": "Resolved: Binds to 127.0.0.1 by default. Fail-closed when no ADMIN_ALLOWED_IPS configured."}
|
|
250
|
+
],
|
|
251
|
+
|
|
252
|
+
"privacyAnalysis": [
|
|
253
|
+
{"category": "Linkability", "risk": "medium", "description": "User actions can be linked via hashed KRN in metrics", "mitigation": "SHA-256 hashing provides pseudonymization"},
|
|
254
|
+
{"category": "Identifiability", "risk": "low", "description": "KRN logged in some paths enables identification", "mitigation": "Replace with getUserHash() in all logs"}
|
|
255
|
+
],
|
|
256
|
+
|
|
257
|
+
"remediation": {
|
|
258
|
+
"p0": [{"action": "Remove hardcoded secrets from Helm values. Move to Secrets Manager. Rotate keys.", "finding": "CR-02", "owner": "DevOps"}],
|
|
259
|
+
"p1": [{"action": "Block attestation fallback or show prominent warning", "finding": "T-FE-1", "owner": "Frontend"}],
|
|
260
|
+
"p2": [{"action": "Extract Redis metrics to shared module (~160 lines)", "finding": "QR-16", "owner": "Backend"}]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## QUALITY REQUIREMENTS
|
|
266
|
+
|
|
267
|
+
**Minimum Thresholds:**
|
|
268
|
+
- 25+ findings for medium codebase (5k-50k lines)
|
|
269
|
+
- 40+ findings for large codebase (50k+ lines)
|
|
270
|
+
- Every HIGH/CRITICAL finding MUST have exact file:line
|
|
271
|
+
- Every finding MUST have DREAD score
|
|
272
|
+
- Every finding MUST have CWE identifier
|
|
273
|
+
- Include at least 5 positive observations
|
|
274
|
+
- Include code quality items with line counts
|
|
275
|
+
- Cross-reference related findings
|
|
276
|
+
- Include at least 3 resolved issues (if any exist)
|
|
277
|
+
- Include LINDDUN privacy analysis
|
|
278
|
+
|
|
279
|
+
**Quality Checks:**
|
|
280
|
+
- NO generic findings like "Consider adding validation"
|
|
281
|
+
- NO findings without file locations
|
|
282
|
+
- NO DREAD scores without calculation
|
|
283
|
+
- ALL code references must use actual function/variable names from the code
|
|
284
|
+
- ALL recommendations must be actionable, not vague
|
|
285
|
+
|
|
286
|
+
## Step 3: Wait for agent
|
|
287
|
+
Use AgentOutputTool to wait for completion.
|
|
17
288
|
|
|
18
289
|
## Step 4: Generate PDF
|
|
19
290
|
```bash
|
package/dist/pdf/generator.js
CHANGED
|
@@ -402,9 +402,7 @@ export class PDFGenerator {
|
|
|
402
402
|
this.y = this.doc.y + spacing.paragraph;
|
|
403
403
|
}
|
|
404
404
|
if (report.architecture?.components?.length) {
|
|
405
|
-
|
|
406
|
-
this.newPage();
|
|
407
|
-
this.y = spacing.page.top;
|
|
405
|
+
this.checkPageBreak(150);
|
|
408
406
|
this.subTitle('Components');
|
|
409
407
|
this.renderSimpleTable(['Component', 'Technology', 'Description'], report.architecture.components.map(c => [c.name, c.technology, c.description]), [100, 150, 245]);
|
|
410
408
|
}
|
|
@@ -616,46 +614,78 @@ export class PDFGenerator {
|
|
|
616
614
|
const boxPadding = 12;
|
|
617
615
|
const boxInnerWidth = layout.content.width - (boxPadding * 2);
|
|
618
616
|
const style = colors.severity[finding.severity];
|
|
619
|
-
// ID in
|
|
620
|
-
|
|
621
|
-
const titleHeight = this.doc
|
|
617
|
+
// Finding ID in accent color
|
|
618
|
+
this.doc
|
|
622
619
|
.font(fonts.weights.bold)
|
|
623
620
|
.fontSize(fonts.sizes.body)
|
|
624
|
-
.
|
|
621
|
+
.fillColor(colors.accent)
|
|
622
|
+
.text(finding.id, spacing.page.margin, this.y);
|
|
623
|
+
this.y += 18;
|
|
624
|
+
// Title - large and prominent
|
|
625
625
|
this.doc
|
|
626
626
|
.font(fonts.weights.bold)
|
|
627
|
-
.fontSize(fonts.sizes.
|
|
628
|
-
.fillColor(style.text)
|
|
629
|
-
.text(`[${finding.id}]`, spacing.page.margin, this.y, { continued: true })
|
|
627
|
+
.fontSize(fonts.sizes.h3)
|
|
630
628
|
.fillColor(colors.text.primary)
|
|
631
|
-
.text(
|
|
632
|
-
this.y = this.doc.y +
|
|
633
|
-
//
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
629
|
+
.text(finding.title, spacing.page.margin, this.y, { width: layout.content.width });
|
|
630
|
+
this.y = this.doc.y + 6;
|
|
631
|
+
// Severity badge (small pill)
|
|
632
|
+
const badgeHeight = 16;
|
|
633
|
+
const severityText = finding.severity.toUpperCase();
|
|
634
|
+
const severityWidth = this.doc.font(fonts.weights.bold).fontSize(7).widthOfString(severityText) + 12;
|
|
635
|
+
this.doc
|
|
636
|
+
.roundedRect(spacing.page.margin, this.y, severityWidth, badgeHeight, 3)
|
|
637
|
+
.fill(style.bg);
|
|
638
|
+
this.doc
|
|
639
|
+
.font(fonts.weights.bold)
|
|
640
|
+
.fontSize(7)
|
|
641
|
+
.fillColor(style.text)
|
|
642
|
+
.text(severityText, spacing.page.margin + 6, this.y + 4);
|
|
643
|
+
let xOffset = spacing.page.margin + severityWidth + 6;
|
|
644
|
+
// Status badge (if present) - small colored text, no box
|
|
645
|
+
if (finding.status) {
|
|
646
|
+
const statusColors = {
|
|
647
|
+
open: '#DC2626',
|
|
648
|
+
partial: '#D97706',
|
|
649
|
+
mitigated: '#059669',
|
|
650
|
+
accepted: '#4F46E5',
|
|
651
|
+
resolved: '#059669',
|
|
652
|
+
};
|
|
653
|
+
const statusColor = statusColors[finding.status] || statusColors.open;
|
|
654
|
+
const statusText = finding.status.charAt(0).toUpperCase() + finding.status.slice(1);
|
|
644
655
|
this.doc
|
|
645
|
-
.font(fonts.weights.
|
|
646
|
-
.fontSize(
|
|
647
|
-
.fillColor(
|
|
648
|
-
.text(
|
|
649
|
-
this.y += 14;
|
|
656
|
+
.font(fonts.weights.bold)
|
|
657
|
+
.fontSize(9)
|
|
658
|
+
.fillColor(statusColor)
|
|
659
|
+
.text(statusText, xOffset, this.y + 3);
|
|
650
660
|
}
|
|
651
|
-
|
|
661
|
+
this.y += badgeHeight + 8;
|
|
662
|
+
// File reference with line numbers (if present)
|
|
652
663
|
if (finding.file) {
|
|
653
664
|
this.doc
|
|
654
665
|
.font(fonts.mono)
|
|
655
666
|
.fontSize(fonts.sizes.small)
|
|
656
667
|
.fillColor(colors.accent)
|
|
657
668
|
.text(`${finding.file}${finding.line ? `:${finding.line}` : ''}`, spacing.page.margin, this.y);
|
|
658
|
-
this.y +=
|
|
669
|
+
this.y += 16;
|
|
670
|
+
}
|
|
671
|
+
// DREAD/CVSS score and metadata line
|
|
672
|
+
const metaParts = [];
|
|
673
|
+
if (finding.dreadScore)
|
|
674
|
+
metaParts.push(`DREAD: ${finding.dreadScore.toFixed(1)}`);
|
|
675
|
+
if (finding.cvssScore)
|
|
676
|
+
metaParts.push(`CVSS: ${finding.cvssScore.toFixed(1)}`);
|
|
677
|
+
if (finding.cwe)
|
|
678
|
+
metaParts.push(finding.cwe);
|
|
679
|
+
if (finding.relatedFindings?.length) {
|
|
680
|
+
metaParts.push(`Related: ${finding.relatedFindings.join(', ')}`);
|
|
681
|
+
}
|
|
682
|
+
if (metaParts.length > 0) {
|
|
683
|
+
this.doc
|
|
684
|
+
.font(fonts.weights.normal)
|
|
685
|
+
.fontSize(fonts.sizes.small)
|
|
686
|
+
.fillColor(colors.text.muted)
|
|
687
|
+
.text(metaParts.join(' | '), spacing.page.margin, this.y);
|
|
688
|
+
this.y += 16;
|
|
659
689
|
}
|
|
660
690
|
// ─────────────────────────────────────────────────────────────
|
|
661
691
|
// Check for structured format (issue/why/fix) vs legacy format
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coverme-security-scanner",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.7",
|
|
4
4
|
"description": "AI-powered security assessment reports with beautiful PDF output",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"postinstall": "node bin/install-command.js --global || true"
|
|
22
23
|
},
|
|
23
24
|
"files": [
|
|
24
25
|
"dist",
|