coverme-security-scanner 3.0.0 → 3.2.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/README.md +55 -65
- package/bin/coverme.js +44 -0
- package/bin/install-command.js +90 -0
- package/commands/coverme.md +326 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -124
- package/dist/pdf/generator.d.ts +48 -0
- package/dist/pdf/generator.js +1235 -0
- package/dist/pdf/styles.d.ts +84 -0
- package/dist/pdf/styles.js +89 -0
- package/dist/pdf/types.d.ts +203 -0
- package/dist/pdf/types.js +1 -0
- package/package.json +24 -23
- package/.claude/commands/coverme.md +0 -349
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/pdf-generator.d.ts +0 -32
- package/dist/pdf-generator.d.ts.map +0 -1
- package/dist/pdf-generator.js +0 -564
- package/dist/pdf-generator.js.map +0 -1
- package/dist/types.d.ts +0 -141
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/src/index.ts +0 -137
- package/src/pdf-generator.ts +0 -684
- package/src/types.ts +0 -204
- package/tsconfig.json +0 -20
package/dist/types.d.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoverMe Scanner v3 - Type Definitions
|
|
3
|
-
* Clean, minimal types for security assessment reports
|
|
4
|
-
*/
|
|
5
|
-
export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
6
|
-
export type ThreatStatus = 'open' | 'partial' | 'mitigated' | 'accepted';
|
|
7
|
-
export type FixOwner = 'developer' | 'devops' | 'architect';
|
|
8
|
-
export interface DreadScore {
|
|
9
|
-
damage: number;
|
|
10
|
-
reproducibility: number;
|
|
11
|
-
exploitability: number;
|
|
12
|
-
affectedUsers: number;
|
|
13
|
-
discoverability: number;
|
|
14
|
-
total: number;
|
|
15
|
-
}
|
|
16
|
-
export interface Finding {
|
|
17
|
-
id: string;
|
|
18
|
-
title: string;
|
|
19
|
-
severity: Severity;
|
|
20
|
-
category: string;
|
|
21
|
-
file: string;
|
|
22
|
-
line: number | string;
|
|
23
|
-
endLine?: number;
|
|
24
|
-
code?: string;
|
|
25
|
-
description: string;
|
|
26
|
-
recommendation: string;
|
|
27
|
-
cwe?: string;
|
|
28
|
-
owasp?: string;
|
|
29
|
-
dread?: DreadScore;
|
|
30
|
-
confidence?: number;
|
|
31
|
-
dpiPriority?: 'Today' | 'This Sprint' | 'Next Sprint' | 'Backlog';
|
|
32
|
-
crossReferences?: string[];
|
|
33
|
-
fixOwner?: FixOwner;
|
|
34
|
-
}
|
|
35
|
-
export interface ThreatModelEntry {
|
|
36
|
-
id: string;
|
|
37
|
-
threat: string;
|
|
38
|
-
category: 'STRIDE' | 'LINDDUN';
|
|
39
|
-
strideType?: 'S' | 'T' | 'R' | 'I' | 'D' | 'E';
|
|
40
|
-
status: ThreatStatus;
|
|
41
|
-
relatedFindings: string[];
|
|
42
|
-
mitigation?: string;
|
|
43
|
-
dreadScore?: number;
|
|
44
|
-
}
|
|
45
|
-
export interface QualityItem {
|
|
46
|
-
type: 'delete' | 'merge' | 'simplify';
|
|
47
|
-
file: string;
|
|
48
|
-
lines?: number;
|
|
49
|
-
title: string;
|
|
50
|
-
description: string;
|
|
51
|
-
reason: string;
|
|
52
|
-
roi: string;
|
|
53
|
-
}
|
|
54
|
-
export interface QualityReview {
|
|
55
|
-
deleteItems: QualityItem[];
|
|
56
|
-
mergeItems: QualityItem[];
|
|
57
|
-
simplifyItems: QualityItem[];
|
|
58
|
-
totalLinesRemovable: number;
|
|
59
|
-
percentageOfCodebase: number;
|
|
60
|
-
}
|
|
61
|
-
export interface ExecutiveSummary {
|
|
62
|
-
headline: string;
|
|
63
|
-
riskLevel: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
64
|
-
overview: string;
|
|
65
|
-
topRisks: string[];
|
|
66
|
-
positives: string[];
|
|
67
|
-
recommendedActions?: RecommendedAction[];
|
|
68
|
-
}
|
|
69
|
-
export interface RecommendedAction {
|
|
70
|
-
priority: number;
|
|
71
|
-
action: string;
|
|
72
|
-
owner: FixOwner;
|
|
73
|
-
effort?: string;
|
|
74
|
-
}
|
|
75
|
-
export interface ArchitectureComponent {
|
|
76
|
-
name: string;
|
|
77
|
-
type: 'service' | 'database' | 'cache' | 'external' | 'frontend';
|
|
78
|
-
description: string;
|
|
79
|
-
trustLevel: 'untrusted' | 'semi-trusted' | 'trusted';
|
|
80
|
-
}
|
|
81
|
-
export interface TrustBoundary {
|
|
82
|
-
name: string;
|
|
83
|
-
from: string;
|
|
84
|
-
to: string;
|
|
85
|
-
protocol: string;
|
|
86
|
-
}
|
|
87
|
-
export interface ArchitectureOverview {
|
|
88
|
-
components: ArchitectureComponent[];
|
|
89
|
-
trustBoundaries: TrustBoundary[];
|
|
90
|
-
criticalAssets: {
|
|
91
|
-
name: string;
|
|
92
|
-
type: string;
|
|
93
|
-
location: string;
|
|
94
|
-
protection: string;
|
|
95
|
-
}[];
|
|
96
|
-
dataFlows: string[];
|
|
97
|
-
}
|
|
98
|
-
export interface PositiveObservation {
|
|
99
|
-
title: string;
|
|
100
|
-
description: string;
|
|
101
|
-
}
|
|
102
|
-
export interface ResolvedIssue {
|
|
103
|
-
id: string;
|
|
104
|
-
title: string;
|
|
105
|
-
originalSeverity: Severity;
|
|
106
|
-
resolution: string;
|
|
107
|
-
resolvedDate: string;
|
|
108
|
-
}
|
|
109
|
-
export interface ScanResult {
|
|
110
|
-
projectName: string;
|
|
111
|
-
scanDate: string;
|
|
112
|
-
branch?: string;
|
|
113
|
-
commit?: string;
|
|
114
|
-
filesScanned: number;
|
|
115
|
-
linesOfCode: number;
|
|
116
|
-
projectTree?: string;
|
|
117
|
-
projectOverview?: {
|
|
118
|
-
name: string;
|
|
119
|
-
type: string;
|
|
120
|
-
stack: string[];
|
|
121
|
-
purpose: string;
|
|
122
|
-
architecture: string;
|
|
123
|
-
keyComponents: string[];
|
|
124
|
-
};
|
|
125
|
-
executiveSummary?: ExecutiveSummary;
|
|
126
|
-
architectureOverview?: ArchitectureOverview;
|
|
127
|
-
findings: Finding[];
|
|
128
|
-
threatModel?: ThreatModelEntry[];
|
|
129
|
-
qualityReview?: QualityReview;
|
|
130
|
-
positiveObservations?: PositiveObservation[];
|
|
131
|
-
previouslyResolved?: ResolvedIssue[];
|
|
132
|
-
summary: {
|
|
133
|
-
total: number;
|
|
134
|
-
critical: number;
|
|
135
|
-
high: number;
|
|
136
|
-
medium: number;
|
|
137
|
-
low: number;
|
|
138
|
-
info: number;
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
DELETED
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/src/index.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
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
|
-
import type { ScanResult } from './types.js';
|
|
11
|
-
|
|
12
|
-
const program = new Command();
|
|
13
|
-
|
|
14
|
-
program
|
|
15
|
-
.name('coverme')
|
|
16
|
-
.description('CoverMe Security Scanner v3 - Generate PDF reports from scan results')
|
|
17
|
-
.version('3.0.0');
|
|
18
|
-
|
|
19
|
-
program
|
|
20
|
-
.command('report <json-file>')
|
|
21
|
-
.description('Generate a PDF report from a JSON scan result')
|
|
22
|
-
.option('-o, --output <path>', 'Output PDF path')
|
|
23
|
-
.option('--open', 'Open PDF after generation')
|
|
24
|
-
.action(async (jsonFile: string, options: { output?: string; open?: boolean }) => {
|
|
25
|
-
try {
|
|
26
|
-
const inputPath = resolve(jsonFile);
|
|
27
|
-
|
|
28
|
-
if (!existsSync(inputPath)) {
|
|
29
|
-
console.error(`Error: File not found: ${inputPath}`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const rawData = readFileSync(inputPath, 'utf-8');
|
|
34
|
-
let data: ScanResult;
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
data = JSON.parse(rawData) as ScanResult;
|
|
38
|
-
} catch {
|
|
39
|
-
console.error('Error: Invalid JSON file');
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Validate required fields
|
|
44
|
-
if (!data.projectName || !data.findings) {
|
|
45
|
-
console.error('Error: JSON must contain projectName and findings array');
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Determine output path
|
|
50
|
-
const outputPath = options.output
|
|
51
|
-
? resolve(options.output)
|
|
52
|
-
: resolve(dirname(inputPath), `${basename(inputPath, extname(inputPath))}.pdf`);
|
|
53
|
-
|
|
54
|
-
console.log(`Generating PDF report for: ${data.projectName}`);
|
|
55
|
-
console.log(`Findings: ${data.findings.length}`);
|
|
56
|
-
|
|
57
|
-
const generator = new PDFGenerator();
|
|
58
|
-
await generator.generate(data, outputPath);
|
|
59
|
-
|
|
60
|
-
console.log(`Report saved to: ${outputPath}`);
|
|
61
|
-
|
|
62
|
-
// Open PDF if requested
|
|
63
|
-
if (options.open) {
|
|
64
|
-
const { exec } = await import('child_process');
|
|
65
|
-
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
66
|
-
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
67
|
-
exec(`${openCmd} "${outputPath}"`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('Error generating report:', error instanceof Error ? error.message : error);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
program
|
|
77
|
-
.command('validate <json-file>')
|
|
78
|
-
.description('Validate a JSON scan result without generating PDF')
|
|
79
|
-
.action((jsonFile: string) => {
|
|
80
|
-
try {
|
|
81
|
-
const inputPath = resolve(jsonFile);
|
|
82
|
-
|
|
83
|
-
if (!existsSync(inputPath)) {
|
|
84
|
-
console.error(`Error: File not found: ${inputPath}`);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const rawData = readFileSync(inputPath, 'utf-8');
|
|
89
|
-
const data = JSON.parse(rawData) as ScanResult;
|
|
90
|
-
|
|
91
|
-
// Validation checks
|
|
92
|
-
const errors: string[] = [];
|
|
93
|
-
const warnings: string[] = [];
|
|
94
|
-
|
|
95
|
-
if (!data.projectName) errors.push('Missing projectName');
|
|
96
|
-
if (!data.findings || !Array.isArray(data.findings)) errors.push('Missing or invalid findings array');
|
|
97
|
-
if (!data.executiveSummary) warnings.push('Missing executiveSummary');
|
|
98
|
-
if (!data.threatModel) warnings.push('Missing threatModel');
|
|
99
|
-
|
|
100
|
-
// Validate findings
|
|
101
|
-
if (data.findings) {
|
|
102
|
-
data.findings.forEach((f, i) => {
|
|
103
|
-
if (!f.title) errors.push(`Finding ${i + 1}: missing title`);
|
|
104
|
-
if (!f.severity) errors.push(`Finding ${i + 1}: missing severity`);
|
|
105
|
-
if (!f.description) warnings.push(`Finding ${i + 1}: missing description`);
|
|
106
|
-
if (!f.dpiPriority) warnings.push(`Finding ${i + 1}: missing dpiPriority`);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (errors.length > 0) {
|
|
111
|
-
console.error('Validation FAILED:');
|
|
112
|
-
errors.forEach(e => console.error(` - ${e}`));
|
|
113
|
-
if (warnings.length > 0) {
|
|
114
|
-
console.warn('Warnings:');
|
|
115
|
-
warnings.forEach(w => console.warn(` - ${w}`));
|
|
116
|
-
}
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log('Validation PASSED');
|
|
121
|
-
console.log(` Project: ${data.projectName}`);
|
|
122
|
-
console.log(` Findings: ${data.findings.length}`);
|
|
123
|
-
console.log(` Threat Model: ${data.threatModel ? 'Yes' : 'No'}`);
|
|
124
|
-
console.log(` Quality Review: ${data.qualityReview ? 'Yes' : 'No'}`);
|
|
125
|
-
|
|
126
|
-
if (warnings.length > 0) {
|
|
127
|
-
console.warn('Warnings:');
|
|
128
|
-
warnings.forEach(w => console.warn(` - ${w}`));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.error('Error:', error instanceof Error ? error.message : error);
|
|
133
|
-
process.exit(1);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
program.parse();
|