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/cli.js
CHANGED
|
@@ -32,69 +32,42 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.isVerbose = isVerbose;
|
|
40
|
+
exports.isDebug = isDebug;
|
|
41
|
+
exports.debugLog = debugLog;
|
|
36
42
|
exports.run = run;
|
|
37
43
|
const commander_1 = require("commander");
|
|
44
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
38
45
|
const onboard_1 = require("./commands/onboard");
|
|
39
46
|
const scan_1 = require("./commands/scan");
|
|
40
47
|
const analyze_1 = require("./commands/analyze");
|
|
41
48
|
const report_1 = require("./commands/report");
|
|
42
49
|
const config_1 = require("./commands/config");
|
|
43
50
|
const doctor_1 = require("./commands/doctor");
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
blue: "\x1b[34m",
|
|
54
|
-
magenta: "\x1b[35m",
|
|
55
|
-
cyan: "\x1b[36m",
|
|
56
|
-
white: "\x1b[37m",
|
|
57
|
-
gray: "\x1b[90m",
|
|
58
|
-
bgBlue: "\x1b[44m",
|
|
59
|
-
bgMagenta: "\x1b[45m",
|
|
60
|
-
brightCyan: "\x1b[96m",
|
|
61
|
-
brightMagenta: "\x1b[95m",
|
|
62
|
-
brightBlue: "\x1b[94m",
|
|
63
|
-
brightGreen: "\x1b[92m",
|
|
64
|
-
brightYellow: "\x1b[93m",
|
|
65
|
-
brightWhite: "\x1b[97m",
|
|
66
|
-
};
|
|
67
|
-
// ─── ASCII Art Banner ──────────────────────────────────────────────
|
|
68
|
-
function printBanner() {
|
|
69
|
-
// Sleek ANSI Shadow style — KRAMSCAN
|
|
70
|
-
const lines = [
|
|
71
|
-
`██╗ ██╗██████╗ █████╗ ███╗ ███╗███████╗ ██████╗ █████╗ ███╗ ██╗`,
|
|
72
|
-
`██║ ██╔╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗████╗ ██║`,
|
|
73
|
-
`█████╔╝ ██████╔╝███████║██╔████╔██║███████╗██║ ███████║██╔██╗ ██║`,
|
|
74
|
-
`██╔═██╗ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██║ ██╔══██║██║╚██╗██║`,
|
|
75
|
-
`██║ ██╗██║ ██║██║ ██║██║ ╚═╝ ██║███████║╚██████╗██║ ██║██║ ╚████║`,
|
|
76
|
-
`╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝`,
|
|
77
|
-
];
|
|
78
|
-
console.log("");
|
|
79
|
-
lines.forEach((line, i) => {
|
|
80
|
-
const shade = i % 2 === 0 ? c.brightWhite : c.gray;
|
|
81
|
-
console.log(` ${shade}${line}${c.reset}`);
|
|
82
|
-
});
|
|
83
|
-
console.log("");
|
|
51
|
+
const agent_1 = require("./commands/agent");
|
|
52
|
+
const scans_1 = require("./commands/scans");
|
|
53
|
+
const ai_1 = require("./commands/ai");
|
|
54
|
+
const config_2 = require("./core/config");
|
|
55
|
+
const theme_1 = require("./utils/theme");
|
|
56
|
+
let verboseMode = false;
|
|
57
|
+
let debugMode = false;
|
|
58
|
+
function isVerbose() {
|
|
59
|
+
return verboseMode;
|
|
84
60
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(` ${c.white}1.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan onboard${c.reset} ${c.gray}to configure your API keys.${c.reset}`);
|
|
93
|
-
console.log(` ${c.white}2.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan scan <url>${c.reset} ${c.gray}to scan a target.${c.reset}`);
|
|
94
|
-
console.log(` ${c.white}3.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan --help${c.reset} ${c.gray}for all commands.${c.reset}`);
|
|
95
|
-
console.log("");
|
|
61
|
+
function isDebug() {
|
|
62
|
+
return debugMode || (0, config_2.isDebugEnabled)();
|
|
63
|
+
}
|
|
64
|
+
function debugLog(...args) {
|
|
65
|
+
if (debugMode || (0, config_2.isDebugEnabled)()) {
|
|
66
|
+
console.log("[DEBUG]", ...args);
|
|
67
|
+
}
|
|
96
68
|
}
|
|
97
69
|
const menuChoices = [
|
|
70
|
+
{ label: "Agent", value: "agent", description: "AI-powered interactive security assistant", icon: "🤖", status: "active" },
|
|
98
71
|
{ label: "Onboard", value: "onboard", description: "First-time setup wizard", icon: "⚡", status: "active" },
|
|
99
72
|
{ label: "Scan", value: "scan", description: "Scan a target URL for vulnerabilities", icon: "🔍", status: "active" },
|
|
100
73
|
{ label: "Analyze", value: "analyze", description: "Deep AI analysis of scan results", icon: "🧠", status: "active" },
|
|
@@ -104,97 +77,124 @@ const menuChoices = [
|
|
|
104
77
|
{ label: "Exit", value: "exit", description: "Quit KramScan", icon: "👋", status: "active" },
|
|
105
78
|
];
|
|
106
79
|
async function showInteractiveMenu() {
|
|
107
|
-
printBanner();
|
|
108
|
-
printInfo();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
: "";
|
|
127
|
-
if (isSelected) {
|
|
128
|
-
console.log(` ${c.brightCyan}${c.bold}❯ ${choice.icon} ${choice.label}${c.reset}${statusTag} ${c.dim}${c.gray}— ${choice.description}${c.reset}`);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
console.log(` ${choice.icon} ${c.white}${choice.label}${c.reset}${statusTag} ${c.dim}${c.gray}— ${choice.description}${c.reset}`);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
80
|
+
(0, theme_1.printBanner)();
|
|
81
|
+
(0, theme_1.printInfo)();
|
|
82
|
+
const choices = menuChoices.map((choice) => ({
|
|
83
|
+
name: `${choice.icon} ${choice.label.padEnd(10)} - ${choice.description}${choice.status === "coming_soon" ? " [coming soon]" : ""}`,
|
|
84
|
+
value: choice.value,
|
|
85
|
+
disabled: choice.status === "coming_soon",
|
|
86
|
+
}));
|
|
87
|
+
const { action } = await inquirer_1.default.prompt([
|
|
88
|
+
{
|
|
89
|
+
type: "list",
|
|
90
|
+
name: "action",
|
|
91
|
+
message: theme_1.theme.cyan("What would you like to do?"),
|
|
92
|
+
choices,
|
|
93
|
+
pageSize: 10,
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
if (action === "exit") {
|
|
97
|
+
console.log(theme_1.theme.gray("\n Goodbye! 👋\n"));
|
|
98
|
+
return;
|
|
134
99
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (process.stdin.isTTY) {
|
|
158
|
-
process.stdin.setRawMode(false);
|
|
159
|
-
}
|
|
160
|
-
process.stdin.pause();
|
|
161
|
-
rl.close();
|
|
162
|
-
const selected = menuChoices[selectedIndex];
|
|
163
|
-
console.log("");
|
|
164
|
-
if (selected.value === "exit") {
|
|
165
|
-
console.log(` ${c.gray}${c.dim}Goodbye! 👋${c.reset}`);
|
|
166
|
-
console.log("");
|
|
167
|
-
resolve();
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (selected.status === "coming_soon") {
|
|
171
|
-
console.log(` ${c.yellow}⚠ ${selected.label}${c.reset} ${c.gray}is coming soon. Stay tuned!${c.reset}`);
|
|
172
|
-
console.log(` ${c.gray}Run ${c.cyan}kramscan --help${c.gray} for available commands.${c.reset}`);
|
|
173
|
-
console.log("");
|
|
174
|
-
resolve();
|
|
175
|
-
return;
|
|
100
|
+
const selected = menuChoices.find((c) => c.value === action);
|
|
101
|
+
if (selected && selected.status === "coming_soon") {
|
|
102
|
+
console.log(theme_1.theme.yellow(`\n [!] ${selected.label} is coming soon. Stay tuned!`));
|
|
103
|
+
console.log(theme_1.theme.gray(` Run ${theme_1.theme.cyan("kramscan --help")} for available commands.\n`));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let args = [action];
|
|
107
|
+
// Specific handling for commands that need input
|
|
108
|
+
if (action === "scan") {
|
|
109
|
+
const { url } = await inquirer_1.default.prompt([
|
|
110
|
+
{
|
|
111
|
+
type: "input",
|
|
112
|
+
name: "url",
|
|
113
|
+
message: theme_1.theme.cyan("Enter the URL to scan:"),
|
|
114
|
+
validate: (input) => {
|
|
115
|
+
try {
|
|
116
|
+
new URL(input);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
121
|
+
}
|
|
176
122
|
}
|
|
177
|
-
// Execute the selected command
|
|
178
|
-
console.log(` ${c.brightGreen}▶${c.reset} ${c.bold}Launching ${selected.label}...${c.reset}`);
|
|
179
|
-
console.log("");
|
|
180
|
-
// Re-run with the command argument
|
|
181
|
-
process.argv.push(selected.value);
|
|
182
|
-
const program = createProgram();
|
|
183
|
-
await program.parseAsync(process.argv);
|
|
184
|
-
resolve();
|
|
185
123
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
124
|
+
]);
|
|
125
|
+
args.push(url);
|
|
126
|
+
}
|
|
127
|
+
else if (action === "analyze" || action === "report") {
|
|
128
|
+
const { listScans } = await Promise.resolve().then(() => __importStar(require("./core/scan-index")));
|
|
129
|
+
const scans = await listScans(10);
|
|
130
|
+
if (scans.length > 0) {
|
|
131
|
+
const { scanFile } = await inquirer_1.default.prompt([
|
|
132
|
+
{
|
|
133
|
+
type: "list",
|
|
134
|
+
name: "scanFile",
|
|
135
|
+
message: theme_1.theme.cyan(`Select a scan to ${action}:`),
|
|
136
|
+
choices: [
|
|
137
|
+
...scans.map(s => ({
|
|
138
|
+
name: `${s.timestamp} - ${s.hostname} (${s.summary.total} findings)`,
|
|
139
|
+
value: s.jsonPath
|
|
140
|
+
})),
|
|
141
|
+
{ name: "Back to menu", value: "back" }
|
|
142
|
+
]
|
|
190
143
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
process.exit(0);
|
|
144
|
+
]);
|
|
145
|
+
if (scanFile === "back") {
|
|
146
|
+
return showInteractiveMenu();
|
|
195
147
|
}
|
|
196
|
-
|
|
197
|
-
|
|
148
|
+
args.push(scanFile);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(theme_1.theme.yellow(`\n [!] No recent scans found. Please run a scan first.\n`));
|
|
152
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
153
|
+
return showInteractiveMenu();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
console.log(theme_1.theme.green(`\n > Launching ${selected?.label || action}...\n`));
|
|
157
|
+
const program = createProgram();
|
|
158
|
+
try {
|
|
159
|
+
await program.parseAsync(["node", "kramscan", ...args]);
|
|
160
|
+
// After execution, ask if they want to go back to the menu
|
|
161
|
+
const { back } = await inquirer_1.default.prompt([
|
|
162
|
+
{
|
|
163
|
+
type: "confirm",
|
|
164
|
+
name: "back",
|
|
165
|
+
message: theme_1.theme.cyan("Return to main menu?"),
|
|
166
|
+
default: true
|
|
167
|
+
}
|
|
168
|
+
]);
|
|
169
|
+
if (back) {
|
|
170
|
+
return showInteractiveMenu();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Error handling is managed by the commands themselves or global handlers
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function showDirectCommandInput() {
|
|
178
|
+
(0, theme_1.printBanner)();
|
|
179
|
+
(0, theme_1.printInfo)();
|
|
180
|
+
const { command } = await inquirer_1.default.prompt([
|
|
181
|
+
{
|
|
182
|
+
type: "input",
|
|
183
|
+
name: "command",
|
|
184
|
+
message: theme_1.theme.cyan("Enter a command (e.g., 'scan https://example.com'):"),
|
|
185
|
+
filter: (input) => input.trim(),
|
|
186
|
+
},
|
|
187
|
+
]);
|
|
188
|
+
if (!command) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const tokens = command.match(/(?:[^\s"]+|"[^"]*")+/g)?.map((token) => token.replace(/^"(.*)"$/, "$1")) ?? [];
|
|
192
|
+
const args = tokens[0]?.toLowerCase() === "kramscan" ? tokens.slice(1) : tokens;
|
|
193
|
+
if (args.length > 0) {
|
|
194
|
+
console.log("");
|
|
195
|
+
const program = createProgram();
|
|
196
|
+
await program.parseAsync(["node", "kramscan", ...args]);
|
|
197
|
+
}
|
|
198
198
|
}
|
|
199
199
|
// ─── Program Setup ─────────────────────────────────────────────────
|
|
200
200
|
function createProgram() {
|
|
@@ -202,19 +202,56 @@ function createProgram() {
|
|
|
202
202
|
program
|
|
203
203
|
.name("kramscan")
|
|
204
204
|
.description("KramScan — AI-powered web app security testing")
|
|
205
|
-
.version(CLI_VERSION)
|
|
205
|
+
.version(theme_1.CLI_VERSION)
|
|
206
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
207
|
+
.option("--debug", "Enable debug mode")
|
|
208
|
+
.hook("preAction", (thisCommand) => {
|
|
209
|
+
const opts = thisCommand.opts();
|
|
210
|
+
verboseMode = opts.verbose || false;
|
|
211
|
+
debugMode = opts.debug || false;
|
|
212
|
+
})
|
|
213
|
+
.enablePositionalOptions();
|
|
206
214
|
(0, onboard_1.registerOnboardCommand)(program);
|
|
207
215
|
(0, scan_1.registerScanCommand)(program);
|
|
208
216
|
(0, analyze_1.registerAnalyzeCommand)(program);
|
|
209
217
|
(0, report_1.registerReportCommand)(program);
|
|
210
218
|
(0, config_1.registerConfigCommand)(program);
|
|
211
219
|
(0, doctor_1.registerDoctorCommand)(program);
|
|
220
|
+
(0, agent_1.registerAgentCommand)(program);
|
|
221
|
+
(0, scans_1.registerScansCommand)(program);
|
|
222
|
+
(0, ai_1.registerAiCommand)(program);
|
|
223
|
+
// Version subcommand with detailed environment info
|
|
224
|
+
program
|
|
225
|
+
.command("version")
|
|
226
|
+
.description("Show detailed version and environment information")
|
|
227
|
+
.action(async () => {
|
|
228
|
+
const os = await Promise.resolve().then(() => __importStar(require("os")));
|
|
229
|
+
let aiProvider = "not configured";
|
|
230
|
+
try {
|
|
231
|
+
const { getConfig } = await Promise.resolve().then(() => __importStar(require("./core/config")));
|
|
232
|
+
const config = await getConfig();
|
|
233
|
+
if (config.ai.enabled) {
|
|
234
|
+
aiProvider = `${config.ai.provider} (${config.ai.defaultModel})`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Config not available
|
|
239
|
+
}
|
|
240
|
+
console.log("");
|
|
241
|
+
console.log(theme_1.theme.brightWhite.bold("KramScan") + " " + theme_1.theme.cyan(`v${theme_1.CLI_VERSION}`));
|
|
242
|
+
console.log(theme_1.theme.gray("─".repeat(40)));
|
|
243
|
+
console.log(theme_1.theme.white(" Node.js: ") + theme_1.theme.cyan(process.version));
|
|
244
|
+
console.log(theme_1.theme.white(" Platform: ") + theme_1.theme.cyan(`${os.platform()} ${os.arch()}`));
|
|
245
|
+
console.log(theme_1.theme.white(" OS: ") + theme_1.theme.cyan(os.release()));
|
|
246
|
+
console.log(theme_1.theme.white(" AI Provider:") + " " + theme_1.theme.cyan(aiProvider));
|
|
247
|
+
console.log("");
|
|
248
|
+
});
|
|
212
249
|
return program;
|
|
213
250
|
}
|
|
214
251
|
// ─── Entry Point ───────────────────────────────────────────────────
|
|
215
252
|
async function run() {
|
|
216
253
|
const args = process.argv.slice(2);
|
|
217
|
-
// If no command is provided, show the interactive
|
|
254
|
+
// If no command is provided, show the interactive menu
|
|
218
255
|
if (args.length === 0) {
|
|
219
256
|
await showInteractiveMenu();
|
|
220
257
|
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent Command
|
|
4
|
+
* Interactive AI-powered security assistant CLI
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
+
};
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.registerAgentCommand = registerAgentCommand;
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const agent_1 = require("../agent");
|
|
46
|
+
const logger_1 = require("../utils/logger");
|
|
47
|
+
const readline = __importStar(require("readline"));
|
|
48
|
+
function registerAgentCommand(program) {
|
|
49
|
+
program
|
|
50
|
+
.command("agent")
|
|
51
|
+
.description("Start interactive AI security assistant")
|
|
52
|
+
.option("-m, --message <text>", "Send a single message and exit")
|
|
53
|
+
.option("--no-confirm", "Skip confirmation prompts for medium risk actions")
|
|
54
|
+
.action(async (options) => {
|
|
55
|
+
// Initialize skill registry with all skills
|
|
56
|
+
const skillRegistry = new agent_1.SkillRegistry();
|
|
57
|
+
skillRegistry.register(new agent_1.WebScanSkill());
|
|
58
|
+
skillRegistry.register(new agent_1.AnalyzeFindingsSkill());
|
|
59
|
+
skillRegistry.register(new agent_1.GenerateReportSkill());
|
|
60
|
+
skillRegistry.register(new agent_1.HealthCheckSkill());
|
|
61
|
+
skillRegistry.register(new agent_1.VerifyFindingSkill());
|
|
62
|
+
// Initialize orchestrator
|
|
63
|
+
const orchestrator = new agent_1.AgentOrchestrator(skillRegistry, {
|
|
64
|
+
enableConfirmation: options.confirm !== false,
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
await orchestrator.initialize();
|
|
68
|
+
if (options.message) {
|
|
69
|
+
// Single message mode
|
|
70
|
+
console.log("");
|
|
71
|
+
console.log(chalk_1.default.bold.cyan("🛡️ KramScan Security Agent"));
|
|
72
|
+
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
73
|
+
console.log("");
|
|
74
|
+
const response = await orchestrator.processUserMessage(options.message);
|
|
75
|
+
console.log(chalk_1.default.bold("Agent:"), response.message);
|
|
76
|
+
console.log("");
|
|
77
|
+
await orchestrator.shutdown();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Interactive mode
|
|
81
|
+
await runInteractiveMode(orchestrator);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger_1.logger.error(`Failed to start agent: ${error instanceof Error ? error.message : String(error)}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function runInteractiveMode(orchestrator) {
|
|
90
|
+
// Ensure we aren't in raw mode from other interactive flows.
|
|
91
|
+
if (process.stdin.isTTY) {
|
|
92
|
+
try {
|
|
93
|
+
process.stdin.setRawMode(false);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const rl = readline.createInterface({
|
|
100
|
+
input: process.stdin,
|
|
101
|
+
output: process.stdout,
|
|
102
|
+
});
|
|
103
|
+
printWelcomeBanner();
|
|
104
|
+
printAvailableSkills(orchestrator);
|
|
105
|
+
// Share this readline interface with confirmations to avoid double-reading stdin.
|
|
106
|
+
orchestrator.setReadlineInterface(rl);
|
|
107
|
+
orchestrator.start();
|
|
108
|
+
console.log(chalk_1.default.gray("Type 'help' for commands or 'exit' to quit.\n"));
|
|
109
|
+
let closed = false;
|
|
110
|
+
rl.on('SIGINT', () => rl.close());
|
|
111
|
+
rl.on('close', () => { closed = true; });
|
|
112
|
+
const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
|
|
113
|
+
while (!closed) {
|
|
114
|
+
const input = await question(chalk_1.default.gray('You: '));
|
|
115
|
+
if (closed)
|
|
116
|
+
break;
|
|
117
|
+
const trimmed = input.trim();
|
|
118
|
+
if (!trimmed)
|
|
119
|
+
continue;
|
|
120
|
+
const commandResult = await handleSpecialCommand(trimmed, orchestrator, rl);
|
|
121
|
+
if (commandResult.handled) {
|
|
122
|
+
if (commandResult.shouldExit)
|
|
123
|
+
break;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
console.log('');
|
|
127
|
+
try {
|
|
128
|
+
const response = await orchestrator.processUserMessage(trimmed);
|
|
129
|
+
console.log(chalk_1.default.bold.cyan('Agent:'));
|
|
130
|
+
console.log(response.message);
|
|
131
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(chalk_1.default.gray('Tools executed:'));
|
|
134
|
+
response.toolCalls.forEach((call) => {
|
|
135
|
+
const result = response.toolCallResults?.find((r) => r.toolCallId === call.id);
|
|
136
|
+
const icon = result?.success ? chalk_1.default.green('OK') : chalk_1.default.red('X');
|
|
137
|
+
console.log(chalk_1.default.gray(' ' + icon + ' ' + call.name));
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
console.log('');
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.log(chalk_1.default.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
144
|
+
console.log('');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log(chalk_1.default.gray('\nGoodbye!\n'));
|
|
148
|
+
await orchestrator.shutdown();
|
|
149
|
+
rl.close();
|
|
150
|
+
}
|
|
151
|
+
async function handleSpecialCommand(input, orchestrator, rl) {
|
|
152
|
+
const command = input.toLowerCase();
|
|
153
|
+
switch (command) {
|
|
154
|
+
case "exit":
|
|
155
|
+
case "quit":
|
|
156
|
+
case "/exit":
|
|
157
|
+
case "/quit":
|
|
158
|
+
rl.close();
|
|
159
|
+
return { handled: true, shouldExit: true };
|
|
160
|
+
case "help":
|
|
161
|
+
case "/help":
|
|
162
|
+
printHelp();
|
|
163
|
+
return { handled: true, shouldExit: false };
|
|
164
|
+
case "clear":
|
|
165
|
+
case "/clear":
|
|
166
|
+
case "/new":
|
|
167
|
+
orchestrator.clearConversation();
|
|
168
|
+
console.log(chalk_1.default.gray("\nConversation history cleared.\n"));
|
|
169
|
+
return { handled: true, shouldExit: false };
|
|
170
|
+
case "status":
|
|
171
|
+
case "/status":
|
|
172
|
+
printStatus(orchestrator);
|
|
173
|
+
return { handled: true, shouldExit: false };
|
|
174
|
+
case "skills":
|
|
175
|
+
case "/skills":
|
|
176
|
+
printAvailableSkills(orchestrator);
|
|
177
|
+
return { handled: true, shouldExit: false };
|
|
178
|
+
default:
|
|
179
|
+
return { handled: false, shouldExit: false };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function printWelcomeBanner() {
|
|
183
|
+
console.log("");
|
|
184
|
+
console.log(chalk_1.default.bold.cyan(`
|
|
185
|
+
██╗ ██╗██████╗ █████╗ ███╗ ███╗███████╗ ██████╗ █████╗ ███╗ ██╗
|
|
186
|
+
██║ ██╔╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗████╗ ██║
|
|
187
|
+
█████╔╝ ██████╔╝███████║██╔████╔██║███████╗██║ ███████║██╔██╗ ██║
|
|
188
|
+
██╔═██╗ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██║ ██╔══██║██║╚██╗██║
|
|
189
|
+
██║ ██╗██║ ██║██║ ██║██║ ╚═╝ ██║███████║╚██████╗██║ ██║██║ ╚████║
|
|
190
|
+
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
|
|
191
|
+
`));
|
|
192
|
+
console.log(chalk_1.default.gray("─".repeat(70)));
|
|
193
|
+
console.log(chalk_1.default.bold.white(" AI-Powered Security Assistant"), chalk_1.default.gray("|"), chalk_1.default.cyan("v0.1.0"));
|
|
194
|
+
console.log(chalk_1.default.gray("─".repeat(70)));
|
|
195
|
+
console.log("");
|
|
196
|
+
}
|
|
197
|
+
function printAvailableSkills(orchestrator) {
|
|
198
|
+
const skills = orchestrator.getAvailableSkills();
|
|
199
|
+
console.log(chalk_1.default.bold("Available Security Skills:"));
|
|
200
|
+
console.log("");
|
|
201
|
+
skills.forEach((skill) => {
|
|
202
|
+
const riskColor = skill.risk === "high"
|
|
203
|
+
? chalk_1.default.red
|
|
204
|
+
: skill.risk === "medium"
|
|
205
|
+
? chalk_1.default.yellow
|
|
206
|
+
: chalk_1.default.green;
|
|
207
|
+
console.log(chalk_1.default.bold.white(` ${skill.name}`));
|
|
208
|
+
console.log(chalk_1.default.gray(` ${skill.description}`));
|
|
209
|
+
console.log(chalk_1.default.gray(` Risk: ${riskColor(skill.risk.toUpperCase())}`), skill.requiresConfirmation ? chalk_1.default.yellow("[Requires Confirmation]") : "");
|
|
210
|
+
console.log("");
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function printHelp() {
|
|
214
|
+
console.log("");
|
|
215
|
+
console.log(chalk_1.default.bold("Commands:"));
|
|
216
|
+
console.log("");
|
|
217
|
+
console.log(chalk_1.default.white(" help "), chalk_1.default.gray("- Show this help message"));
|
|
218
|
+
console.log(chalk_1.default.white(" status "), chalk_1.default.gray("- Show session status"));
|
|
219
|
+
console.log(chalk_1.default.white(" skills "), chalk_1.default.gray("- List available skills"));
|
|
220
|
+
console.log(chalk_1.default.white(" clear "), chalk_1.default.gray("- Clear conversation history"));
|
|
221
|
+
console.log(chalk_1.default.white(" exit "), chalk_1.default.gray("- Exit the agent"));
|
|
222
|
+
console.log("");
|
|
223
|
+
console.log(chalk_1.default.bold("Examples:"));
|
|
224
|
+
console.log("");
|
|
225
|
+
console.log(chalk_1.default.gray(" Scan a website:"));
|
|
226
|
+
console.log(chalk_1.default.cyan(' scan https://example.com'));
|
|
227
|
+
console.log("");
|
|
228
|
+
console.log(chalk_1.default.gray(" Analyze findings:"));
|
|
229
|
+
console.log(chalk_1.default.cyan(' analyze the results'));
|
|
230
|
+
console.log("");
|
|
231
|
+
console.log(chalk_1.default.gray(" Generate report:"));
|
|
232
|
+
console.log(chalk_1.default.cyan(' create a report'));
|
|
233
|
+
console.log("");
|
|
234
|
+
console.log(chalk_1.default.gray(" Check system health:"));
|
|
235
|
+
console.log(chalk_1.default.cyan(' health check'));
|
|
236
|
+
console.log("");
|
|
237
|
+
}
|
|
238
|
+
function printStatus(orchestrator) {
|
|
239
|
+
const summary = orchestrator.getConversationSummary();
|
|
240
|
+
console.log("");
|
|
241
|
+
console.log(chalk_1.default.bold("Session Status:"));
|
|
242
|
+
console.log("");
|
|
243
|
+
console.log(chalk_1.default.gray(` Messages: ${summary.totalMessages}`));
|
|
244
|
+
console.log(chalk_1.default.gray(` Duration: ${summary.sessionDuration}`));
|
|
245
|
+
if (summary.currentTarget) {
|
|
246
|
+
console.log(chalk_1.default.gray(` Target: ${summary.currentTarget}`));
|
|
247
|
+
}
|
|
248
|
+
console.log(chalk_1.default.gray(` Scan Results: ${summary.hasScanResults ? "Yes" : "No"}`));
|
|
249
|
+
console.log("");
|
|
250
|
+
}
|