coverme-security-scanner 3.7.2 → 3.7.4
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/bin/coverme.js +0 -0
- package/bin/install-command.js +0 -0
- package/bin/merge-reports.js +0 -0
- package/commands/coverme.md +8 -23
- package/dist/pdf/generator.js +56 -29
- package/package.json +1 -1
package/bin/coverme.js
CHANGED
|
File without changes
|
package/bin/install-command.js
CHANGED
|
File without changes
|
package/bin/merge-reports.js
CHANGED
|
File without changes
|
package/commands/coverme.md
CHANGED
|
@@ -1,36 +1,21 @@
|
|
|
1
1
|
# Security Assessment
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run a security scan and generate a PDF report. Execute automatically without asking questions.
|
|
4
4
|
|
|
5
|
-
## Step 1:
|
|
5
|
+
## Step 1: Setup
|
|
6
6
|
```bash
|
|
7
7
|
mkdir -p .coverme
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
## Step 2:
|
|
10
|
+
## Step 2: Run Security Scan
|
|
11
|
+
Use ONE Task agent with subagent_type="general-purpose" to perform the full security assessment:
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
prompt: "Perform a comprehensive security assessment of this codebase. Analyze: 1) Architecture and tech stack 2) API endpoints and attack surface 3) Security vulnerabilities (secrets, injection, XSS, auth issues) 4) Infrastructure (Docker, CI/CD, dependencies). When done, use the Write tool to create .coverme/scan.json with this structure: {\"project\":\"<name>\",\"date\":\"2026-02-19\",\"executiveSummary\":\"<summary>\",\"summary\":{\"critical\":0,\"high\":0,\"medium\":0,\"low\":0,\"total\":0},\"overallRiskLevel\":\"low|medium|high|critical\",\"findings\":[{\"id\":\"VULN-01\",\"title\":\"\",\"severity\":\"critical|high|medium|low\",\"file\":\"\",\"line\":0,\"issue\":\"\",\"why\":\"\",\"fix\":\"\",\"cwe\":\"\"}],\"architecture\":{\"overview\":\"\",\"components\":[]},\"positiveObservations\":[{\"title\":\"\",\"description\":\"\"}]}"
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Task 2: description="Attack Surface", subagent_type="general-purpose", prompt="Map API endpoints and entry points. Then use the Write tool to create .coverme/partial-02-surface.json with: {\"network\":{\"diagram\":\"\",\"ports\":[],\"externalDeps\":[]},\"findings\":[]}"
|
|
17
|
-
|
|
18
|
-
Task 3: description="Vulnerability Scan", subagent_type="general-purpose", prompt="Find security vulnerabilities with code evidence. Then use the Write tool to create .coverme/partial-03-vulns.json with: {\"findings\":[{\"id\":\"\",\"title\":\"\",\"severity\":\"\",\"file\":\"\",\"line\":0,\"issue\":\"\",\"why\":\"\",\"fix\":\"\",\"cwe\":\"\",\"codeEvidence\":[],\"proofOfConcept\":\"\"}]}"
|
|
19
|
-
|
|
20
|
-
Task 4: description="Attack Chains", subagent_type="general-purpose", prompt="Identify attack chains. Then use the Write tool to create .coverme/partial-04-chains.json with: {\"attackChains\":[{\"id\":\"\",\"name\":\"\",\"description\":\"\",\"likelihood\":\"\",\"impact\":\"\",\"steps\":[],\"mitigationStrategy\":\"\"}],\"riskMatrix\":[]}"
|
|
21
|
-
|
|
22
|
-
Task 5: description="Business Logic", subagent_type="general-purpose", prompt="Find business logic flaws. Then use the Write tool to create .coverme/partial-05-business.json with: {\"findings\":[],\"threatModel\":[]}"
|
|
23
|
-
|
|
24
|
-
Task 6: description="Infrastructure", subagent_type="general-purpose", prompt="Check Docker, CI/CD, dependencies. Then use the Write tool to create .coverme/partial-06-infra.json with: {\"findings\":[],\"qualityReview\":{}}"
|
|
25
|
-
|
|
26
|
-
Task 7: description="Compliance", subagent_type="general-purpose", prompt="Map to compliance frameworks. Then use the Write tool to create .coverme/partial-07-compliance.json with: {\"complianceMapping\":[],\"remediation\":{\"p0\":[],\"p1\":[],\"p2\":[],\"p3\":[]},\"positiveObservations\":[],\"privacyAnalysis\":[]}"
|
|
27
|
-
|
|
28
|
-
## Step 3: Wait for agents
|
|
29
|
-
Use AgentOutputTool to wait for all 7 agents to complete.
|
|
15
|
+
## Step 3: Wait for agent to complete
|
|
16
|
+
Use AgentOutputTool to wait for the agent.
|
|
30
17
|
|
|
31
18
|
## Step 4: Generate PDF
|
|
32
19
|
```bash
|
|
33
|
-
coverme
|
|
20
|
+
coverme .coverme/scan.json security-report.pdf && open security-report.pdf
|
|
34
21
|
```
|
|
35
|
-
|
|
36
|
-
CRITICAL: Use subagent_type="general-purpose" for ALL agents. They MUST write JSON files using the Write tool.
|
package/dist/pdf/generator.js
CHANGED
|
@@ -402,6 +402,9 @@ export class PDFGenerator {
|
|
|
402
402
|
this.y = this.doc.y + spacing.paragraph;
|
|
403
403
|
}
|
|
404
404
|
if (report.architecture?.components?.length) {
|
|
405
|
+
// Start components table on new page to keep it together
|
|
406
|
+
this.newPage();
|
|
407
|
+
this.y = spacing.page.top;
|
|
405
408
|
this.subTitle('Components');
|
|
406
409
|
this.renderSimpleTable(['Component', 'Technology', 'Description'], report.architecture.components.map(c => [c.name, c.technology, c.description]), [100, 150, 245]);
|
|
407
410
|
}
|
|
@@ -434,7 +437,6 @@ export class PDFGenerator {
|
|
|
434
437
|
this.y += 18;
|
|
435
438
|
});
|
|
436
439
|
}
|
|
437
|
-
this.checkPageBreak();
|
|
438
440
|
}
|
|
439
441
|
// ─────────────────────────────────────────────────────────────────
|
|
440
442
|
// Network
|
|
@@ -614,15 +616,20 @@ export class PDFGenerator {
|
|
|
614
616
|
const boxPadding = 12;
|
|
615
617
|
const boxInnerWidth = layout.content.width - (boxPadding * 2);
|
|
616
618
|
const style = colors.severity[finding.severity];
|
|
617
|
-
// ID in severity color, title in black
|
|
619
|
+
// ID in severity color, title in black - allow wrapping for long titles
|
|
620
|
+
const titleText = `[${finding.id}] ${finding.title}`;
|
|
621
|
+
const titleHeight = this.doc
|
|
622
|
+
.font(fonts.weights.bold)
|
|
623
|
+
.fontSize(fonts.sizes.body)
|
|
624
|
+
.heightOfString(titleText, { width: layout.content.width });
|
|
618
625
|
this.doc
|
|
619
626
|
.font(fonts.weights.bold)
|
|
620
627
|
.fontSize(fonts.sizes.body)
|
|
621
628
|
.fillColor(style.text)
|
|
622
629
|
.text(`[${finding.id}]`, spacing.page.margin, this.y, { continued: true })
|
|
623
630
|
.fillColor(colors.text.primary)
|
|
624
|
-
.text(` ${finding.title}
|
|
625
|
-
this.y
|
|
631
|
+
.text(` ${finding.title}`, { width: layout.content.width - 60 });
|
|
632
|
+
this.y = this.doc.y + 4;
|
|
626
633
|
// Cross-references and DREAD score line
|
|
627
634
|
const hasRefs = finding.relatedFindings?.length || finding.dreadScore || finding.cwe;
|
|
628
635
|
if (hasRefs) {
|
|
@@ -1141,53 +1148,73 @@ export class PDFGenerator {
|
|
|
1141
1148
|
this.y += 20;
|
|
1142
1149
|
}
|
|
1143
1150
|
renderSimpleTable(headers, rows, colWidths) {
|
|
1144
|
-
const
|
|
1151
|
+
const minRowHeight = 24;
|
|
1145
1152
|
const x = spacing.page.margin;
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
.fill(colors.table.header);
|
|
1150
|
-
let colX = x;
|
|
1151
|
-
headers.forEach((header, i) => {
|
|
1153
|
+
const cellPadding = 8;
|
|
1154
|
+
// Helper to render table header
|
|
1155
|
+
const renderHeader = () => {
|
|
1152
1156
|
this.doc
|
|
1153
|
-
.
|
|
1154
|
-
.
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1157
|
+
.rect(x, this.y, layout.content.width, minRowHeight)
|
|
1158
|
+
.fill(colors.table.header);
|
|
1159
|
+
let colX = x;
|
|
1160
|
+
headers.forEach((header, i) => {
|
|
1161
|
+
this.doc
|
|
1162
|
+
.font(fonts.weights.bold)
|
|
1163
|
+
.fontSize(fonts.sizes.small)
|
|
1164
|
+
.fillColor(colors.table.headerText)
|
|
1165
|
+
.text(header, colX + cellPadding, this.y + 7, {
|
|
1166
|
+
width: colWidths[i] - (cellPadding * 2),
|
|
1167
|
+
});
|
|
1168
|
+
colX += colWidths[i];
|
|
1158
1169
|
});
|
|
1159
|
-
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1170
|
+
this.y += minRowHeight;
|
|
1171
|
+
};
|
|
1172
|
+
// Initial header
|
|
1173
|
+
renderHeader();
|
|
1174
|
+
// Rows - calculate dynamic height based on content
|
|
1163
1175
|
rows.forEach((row, rowIndex) => {
|
|
1164
|
-
this
|
|
1176
|
+
// Calculate the height needed for this row based on longest cell
|
|
1177
|
+
let maxCellHeight = minRowHeight;
|
|
1178
|
+
row.forEach((cell, i) => {
|
|
1179
|
+
const cellWidth = colWidths[i] - (cellPadding * 2);
|
|
1180
|
+
const textHeight = this.doc
|
|
1181
|
+
.font(fonts.weights.normal)
|
|
1182
|
+
.fontSize(fonts.sizes.small)
|
|
1183
|
+
.heightOfString(cell, { width: cellWidth });
|
|
1184
|
+
const cellHeight = Math.max(minRowHeight, textHeight + (cellPadding * 2));
|
|
1185
|
+
maxCellHeight = Math.max(maxCellHeight, cellHeight);
|
|
1186
|
+
});
|
|
1187
|
+
// Check if we need a page break - if so, re-render header on new page
|
|
1188
|
+
const needsPageBreak = this.y > layout.page.height - spacing.page.bottom - maxCellHeight - 20;
|
|
1189
|
+
if (needsPageBreak) {
|
|
1190
|
+
this.newPage();
|
|
1191
|
+
renderHeader();
|
|
1192
|
+
}
|
|
1165
1193
|
// Alternating background
|
|
1166
1194
|
if (rowIndex % 2 === 1) {
|
|
1167
1195
|
this.doc
|
|
1168
|
-
.rect(x, this.y, layout.content.width,
|
|
1196
|
+
.rect(x, this.y, layout.content.width, maxCellHeight)
|
|
1169
1197
|
.fill(colors.table.altRow);
|
|
1170
1198
|
}
|
|
1171
1199
|
// Border
|
|
1172
1200
|
this.doc
|
|
1173
|
-
.moveTo(x, this.y +
|
|
1174
|
-
.lineTo(x + layout.content.width, this.y +
|
|
1201
|
+
.moveTo(x, this.y + maxCellHeight)
|
|
1202
|
+
.lineTo(x + layout.content.width, this.y + maxCellHeight)
|
|
1175
1203
|
.strokeColor(colors.table.border)
|
|
1176
1204
|
.lineWidth(0.5)
|
|
1177
1205
|
.stroke();
|
|
1178
|
-
colX = x;
|
|
1206
|
+
let colX = x;
|
|
1179
1207
|
row.forEach((cell, i) => {
|
|
1180
1208
|
this.doc
|
|
1181
1209
|
.font(fonts.weights.normal)
|
|
1182
1210
|
.fontSize(fonts.sizes.small)
|
|
1183
1211
|
.fillColor(colors.text.primary)
|
|
1184
|
-
.text(cell, colX +
|
|
1185
|
-
width: colWidths[i] -
|
|
1186
|
-
ellipsis: true,
|
|
1212
|
+
.text(cell, colX + cellPadding, this.y + 7, {
|
|
1213
|
+
width: colWidths[i] - (cellPadding * 2),
|
|
1187
1214
|
});
|
|
1188
1215
|
colX += colWidths[i];
|
|
1189
1216
|
});
|
|
1190
|
-
this.y +=
|
|
1217
|
+
this.y += maxCellHeight;
|
|
1191
1218
|
});
|
|
1192
1219
|
this.y += spacing.paragraph;
|
|
1193
1220
|
}
|