kramscan 0.1.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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/bin/kramscan.js +4 -0
  4. package/bin/openscan.js +4 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +225 -0
  7. package/dist/commands/analyze.d.ts +2 -0
  8. package/dist/commands/analyze.js +115 -0
  9. package/dist/commands/config.d.ts +2 -0
  10. package/dist/commands/config.js +139 -0
  11. package/dist/commands/doctor.d.ts +2 -0
  12. package/dist/commands/doctor.js +234 -0
  13. package/dist/commands/onboard.d.ts +2 -0
  14. package/dist/commands/onboard.js +146 -0
  15. package/dist/commands/report.d.ts +2 -0
  16. package/dist/commands/report.js +225 -0
  17. package/dist/commands/scan.d.ts +2 -0
  18. package/dist/commands/scan.js +125 -0
  19. package/dist/core/ai-client.d.ts +12 -0
  20. package/dist/core/ai-client.js +89 -0
  21. package/dist/core/config.d.ts +45 -0
  22. package/dist/core/config.js +146 -0
  23. package/dist/core/executor.d.ts +2 -0
  24. package/dist/core/executor.js +74 -0
  25. package/dist/core/logger.d.ts +12 -0
  26. package/dist/core/logger.js +51 -0
  27. package/dist/core/registry.d.ts +3 -0
  28. package/dist/core/registry.js +35 -0
  29. package/dist/core/scanner.d.ts +24 -0
  30. package/dist/core/scanner.js +197 -0
  31. package/dist/core/storage.d.ts +4 -0
  32. package/dist/core/storage.js +39 -0
  33. package/dist/core/types.d.ts +24 -0
  34. package/dist/core/types.js +2 -0
  35. package/dist/core/vulnerability-detector.d.ts +47 -0
  36. package/dist/core/vulnerability-detector.js +150 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +7 -0
  39. package/dist/skills/base.d.ts +8 -0
  40. package/dist/skills/base.js +6 -0
  41. package/dist/skills/builtin.d.ts +4 -0
  42. package/dist/skills/builtin.js +71 -0
  43. package/dist/skills/loader.d.ts +2 -0
  44. package/dist/skills/loader.js +27 -0
  45. package/dist/skills/types.d.ts +46 -0
  46. package/dist/skills/types.js +2 -0
  47. package/dist/utils/logger.d.ts +9 -0
  48. package/dist/utils/logger.js +34 -0
  49. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Akram Shaikh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # KramScan 🛡️
2
+
3
+ KramScan is a personal, AI-powered command-line interface (CLI) for web application security testing. It combines automated browser interactions (via Puppeteer) with AI analysis to identify vulnerabilities in modern web apps.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # Install dependencies
11
+ npm install
12
+
13
+ # Build the project
14
+ npm run build
15
+
16
+ # Link globally so you can use "kramscan" from anywhere
17
+ npm link
18
+
19
+ # Launch the interactive dashboard
20
+ kramscan
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Commands
26
+
27
+ | Command | Description | Status |
28
+ |:-------------------|:-------------------------------------|:-------------|
29
+ | `kramscan` | Launch interactive dashboard | ✅ Active |
30
+ | `kramscan onboard` | First-time setup wizard | ✅ Active |
31
+ | `kramscan scan` | Scan a target URL | 🔜 Coming |
32
+ | `kramscan analyze` | AI-powered analysis of scan results | 🔜 Coming |
33
+ | `kramscan report` | Generate a professional report | 🔜 Coming |
34
+ | `kramscan doctor` | Check environment health | 🔜 Coming |
35
+ | `kramscan --help` | Show all available commands | ✅ Active |
36
+
37
+ ---
38
+
39
+ ## Setup Wizard
40
+
41
+ Run `kramscan onboard` to configure:
42
+
43
+ 1. **AI Provider** — OpenAI or Anthropic
44
+ 2. **API Key** — Your provider API key
45
+ 3. **Default Model** — e.g. `gpt-4`
46
+ 4. **Report Format** — Word, TXT, or JSON
47
+ 5. **Scope Enforcement** — Strict mode on/off
48
+ 6. **Rate Limiting** — Requests per second
49
+
50
+ Configuration is saved to `~/.kramscan/config.json`.
51
+
52
+ ---
53
+
54
+ ## Development
55
+
56
+ ```bash
57
+ # Run without building (using tsx)
58
+ npx tsx src/index.ts
59
+
60
+ # Run a specific command
61
+ npx tsx src/index.ts onboard
62
+
63
+ # Build
64
+ npm run build
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Tech Stack
70
+
71
+ - **TypeScript** + **Node.js**
72
+ - **Commander.js** — CLI framework
73
+ - **Inquirer.js** — Interactive prompts
74
+ - **Puppeteer** — Browser automation
75
+ - **ConfigStore** — Persistent configuration
76
+
77
+ ---
78
+
79
+ ## Author
80
+
81
+ **Akram** — *KramScan*
82
+
83
+ ---
84
+
85
+ ## License
86
+
87
+ ISC
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ require("../dist/index.js");
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ require("../dist/index.js");
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function run(): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,225 @@
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
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.run = run;
37
+ const commander_1 = require("commander");
38
+ const onboard_1 = require("./commands/onboard");
39
+ const scan_1 = require("./commands/scan");
40
+ const analyze_1 = require("./commands/analyze");
41
+ const report_1 = require("./commands/report");
42
+ const config_1 = require("./commands/config");
43
+ const doctor_1 = require("./commands/doctor");
44
+ const CLI_VERSION = "0.1.0";
45
+ // ─── ANSI Color Helpers ────────────────────────────────────────────
46
+ const c = {
47
+ reset: "\x1b[0m",
48
+ bold: "\x1b[1m",
49
+ dim: "\x1b[2m",
50
+ red: "\x1b[31m",
51
+ green: "\x1b[32m",
52
+ yellow: "\x1b[33m",
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("");
84
+ }
85
+ // ─── Dashboard Info ────────────────────────────────────────────────
86
+ function printInfo() {
87
+ console.log(` ${c.gray}${c.dim}───────────────────────────────────────────────────────${c.reset}`);
88
+ console.log(` ${c.brightWhite}${c.bold} KramScan${c.reset} ${c.gray}v${CLI_VERSION}${c.reset} ${c.dim}${c.gray}|${c.reset} ${c.cyan}AI-Powered Web Security Scanner${c.reset}`);
89
+ console.log(` ${c.gray}${c.dim}───────────────────────────────────────────────────────${c.reset}`);
90
+ console.log("");
91
+ console.log(` ${c.brightYellow}${c.bold}Tips for getting started:${c.reset}`);
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("");
96
+ }
97
+ const menuChoices = [
98
+ { label: "Onboard", value: "onboard", description: "First-time setup wizard", icon: "⚡", status: "active" },
99
+ { label: "Scan", value: "scan", description: "Scan a target URL for vulnerabilities", icon: "🔍", status: "active" },
100
+ { label: "Analyze", value: "analyze", description: "Deep AI analysis of scan results", icon: "🧠", status: "active" },
101
+ { label: "Report", value: "report", description: "Generate a professional report", icon: "📄", status: "active" },
102
+ { label: "Config", value: "config", description: "View or edit your configuration", icon: "⚙️", status: "active" },
103
+ { label: "Doctor", value: "doctor", description: "Check environment health", icon: "🩺", status: "active" },
104
+ { label: "Exit", value: "exit", description: "Quit KramScan", icon: "👋", status: "active" },
105
+ ];
106
+ async function showInteractiveMenu() {
107
+ printBanner();
108
+ printInfo();
109
+ // Use readline for a simple, CommonJS-compatible interactive menu
110
+ const readline = await Promise.resolve().then(() => __importStar(require("readline")));
111
+ const rl = readline.createInterface({
112
+ input: process.stdin,
113
+ output: process.stdout,
114
+ });
115
+ function renderMenu(selectedIndex) {
116
+ // Move cursor up to redraw the menu (clear previous render)
117
+ if (selectedIndex >= 0) {
118
+ process.stdout.write(`\x1b[${menuChoices.length + 2}A`);
119
+ }
120
+ console.log(` ${c.brightWhite}${c.bold}What would you like to do?${c.reset}`);
121
+ console.log("");
122
+ menuChoices.forEach((choice, i) => {
123
+ const isSelected = i === selectedIndex;
124
+ const statusTag = choice.status === "coming_soon"
125
+ ? ` ${c.yellow}[coming soon]${c.reset}`
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
+ });
134
+ }
135
+ return new Promise((resolve) => {
136
+ let selectedIndex = 0;
137
+ // Enable raw mode for arrow key support
138
+ if (process.stdin.isTTY) {
139
+ process.stdin.setRawMode(true);
140
+ }
141
+ process.stdin.resume();
142
+ renderMenu(-1); // Initial render (no cursor-up needed)
143
+ process.stdin.on("data", async (key) => {
144
+ const str = key.toString();
145
+ if (str === "\x1b[A") {
146
+ // Arrow Up
147
+ selectedIndex = (selectedIndex - 1 + menuChoices.length) % menuChoices.length;
148
+ renderMenu(selectedIndex);
149
+ }
150
+ else if (str === "\x1b[B") {
151
+ // Arrow Down
152
+ selectedIndex = (selectedIndex + 1) % menuChoices.length;
153
+ renderMenu(selectedIndex);
154
+ }
155
+ else if (str === "\r" || str === "\n") {
156
+ // Enter
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;
176
+ }
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
+ }
186
+ else if (str === "\x03") {
187
+ // Ctrl+C
188
+ if (process.stdin.isTTY) {
189
+ process.stdin.setRawMode(false);
190
+ }
191
+ process.stdin.pause();
192
+ rl.close();
193
+ console.log(`\n ${c.gray}${c.dim}Interrupted. Goodbye! 👋${c.reset}\n`);
194
+ process.exit(0);
195
+ }
196
+ });
197
+ });
198
+ }
199
+ // ─── Program Setup ─────────────────────────────────────────────────
200
+ function createProgram() {
201
+ const program = new commander_1.Command();
202
+ program
203
+ .name("kramscan")
204
+ .description("KramScan — AI-powered web app security testing")
205
+ .version(CLI_VERSION);
206
+ (0, onboard_1.registerOnboardCommand)(program);
207
+ (0, scan_1.registerScanCommand)(program);
208
+ (0, analyze_1.registerAnalyzeCommand)(program);
209
+ (0, report_1.registerReportCommand)(program);
210
+ (0, config_1.registerConfigCommand)(program);
211
+ (0, doctor_1.registerDoctorCommand)(program);
212
+ return program;
213
+ }
214
+ // ─── Entry Point ───────────────────────────────────────────────────
215
+ async function run() {
216
+ const args = process.argv.slice(2);
217
+ // If no command is provided, show the interactive dashboard
218
+ if (args.length === 0) {
219
+ await showInteractiveMenu();
220
+ }
221
+ else {
222
+ const program = createProgram();
223
+ await program.parseAsync();
224
+ }
225
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerAnalyzeCommand(program: Command): void;
@@ -0,0 +1,115 @@
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.registerAnalyzeCommand = registerAnalyzeCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ai_client_1 = require("../core/ai-client");
9
+ const logger_1 = require("../utils/logger");
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ function registerAnalyzeCommand(program) {
14
+ program
15
+ .command("analyze [scan-file]")
16
+ .description("AI-powered analysis of scan results")
17
+ .option("-m, --model <name>", "Override default AI model")
18
+ .option("-v, --verbose", "Show detailed analysis")
19
+ .action(async (scanFile, options) => {
20
+ console.log("");
21
+ console.log(chalk_1.default.bold.cyan("🧠 AI Security Analysis"));
22
+ console.log(chalk_1.default.gray("─".repeat(50)));
23
+ console.log("");
24
+ try {
25
+ // Load scan results
26
+ let filepath;
27
+ if (scanFile) {
28
+ filepath = path_1.default.isAbsolute(scanFile)
29
+ ? scanFile
30
+ : path_1.default.join(process.cwd(), scanFile);
31
+ }
32
+ else {
33
+ // Find latest scan
34
+ const scanDir = path_1.default.join(os_1.default.homedir(), ".kramscan", "scans");
35
+ const files = await promises_1.default.readdir(scanDir);
36
+ const scanFiles = files.filter((f) => f.endsWith(".json"));
37
+ if (scanFiles.length === 0) {
38
+ logger_1.logger.error("No scan results found. Run 'kramscan scan <url>' first.");
39
+ process.exit(1);
40
+ }
41
+ // Get most recent
42
+ scanFiles.sort().reverse();
43
+ filepath = path_1.default.join(scanDir, scanFiles[0]);
44
+ logger_1.logger.info(`Using latest scan: ${scanFiles[0]}`);
45
+ }
46
+ const content = await promises_1.default.readFile(filepath, "utf-8");
47
+ const scanResult = JSON.parse(content);
48
+ if (scanResult.vulnerabilities.length === 0) {
49
+ logger_1.logger.success("No vulnerabilities to analyze!");
50
+ return;
51
+ }
52
+ const spinner = logger_1.logger.spinner("Analyzing vulnerabilities with AI...");
53
+ // Create AI client
54
+ const aiClient = (0, ai_client_1.createAIClient)();
55
+ // Build analysis prompt
56
+ const prompt = buildAnalysisPrompt(scanResult);
57
+ // Get AI analysis
58
+ const response = await aiClient.analyze(prompt);
59
+ spinner.succeed("Analysis complete!");
60
+ console.log("");
61
+ console.log(chalk_1.default.bold("📝 AI Analysis"));
62
+ console.log(chalk_1.default.gray("─".repeat(50)));
63
+ console.log("");
64
+ console.log(response.content);
65
+ console.log("");
66
+ if (response.usage) {
67
+ console.log(chalk_1.default.gray("─".repeat(50)));
68
+ console.log(chalk_1.default.gray(`Tokens used: ${response.usage.totalTokens} (${response.usage.promptTokens} prompt + ${response.usage.completionTokens} completion)`));
69
+ console.log("");
70
+ }
71
+ // Save enhanced results
72
+ const enhancedResult = {
73
+ ...scanResult,
74
+ aiAnalysis: {
75
+ timestamp: new Date().toISOString(),
76
+ analysis: response.content,
77
+ usage: response.usage,
78
+ },
79
+ };
80
+ await promises_1.default.writeFile(filepath, JSON.stringify(enhancedResult, null, 2));
81
+ logger_1.logger.success(`Enhanced results saved to ${filepath}`);
82
+ console.log("");
83
+ }
84
+ catch (error) {
85
+ logger_1.logger.error(error.message);
86
+ process.exit(1);
87
+ }
88
+ });
89
+ }
90
+ function buildAnalysisPrompt(scanResult) {
91
+ const vulnList = scanResult.vulnerabilities
92
+ .map((v, i) => `${i + 1}. [${v.severity.toUpperCase()}] ${v.title}
93
+ URL: ${v.url}
94
+ Description: ${v.description}
95
+ ${v.evidence ? `Evidence: ${v.evidence}` : ""}
96
+ ${v.cwe ? `CWE: ${v.cwe}` : ""}`)
97
+ .join("\n\n");
98
+ return `You are a security expert analyzing web application vulnerabilities.
99
+
100
+ Target: ${scanResult.target}
101
+ Scan Date: ${scanResult.timestamp}
102
+ Total Vulnerabilities: ${scanResult.summary.total}
103
+
104
+ Vulnerabilities Found:
105
+ ${vulnList}
106
+
107
+ Please provide:
108
+ 1. **Executive Summary**: Brief overview of the security posture
109
+ 2. **Risk Assessment**: Overall risk level and business impact
110
+ 3. **Priority Recommendations**: Top 3-5 vulnerabilities to fix first, with specific remediation steps
111
+ 4. **Attack Scenarios**: How an attacker could chain these vulnerabilities
112
+ 5. **Remediation Roadmap**: Step-by-step plan to address all findings
113
+
114
+ Format your response in clear markdown with headers and bullet points.`;
115
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerConfigCommand(program: Command): void;
@@ -0,0 +1,139 @@
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.registerConfigCommand = registerConfigCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const config_1 = require("../core/config");
10
+ const logger_1 = require("../utils/logger");
11
+ function registerConfigCommand(program) {
12
+ const configCmd = program
13
+ .command("config")
14
+ .description("View or edit configuration");
15
+ configCmd
16
+ .command("get <key>")
17
+ .description("Get a configuration value")
18
+ .action((key) => {
19
+ const config = (0, config_1.getConfig)();
20
+ const value = getNestedValue(config, key);
21
+ if (value === undefined) {
22
+ logger_1.logger.error(`Configuration key '${key}' not found`);
23
+ process.exit(1);
24
+ }
25
+ console.log(chalk_1.default.cyan(key), "=", chalk_1.default.white(JSON.stringify(value, null, 2)));
26
+ });
27
+ configCmd
28
+ .command("set <key> <value>")
29
+ .description("Set a configuration value")
30
+ .action((key, value) => {
31
+ const config = (0, config_1.getConfig)();
32
+ // Parse value
33
+ let parsedValue = value;
34
+ if (value === "true")
35
+ parsedValue = true;
36
+ else if (value === "false")
37
+ parsedValue = false;
38
+ else if (!isNaN(Number(value)))
39
+ parsedValue = Number(value);
40
+ setNestedValue(config, key, parsedValue);
41
+ (0, config_1.setConfig)(config);
42
+ logger_1.logger.success(`Set ${chalk_1.default.cyan(key)} = ${chalk_1.default.white(JSON.stringify(parsedValue))}`);
43
+ });
44
+ configCmd
45
+ .command("list")
46
+ .description("List all configuration")
47
+ .action(() => {
48
+ const config = (0, config_1.getConfig)();
49
+ console.log("");
50
+ console.log(chalk_1.default.bold.cyan("📋 Current Configuration"));
51
+ console.log(chalk_1.default.gray("─".repeat(50)));
52
+ console.log("");
53
+ console.log(JSON.stringify(config, null, 2));
54
+ console.log("");
55
+ });
56
+ configCmd
57
+ .command("edit")
58
+ .description("Interactively edit configuration")
59
+ .action(async () => {
60
+ const config = (0, config_1.getConfig)();
61
+ console.log("");
62
+ console.log(chalk_1.default.bold.cyan("⚙️ Configuration Editor"));
63
+ console.log(chalk_1.default.gray("─".repeat(50)));
64
+ console.log("");
65
+ const answers = await inquirer_1.default.prompt([
66
+ {
67
+ type: "confirm",
68
+ name: "aiEnabled",
69
+ message: "Enable AI analysis?",
70
+ default: config.ai.enabled,
71
+ },
72
+ {
73
+ type: "list",
74
+ name: "aiProvider",
75
+ message: "AI provider:",
76
+ choices: ["openai", "anthropic"],
77
+ default: config.ai.provider,
78
+ when: (answers) => answers.aiEnabled,
79
+ },
80
+ {
81
+ type: "input",
82
+ name: "apiKey",
83
+ message: "API key:",
84
+ default: config.ai.apiKey,
85
+ when: (answers) => answers.aiEnabled,
86
+ },
87
+ {
88
+ type: "input",
89
+ name: "model",
90
+ message: "Default AI model:",
91
+ default: config.ai.defaultModel,
92
+ when: (answers) => answers.aiEnabled,
93
+ },
94
+ {
95
+ type: "list",
96
+ name: "reportFormat",
97
+ message: "Default report format:",
98
+ choices: ["word", "json", "txt"],
99
+ default: config.report.defaultFormat,
100
+ },
101
+ {
102
+ type: "number",
103
+ name: "rateLimit",
104
+ message: "Requests per second rate limit:",
105
+ default: config.scan.rateLimitPerSecond,
106
+ },
107
+ ]);
108
+ // Update config
109
+ if (answers.aiEnabled !== undefined)
110
+ config.ai.enabled = answers.aiEnabled;
111
+ if (answers.aiProvider)
112
+ config.ai.provider = answers.aiProvider;
113
+ if (answers.apiKey)
114
+ config.ai.apiKey = answers.apiKey;
115
+ if (answers.model)
116
+ config.ai.defaultModel = answers.model;
117
+ if (answers.reportFormat)
118
+ config.report.defaultFormat = answers.reportFormat;
119
+ if (answers.rateLimit)
120
+ config.scan.rateLimitPerSecond = answers.rateLimit;
121
+ (0, config_1.setConfig)(config);
122
+ console.log("");
123
+ logger_1.logger.success("Configuration updated successfully!");
124
+ console.log("");
125
+ });
126
+ }
127
+ function getNestedValue(obj, path) {
128
+ return path.split(".").reduce((current, key) => current?.[key], obj);
129
+ }
130
+ function setNestedValue(obj, path, value) {
131
+ const keys = path.split(".");
132
+ const lastKey = keys.pop();
133
+ const target = keys.reduce((current, key) => {
134
+ if (!current[key])
135
+ current[key] = {};
136
+ return current[key];
137
+ }, obj);
138
+ target[lastKey] = value;
139
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDoctorCommand(program: Command): void;