coverme-security-scanner 3.0.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.
- package/.claude/commands/coverme.md +349 -0
- package/README.md +97 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +124 -0
- package/dist/index.js.map +1 -0
- package/dist/pdf-generator.d.ts +32 -0
- package/dist/pdf-generator.d.ts.map +1 -0
- package/dist/pdf-generator.js +564 -0
- package/dist/pdf-generator.js.map +1 -0
- package/dist/types.d.ts +141 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
- package/src/index.ts +137 -0
- package/src/pdf-generator.ts +684 -0
- package/src/types.ts +204 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# CoverMe Security Assessment v3
|
|
2
|
+
|
|
3
|
+
You are a **senior security consultant** writing a Combined Assessment Report.
|
|
4
|
+
|
|
5
|
+
**Ultrathink** - Read actual code, trace data flows, think like an attacker.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## OUTPUT QUALITY STANDARD
|
|
10
|
+
|
|
11
|
+
Every finding MUST match this quality level. Study these examples carefully.
|
|
12
|
+
|
|
13
|
+
### GOOD Finding Examples (from real enterprise reports):
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"id": "T-FE-1",
|
|
18
|
+
"title": "Attestation Fallback Accepts Unverified Enclave Keys",
|
|
19
|
+
"severity": "high",
|
|
20
|
+
"file": "frontend/lib/e2e-encryption.ts",
|
|
21
|
+
"line": "51-91",
|
|
22
|
+
"description": "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 public keys that have not been cryptographically bound to hardware attestation - enabling a man-in-the-middle attack by a compromised gateway. DREAD-D: 6.3.",
|
|
23
|
+
"recommendation": "Block the request or display a prominent security warning when attestation fails. Do not silently proceed with unverified keys."
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"id": "CR-03",
|
|
30
|
+
"title": "Unauthenticated Enclave Registration and Status Endpoints",
|
|
31
|
+
"severity": "high",
|
|
32
|
+
"file": "backend-eks/src/routes/v1/enclave.js",
|
|
33
|
+
"line": "379-558",
|
|
34
|
+
"description": "POST /api/v1/enclave/status and POST /api/v1/enclave/register have no application-level authentication. Any party that can reach these endpoints can register fake enclaves with poisoned public keys, send fake heartbeats to influence load balancing, or retrieve VM configuration. Network-level protection exists via ingress CIDR whitelisting in Helm values, but defense in depth requires application-level auth.",
|
|
35
|
+
"recommendation": "Add a shared secret or mTLS verification. Enclave VMs already have access to secrets via RABBITMQ_CREDENTIALS - use a similar pattern."
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"id": "CR-01",
|
|
42
|
+
"title": "Massive Metrics Code Duplication in Chat Handler",
|
|
43
|
+
"severity": "high",
|
|
44
|
+
"file": "backend-eks/src/routes/v1/chat.js",
|
|
45
|
+
"line": "604-691,793-868",
|
|
46
|
+
"description": "160 lines of Redis metrics tracking code is duplicated verbatim between the streaming path and the non-streaming path. Both blocks perform identical operations: per-hour/minute/second counters, HyperLogLog unique user tracking at multiple granularities, and monthly counts. Any change to the metrics schema requires updating both locations.",
|
|
47
|
+
"recommendation": "Extract to trackRedisMetrics(redis, models, userIdentifier) and call from both paths."
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### BAD Findings (DO NOT write like this):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
// BAD - Title too generic
|
|
55
|
+
{ "title": "Missing input validation" }
|
|
56
|
+
|
|
57
|
+
// BAD - Description has no specifics
|
|
58
|
+
{ "description": "Input is not validated properly" }
|
|
59
|
+
|
|
60
|
+
// BAD - Recommendation not actionable
|
|
61
|
+
{ "recommendation": "Add validation" }
|
|
62
|
+
|
|
63
|
+
// BAD - No DREAD score for HIGH severity
|
|
64
|
+
{ "severity": "high", "description": "..." } // Missing DREAD-D: X.X
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Quality Checklist (verify before saving):
|
|
68
|
+
|
|
69
|
+
- [ ] Every HIGH/CRITICAL finding has DREAD-D score inline in description
|
|
70
|
+
- [ ] Every title is specific to the code flow (not generic categories)
|
|
71
|
+
- [ ] Description traces: function -> input -> vulnerability -> consequence
|
|
72
|
+
- [ ] Recommendation names specific functions/patterns to implement
|
|
73
|
+
- [ ] Line numbers are exact (use ranges like "51-91" when applicable)
|
|
74
|
+
- [ ] Code snippets show the actual vulnerable code
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## SCAN PROCESS
|
|
79
|
+
|
|
80
|
+
### Phase 1: Discovery
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
mkdir -p .coverme
|
|
84
|
+
|
|
85
|
+
# Project structure
|
|
86
|
+
ls -la
|
|
87
|
+
find . -maxdepth 2 -type d -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" 2>/dev/null | head -40
|
|
88
|
+
|
|
89
|
+
# Count files and lines
|
|
90
|
+
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.tsx" -o -name "*.jsx" -o -name "*.py" \) -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | wc -l
|
|
91
|
+
|
|
92
|
+
# Tech stack
|
|
93
|
+
cat package.json 2>/dev/null | head -50
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Phase 2: Map Critical Assets
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Authentication & Authorization
|
|
100
|
+
find . -type f \( -name "*auth*" -o -name "*session*" -o -name "*jwt*" -o -name "*login*" \) -not -path "*/node_modules/*" 2>/dev/null | head -15
|
|
101
|
+
|
|
102
|
+
# API Routes
|
|
103
|
+
find . -type f \( -name "*route*" -o -name "*controller*" -o -name "*api*" \) -not -path "*/node_modules/*" 2>/dev/null | head -15
|
|
104
|
+
|
|
105
|
+
# Database
|
|
106
|
+
find . -type f \( -name "*model*" -o -name "*repository*" -o -name "*db*" \) -not -path "*/node_modules/*" 2>/dev/null | head -15
|
|
107
|
+
|
|
108
|
+
# Config & Secrets
|
|
109
|
+
find . -type f \( -name "*.env*" -o -name "*secret*" -o -name "*config*" \) -not -path "*/node_modules/*" 2>/dev/null | head -15
|
|
110
|
+
|
|
111
|
+
# Infrastructure
|
|
112
|
+
find . -type f \( -name "Dockerfile*" -o -name "*.yaml" -o -name "*.yml" \) -not -path "*/node_modules/*" 2>/dev/null | head -15
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Phase 3: Deep Analysis
|
|
116
|
+
|
|
117
|
+
**READ the critical files you found.** For each, check:
|
|
118
|
+
|
|
119
|
+
**Security Issues:**
|
|
120
|
+
- SQL/NoSQL Injection (string concatenation in queries)
|
|
121
|
+
- Command Injection (user input in exec/spawn)
|
|
122
|
+
- XSS (unescaped output, dangerouslySetInnerHTML)
|
|
123
|
+
- SSRF (user-controlled URLs)
|
|
124
|
+
- Path Traversal (../ in file operations)
|
|
125
|
+
- Hardcoded Secrets (API keys, passwords in code)
|
|
126
|
+
- Auth Bypass (missing checks, fail-open patterns)
|
|
127
|
+
- Race Conditions (TOCTOU, non-atomic operations)
|
|
128
|
+
|
|
129
|
+
**Quality Issues:**
|
|
130
|
+
- Dead Code (unused functions, unreachable code)
|
|
131
|
+
- Code Duplication (copy-pasted blocks)
|
|
132
|
+
- High Complexity (functions > 100 lines)
|
|
133
|
+
- Missing Error Handling
|
|
134
|
+
|
|
135
|
+
### Phase 4: Check Previous Scan
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cat .coverme/scan.json 2>/dev/null | head -50 || echo "NO_PREVIOUS_SCAN"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If previous scan exists, compare findings and note what was resolved.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## REQUIRED OUTPUT FORMAT
|
|
146
|
+
|
|
147
|
+
Save this JSON to `.coverme/scan.json`:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"projectName": "project-name",
|
|
152
|
+
"scanDate": "2026-02-18T12:00:00.000Z",
|
|
153
|
+
"branch": "main",
|
|
154
|
+
"filesScanned": 45,
|
|
155
|
+
"linesOfCode": 4850,
|
|
156
|
+
"projectTree": "project/\n├── src/\n│ ├── api/\n│ └── services/\n└── package.json",
|
|
157
|
+
|
|
158
|
+
"projectOverview": {
|
|
159
|
+
"name": "project-name",
|
|
160
|
+
"type": "Backend API",
|
|
161
|
+
"stack": ["Node.js", "TypeScript", "PostgreSQL"],
|
|
162
|
+
"purpose": "Brief description of what this project does",
|
|
163
|
+
"architecture": "Monolith",
|
|
164
|
+
"keyComponents": ["auth service", "api routes", "database"]
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
"executiveSummary": {
|
|
168
|
+
"headline": "0 Critical + 3 High findings - key gaps remain",
|
|
169
|
+
"riskLevel": "HIGH",
|
|
170
|
+
"overview": "Project-name is a [describe architecture]. No critical vulnerabilities found. The most significant remaining issues are: [specific issue 1], [specific issue 2], and [specific issue 3].",
|
|
171
|
+
"topRisks": [
|
|
172
|
+
"Specific risk with file reference and technical detail",
|
|
173
|
+
"Another specific risk with impact quantification"
|
|
174
|
+
],
|
|
175
|
+
"positives": [
|
|
176
|
+
"Specific strength citing libraries/patterns/algorithms used"
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
"findings": [
|
|
181
|
+
{
|
|
182
|
+
"id": "SEC-001",
|
|
183
|
+
"title": "Specific Descriptive Title for This Exact Vulnerability",
|
|
184
|
+
"severity": "high",
|
|
185
|
+
"category": "security",
|
|
186
|
+
"file": "src/routes/users.ts",
|
|
187
|
+
"line": "45-52",
|
|
188
|
+
"code": "const query = `SELECT * FROM users WHERE id = ${userId}`;",
|
|
189
|
+
"description": "The getUserById function at line 45 concatenates userId directly into SQL without parameterization. An attacker sending id=1 OR 1=1 can extract all user records. DREAD-D: 7.5.",
|
|
190
|
+
"recommendation": "Use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId])",
|
|
191
|
+
"cwe": "CWE-89",
|
|
192
|
+
"dread": {
|
|
193
|
+
"damage": 9,
|
|
194
|
+
"reproducibility": 9,
|
|
195
|
+
"exploitability": 8,
|
|
196
|
+
"affectedUsers": 8,
|
|
197
|
+
"discoverability": 6,
|
|
198
|
+
"total": 8.0
|
|
199
|
+
},
|
|
200
|
+
"confidence": 95,
|
|
201
|
+
"fixOwner": "developer"
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
|
|
205
|
+
"threatModel": [
|
|
206
|
+
{
|
|
207
|
+
"id": "T-001",
|
|
208
|
+
"threat": "SQL Injection via user input",
|
|
209
|
+
"category": "STRIDE",
|
|
210
|
+
"strideType": "T",
|
|
211
|
+
"status": "open",
|
|
212
|
+
"relatedFindings": ["SEC-001"],
|
|
213
|
+
"mitigation": "Use parameterized queries",
|
|
214
|
+
"dreadScore": 8.0
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
|
|
218
|
+
"qualityReview": {
|
|
219
|
+
"deleteItems": [
|
|
220
|
+
{
|
|
221
|
+
"type": "delete",
|
|
222
|
+
"file": "src/legacy/old-auth.ts",
|
|
223
|
+
"lines": 250,
|
|
224
|
+
"title": "Dead legacy auth module",
|
|
225
|
+
"description": "Entire file unused since migration to Clerk",
|
|
226
|
+
"reason": "No imports found in codebase",
|
|
227
|
+
"roi": "~250 lines"
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
"mergeItems": [
|
|
231
|
+
{
|
|
232
|
+
"type": "merge",
|
|
233
|
+
"file": "src/utils/validate.ts + src/helpers/validation.ts",
|
|
234
|
+
"title": "Duplicate validation modules",
|
|
235
|
+
"description": "Two files with overlapping validation functions",
|
|
236
|
+
"reason": "DRY violation - consolidate",
|
|
237
|
+
"roi": "~120 lines"
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"simplifyItems": [],
|
|
241
|
+
"totalLinesRemovable": 370,
|
|
242
|
+
"percentageOfCodebase": 3.5
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
"positiveObservations": [
|
|
246
|
+
{
|
|
247
|
+
"title": "Strong Authentication Implementation",
|
|
248
|
+
"description": "Clerk integration with proper session management. All tokens kept server-side. httpOnly + secure cookies with SameSite=strict."
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"title": "Comprehensive Input Validation",
|
|
252
|
+
"description": "Zod schemas used consistently across all API endpoints with proper error handling."
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
|
|
256
|
+
"previouslyResolved": [],
|
|
257
|
+
|
|
258
|
+
"summary": {
|
|
259
|
+
"total": 5,
|
|
260
|
+
"critical": 0,
|
|
261
|
+
"high": 3,
|
|
262
|
+
"medium": 2,
|
|
263
|
+
"low": 0,
|
|
264
|
+
"info": 0
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## DREAD SCORING (for HIGH and CRITICAL)
|
|
272
|
+
|
|
273
|
+
Calculate and include in description as "DREAD-D: X.X":
|
|
274
|
+
|
|
275
|
+
| Score | Meaning |
|
|
276
|
+
|-------|---------|
|
|
277
|
+
| **Damage** | 10 = full system compromise, 1 = minimal |
|
|
278
|
+
| **Reproducibility** | 10 = always works, 1 = rare conditions |
|
|
279
|
+
| **Exploitability** | 10 = script kiddie, 1 = expert required |
|
|
280
|
+
| **Affected Users** | 10 = all users, 1 = single admin |
|
|
281
|
+
| **Discoverability** | 10 = obvious, 1 = requires source code |
|
|
282
|
+
|
|
283
|
+
Average = DREAD-D score. Include in description for HIGH/CRITICAL findings.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## ID PREFIXES
|
|
288
|
+
|
|
289
|
+
Use component-based prefixes:
|
|
290
|
+
- `SEC-` Security issues
|
|
291
|
+
- `AUTH-` Authentication/authorization
|
|
292
|
+
- `API-` API security
|
|
293
|
+
- `DB-` Database issues
|
|
294
|
+
- `INFRA-` Infrastructure
|
|
295
|
+
- `QUAL-` Code quality
|
|
296
|
+
- `PERF-` Performance
|
|
297
|
+
|
|
298
|
+
For threat model: `T-` prefix with component (e.g., `T-FE-1`, `T-EKS-2`)
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## AFTER SCAN
|
|
303
|
+
|
|
304
|
+
1. Save JSON to `.coverme/scan.json`
|
|
305
|
+
2. Generate PDF report:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
npx coverme report .coverme/scan.json --format pdf
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Or if coverme is installed globally:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
coverme report .coverme/scan.json --format pdf
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The PDF will be saved to `.coverme/assessment-report.pdf`
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## EXECUTIVE SUMMARY WRITING GUIDE
|
|
322
|
+
|
|
323
|
+
The overview field must read like a professional security consultant's opening paragraph:
|
|
324
|
+
|
|
325
|
+
**Structure:**
|
|
326
|
+
1. One sentence describing what the project IS (architecture, purpose)
|
|
327
|
+
2. State overall security posture clearly
|
|
328
|
+
3. List the most significant remaining issues
|
|
329
|
+
|
|
330
|
+
**Example:**
|
|
331
|
+
> "Express-AI Officely is a confidential AI platform built on a three-tier encrypted architecture: a Next.js frontend with BFF pattern, an Express.js API gateway on EKS, and backend enclaves running inside AMD SEV-SNP encrypted VMs. No critical vulnerabilities remain. The most significant remaining issues are: unauthenticated enclave registration endpoints (mitigated by network whitelisting), hardcoded secrets in Helm values, and zero test coverage across all components."
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## POSITIVE OBSERVATIONS GUIDE
|
|
336
|
+
|
|
337
|
+
Each observation must cite specific technical evidence:
|
|
338
|
+
|
|
339
|
+
**BAD:** "Good authentication implementation"
|
|
340
|
+
|
|
341
|
+
**GOOD:** "Zero-Knowledge Architecture - EKS gateway genuinely never sees plaintext. Encrypted payloads flow through without decryption. Well-enforced across all components."
|
|
342
|
+
|
|
343
|
+
**GOOD:** "Atomic Credit Operations - Lua scripts for token burning and balance deduction prevent cross-pod race conditions. Check-and-deduct is atomic."
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## START SCANNING
|
|
348
|
+
|
|
349
|
+
Begin with Phase 1: Discovery. Read critical files. Find real issues with real specifics.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# CoverMe Scanner v3
|
|
2
|
+
|
|
3
|
+
AI-powered security assessment tool for Claude Code.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
CoverMe is a slash command for Claude Code that performs comprehensive security assessments and generates professional PDF reports. It uses Claude's intelligence to analyze code like a senior security consultant.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd coverme-v3
|
|
13
|
+
npm install
|
|
14
|
+
npm run build
|
|
15
|
+
npm link # Makes 'coverme' command available globally
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### 1. Run Security Scan (in Claude Code)
|
|
21
|
+
|
|
22
|
+
Navigate to your project and run:
|
|
23
|
+
```
|
|
24
|
+
/coverme
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Claude will:
|
|
28
|
+
1. Discover and read relevant source files
|
|
29
|
+
2. Analyze security patterns and vulnerabilities
|
|
30
|
+
3. Generate a threat model (STRIDE + DREAD)
|
|
31
|
+
4. Create quality recommendations
|
|
32
|
+
5. Save results to `coverme-report.json`
|
|
33
|
+
|
|
34
|
+
### 2. Generate PDF Report
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
coverme report coverme-report.json
|
|
38
|
+
coverme report coverme-report.json --output my-report.pdf
|
|
39
|
+
coverme report coverme-report.json --open # Opens PDF after generation
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Validate JSON
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
coverme validate coverme-report.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Output Format
|
|
49
|
+
|
|
50
|
+
The scan produces a JSON file with:
|
|
51
|
+
|
|
52
|
+
- **projectName**: Project identifier
|
|
53
|
+
- **scanDate**: ISO timestamp
|
|
54
|
+
- **executiveSummary**: Architecture overview and security posture
|
|
55
|
+
- **findings**: Array of security findings with DREAD scores
|
|
56
|
+
- **threatModel**: STRIDE-based threat analysis
|
|
57
|
+
- **qualityReview**: Delete/Merge/Simplify recommendations
|
|
58
|
+
- **positiveObservations**: Good security practices found
|
|
59
|
+
- **previouslyResolved**: Fixed issues from prior scans
|
|
60
|
+
|
|
61
|
+
## Finding Quality Standard
|
|
62
|
+
|
|
63
|
+
Each finding includes:
|
|
64
|
+
- **Severity**: critical / high / medium / low / info
|
|
65
|
+
- **DPI Priority**: "Today" / "This Sprint" / "Next Sprint" / "Backlog"
|
|
66
|
+
- **DREAD Score**: Damage, Reproducibility, Exploitability, Affected Users, Discoverability
|
|
67
|
+
- **Specific Location**: File path and line numbers
|
|
68
|
+
- **Evidence**: Actual code snippets
|
|
69
|
+
- **Remediation**: Concrete fix with code example
|
|
70
|
+
|
|
71
|
+
## Project Structure
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
coverme-v3/
|
|
75
|
+
├── .claude/
|
|
76
|
+
│ └── commands/
|
|
77
|
+
│ └── coverme.md # The main prompt (~350 lines)
|
|
78
|
+
├── src/
|
|
79
|
+
│ ├── index.ts # CLI entry (~120 lines)
|
|
80
|
+
│ ├── types.ts # Type definitions (~200 lines)
|
|
81
|
+
│ └── pdf-generator.ts # PDF generation (~700 lines)
|
|
82
|
+
├── package.json
|
|
83
|
+
├── tsconfig.json
|
|
84
|
+
└── README.md
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Total: ~1,350 lines (vs previous ~5,000 lines)
|
|
88
|
+
|
|
89
|
+
## Requirements
|
|
90
|
+
|
|
91
|
+
- Node.js 20+
|
|
92
|
+
- Claude Code CLI
|
|
93
|
+
- PDFKit (installed via npm)
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CoverMe Scanner v3 - CLI Entry Point
|
|
4
|
+
* Simple CLI: coverme report <json-file>
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { readFileSync, existsSync } from 'fs';
|
|
8
|
+
import { resolve, dirname, basename, extname } from 'path';
|
|
9
|
+
import { PDFGenerator } from './pdf-generator.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('coverme')
|
|
13
|
+
.description('CoverMe Security Scanner v3 - Generate PDF reports from scan results')
|
|
14
|
+
.version('3.0.0');
|
|
15
|
+
program
|
|
16
|
+
.command('report <json-file>')
|
|
17
|
+
.description('Generate a PDF report from a JSON scan result')
|
|
18
|
+
.option('-o, --output <path>', 'Output PDF path')
|
|
19
|
+
.option('--open', 'Open PDF after generation')
|
|
20
|
+
.action(async (jsonFile, options) => {
|
|
21
|
+
try {
|
|
22
|
+
const inputPath = resolve(jsonFile);
|
|
23
|
+
if (!existsSync(inputPath)) {
|
|
24
|
+
console.error(`Error: File not found: ${inputPath}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const rawData = readFileSync(inputPath, 'utf-8');
|
|
28
|
+
let data;
|
|
29
|
+
try {
|
|
30
|
+
data = JSON.parse(rawData);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
console.error('Error: Invalid JSON file');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
// Validate required fields
|
|
37
|
+
if (!data.projectName || !data.findings) {
|
|
38
|
+
console.error('Error: JSON must contain projectName and findings array');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
// Determine output path
|
|
42
|
+
const outputPath = options.output
|
|
43
|
+
? resolve(options.output)
|
|
44
|
+
: resolve(dirname(inputPath), `${basename(inputPath, extname(inputPath))}.pdf`);
|
|
45
|
+
console.log(`Generating PDF report for: ${data.projectName}`);
|
|
46
|
+
console.log(`Findings: ${data.findings.length}`);
|
|
47
|
+
const generator = new PDFGenerator();
|
|
48
|
+
await generator.generate(data, outputPath);
|
|
49
|
+
console.log(`Report saved to: ${outputPath}`);
|
|
50
|
+
// Open PDF if requested
|
|
51
|
+
if (options.open) {
|
|
52
|
+
const { exec } = await import('child_process');
|
|
53
|
+
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
54
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
55
|
+
exec(`${openCmd} "${outputPath}"`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Error generating report:', error instanceof Error ? error.message : error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
program
|
|
64
|
+
.command('validate <json-file>')
|
|
65
|
+
.description('Validate a JSON scan result without generating PDF')
|
|
66
|
+
.action((jsonFile) => {
|
|
67
|
+
try {
|
|
68
|
+
const inputPath = resolve(jsonFile);
|
|
69
|
+
if (!existsSync(inputPath)) {
|
|
70
|
+
console.error(`Error: File not found: ${inputPath}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const rawData = readFileSync(inputPath, 'utf-8');
|
|
74
|
+
const data = JSON.parse(rawData);
|
|
75
|
+
// Validation checks
|
|
76
|
+
const errors = [];
|
|
77
|
+
const warnings = [];
|
|
78
|
+
if (!data.projectName)
|
|
79
|
+
errors.push('Missing projectName');
|
|
80
|
+
if (!data.findings || !Array.isArray(data.findings))
|
|
81
|
+
errors.push('Missing or invalid findings array');
|
|
82
|
+
if (!data.executiveSummary)
|
|
83
|
+
warnings.push('Missing executiveSummary');
|
|
84
|
+
if (!data.threatModel)
|
|
85
|
+
warnings.push('Missing threatModel');
|
|
86
|
+
// Validate findings
|
|
87
|
+
if (data.findings) {
|
|
88
|
+
data.findings.forEach((f, i) => {
|
|
89
|
+
if (!f.title)
|
|
90
|
+
errors.push(`Finding ${i + 1}: missing title`);
|
|
91
|
+
if (!f.severity)
|
|
92
|
+
errors.push(`Finding ${i + 1}: missing severity`);
|
|
93
|
+
if (!f.description)
|
|
94
|
+
warnings.push(`Finding ${i + 1}: missing description`);
|
|
95
|
+
if (!f.dpiPriority)
|
|
96
|
+
warnings.push(`Finding ${i + 1}: missing dpiPriority`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (errors.length > 0) {
|
|
100
|
+
console.error('Validation FAILED:');
|
|
101
|
+
errors.forEach(e => console.error(` - ${e}`));
|
|
102
|
+
if (warnings.length > 0) {
|
|
103
|
+
console.warn('Warnings:');
|
|
104
|
+
warnings.forEach(w => console.warn(` - ${w}`));
|
|
105
|
+
}
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
console.log('Validation PASSED');
|
|
109
|
+
console.log(` Project: ${data.projectName}`);
|
|
110
|
+
console.log(` Findings: ${data.findings.length}`);
|
|
111
|
+
console.log(` Threat Model: ${data.threatModel ? 'Yes' : 'No'}`);
|
|
112
|
+
console.log(` Quality Review: ${data.qualityReview ? 'Yes' : 'No'}`);
|
|
113
|
+
if (warnings.length > 0) {
|
|
114
|
+
console.warn('Warnings:');
|
|
115
|
+
warnings.forEach(w => console.warn(` - ${w}`));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
program.parse();
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CoverMe Scanner v3 - PDF Generator
|
|
3
|
+
* Generates professional security assessment reports
|
|
4
|
+
*/
|
|
5
|
+
import type { ScanResult } from './types.js';
|
|
6
|
+
export declare class PDFGenerator {
|
|
7
|
+
private doc;
|
|
8
|
+
private yPosition;
|
|
9
|
+
private pageHeight;
|
|
10
|
+
private pageWidth;
|
|
11
|
+
private margin;
|
|
12
|
+
constructor();
|
|
13
|
+
generate(result: ScanResult, outputPath: string): Promise<void>;
|
|
14
|
+
private renderCoverPage;
|
|
15
|
+
private renderCoverTable;
|
|
16
|
+
private renderSeverityBoxes;
|
|
17
|
+
private renderTableOfContents;
|
|
18
|
+
private renderExecutiveSummary;
|
|
19
|
+
private renderArchitectureOverview;
|
|
20
|
+
private renderFindingsSection;
|
|
21
|
+
private renderFinding;
|
|
22
|
+
private renderLowSeveritySection;
|
|
23
|
+
private renderThreatModel;
|
|
24
|
+
private renderQualityReview;
|
|
25
|
+
private renderQualityItem;
|
|
26
|
+
private renderPreviouslyResolved;
|
|
27
|
+
private renderPositiveObservations;
|
|
28
|
+
private renderSummaryPage;
|
|
29
|
+
private renderSectionHeader;
|
|
30
|
+
private addPage;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=pdf-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-generator.d.ts","sourceRoot":"","sources":["../src/pdf-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAyC,MAAM,YAAY,CAAC;AAEpF,qBAAa,YAAY;IACvB,OAAO,CAAC,GAAG,CAAqB;IAChC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,MAAM,CAAM;;IASd,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoFrE,OAAO,CAAC,eAAe;IAmEvB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,qBAAqB;IAyB7B,OAAO,CAAC,sBAAsB;IAoD9B,OAAO,CAAC,0BAA0B;IAwClC,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,aAAa;IAiErB,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,iBAAiB;IA8EzB,OAAO,CAAC,mBAAmB;IAuC3B,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,wBAAwB;IA2BhC,OAAO,CAAC,0BAA0B;IA4BlC,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,OAAO;CAIhB"}
|