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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/dist/analyzers/analyzer.interface.d.ts +32 -0
  4. package/dist/analyzers/analyzer.interface.d.ts.map +1 -0
  5. package/dist/analyzers/analyzer.interface.js +2 -0
  6. package/dist/analyzers/analyzer.interface.js.map +1 -0
  7. package/dist/analyzers/analyzer.registry.d.ts +16 -0
  8. package/dist/analyzers/analyzer.registry.d.ts.map +1 -0
  9. package/dist/analyzers/analyzer.registry.js +40 -0
  10. package/dist/analyzers/analyzer.registry.js.map +1 -0
  11. package/dist/analyzers/dependency-confusion/index.d.ts +14 -0
  12. package/dist/analyzers/dependency-confusion/index.d.ts.map +1 -0
  13. package/dist/analyzers/dependency-confusion/index.js +147 -0
  14. package/dist/analyzers/dependency-confusion/index.js.map +1 -0
  15. package/dist/analyzers/dormant-package/index.d.ts +14 -0
  16. package/dist/analyzers/dormant-package/index.d.ts.map +1 -0
  17. package/dist/analyzers/dormant-package/index.js +137 -0
  18. package/dist/analyzers/dormant-package/index.js.map +1 -0
  19. package/dist/analyzers/file-based-analyzer.d.ts +20 -0
  20. package/dist/analyzers/file-based-analyzer.d.ts.map +1 -0
  21. package/dist/analyzers/file-based-analyzer.js +35 -0
  22. package/dist/analyzers/file-based-analyzer.js.map +1 -0
  23. package/dist/analyzers/install-scripts/index.d.ts +13 -0
  24. package/dist/analyzers/install-scripts/index.d.ts.map +1 -0
  25. package/dist/analyzers/install-scripts/index.js +125 -0
  26. package/dist/analyzers/install-scripts/index.js.map +1 -0
  27. package/dist/analyzers/license/index.d.ts +12 -0
  28. package/dist/analyzers/license/index.d.ts.map +1 -0
  29. package/dist/analyzers/license/index.js +199 -0
  30. package/dist/analyzers/license/index.js.map +1 -0
  31. package/dist/analyzers/maintainer/index.d.ts +12 -0
  32. package/dist/analyzers/maintainer/index.d.ts.map +1 -0
  33. package/dist/analyzers/maintainer/index.js +93 -0
  34. package/dist/analyzers/maintainer/index.js.map +1 -0
  35. package/dist/analyzers/network-calls/index.d.ts +15 -0
  36. package/dist/analyzers/network-calls/index.d.ts.map +1 -0
  37. package/dist/analyzers/network-calls/index.js +212 -0
  38. package/dist/analyzers/network-calls/index.js.map +1 -0
  39. package/dist/analyzers/obfuscation/index.d.ts +19 -0
  40. package/dist/analyzers/obfuscation/index.d.ts.map +1 -0
  41. package/dist/analyzers/obfuscation/index.js +218 -0
  42. package/dist/analyzers/obfuscation/index.js.map +1 -0
  43. package/dist/analyzers/prototype-pollution/index.d.ts +18 -0
  44. package/dist/analyzers/prototype-pollution/index.d.ts.map +1 -0
  45. package/dist/analyzers/prototype-pollution/index.js +257 -0
  46. package/dist/analyzers/prototype-pollution/index.js.map +1 -0
  47. package/dist/analyzers/sensitive-data/index.d.ts +16 -0
  48. package/dist/analyzers/sensitive-data/index.d.ts.map +1 -0
  49. package/dist/analyzers/sensitive-data/index.js +254 -0
  50. package/dist/analyzers/sensitive-data/index.js.map +1 -0
  51. package/dist/analyzers/typosquatting/index.d.ts +14 -0
  52. package/dist/analyzers/typosquatting/index.d.ts.map +1 -0
  53. package/dist/analyzers/typosquatting/index.js +127 -0
  54. package/dist/analyzers/typosquatting/index.js.map +1 -0
  55. package/dist/analyzers/typosquatting/popular-packages.d.ts +9 -0
  56. package/dist/analyzers/typosquatting/popular-packages.d.ts.map +1 -0
  57. package/dist/analyzers/typosquatting/popular-packages.js +236 -0
  58. package/dist/analyzers/typosquatting/popular-packages.js.map +1 -0
  59. package/dist/analyzers/vulnerability/index.d.ts +12 -0
  60. package/dist/analyzers/vulnerability/index.d.ts.map +1 -0
  61. package/dist/analyzers/vulnerability/index.js +147 -0
  62. package/dist/analyzers/vulnerability/index.js.map +1 -0
  63. package/dist/cli/commands/check.d.ts +21 -0
  64. package/dist/cli/commands/check.d.ts.map +1 -0
  65. package/dist/cli/commands/check.js +204 -0
  66. package/dist/cli/commands/check.js.map +1 -0
  67. package/dist/cli/formatters/formatter.interface.d.ts +14 -0
  68. package/dist/cli/formatters/formatter.interface.d.ts.map +1 -0
  69. package/dist/cli/formatters/formatter.interface.js +2 -0
  70. package/dist/cli/formatters/formatter.interface.js.map +1 -0
  71. package/dist/cli/formatters/json.d.ts +12 -0
  72. package/dist/cli/formatters/json.d.ts.map +1 -0
  73. package/dist/cli/formatters/json.js +12 -0
  74. package/dist/cli/formatters/json.js.map +1 -0
  75. package/dist/cli/formatters/sarif.d.ts +13 -0
  76. package/dist/cli/formatters/sarif.d.ts.map +1 -0
  77. package/dist/cli/formatters/sarif.js +101 -0
  78. package/dist/cli/formatters/sarif.js.map +1 -0
  79. package/dist/cli/formatters/terminal.d.ts +13 -0
  80. package/dist/cli/formatters/terminal.d.ts.map +1 -0
  81. package/dist/cli/formatters/terminal.js +110 -0
  82. package/dist/cli/formatters/terminal.js.map +1 -0
  83. package/dist/cli/index.d.ts +9 -0
  84. package/dist/cli/index.d.ts.map +1 -0
  85. package/dist/cli/index.js +86 -0
  86. package/dist/cli/index.js.map +1 -0
  87. package/dist/core/engine.d.ts +23 -0
  88. package/dist/core/engine.d.ts.map +1 -0
  89. package/dist/core/engine.js +55 -0
  90. package/dist/core/engine.js.map +1 -0
  91. package/dist/core/scorer.d.ts +30 -0
  92. package/dist/core/scorer.d.ts.map +1 -0
  93. package/dist/core/scorer.js +88 -0
  94. package/dist/core/scorer.js.map +1 -0
  95. package/dist/core/types.d.ts +76 -0
  96. package/dist/core/types.d.ts.map +1 -0
  97. package/dist/core/types.js +30 -0
  98. package/dist/core/types.js.map +1 -0
  99. package/dist/index.d.ts +33 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +30 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/registry/client.d.ts +27 -0
  104. package/dist/registry/client.d.ts.map +1 -0
  105. package/dist/registry/client.js +189 -0
  106. package/dist/registry/client.js.map +1 -0
  107. package/dist/registry/tarball.d.ts +34 -0
  108. package/dist/registry/tarball.d.ts.map +1 -0
  109. package/dist/registry/tarball.js +103 -0
  110. package/dist/registry/tarball.js.map +1 -0
  111. package/dist/utils/ast.d.ts +74 -0
  112. package/dist/utils/ast.d.ts.map +1 -0
  113. package/dist/utils/ast.js +150 -0
  114. package/dist/utils/ast.js.map +1 -0
  115. package/dist/utils/fs.d.ts +28 -0
  116. package/dist/utils/fs.d.ts.map +1 -0
  117. package/dist/utils/fs.js +78 -0
  118. package/dist/utils/fs.js.map +1 -0
  119. package/dist/utils/http.d.ts +40 -0
  120. package/dist/utils/http.d.ts.map +1 -0
  121. package/dist/utils/http.js +116 -0
  122. package/dist/utils/http.js.map +1 -0
  123. package/dist/utils/logger.d.ts +46 -0
  124. package/dist/utils/logger.d.ts.map +1 -0
  125. package/dist/utils/logger.js +91 -0
  126. package/dist/utils/logger.js.map +1 -0
  127. package/dist/utils/strings.d.ts +8 -0
  128. package/dist/utils/strings.d.ts.map +1 -0
  129. package/dist/utils/strings.js +12 -0
  130. package/dist/utils/strings.js.map +1 -0
  131. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=formatter.interface.js.map
@@ -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,9 @@
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
+ export {};
9
+ //# sourceMappingURL=index.d.ts.map
@@ -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