mitnick-cli 1.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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/analyzers/analyzer.interface.d.ts +32 -0
- package/dist/analyzers/analyzer.interface.d.ts.map +1 -0
- package/dist/analyzers/analyzer.interface.js +2 -0
- package/dist/analyzers/analyzer.interface.js.map +1 -0
- package/dist/analyzers/analyzer.registry.d.ts +16 -0
- package/dist/analyzers/analyzer.registry.d.ts.map +1 -0
- package/dist/analyzers/analyzer.registry.js +40 -0
- package/dist/analyzers/analyzer.registry.js.map +1 -0
- package/dist/analyzers/dependency-confusion/index.d.ts +14 -0
- package/dist/analyzers/dependency-confusion/index.d.ts.map +1 -0
- package/dist/analyzers/dependency-confusion/index.js +147 -0
- package/dist/analyzers/dependency-confusion/index.js.map +1 -0
- package/dist/analyzers/dormant-package/index.d.ts +14 -0
- package/dist/analyzers/dormant-package/index.d.ts.map +1 -0
- package/dist/analyzers/dormant-package/index.js +137 -0
- package/dist/analyzers/dormant-package/index.js.map +1 -0
- package/dist/analyzers/file-based-analyzer.d.ts +20 -0
- package/dist/analyzers/file-based-analyzer.d.ts.map +1 -0
- package/dist/analyzers/file-based-analyzer.js +35 -0
- package/dist/analyzers/file-based-analyzer.js.map +1 -0
- package/dist/analyzers/install-scripts/index.d.ts +13 -0
- package/dist/analyzers/install-scripts/index.d.ts.map +1 -0
- package/dist/analyzers/install-scripts/index.js +125 -0
- package/dist/analyzers/install-scripts/index.js.map +1 -0
- package/dist/analyzers/license/index.d.ts +12 -0
- package/dist/analyzers/license/index.d.ts.map +1 -0
- package/dist/analyzers/license/index.js +199 -0
- package/dist/analyzers/license/index.js.map +1 -0
- package/dist/analyzers/maintainer/index.d.ts +12 -0
- package/dist/analyzers/maintainer/index.d.ts.map +1 -0
- package/dist/analyzers/maintainer/index.js +93 -0
- package/dist/analyzers/maintainer/index.js.map +1 -0
- package/dist/analyzers/network-calls/index.d.ts +15 -0
- package/dist/analyzers/network-calls/index.d.ts.map +1 -0
- package/dist/analyzers/network-calls/index.js +212 -0
- package/dist/analyzers/network-calls/index.js.map +1 -0
- package/dist/analyzers/obfuscation/index.d.ts +19 -0
- package/dist/analyzers/obfuscation/index.d.ts.map +1 -0
- package/dist/analyzers/obfuscation/index.js +218 -0
- package/dist/analyzers/obfuscation/index.js.map +1 -0
- package/dist/analyzers/prototype-pollution/index.d.ts +18 -0
- package/dist/analyzers/prototype-pollution/index.d.ts.map +1 -0
- package/dist/analyzers/prototype-pollution/index.js +257 -0
- package/dist/analyzers/prototype-pollution/index.js.map +1 -0
- package/dist/analyzers/sensitive-data/index.d.ts +16 -0
- package/dist/analyzers/sensitive-data/index.d.ts.map +1 -0
- package/dist/analyzers/sensitive-data/index.js +254 -0
- package/dist/analyzers/sensitive-data/index.js.map +1 -0
- package/dist/analyzers/typosquatting/index.d.ts +14 -0
- package/dist/analyzers/typosquatting/index.d.ts.map +1 -0
- package/dist/analyzers/typosquatting/index.js +127 -0
- package/dist/analyzers/typosquatting/index.js.map +1 -0
- package/dist/analyzers/typosquatting/popular-packages.d.ts +9 -0
- package/dist/analyzers/typosquatting/popular-packages.d.ts.map +1 -0
- package/dist/analyzers/typosquatting/popular-packages.js +236 -0
- package/dist/analyzers/typosquatting/popular-packages.js.map +1 -0
- package/dist/analyzers/vulnerability/index.d.ts +12 -0
- package/dist/analyzers/vulnerability/index.d.ts.map +1 -0
- package/dist/analyzers/vulnerability/index.js +147 -0
- package/dist/analyzers/vulnerability/index.js.map +1 -0
- package/dist/cli/commands/check.d.ts +21 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +204 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/formatters/formatter.interface.d.ts +14 -0
- package/dist/cli/formatters/formatter.interface.d.ts.map +1 -0
- package/dist/cli/formatters/formatter.interface.js +2 -0
- package/dist/cli/formatters/formatter.interface.js.map +1 -0
- package/dist/cli/formatters/json.d.ts +12 -0
- package/dist/cli/formatters/json.d.ts.map +1 -0
- package/dist/cli/formatters/json.js +12 -0
- package/dist/cli/formatters/json.js.map +1 -0
- package/dist/cli/formatters/sarif.d.ts +13 -0
- package/dist/cli/formatters/sarif.d.ts.map +1 -0
- package/dist/cli/formatters/sarif.js +101 -0
- package/dist/cli/formatters/sarif.js.map +1 -0
- package/dist/cli/formatters/terminal.d.ts +13 -0
- package/dist/cli/formatters/terminal.d.ts.map +1 -0
- package/dist/cli/formatters/terminal.js +110 -0
- package/dist/cli/formatters/terminal.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +86 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/engine.d.ts +23 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +55 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/scorer.d.ts +30 -0
- package/dist/core/scorer.d.ts.map +1 -0
- package/dist/core/scorer.js +88 -0
- package/dist/core/scorer.js.map +1 -0
- package/dist/core/types.d.ts +76 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +30 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/client.d.ts +27 -0
- package/dist/registry/client.d.ts.map +1 -0
- package/dist/registry/client.js +189 -0
- package/dist/registry/client.js.map +1 -0
- package/dist/registry/tarball.d.ts +34 -0
- package/dist/registry/tarball.d.ts.map +1 -0
- package/dist/registry/tarball.js +103 -0
- package/dist/registry/tarball.js.map +1 -0
- package/dist/utils/ast.d.ts +74 -0
- package/dist/utils/ast.d.ts.map +1 -0
- package/dist/utils/ast.js +150 -0
- package/dist/utils/ast.js.map +1 -0
- package/dist/utils/fs.d.ts +28 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +78 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/http.d.ts +40 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +116 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/logger.d.ts +46 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/strings.d.ts +8 -0
- package/dist/utils/strings.d.ts.map +1 -0
- package/dist/utils/strings.js +12 -0
- package/dist/utils/strings.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "check" command handler.
|
|
3
|
+
*
|
|
4
|
+
* Parses package specifiers, fetches metadata and tarballs from the
|
|
5
|
+
* npm registry, runs the analysis engine, and outputs formatted results.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import { TerminalFormatter } from '../formatters/terminal.js';
|
|
11
|
+
import { JsonFormatter } from '../formatters/json.js';
|
|
12
|
+
import { SarifFormatter } from '../formatters/sarif.js';
|
|
13
|
+
import { AnalysisEngine } from '../../core/engine.js';
|
|
14
|
+
import { hasFindsAtOrAbove } from '../../core/scorer.js';
|
|
15
|
+
import { createAnalyzers } from '../../analyzers/analyzer.registry.js';
|
|
16
|
+
import { fetchPackageMetadata } from '../../registry/client.js';
|
|
17
|
+
import { downloadAndExtract } from '../../registry/tarball.js';
|
|
18
|
+
import { logger } from '../../utils/logger.js';
|
|
19
|
+
const MAX_PACKAGE_JSON_SIZE = 5 * 1024 * 1024; // 5MB
|
|
20
|
+
// ─── Package Specifier Parsing ───────────────────────────
|
|
21
|
+
/**
|
|
22
|
+
* Parse a package specifier string into name and optional version.
|
|
23
|
+
*
|
|
24
|
+
* Handles:
|
|
25
|
+
* - "express" -> { name: "express" }
|
|
26
|
+
* - "express@4.19.2" -> { name: "express", version: "4.19.2" }
|
|
27
|
+
* - "@scope/pkg" -> { name: "@scope/pkg" }
|
|
28
|
+
* - "@scope/pkg@1.0.0" -> { name: "@scope/pkg", version: "1.0.0" }
|
|
29
|
+
*/
|
|
30
|
+
function parsePackageSpecifier(specifier) {
|
|
31
|
+
const trimmed = specifier.trim();
|
|
32
|
+
if (trimmed.startsWith('@')) {
|
|
33
|
+
// Scoped package: @scope/name or @scope/name@version
|
|
34
|
+
const slashIndex = trimmed.indexOf('/');
|
|
35
|
+
if (slashIndex === -1) {
|
|
36
|
+
return { name: trimmed };
|
|
37
|
+
}
|
|
38
|
+
const afterSlash = trimmed.slice(slashIndex + 1);
|
|
39
|
+
const atIndex = afterSlash.indexOf('@');
|
|
40
|
+
if (atIndex === -1) {
|
|
41
|
+
return { name: trimmed };
|
|
42
|
+
}
|
|
43
|
+
const name = trimmed.slice(0, slashIndex + 1 + atIndex);
|
|
44
|
+
const version = afterSlash.slice(atIndex + 1);
|
|
45
|
+
if (version === '') {
|
|
46
|
+
logger.warn(`Trailing "@" in specifier "${trimmed}" — defaulting to latest version`);
|
|
47
|
+
return { name };
|
|
48
|
+
}
|
|
49
|
+
return { name, version };
|
|
50
|
+
}
|
|
51
|
+
// Unscoped package: name or name@version
|
|
52
|
+
const atIndex = trimmed.indexOf('@');
|
|
53
|
+
if (atIndex <= 0) {
|
|
54
|
+
return { name: trimmed };
|
|
55
|
+
}
|
|
56
|
+
const name = trimmed.slice(0, atIndex);
|
|
57
|
+
const version = trimmed.slice(atIndex + 1);
|
|
58
|
+
if (version === '') {
|
|
59
|
+
logger.warn(`Trailing "@" in specifier "${trimmed}" — defaulting to latest version`);
|
|
60
|
+
return { name };
|
|
61
|
+
}
|
|
62
|
+
return { name, version };
|
|
63
|
+
}
|
|
64
|
+
// ─── Formatter Factory ───────────────────────────────────
|
|
65
|
+
function createFormatter(format) {
|
|
66
|
+
switch (format) {
|
|
67
|
+
case 'json':
|
|
68
|
+
return new JsonFormatter();
|
|
69
|
+
case 'sarif':
|
|
70
|
+
return new SarifFormatter();
|
|
71
|
+
case 'terminal':
|
|
72
|
+
return new TerminalFormatter();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ─── Command Handler ────────────────────────────────────
|
|
76
|
+
/**
|
|
77
|
+
* Execute the "check" command for one or more packages.
|
|
78
|
+
*
|
|
79
|
+
* For each package:
|
|
80
|
+
* 1. Fetch metadata from npm registry
|
|
81
|
+
* 2. Download and extract tarball to temp directory
|
|
82
|
+
* 3. Run all analyzers via the engine
|
|
83
|
+
* 4. Format and output results
|
|
84
|
+
*
|
|
85
|
+
* Returns true if all packages pass (no findings at or above --fail-on),
|
|
86
|
+
* false if any package fails the threshold check.
|
|
87
|
+
*/
|
|
88
|
+
export async function executeCheck(options) {
|
|
89
|
+
const { packages, format, failOn, verbose } = options;
|
|
90
|
+
const formatter = createFormatter(format);
|
|
91
|
+
const analyzers = createAnalyzers();
|
|
92
|
+
const engine = new AnalysisEngine(analyzers);
|
|
93
|
+
const isTerminal = format === 'terminal';
|
|
94
|
+
let allPassed = true;
|
|
95
|
+
for (const specifier of packages) {
|
|
96
|
+
const parsed = parsePackageSpecifier(specifier);
|
|
97
|
+
let tarballCleanup;
|
|
98
|
+
try {
|
|
99
|
+
// Fetch metadata
|
|
100
|
+
const metadataSpinner = isTerminal
|
|
101
|
+
? ora(`Fetching metadata for ${parsed.name}...`).start()
|
|
102
|
+
: undefined;
|
|
103
|
+
const registryResult = await fetchPackageMetadata(parsed.name, parsed.version);
|
|
104
|
+
if (!registryResult.ok) {
|
|
105
|
+
metadataSpinner?.fail(`Failed to fetch metadata for ${parsed.name}`);
|
|
106
|
+
const pkg = parsed.version !== undefined ? `${parsed.name}@${parsed.version}` : parsed.name;
|
|
107
|
+
console.error(`\n Error: ${registryResult.message} (${pkg})\n`);
|
|
108
|
+
allPassed = false;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const { metadata, tarballUrl } = registryResult;
|
|
112
|
+
metadataSpinner?.succeed(`Fetched metadata for ${metadata.name}@${metadata.version}`);
|
|
113
|
+
// Download and extract tarball
|
|
114
|
+
const tarballSpinner = isTerminal
|
|
115
|
+
? ora(`Downloading ${metadata.name}@${metadata.version}...`).start()
|
|
116
|
+
: undefined;
|
|
117
|
+
const tarballResult = await downloadAndExtract(tarballUrl, metadata.name);
|
|
118
|
+
if (!tarballResult.ok) {
|
|
119
|
+
tarballSpinner?.fail(`Failed to download ${metadata.name}@${metadata.version}`);
|
|
120
|
+
console.error(`\n Error: ${tarballResult.message}\n`);
|
|
121
|
+
allPassed = false;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
tarballCleanup = tarballResult.cleanup;
|
|
125
|
+
tarballSpinner?.succeed(`Extracted ${metadata.name}@${metadata.version}`);
|
|
126
|
+
// Read package.json from extracted tarball
|
|
127
|
+
const packageJsonPath = join(tarballResult.extractedPath, 'package.json');
|
|
128
|
+
let packageJsonRaw;
|
|
129
|
+
try {
|
|
130
|
+
const packageJsonStat = await stat(packageJsonPath);
|
|
131
|
+
if (packageJsonStat.size > MAX_PACKAGE_JSON_SIZE) {
|
|
132
|
+
const pkg = `${metadata.name}@${metadata.version}`;
|
|
133
|
+
console.error(`\n Error: package.json for ${pkg} exceeds maximum allowed size of ${MAX_PACKAGE_JSON_SIZE} bytes\n`);
|
|
134
|
+
allPassed = false;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
packageJsonRaw = await readFile(packageJsonPath, 'utf-8');
|
|
138
|
+
}
|
|
139
|
+
catch (readError) {
|
|
140
|
+
const pkg = `${metadata.name}@${metadata.version}`;
|
|
141
|
+
const msg = readError instanceof Error ? readError.message : String(readError);
|
|
142
|
+
console.error(`\n Error: Failed to read package.json for ${pkg}: ${msg}\n`);
|
|
143
|
+
allPassed = false;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
let packageJson;
|
|
147
|
+
try {
|
|
148
|
+
packageJson = JSON.parse(packageJsonRaw);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
const pkg = `${metadata.name}@${metadata.version}`;
|
|
152
|
+
console.error(`\n Error: Malformed package.json for ${pkg}\n`);
|
|
153
|
+
allPassed = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// Build analysis context
|
|
157
|
+
const context = {
|
|
158
|
+
packageName: metadata.name,
|
|
159
|
+
version: metadata.version,
|
|
160
|
+
packageJson,
|
|
161
|
+
extractedPath: tarballResult.extractedPath,
|
|
162
|
+
registryMetadata: metadata,
|
|
163
|
+
};
|
|
164
|
+
// Run analysis
|
|
165
|
+
const analysisSpinner = isTerminal ? ora('Running security analysis...').start() : undefined;
|
|
166
|
+
const report = await engine.analyze(context);
|
|
167
|
+
analysisSpinner?.stop();
|
|
168
|
+
// Output results
|
|
169
|
+
const output = formatter.format(report);
|
|
170
|
+
console.log(output);
|
|
171
|
+
// Check fail-on threshold
|
|
172
|
+
if (failOn !== undefined && hasFindsAtOrAbove(report.results, failOn)) {
|
|
173
|
+
allPassed = false;
|
|
174
|
+
if (isTerminal) {
|
|
175
|
+
console.error(`\n Findings at or above "${failOn}" severity detected. Failing.\n`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Log verbose info
|
|
179
|
+
if (verbose && isTerminal) {
|
|
180
|
+
console.log(` Analyzers run: ${report.results.length}`);
|
|
181
|
+
console.log(` Total findings: ${report.totalFindings}`);
|
|
182
|
+
console.log(` Analysis duration: ${report.duration}ms\n`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
+
const pkg = parsed.version !== undefined ? `${parsed.name}@${parsed.version}` : parsed.name;
|
|
188
|
+
if (isTerminal) {
|
|
189
|
+
console.error(`\n Error analyzing ${pkg}: ${message}\n`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.error(JSON.stringify({ error: message, package: pkg }));
|
|
193
|
+
}
|
|
194
|
+
allPassed = false;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
if (tarballCleanup) {
|
|
198
|
+
await tarballCleanup();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return allPassed;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SecurityReport } from '../../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Contract for output formatters.
|
|
4
|
+
*
|
|
5
|
+
* Formatters transform a SecurityReport into a string representation
|
|
6
|
+
* suitable for a specific output target (terminal, JSON file, CI system).
|
|
7
|
+
*/
|
|
8
|
+
export interface Formatter {
|
|
9
|
+
/** Format identifier */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Transform a security report into formatted string output */
|
|
12
|
+
format(report: SecurityReport): string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=formatter.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.interface.d.ts","sourceRoot":"","sources":["../../../src/cli/formatters/formatter.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,wBAAwB;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,+DAA+D;IAC/D,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.interface.js","sourceRoot":"","sources":["../../../src/cli/formatters/formatter.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON formatter — outputs SecurityReport as pretty-printed JSON.
|
|
3
|
+
*
|
|
4
|
+
* Suitable for programmatic consumption and piping to other tools.
|
|
5
|
+
*/
|
|
6
|
+
import type { Formatter } from './formatter.interface.js';
|
|
7
|
+
import type { SecurityReport } from '../../core/types.js';
|
|
8
|
+
export declare class JsonFormatter implements Formatter {
|
|
9
|
+
readonly name: "json";
|
|
10
|
+
format(report: SecurityReport): string;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/cli/formatters/json.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,qBAAa,aAAc,YAAW,SAAS;IAC7C,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAEhC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAGvC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON formatter — outputs SecurityReport as pretty-printed JSON.
|
|
3
|
+
*
|
|
4
|
+
* Suitable for programmatic consumption and piping to other tools.
|
|
5
|
+
*/
|
|
6
|
+
export class JsonFormatter {
|
|
7
|
+
name = 'json';
|
|
8
|
+
format(report) {
|
|
9
|
+
return JSON.stringify(report, null, 2);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/cli/formatters/json.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,MAAe,CAAC;IAEhC,MAAM,CAAC,MAAsB;QAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF v2.1.0 formatter — outputs SecurityReport in Static Analysis
|
|
3
|
+
* Results Interchange Format for CI/CD integration (e.g., GitHub Security tab).
|
|
4
|
+
*
|
|
5
|
+
* Specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
6
|
+
*/
|
|
7
|
+
import type { Formatter } from './formatter.interface.js';
|
|
8
|
+
import type { SecurityReport } from '../../core/types.js';
|
|
9
|
+
export declare class SarifFormatter implements Formatter {
|
|
10
|
+
readonly name: "sarif";
|
|
11
|
+
format(report: SecurityReport): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../../src/cli/formatters/sarif.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAW,cAAc,EAAY,MAAM,qBAAqB,CAAC;AA+I7E,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IAEjC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CAwBvC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF v2.1.0 formatter — outputs SecurityReport in Static Analysis
|
|
3
|
+
* Results Interchange Format for CI/CD integration (e.g., GitHub Security tab).
|
|
4
|
+
*
|
|
5
|
+
* Specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
6
|
+
*/
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require('../../../package.json');
|
|
10
|
+
// ─── Severity Mapping ────────────────────────────────────
|
|
11
|
+
const SEVERITY_TO_SARIF_LEVEL = {
|
|
12
|
+
critical: 'error',
|
|
13
|
+
high: 'error',
|
|
14
|
+
medium: 'warning',
|
|
15
|
+
low: 'note',
|
|
16
|
+
info: 'none',
|
|
17
|
+
};
|
|
18
|
+
// ─── Helpers ─────────────────────────────────────────────
|
|
19
|
+
function buildRuleId(finding) {
|
|
20
|
+
const sanitized = finding.analyzer.replace(/[^a-zA-Z0-9-]/g, '-');
|
|
21
|
+
const titleSlug = finding.title.replace(/[^a-zA-Z0-9-]/g, '-').substring(0, 50);
|
|
22
|
+
return `${sanitized}/${titleSlug}`;
|
|
23
|
+
}
|
|
24
|
+
function buildRules(findings) {
|
|
25
|
+
const ruleMap = new Map();
|
|
26
|
+
for (let i = 0; i < findings.length; i++) {
|
|
27
|
+
const finding = findings[i];
|
|
28
|
+
if (!finding)
|
|
29
|
+
continue;
|
|
30
|
+
const ruleId = buildRuleId(finding);
|
|
31
|
+
const rule = {
|
|
32
|
+
id: ruleId,
|
|
33
|
+
shortDescription: { text: finding.title },
|
|
34
|
+
defaultConfiguration: {
|
|
35
|
+
level: SEVERITY_TO_SARIF_LEVEL[finding.severity],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
if (finding.description !== '') {
|
|
39
|
+
ruleMap.set(ruleId, {
|
|
40
|
+
...rule,
|
|
41
|
+
fullDescription: { text: finding.description },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
ruleMap.set(ruleId, rule);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return [...ruleMap.values()];
|
|
49
|
+
}
|
|
50
|
+
function buildResults(findings) {
|
|
51
|
+
return findings.map((finding) => {
|
|
52
|
+
const ruleId = buildRuleId(finding);
|
|
53
|
+
const level = SEVERITY_TO_SARIF_LEVEL[finding.severity];
|
|
54
|
+
const result = {
|
|
55
|
+
ruleId,
|
|
56
|
+
level,
|
|
57
|
+
message: {
|
|
58
|
+
text: finding.description !== '' ? finding.description : finding.title,
|
|
59
|
+
},
|
|
60
|
+
...(finding.file !== undefined
|
|
61
|
+
? {
|
|
62
|
+
locations: [
|
|
63
|
+
{
|
|
64
|
+
physicalLocation: {
|
|
65
|
+
artifactLocation: { uri: finding.file },
|
|
66
|
+
...(finding.line !== undefined ? { region: { startLine: finding.line } } : {}),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
: {}),
|
|
72
|
+
};
|
|
73
|
+
return result;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// ─── Formatter ───────────────────────────────────────────
|
|
77
|
+
export class SarifFormatter {
|
|
78
|
+
name = 'sarif';
|
|
79
|
+
format(report) {
|
|
80
|
+
const allFindings = report.results.flatMap((r) => r.findings);
|
|
81
|
+
const sarifLog = {
|
|
82
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json',
|
|
83
|
+
version: '2.1.0',
|
|
84
|
+
runs: [
|
|
85
|
+
{
|
|
86
|
+
tool: {
|
|
87
|
+
driver: {
|
|
88
|
+
name: 'mitnick',
|
|
89
|
+
version: pkg.version,
|
|
90
|
+
informationUri: 'https://github.com/mitnick-cli/mitnick',
|
|
91
|
+
rules: buildRules(allFindings),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
results: buildResults(allFindings),
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
return JSON.stringify(sarifLog, null, 2);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../../src/cli/formatters/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal formatter — beautiful human-readable output using chalk.
|
|
3
|
+
*
|
|
4
|
+
* Displays analyzer results with status icons, a findings table
|
|
5
|
+
* sorted by severity, the overall score with color coding, and duration.
|
|
6
|
+
*/
|
|
7
|
+
import type { Formatter } from './formatter.interface.js';
|
|
8
|
+
import { type SecurityReport } from '../../core/types.js';
|
|
9
|
+
export declare class TerminalFormatter implements Formatter {
|
|
10
|
+
readonly name: "terminal";
|
|
11
|
+
format(report: SecurityReport): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../../src/cli/formatters/terminal.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAKL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AA4G7B,qBAAa,iBAAkB,YAAW,SAAS;IACjD,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IAEpC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM;CA8BvC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal formatter — beautiful human-readable output using chalk.
|
|
3
|
+
*
|
|
4
|
+
* Displays analyzer results with status icons, a findings table
|
|
5
|
+
* sorted by severity, the overall score with color coding, and duration.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { SEVERITY_ORDER, } from '../../core/types.js';
|
|
9
|
+
// ─── Constants ───────────────────────────────────────────
|
|
10
|
+
const SEVERITY_COLORS = {
|
|
11
|
+
critical: chalk.bgRed.white.bold,
|
|
12
|
+
high: chalk.red.bold,
|
|
13
|
+
medium: chalk.yellow,
|
|
14
|
+
low: chalk.blue,
|
|
15
|
+
info: chalk.gray,
|
|
16
|
+
};
|
|
17
|
+
const GRADE_COLORS = {
|
|
18
|
+
A: chalk.green.bold,
|
|
19
|
+
B: chalk.green,
|
|
20
|
+
C: chalk.yellow,
|
|
21
|
+
D: chalk.red,
|
|
22
|
+
F: chalk.red.bold,
|
|
23
|
+
};
|
|
24
|
+
const CHECK_MARK = chalk.green('\u2713');
|
|
25
|
+
const CROSS_MARK = chalk.red('\u2717');
|
|
26
|
+
// ─── Helpers ─────────────────────────────────────────────
|
|
27
|
+
function padRight(text, width) {
|
|
28
|
+
return text.length >= width ? text : text + ' '.repeat(width - text.length);
|
|
29
|
+
}
|
|
30
|
+
function formatDuration(ms) {
|
|
31
|
+
if (ms < 1000) {
|
|
32
|
+
return `${ms}ms`;
|
|
33
|
+
}
|
|
34
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
35
|
+
}
|
|
36
|
+
function formatAnalyzerLine(result) {
|
|
37
|
+
const icon = result.findings.length > 0 ? CROSS_MARK : CHECK_MARK;
|
|
38
|
+
const name = padRight(result.analyzer, 28);
|
|
39
|
+
const count = result.findings.length;
|
|
40
|
+
const suffix = count === 1 ? 'finding' : 'findings';
|
|
41
|
+
const countText = count > 0 ? chalk.yellow(`${count} ${suffix}`) : chalk.gray(`0 ${suffix}`);
|
|
42
|
+
return ` ${icon} ${name} ${countText}`;
|
|
43
|
+
}
|
|
44
|
+
function sortFindingsBySeverity(findings) {
|
|
45
|
+
return [...findings].sort((a, b) => {
|
|
46
|
+
const orderA = SEVERITY_ORDER[a.severity];
|
|
47
|
+
const orderB = SEVERITY_ORDER[b.severity];
|
|
48
|
+
return orderA - orderB;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function buildFindingsTable(findings) {
|
|
52
|
+
if (findings.length === 0) {
|
|
53
|
+
return `\n ${chalk.green('No findings — package looks clean!')}\n`;
|
|
54
|
+
}
|
|
55
|
+
const sorted = sortFindingsBySeverity(findings);
|
|
56
|
+
// Calculate column widths
|
|
57
|
+
const sevWidth = 10;
|
|
58
|
+
const analyzerWidth = Math.max(10, ...sorted.map((f) => f.analyzer.length)) + 2;
|
|
59
|
+
const titleWidth = Math.max(20, ...sorted.map((f) => f.title.length));
|
|
60
|
+
const headerSev = padRight('Severity', sevWidth);
|
|
61
|
+
const headerAnalyzer = padRight('Analyzer', analyzerWidth);
|
|
62
|
+
const headerFinding = padRight('Finding', titleWidth);
|
|
63
|
+
const dividerSev = '\u2500'.repeat(sevWidth);
|
|
64
|
+
const dividerAnalyzer = '\u2500'.repeat(analyzerWidth);
|
|
65
|
+
const dividerFinding = '\u2500'.repeat(titleWidth);
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push(` \u250c${'\u2500'.repeat(sevWidth + 1)}\u252c${'\u2500'.repeat(analyzerWidth + 1)}\u252c${'\u2500'.repeat(titleWidth + 1)}\u2510`);
|
|
69
|
+
lines.push(` \u2502 ${chalk.bold(headerSev)}\u2502 ${chalk.bold(headerAnalyzer)}\u2502 ${chalk.bold(headerFinding)}\u2502`);
|
|
70
|
+
lines.push(` \u251c${dividerSev}\u2500\u253c${dividerAnalyzer}\u2500\u253c${dividerFinding}\u2500\u2524`);
|
|
71
|
+
for (const finding of sorted) {
|
|
72
|
+
const sevLabel = finding.severity.toUpperCase();
|
|
73
|
+
const colorize = SEVERITY_COLORS[finding.severity];
|
|
74
|
+
const sevCell = padRight(colorize(sevLabel), sevWidth + (colorize(sevLabel).length - sevLabel.length));
|
|
75
|
+
const analyzerCell = padRight(finding.analyzer, analyzerWidth);
|
|
76
|
+
const titleCell = padRight(finding.title, titleWidth);
|
|
77
|
+
lines.push(` \u2502 ${sevCell}\u2502 ${analyzerCell}\u2502 ${titleCell}\u2502`);
|
|
78
|
+
}
|
|
79
|
+
lines.push(` \u2514${'\u2500'.repeat(sevWidth + 1)}\u2534${'\u2500'.repeat(analyzerWidth + 1)}\u2534${'\u2500'.repeat(titleWidth + 1)}\u2518`);
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
// ─── Formatter ───────────────────────────────────────────
|
|
83
|
+
export class TerminalFormatter {
|
|
84
|
+
name = 'terminal';
|
|
85
|
+
format(report) {
|
|
86
|
+
const lines = [];
|
|
87
|
+
// Header
|
|
88
|
+
lines.push('');
|
|
89
|
+
lines.push(` ${chalk.bold.cyan('mitnick')} — Security Analysis`);
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push(` Checking ${chalk.bold(`${report.packageName}@${report.version}`)}...`);
|
|
92
|
+
lines.push('');
|
|
93
|
+
// Analyzer results
|
|
94
|
+
for (const result of report.results) {
|
|
95
|
+
lines.push(formatAnalyzerLine(result));
|
|
96
|
+
}
|
|
97
|
+
// Score
|
|
98
|
+
const gradeColor = GRADE_COLORS[report.grade];
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push(` Score: ${gradeColor(`${report.score}/100`)} (${gradeColor(report.grade)})`);
|
|
101
|
+
// Findings table
|
|
102
|
+
const allFindings = report.results.flatMap((r) => r.findings);
|
|
103
|
+
lines.push(buildFindingsTable(allFindings));
|
|
104
|
+
// Duration
|
|
105
|
+
lines.push(` Analyzed in ${formatDuration(report.duration)}`);
|
|
106
|
+
lines.push('');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../../src/cli/formatters/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mitnick CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Uses Commander.js to define the program structure, commands,
|
|
6
|
+
* and options. Handles errors with user-friendly messages.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import { executeCheck } from './commands/check.js';
|
|
11
|
+
import { SEVERITY_LEVELS, } from '../core/types.js';
|
|
12
|
+
// ─── Version ─────────────────────────────────────────────
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const packageJson = require('../../package.json');
|
|
15
|
+
// ─── Validation ──────────────────────────────────────────
|
|
16
|
+
function isValidSeverity(value) {
|
|
17
|
+
return SEVERITY_LEVELS.includes(value);
|
|
18
|
+
}
|
|
19
|
+
function parseSeverity(value) {
|
|
20
|
+
const lower = value.toLowerCase();
|
|
21
|
+
if (!isValidSeverity(lower)) {
|
|
22
|
+
throw new Error(`Invalid severity "${value}". Must be one of: ${SEVERITY_LEVELS.join(', ')}`);
|
|
23
|
+
}
|
|
24
|
+
return lower;
|
|
25
|
+
}
|
|
26
|
+
// ─── Program ─────────────────────────────────────────────
|
|
27
|
+
const program = new Command();
|
|
28
|
+
program
|
|
29
|
+
.name('mitnick')
|
|
30
|
+
.description('Pre-install security analysis CLI for npm packages')
|
|
31
|
+
.version(packageJson.version);
|
|
32
|
+
program
|
|
33
|
+
.command('check')
|
|
34
|
+
.description('Analyze npm packages for security issues before installation')
|
|
35
|
+
.argument('<packages...>', 'Package specifiers (e.g., express, lodash@4.17.21)')
|
|
36
|
+
.option('--json', 'Output results as JSON', false)
|
|
37
|
+
.option('--sarif', 'Output results in SARIF v2.1.0 format', false)
|
|
38
|
+
.option('--fail-on <severity>', 'Exit with code 1 if findings at or above severity (critical, high, medium, low, info)')
|
|
39
|
+
.option('--verbose', 'Show additional analysis details', false)
|
|
40
|
+
.action(async (packages, cmdOptions) => {
|
|
41
|
+
// Determine output format
|
|
42
|
+
let format = 'terminal';
|
|
43
|
+
if (cmdOptions.json) {
|
|
44
|
+
format = 'json';
|
|
45
|
+
}
|
|
46
|
+
else if (cmdOptions.sarif) {
|
|
47
|
+
format = 'sarif';
|
|
48
|
+
}
|
|
49
|
+
// Parse --fail-on
|
|
50
|
+
let failOn;
|
|
51
|
+
if (cmdOptions.failOn !== undefined) {
|
|
52
|
+
failOn = parseSeverity(cmdOptions.failOn);
|
|
53
|
+
}
|
|
54
|
+
const baseOptions = {
|
|
55
|
+
packages,
|
|
56
|
+
format,
|
|
57
|
+
verbose: cmdOptions.verbose,
|
|
58
|
+
};
|
|
59
|
+
const options = failOn !== undefined ? { ...baseOptions, failOn } : baseOptions;
|
|
60
|
+
const passed = await executeCheck(options);
|
|
61
|
+
if (!passed) {
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// ─── Error Handling ──────────────────────────────────────
|
|
66
|
+
program.exitOverride();
|
|
67
|
+
async function main() {
|
|
68
|
+
try {
|
|
69
|
+
await program.parseAsync(process.argv);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Commander throws for --help and --version, which is expected
|
|
73
|
+
if (error instanceof Error && 'code' in error) {
|
|
74
|
+
const code = error.code;
|
|
75
|
+
if (code === 'commander.helpDisplayed' || code === 'commander.version') {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
console.error(`\nError: ${message}\n`);
|
|
81
|
+
console.error('Run "mitnick --help" for usage information.\n');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
void main();
|
|
86
|
+
//# sourceMappingURL=index.js.map
|