kramscan 0.1.0 → 0.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 +392 -87
- package/dist/agent/confirmation.d.ts +38 -0
- package/dist/agent/confirmation.js +210 -0
- package/dist/agent/context.d.ts +81 -0
- package/dist/agent/context.js +227 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.js +32 -0
- package/dist/agent/orchestrator.d.ts +63 -0
- package/dist/agent/orchestrator.js +370 -0
- package/dist/agent/prompts/system.d.ts +6 -0
- package/dist/agent/prompts/system.js +116 -0
- package/dist/agent/skill-registry.d.ts +78 -0
- package/dist/agent/skill-registry.js +202 -0
- package/dist/agent/skills/analyze-findings.d.ts +22 -0
- package/dist/agent/skills/analyze-findings.js +191 -0
- package/dist/agent/skills/generate-report.d.ts +26 -0
- package/dist/agent/skills/generate-report.js +436 -0
- package/dist/agent/skills/health-check.d.ts +28 -0
- package/dist/agent/skills/health-check.js +344 -0
- package/dist/agent/skills/index.d.ts +9 -0
- package/dist/agent/skills/index.js +17 -0
- package/dist/agent/skills/verify-finding.d.ts +17 -0
- package/dist/agent/skills/verify-finding.js +91 -0
- package/dist/agent/skills/web-scan.d.ts +22 -0
- package/dist/agent/skills/web-scan.js +203 -0
- package/dist/agent/types.d.ts +141 -0
- package/dist/agent/types.js +16 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +176 -139
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +250 -0
- package/dist/commands/ai.d.ts +2 -0
- package/dist/commands/ai.js +112 -0
- package/dist/commands/analyze.js +104 -55
- package/dist/commands/config.js +63 -37
- package/dist/commands/doctor.js +22 -17
- package/dist/commands/onboard.js +190 -125
- package/dist/commands/report.js +69 -77
- package/dist/commands/scan.js +261 -81
- package/dist/commands/scans.d.ts +2 -0
- package/dist/commands/scans.js +51 -0
- package/dist/core/ai-client.d.ts +7 -2
- package/dist/core/ai-client.js +231 -20
- package/dist/core/ai-payloads.d.ts +17 -0
- package/dist/core/ai-payloads.js +54 -0
- package/dist/core/config-schema.d.ts +197 -0
- package/dist/core/config-schema.js +68 -0
- package/dist/core/config-schema.test.d.ts +1 -0
- package/dist/core/config-schema.test.js +151 -0
- package/dist/core/config.d.ts +17 -36
- package/dist/core/config.js +261 -20
- package/dist/core/errors.d.ts +71 -0
- package/dist/core/errors.js +162 -0
- package/dist/core/scan-index.d.ts +19 -0
- package/dist/core/scan-index.js +52 -0
- package/dist/core/scan-storage.d.ts +11 -0
- package/dist/core/scan-storage.js +69 -0
- package/dist/core/scanner.d.ts +101 -4
- package/dist/core/scanner.js +432 -63
- package/dist/core/vulnerability-detector.d.ts +18 -2
- package/dist/core/vulnerability-detector.js +349 -38
- package/dist/core/vulnerability-detector.test.d.ts +1 -0
- package/dist/core/vulnerability-detector.test.js +210 -0
- package/dist/index.js +3 -0
- package/dist/plugins/PluginManager.d.ts +27 -0
- package/dist/plugins/PluginManager.js +166 -0
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.js +19 -0
- package/dist/plugins/types.d.ts +55 -0
- package/dist/plugins/types.js +25 -0
- package/dist/plugins/vulnerabilities/CSRFPlugin.d.ts +8 -0
- package/dist/plugins/vulnerabilities/CSRFPlugin.js +34 -0
- package/dist/plugins/vulnerabilities/SQLInjectionPlugin.d.ts +11 -0
- package/dist/plugins/vulnerabilities/SQLInjectionPlugin.js +109 -0
- package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.d.ts +11 -0
- package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.js +63 -0
- package/dist/plugins/vulnerabilities/SensitiveDataPlugin.d.ts +9 -0
- package/dist/plugins/vulnerabilities/SensitiveDataPlugin.js +32 -0
- package/dist/plugins/vulnerabilities/XSSPlugin.d.ts +15 -0
- package/dist/plugins/vulnerabilities/XSSPlugin.js +81 -0
- package/dist/reports/PdfGenerator.d.ts +36 -0
- package/dist/reports/PdfGenerator.js +379 -0
- package/dist/utils/logger.d.ts +33 -1
- package/dist/utils/logger.js +127 -8
- package/dist/utils/theme.d.ts +55 -0
- package/dist/utils/theme.js +195 -0
- package/package.json +27 -6
- package/dist/core/executor.d.ts +0 -2
- package/dist/core/executor.js +0 -74
- package/dist/core/logger.d.ts +0 -12
- package/dist/core/logger.js +0 -51
- package/dist/core/registry.d.ts +0 -3
- package/dist/core/registry.js +0 -35
- package/dist/core/storage.d.ts +0 -4
- package/dist/core/storage.js +0 -39
- package/dist/core/types.d.ts +0 -24
- package/dist/core/types.js +0 -2
- package/dist/skills/base.d.ts +0 -8
- package/dist/skills/base.js +0 -6
- package/dist/skills/builtin.d.ts +0 -4
- package/dist/skills/builtin.js +0 -71
- package/dist/skills/loader.d.ts +0 -2
- package/dist/skills/loader.js +0 -27
- package/dist/skills/types.d.ts +0 -46
- package/dist/skills/types.js +0 -2
package/dist/commands/scan.js
CHANGED
|
@@ -1,124 +1,304 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.registerScanCommand = registerScanCommand;
|
|
7
|
-
const
|
|
40
|
+
const commander_1 = require("commander");
|
|
8
41
|
const scanner_1 = require("../core/scanner");
|
|
42
|
+
const scan_index_1 = require("../core/scan-index");
|
|
43
|
+
const scan_storage_1 = require("../core/scan-storage");
|
|
44
|
+
const config_1 = require("../core/config");
|
|
45
|
+
const PdfGenerator_1 = require("../reports/PdfGenerator");
|
|
46
|
+
const theme_1 = require("../utils/theme");
|
|
9
47
|
const logger_1 = require("../utils/logger");
|
|
10
48
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
49
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const os_1 = __importDefault(require("os"));
|
|
13
50
|
function registerScanCommand(program) {
|
|
14
51
|
program
|
|
15
52
|
.command("scan <url>")
|
|
16
53
|
.description("Scan a target URL for vulnerabilities")
|
|
17
|
-
.option("
|
|
18
|
-
.option("-
|
|
54
|
+
.option("--profile <name>", "Scan profile: quick|balanced|deep", "balanced")
|
|
55
|
+
.option("-d, --depth <number>", "Crawl depth (overrides profile)")
|
|
56
|
+
.option("-t, --timeout <ms>", "Request timeout (overrides profile)")
|
|
57
|
+
.option("--max-pages <number>", "Maximum pages to crawl (overrides profile)")
|
|
58
|
+
.option("--max-links-per-page <number>", "Maximum links to follow per page (overrides profile)")
|
|
59
|
+
.option("--include <regex...>", "Only include URLs matching these regex patterns")
|
|
60
|
+
.option("--exclude <regex...>", "Exclude URLs matching these regex patterns")
|
|
61
|
+
.option("--no-pdf", "Disable automatic PDF report generation")
|
|
62
|
+
.option("--json", "Output scan results as JSON to stdout (CI/CD mode)")
|
|
19
63
|
.option("-o, --output <file>", "Save results to file")
|
|
20
64
|
.option("--headless", "Run in headless mode", true)
|
|
65
|
+
.option("--no-plugins", "Disable plugin-based scanning (use legacy mode)")
|
|
21
66
|
.action(async (url, options) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
67
|
+
const jsonMode = options.json === true;
|
|
68
|
+
if (!jsonMode) {
|
|
69
|
+
console.log("");
|
|
70
|
+
console.log(theme_1.theme.brand.bold("🔍 Starting Security Scan"));
|
|
71
|
+
console.log(theme_1.theme.gray("─".repeat(50)));
|
|
72
|
+
console.log("");
|
|
73
|
+
}
|
|
26
74
|
// Validate URL
|
|
27
75
|
try {
|
|
28
76
|
new URL(url);
|
|
29
77
|
}
|
|
30
78
|
catch (error) {
|
|
31
|
-
|
|
79
|
+
if (jsonMode) {
|
|
80
|
+
console.log(JSON.stringify({ error: `Invalid URL: ${url}` }));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
logger_1.logger.error(`Invalid URL: ${url}`);
|
|
84
|
+
}
|
|
32
85
|
process.exit(1);
|
|
33
86
|
}
|
|
34
|
-
const spinner = logger_1.logger.spinner("Initializing scanner...");
|
|
87
|
+
const spinner = jsonMode ? null : logger_1.logger.spinner("Initializing scanner...");
|
|
35
88
|
try {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
89
|
+
const profile = String(options.profile || "balanced").toLowerCase();
|
|
90
|
+
const defaults = config_1.scanProfiles[profile] || config_1.scanProfiles.balanced;
|
|
91
|
+
const parsedDepth = Number.parseInt(options.depth ?? String(defaults.depth), 10);
|
|
92
|
+
const parsedTimeout = Number.parseInt(options.timeout ?? String(defaults.timeout), 10);
|
|
93
|
+
const parsedMaxPages = Number.parseInt(options.maxPages ?? String(defaults.maxPages), 10);
|
|
94
|
+
const parsedMaxLinksPerPage = Number.parseInt(options.maxLinksPerPage ?? String(defaults.maxLinksPerPage), 10);
|
|
95
|
+
if (!Number.isFinite(parsedDepth) || parsedDepth < 1 || parsedDepth > 5) {
|
|
96
|
+
throw new Error("Depth must be a number between 1 and 5.");
|
|
97
|
+
}
|
|
98
|
+
if (!Number.isFinite(parsedTimeout) || parsedTimeout < 1000) {
|
|
99
|
+
throw new Error("Timeout must be a positive number (milliseconds).");
|
|
100
|
+
}
|
|
101
|
+
if (!Number.isFinite(parsedMaxPages) || parsedMaxPages < 1) {
|
|
102
|
+
throw new Error("max-pages must be a positive number.");
|
|
103
|
+
}
|
|
104
|
+
if (!Number.isFinite(parsedMaxLinksPerPage) || parsedMaxLinksPerPage < 1) {
|
|
105
|
+
throw new Error("max-links-per-page must be a positive number.");
|
|
106
|
+
}
|
|
107
|
+
// Display scan estimate
|
|
108
|
+
if (!jsonMode) {
|
|
109
|
+
const estimateMap = {
|
|
110
|
+
quick: "~15–30s",
|
|
111
|
+
balanced: "~30–90s",
|
|
112
|
+
deep: "~2–5min",
|
|
113
|
+
};
|
|
114
|
+
const estimate = estimateMap[profile] || estimateMap.balanced;
|
|
115
|
+
console.log(theme_1.theme.gray(` ⏱ Estimated duration: ${estimate} (${profile} profile)`));
|
|
116
|
+
console.log("");
|
|
117
|
+
}
|
|
118
|
+
const scanner = new scanner_1.Scanner(options.plugins !== false);
|
|
119
|
+
// Set up event listeners for progress feedback
|
|
120
|
+
let currentStage = "initializing";
|
|
121
|
+
let vulnerabilitiesFound = 0;
|
|
122
|
+
scanner.on("scan:start", () => {
|
|
123
|
+
if (spinner)
|
|
124
|
+
spinner.text = `Starting scan of ${url}...`;
|
|
125
|
+
currentStage = "scanning";
|
|
126
|
+
});
|
|
127
|
+
scanner.on("crawl:page", (data) => {
|
|
128
|
+
if (spinner)
|
|
129
|
+
spinner.text = `Crawling: ${data.url} (${data.crawledCount}/${data.maxPages})`;
|
|
130
|
+
currentStage = "crawling";
|
|
131
|
+
});
|
|
132
|
+
scanner.on("form:test", (data) => {
|
|
133
|
+
if (spinner)
|
|
134
|
+
spinner.text = `Testing forms on ${data.url} (${data.formCount} forms)...`;
|
|
135
|
+
currentStage = "testing forms";
|
|
42
136
|
});
|
|
43
|
-
|
|
137
|
+
scanner.on("vuln:found", (data) => {
|
|
138
|
+
vulnerabilitiesFound++;
|
|
139
|
+
if (spinner) {
|
|
140
|
+
spinner.stopAndPersist({
|
|
141
|
+
symbol: theme_1.theme.warning("⚠️"),
|
|
142
|
+
text: `Found ${data.vulnerability.severity} vulnerability: ${data.vulnerability.title}`
|
|
143
|
+
});
|
|
144
|
+
spinner.start(`Continuing scan (${vulnerabilitiesFound} vulns found)...`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
scanner.on("scan:complete", () => {
|
|
148
|
+
if (spinner)
|
|
149
|
+
spinner.text = "Finalizing scan results...";
|
|
150
|
+
});
|
|
151
|
+
scanner.on("crawl:error", (data) => {
|
|
152
|
+
if (!jsonMode)
|
|
153
|
+
logger_1.logger.warn(`Failed to crawl ${data.url}: ${data.error.message}`);
|
|
154
|
+
});
|
|
155
|
+
const scanOptions = {
|
|
156
|
+
depth: parsedDepth,
|
|
157
|
+
timeout: parsedTimeout,
|
|
158
|
+
headless: options.headless,
|
|
159
|
+
maxPages: parsedMaxPages,
|
|
160
|
+
maxLinksPerPage: parsedMaxLinksPerPage,
|
|
161
|
+
include: options.include,
|
|
162
|
+
exclude: options.exclude,
|
|
163
|
+
profile,
|
|
164
|
+
};
|
|
165
|
+
const result = await scanner.scan(url, scanOptions);
|
|
166
|
+
if (spinner)
|
|
167
|
+
spinner.succeed("Scan complete!");
|
|
44
168
|
// Save results
|
|
45
|
-
const scanDir =
|
|
46
|
-
await promises_1.default.mkdir(scanDir, { recursive: true });
|
|
169
|
+
const scanDir = await (0, scan_storage_1.ensureScansDirectory)();
|
|
47
170
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
48
171
|
const filename = options.output || `scan-${timestamp}.json`;
|
|
49
172
|
const filepath = path_1.default.isAbsolute(filename)
|
|
50
173
|
? filename
|
|
51
174
|
: path_1.default.join(scanDir, filename);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.log("");
|
|
68
|
-
const { summary } = result;
|
|
69
|
-
if (summary.total === 0) {
|
|
70
|
-
console.log(chalk_1.default.green("✓ No vulnerabilities found!"));
|
|
175
|
+
// Include error data in JSON
|
|
176
|
+
const scanErrors = scanner.getScanErrors();
|
|
177
|
+
const pluginErrors = scanner.getPluginErrors();
|
|
178
|
+
const resultWithErrors = {
|
|
179
|
+
...result,
|
|
180
|
+
errors: {
|
|
181
|
+
scan: scanErrors,
|
|
182
|
+
plugins: Object.fromEntries(pluginErrors),
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
await promises_1.default.writeFile(filepath, JSON.stringify(resultWithErrors, null, 2));
|
|
186
|
+
// In JSON mode, output the result to stdout and exit
|
|
187
|
+
if (jsonMode) {
|
|
188
|
+
console.log(JSON.stringify(resultWithErrors, null, 2));
|
|
189
|
+
return;
|
|
71
190
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
191
|
+
let pdfPath = null;
|
|
192
|
+
if (options.pdf !== false) {
|
|
193
|
+
const pdfSpinner = logger_1.logger.spinner("Generating PDF report...");
|
|
194
|
+
try {
|
|
195
|
+
const pdfData = {
|
|
196
|
+
scanResult: result,
|
|
197
|
+
scanErrors,
|
|
198
|
+
pluginErrors,
|
|
199
|
+
};
|
|
200
|
+
pdfPath = await PdfGenerator_1.pdfGenerator.generate(pdfData);
|
|
201
|
+
pdfSpinner.succeed("PDF report generated!");
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
pdfSpinner.fail("PDF report generation failed");
|
|
205
|
+
logger_1.logger.warn(`Could not generate PDF automatically: ${error.message}`);
|
|
206
|
+
}
|
|
83
207
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
208
|
+
try {
|
|
209
|
+
const target = new URL(result.target);
|
|
210
|
+
await (0, scan_index_1.addScanToIndex)({
|
|
211
|
+
target: result.target,
|
|
212
|
+
hostname: target.hostname || "unknown",
|
|
213
|
+
timestamp: result.timestamp,
|
|
214
|
+
jsonPath: filepath,
|
|
215
|
+
pdfPath: pdfPath || undefined,
|
|
216
|
+
summary: result.summary,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
logger_1.logger.debug(`Failed to update scan index: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
// Display summary using theme
|
|
223
|
+
(0, theme_1.displayScanSummary)({
|
|
224
|
+
target: result.target,
|
|
225
|
+
duration: result.duration,
|
|
226
|
+
metadata: result.metadata,
|
|
227
|
+
summary: result.summary,
|
|
228
|
+
vulnerabilities: result.vulnerabilities,
|
|
229
|
+
filepath,
|
|
230
|
+
pdfPath,
|
|
231
|
+
});
|
|
232
|
+
// Display any scan errors
|
|
233
|
+
const scanErrorsList = scanner.getScanErrors();
|
|
234
|
+
const pluginErrorsMap = scanner.getPluginErrors();
|
|
235
|
+
if (scanErrorsList.length > 0 || pluginErrorsMap.size > 0) {
|
|
236
|
+
console.log(theme_1.theme.warning("⚠️ Some URLs/plugins encountered errors:"));
|
|
237
|
+
if (scanErrorsList.length > 0) {
|
|
238
|
+
console.log(theme_1.theme.yellow(" Crawl Errors:"));
|
|
239
|
+
for (const error of scanErrorsList.slice(0, 5)) {
|
|
240
|
+
console.log(theme_1.theme.gray(` - ${error.url}: ${error.error}`));
|
|
241
|
+
}
|
|
242
|
+
if (scanErrorsList.length > 5) {
|
|
243
|
+
console.log(theme_1.theme.gray(` ... and ${scanErrorsList.length - 5} more`));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (pluginErrorsMap.size > 0) {
|
|
247
|
+
console.log(theme_1.theme.yellow(" Plugin Errors:"));
|
|
248
|
+
for (const [pluginName, errors] of pluginErrorsMap) {
|
|
249
|
+
console.log(theme_1.theme.gray(` ${pluginName}:`));
|
|
250
|
+
for (const error of errors.slice(0, 3)) {
|
|
251
|
+
console.log(theme_1.theme.gray(` - ${error.url}: ${error.error}`));
|
|
252
|
+
}
|
|
253
|
+
if (errors.length > 3) {
|
|
254
|
+
console.log(theme_1.theme.gray(` ... and ${errors.length - 3} more`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const totalPluginErrors = Array.from(pluginErrorsMap.values()).reduce((sum, errs) => sum + errs.length, 0);
|
|
258
|
+
if (totalPluginErrors > 10) {
|
|
259
|
+
console.log(theme_1.theme.gray(` Total plugin errors: ${totalPluginErrors}`));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
91
262
|
console.log("");
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
:
|
|
102
|
-
|
|
103
|
-
:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
263
|
+
}
|
|
264
|
+
// Add "What's Next" interactive prompt
|
|
265
|
+
if (!jsonMode) {
|
|
266
|
+
const inquirer = (await Promise.resolve().then(() => __importStar(require("inquirer")))).default;
|
|
267
|
+
const { nextAction } = await inquirer.prompt([
|
|
268
|
+
{
|
|
269
|
+
type: "list",
|
|
270
|
+
name: "nextAction",
|
|
271
|
+
message: theme_1.theme.cyan("Scan complete! What would you like to do next?"),
|
|
272
|
+
choices: [
|
|
273
|
+
{ name: "🧠 Analyze findings with AI", value: "analyze" },
|
|
274
|
+
{ name: "📄 Generate a professional report", value: "report" },
|
|
275
|
+
{ name: "👋 Exit to main menu", value: "exit" }
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
]);
|
|
279
|
+
if (nextAction === "analyze") {
|
|
280
|
+
const { registerAnalyzeCommand } = await Promise.resolve().then(() => __importStar(require("./analyze")));
|
|
281
|
+
const analyzeProgram = new commander_1.Command();
|
|
282
|
+
registerAnalyzeCommand(analyzeProgram);
|
|
283
|
+
await analyzeProgram.parseAsync(["node", "kramscan", "analyze", filepath]);
|
|
108
284
|
}
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
285
|
+
else if (nextAction === "report") {
|
|
286
|
+
const { registerReportCommand } = await Promise.resolve().then(() => __importStar(require("./report")));
|
|
287
|
+
const reportProgram = new commander_1.Command();
|
|
288
|
+
registerReportCommand(reportProgram);
|
|
289
|
+
await reportProgram.parseAsync(["node", "kramscan", "report", filepath]);
|
|
112
290
|
}
|
|
113
291
|
}
|
|
114
|
-
console.log(chalk_1.default.cyan("💡 Next steps:"));
|
|
115
|
-
console.log(chalk_1.default.white(` 1. Run ${chalk_1.default.cyan(`kramscan analyze ${filepath}`)} for AI-powered insights`));
|
|
116
|
-
console.log(chalk_1.default.white(` 2. Run ${chalk_1.default.cyan(`kramscan report ${filepath}`)} to generate a report`));
|
|
117
|
-
console.log("");
|
|
118
292
|
}
|
|
119
293
|
catch (error) {
|
|
120
|
-
spinner
|
|
121
|
-
|
|
294
|
+
if (spinner)
|
|
295
|
+
spinner.fail("Scan failed");
|
|
296
|
+
if (jsonMode) {
|
|
297
|
+
console.log(JSON.stringify({ error: error.message }));
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
logger_1.logger.error(error.message);
|
|
301
|
+
}
|
|
122
302
|
process.exit(1);
|
|
123
303
|
}
|
|
124
304
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerScansCommand = registerScansCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const scan_index_1 = require("../core/scan-index");
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
function registerScansCommand(program) {
|
|
11
|
+
const scans = program.command("scans").description("Manage saved scans");
|
|
12
|
+
scans
|
|
13
|
+
.command("list")
|
|
14
|
+
.description("List recent scans")
|
|
15
|
+
.option("-n, --limit <number>", "How many scans to show", "20")
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
const limit = Number.parseInt(options.limit, 10);
|
|
18
|
+
const entries = await (0, scan_index_1.listScans)(Number.isFinite(limit) ? limit : 20);
|
|
19
|
+
if (entries.length === 0) {
|
|
20
|
+
logger_1.logger.warn("No scans found in index yet. Run 'kramscan scan <url>' first.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log(chalk_1.default.bold.cyan("Recent Scans"));
|
|
25
|
+
console.log(chalk_1.default.gray("-".repeat(60)));
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
console.log(chalk_1.default.white(entry.timestamp), chalk_1.default.gray("-"), chalk_1.default.cyan(entry.hostname));
|
|
28
|
+
console.log(chalk_1.default.gray(" JSON:"), chalk_1.default.white(entry.jsonPath));
|
|
29
|
+
if (entry.pdfPath) {
|
|
30
|
+
console.log(chalk_1.default.gray(" PDF :"), chalk_1.default.white(entry.pdfPath));
|
|
31
|
+
}
|
|
32
|
+
console.log(chalk_1.default.gray(" Findings:"), chalk_1.default.white(`${entry.summary.total} total (${entry.summary.critical}C ${entry.summary.high}H ${entry.summary.medium}M ${entry.summary.low}L ${entry.summary.info}I)`));
|
|
33
|
+
console.log("");
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
scans
|
|
37
|
+
.command("latest")
|
|
38
|
+
.description("Show the latest scan paths")
|
|
39
|
+
.action(async () => {
|
|
40
|
+
const latest = await (0, scan_index_1.getLatestScan)();
|
|
41
|
+
if (!latest) {
|
|
42
|
+
logger_1.logger.warn("No scans found in index yet. Run 'kramscan scan <url>' first.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log(chalk_1.default.bold("Latest scan:"));
|
|
46
|
+
console.log(chalk_1.default.gray("Target:"), chalk_1.default.cyan(latest.target));
|
|
47
|
+
console.log(chalk_1.default.gray("Time :"), chalk_1.default.white(latest.timestamp));
|
|
48
|
+
console.log(chalk_1.default.gray("JSON :"), chalk_1.default.white(latest.jsonPath));
|
|
49
|
+
console.log(chalk_1.default.gray("PDF :"), chalk_1.default.white(latest.pdfPath || "N/A"));
|
|
50
|
+
});
|
|
51
|
+
}
|
package/dist/core/ai-client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ScanResult } from "./vulnerability-detector";
|
|
1
2
|
export interface AIResponse {
|
|
2
3
|
content: string;
|
|
3
4
|
usage?: {
|
|
@@ -7,6 +8,10 @@ export interface AIResponse {
|
|
|
7
8
|
};
|
|
8
9
|
}
|
|
9
10
|
export interface AIClient {
|
|
10
|
-
analyze(prompt: string): Promise<AIResponse>;
|
|
11
|
+
analyze(prompt: string, systemPrompt?: string): Promise<AIResponse>;
|
|
11
12
|
}
|
|
12
|
-
export declare function createAIClient(): AIClient
|
|
13
|
+
export declare function createAIClient(): Promise<AIClient>;
|
|
14
|
+
/**
|
|
15
|
+
* Generates a high-level executive summary of the scan results using AI.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateExecutiveSummary(client: AIClient, result: ScanResult): Promise<string>;
|