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
@@ -0,0 +1,234 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.registerDoctorCommand = registerDoctorCommand;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const config_1 = require("../core/config");
42
+ const logger_1 = require("../utils/logger");
43
+ const child_process_1 = require("child_process");
44
+ const util_1 = require("util");
45
+ const promises_1 = __importDefault(require("fs/promises"));
46
+ const os_1 = __importDefault(require("os"));
47
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
48
+ function registerDoctorCommand(program) {
49
+ program
50
+ .command("doctor")
51
+ .description("Check environment health and configuration")
52
+ .action(async () => {
53
+ console.log("");
54
+ console.log(chalk_1.default.bold.cyan("🩺 KramScan Health Check"));
55
+ console.log(chalk_1.default.gray("─".repeat(50)));
56
+ console.log("");
57
+ const checks = [];
58
+ // Check Node.js version
59
+ checks.push(await checkNodeVersion());
60
+ // Check Puppeteer/Chrome
61
+ checks.push(await checkPuppeteer());
62
+ // Check config file
63
+ checks.push(await checkConfig());
64
+ // Check API keys
65
+ checks.push(await checkAPIKeys());
66
+ // Check disk space
67
+ checks.push(await checkDiskSpace());
68
+ // Check network connectivity
69
+ checks.push(await checkNetwork());
70
+ // Display results
71
+ let passCount = 0;
72
+ let failCount = 0;
73
+ let warnCount = 0;
74
+ for (const check of checks) {
75
+ const icon = check.status === "pass"
76
+ ? chalk_1.default.green("✓")
77
+ : check.status === "fail"
78
+ ? chalk_1.default.red("✗")
79
+ : chalk_1.default.yellow("⚠");
80
+ console.log(`${icon} ${chalk_1.default.bold(check.name)}`);
81
+ console.log(` ${chalk_1.default.gray(check.message)}`);
82
+ console.log("");
83
+ if (check.status === "pass")
84
+ passCount++;
85
+ else if (check.status === "fail")
86
+ failCount++;
87
+ else
88
+ warnCount++;
89
+ }
90
+ // Summary
91
+ console.log(chalk_1.default.gray("─".repeat(50)));
92
+ console.log(`${chalk_1.default.green(`${passCount} passed`)} | ${chalk_1.default.yellow(`${warnCount} warnings`)} | ${chalk_1.default.red(`${failCount} failed`)}`);
93
+ console.log("");
94
+ if (failCount > 0) {
95
+ logger_1.logger.error("Some checks failed. Please fix the issues above.");
96
+ process.exit(1);
97
+ }
98
+ else if (warnCount > 0) {
99
+ logger_1.logger.warn("Some checks have warnings. KramScan should work but may have limitations.");
100
+ }
101
+ else {
102
+ logger_1.logger.success("All checks passed! KramScan is ready to use.");
103
+ }
104
+ });
105
+ }
106
+ async function checkNodeVersion() {
107
+ const version = process.version;
108
+ const major = parseInt(version.slice(1).split(".")[0]);
109
+ if (major >= 18) {
110
+ return {
111
+ name: "Node.js Version",
112
+ status: "pass",
113
+ message: `${version} (>= 18 required)`,
114
+ };
115
+ }
116
+ else {
117
+ return {
118
+ name: "Node.js Version",
119
+ status: "fail",
120
+ message: `${version} - Please upgrade to Node.js 18 or higher`,
121
+ };
122
+ }
123
+ }
124
+ async function checkPuppeteer() {
125
+ try {
126
+ const puppeteer = await Promise.resolve().then(() => __importStar(require("puppeteer")));
127
+ return {
128
+ name: "Puppeteer",
129
+ status: "pass",
130
+ message: "Installed and ready",
131
+ };
132
+ }
133
+ catch (error) {
134
+ return {
135
+ name: "Puppeteer",
136
+ status: "fail",
137
+ message: "Not installed. Run: npm install puppeteer",
138
+ };
139
+ }
140
+ }
141
+ async function checkConfig() {
142
+ try {
143
+ const config = (0, config_1.getConfig)();
144
+ return {
145
+ name: "Configuration",
146
+ status: "pass",
147
+ message: `Config loaded from ~/.kramscan/config.json`,
148
+ };
149
+ }
150
+ catch (error) {
151
+ return {
152
+ name: "Configuration",
153
+ status: "warn",
154
+ message: "Config file not found. Run 'kramscan onboard' to create it.",
155
+ };
156
+ }
157
+ }
158
+ async function checkAPIKeys() {
159
+ try {
160
+ const config = (0, config_1.getConfig)();
161
+ if (!config.ai.enabled) {
162
+ return {
163
+ name: "AI Configuration",
164
+ status: "warn",
165
+ message: "AI analysis is disabled. Run 'kramscan onboard' to enable it.",
166
+ };
167
+ }
168
+ if (!config.ai.apiKey) {
169
+ return {
170
+ name: "AI Configuration",
171
+ status: "warn",
172
+ message: `${config.ai.provider} API key not configured. Run 'kramscan onboard' to set it.`,
173
+ };
174
+ }
175
+ return {
176
+ name: "AI Configuration",
177
+ status: "pass",
178
+ message: `${config.ai.provider} configured with model ${config.ai.defaultModel}`,
179
+ };
180
+ }
181
+ catch (error) {
182
+ return {
183
+ name: "AI Configuration",
184
+ status: "warn",
185
+ message: "Unable to check AI configuration",
186
+ };
187
+ }
188
+ }
189
+ async function checkDiskSpace() {
190
+ try {
191
+ const homeDir = os_1.default.homedir();
192
+ const kramScanDir = `${homeDir}/.kramscan`;
193
+ // Create directory if it doesn't exist
194
+ await promises_1.default.mkdir(kramScanDir, { recursive: true });
195
+ return {
196
+ name: "Disk Space",
197
+ status: "pass",
198
+ message: `Scan directory: ${kramScanDir}`,
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ name: "Disk Space",
204
+ status: "fail",
205
+ message: "Unable to access scan directory",
206
+ };
207
+ }
208
+ }
209
+ async function checkNetwork() {
210
+ try {
211
+ // Simple DNS check
212
+ const { exec } = require("child_process");
213
+ await new Promise((resolve, reject) => {
214
+ exec("ping -n 1 8.8.8.8", (error) => {
215
+ if (error)
216
+ reject(error);
217
+ else
218
+ resolve(true);
219
+ });
220
+ });
221
+ return {
222
+ name: "Network Connectivity",
223
+ status: "pass",
224
+ message: "Internet connection available",
225
+ };
226
+ }
227
+ catch (error) {
228
+ return {
229
+ name: "Network Connectivity",
230
+ status: "warn",
231
+ message: "Unable to verify internet connection",
232
+ };
233
+ }
234
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerOnboardCommand(program: Command): void;
@@ -0,0 +1,146 @@
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.registerOnboardCommand = registerOnboardCommand;
37
+ const readline = __importStar(require("readline"));
38
+ const config_1 = require("../core/config");
39
+ const logger_1 = require("../core/logger");
40
+ // ─── ANSI Helpers ──────────────────────────────────────────────────
41
+ const c = {
42
+ reset: "\x1b[0m",
43
+ bold: "\x1b[1m",
44
+ dim: "\x1b[2m",
45
+ cyan: "\x1b[36m",
46
+ green: "\x1b[32m",
47
+ yellow: "\x1b[33m",
48
+ gray: "\x1b[90m",
49
+ white: "\x1b[37m",
50
+ brightCyan: "\x1b[96m",
51
+ };
52
+ // ─── Prompt Utilities ──────────────────────────────────────────────
53
+ function ask(rl, question, defaultVal) {
54
+ const defaultHint = defaultVal ? ` ${c.gray}(${defaultVal})${c.reset}` : "";
55
+ return new Promise((resolve) => {
56
+ rl.question(` ${c.cyan}?${c.reset} ${question}${defaultHint} `, (answer) => {
57
+ resolve(answer.trim() || defaultVal || "");
58
+ });
59
+ });
60
+ }
61
+ function askConfirm(rl, question, defaultVal = true) {
62
+ const hint = defaultVal ? `${c.gray}(Y/n)${c.reset}` : `${c.gray}(y/N)${c.reset}`;
63
+ return new Promise((resolve) => {
64
+ rl.question(` ${c.cyan}?${c.reset} ${question} ${hint} `, (answer) => {
65
+ const a = answer.trim().toLowerCase();
66
+ if (a === "")
67
+ resolve(defaultVal);
68
+ else
69
+ resolve(a === "y" || a === "yes");
70
+ });
71
+ });
72
+ }
73
+ function askList(rl, question, choices, defaultVal) {
74
+ const choicesStr = choices
75
+ .map((ch, i) => {
76
+ const isDefault = ch === defaultVal;
77
+ return ` ${isDefault ? c.brightCyan + "❯" : " "} ${ch}${c.reset}`;
78
+ })
79
+ .join("\n");
80
+ return new Promise((resolve) => {
81
+ console.log(` ${c.cyan}?${c.reset} ${question}`);
82
+ console.log(choicesStr);
83
+ rl.question(` ${c.gray}Enter choice:${c.reset} `, (answer) => {
84
+ const trimmed = answer.trim();
85
+ if (choices.includes(trimmed)) {
86
+ resolve(trimmed);
87
+ }
88
+ else {
89
+ resolve(defaultVal || choices[0]);
90
+ }
91
+ });
92
+ });
93
+ }
94
+ function askPassword(rl, question) {
95
+ return new Promise((resolve) => {
96
+ rl.question(` ${c.cyan}?${c.reset} ${question} ${c.gray}(hidden)${c.reset} `, (answer) => {
97
+ resolve(answer.trim());
98
+ });
99
+ });
100
+ }
101
+ // ─── Command Registration ─────────────────────────────────────────
102
+ function registerOnboardCommand(program) {
103
+ program
104
+ .command("onboard")
105
+ .description("First-time setup wizard")
106
+ .action(async () => {
107
+ const logger = (0, logger_1.createLogger)();
108
+ const store = (0, config_1.getConfigStore)();
109
+ const rl = readline.createInterface({
110
+ input: process.stdin,
111
+ output: process.stdout,
112
+ });
113
+ console.log("");
114
+ console.log(` ${c.bold}${c.brightCyan}━━━ KramScan Setup Wizard ━━━${c.reset}`);
115
+ console.log(` ${c.gray}Configure your scanning environment${c.reset}`);
116
+ console.log("");
117
+ // AI Configuration
118
+ const aiEnabled = await askConfirm(rl, "Enable AI analysis?", false);
119
+ store.set("ai.enabled", aiEnabled);
120
+ if (aiEnabled) {
121
+ const aiProvider = await askList(rl, "Select AI provider", ["openai", "anthropic"], "openai");
122
+ store.set("ai.provider", aiProvider);
123
+ const apiKey = await askPassword(rl, "API key (leave blank to skip)");
124
+ if (apiKey) {
125
+ store.set("ai.apiKey", apiKey);
126
+ }
127
+ const model = await ask(rl, "Default AI model", aiProvider === "openai" ? "gpt-4" : "claude-3-opus-20240229");
128
+ store.set("ai.model", model);
129
+ }
130
+ // Report Configuration
131
+ const reportFormat = await askList(rl, "Default report format", ["word", "txt", "json"], "word");
132
+ store.set("report.defaultFormat", reportFormat);
133
+ // Scan Configuration
134
+ const strictScope = await askConfirm(rl, "Enable strict scope enforcement?", true);
135
+ store.set("scan.strictScope", strictScope);
136
+ const rateLimitStr = await ask(rl, "Requests per second rate limit", "5");
137
+ const rateLimit = parseInt(rateLimitStr, 10) || 5;
138
+ store.set("scan.rateLimitPerSecond", rateLimit);
139
+ rl.close();
140
+ console.log("");
141
+ logger.success("Onboarding complete! Your configuration has been saved.");
142
+ console.log(` ${c.gray}Config location: ~/.kramscan/config.json${c.reset}`);
143
+ console.log(` ${c.gray}Run ${c.cyan}kramscan${c.gray} to get started.${c.reset}`);
144
+ console.log("");
145
+ });
146
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerReportCommand(program: Command): void;
@@ -0,0 +1,225 @@
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.registerReportCommand = registerReportCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const logger_1 = require("../utils/logger");
9
+ const config_1 = require("../core/config");
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ const docx_1 = require("docx");
14
+ function registerReportCommand(program) {
15
+ program
16
+ .command("report [scan-file]")
17
+ .description("Generate a professional security report")
18
+ .option("-f, --format <type>", "Report format: word|json|txt")
19
+ .option("-o, --output <file>", "Output filename")
20
+ .action(async (scanFile, options) => {
21
+ console.log("");
22
+ console.log(chalk_1.default.bold.cyan("📄 Generating Security Report"));
23
+ console.log(chalk_1.default.gray("─".repeat(50)));
24
+ console.log("");
25
+ try {
26
+ // Load scan results
27
+ let filepath;
28
+ if (scanFile) {
29
+ filepath = path_1.default.isAbsolute(scanFile)
30
+ ? scanFile
31
+ : path_1.default.join(process.cwd(), scanFile);
32
+ }
33
+ else {
34
+ // Find latest scan
35
+ const scanDir = path_1.default.join(os_1.default.homedir(), ".kramscan", "scans");
36
+ const files = await promises_1.default.readdir(scanDir);
37
+ const scanFiles = files.filter((f) => f.endsWith(".json"));
38
+ if (scanFiles.length === 0) {
39
+ logger_1.logger.error("No scan results found. Run 'kramscan scan <url>' first.");
40
+ process.exit(1);
41
+ }
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
+ // Determine format
49
+ const config = (0, config_1.getConfig)();
50
+ const format = options.format || config.report.defaultFormat;
51
+ const spinner = logger_1.logger.spinner(`Generating ${format.toUpperCase()} report...`);
52
+ let outputPath;
53
+ switch (format) {
54
+ case "word":
55
+ outputPath = await generateWordReport(scanResult, options.output);
56
+ break;
57
+ case "json":
58
+ outputPath = await generateJsonReport(scanResult, options.output);
59
+ break;
60
+ case "txt":
61
+ outputPath = await generateTxtReport(scanResult, options.output);
62
+ break;
63
+ default:
64
+ throw new Error(`Unsupported format: ${format}`);
65
+ }
66
+ spinner.succeed("Report generated!");
67
+ console.log("");
68
+ logger_1.logger.success(`Report saved to: ${outputPath}`);
69
+ console.log("");
70
+ }
71
+ catch (error) {
72
+ logger_1.logger.error(error.message);
73
+ process.exit(1);
74
+ }
75
+ });
76
+ }
77
+ async function generateWordReport(scanResult, outputFile) {
78
+ const doc = new docx_1.Document({
79
+ sections: [
80
+ {
81
+ properties: {},
82
+ children: [
83
+ // Title
84
+ new docx_1.Paragraph({
85
+ text: "Security Assessment Report",
86
+ heading: docx_1.HeadingLevel.HEADING_1,
87
+ alignment: docx_1.AlignmentType.CENTER,
88
+ }),
89
+ new docx_1.Paragraph({
90
+ text: `Target: ${scanResult.target}`,
91
+ alignment: docx_1.AlignmentType.CENTER,
92
+ }),
93
+ new docx_1.Paragraph({
94
+ text: `Date: ${new Date(scanResult.timestamp).toLocaleString()}`,
95
+ alignment: docx_1.AlignmentType.CENTER,
96
+ }),
97
+ new docx_1.Paragraph({ text: "" }),
98
+ // Executive Summary
99
+ new docx_1.Paragraph({
100
+ text: "Executive Summary",
101
+ heading: docx_1.HeadingLevel.HEADING_2,
102
+ }),
103
+ new docx_1.Paragraph({
104
+ children: [
105
+ new docx_1.TextRun({
106
+ text: `This report contains the results of an automated security assessment performed on ${scanResult.target}. `,
107
+ }),
108
+ new docx_1.TextRun({
109
+ text: `A total of ${scanResult.summary.total} vulnerabilities were identified, `,
110
+ }),
111
+ new docx_1.TextRun({
112
+ text: `including ${scanResult.summary.critical} critical and ${scanResult.summary.high} high severity issues.`,
113
+ }),
114
+ ],
115
+ }),
116
+ new docx_1.Paragraph({ text: "" }),
117
+ // Scan Statistics
118
+ new docx_1.Paragraph({
119
+ text: "Scan Statistics",
120
+ heading: docx_1.HeadingLevel.HEADING_2,
121
+ }),
122
+ new docx_1.Paragraph({ text: `• URLs Crawled: ${scanResult.metadata.crawledUrls}` }),
123
+ new docx_1.Paragraph({ text: `• Forms Tested: ${scanResult.metadata.testedForms}` }),
124
+ new docx_1.Paragraph({ text: `• Requests Made: ${scanResult.metadata.requestsMade}` }),
125
+ new docx_1.Paragraph({
126
+ text: `• Duration: ${(scanResult.duration / 1000).toFixed(2)} seconds`,
127
+ }),
128
+ new docx_1.Paragraph({ text: "" }),
129
+ // Findings
130
+ new docx_1.Paragraph({
131
+ text: "Detailed Findings",
132
+ heading: docx_1.HeadingLevel.HEADING_2,
133
+ }),
134
+ ...scanResult.vulnerabilities.flatMap((vuln, i) => [
135
+ new docx_1.Paragraph({
136
+ text: `${i + 1}. ${vuln.title} [${vuln.severity.toUpperCase()}]`,
137
+ heading: docx_1.HeadingLevel.HEADING_3,
138
+ }),
139
+ new docx_1.Paragraph({ text: `URL: ${vuln.url}` }),
140
+ new docx_1.Paragraph({ text: `Type: ${vuln.type}` }),
141
+ new docx_1.Paragraph({ text: `Description: ${vuln.description}` }),
142
+ ...(vuln.evidence
143
+ ? [new docx_1.Paragraph({ text: `Evidence: ${vuln.evidence}` })]
144
+ : []),
145
+ ...(vuln.remediation
146
+ ? [new docx_1.Paragraph({ text: `Remediation: ${vuln.remediation}` })]
147
+ : []),
148
+ ...(vuln.cwe ? [new docx_1.Paragraph({ text: `CWE: ${vuln.cwe}` })] : []),
149
+ new docx_1.Paragraph({ text: "" }),
150
+ ]),
151
+ ],
152
+ },
153
+ ],
154
+ });
155
+ const buffer = await docx_1.Packer.toBuffer(doc);
156
+ const reportsDir = path_1.default.join(os_1.default.homedir(), ".kramscan", "reports");
157
+ await promises_1.default.mkdir(reportsDir, { recursive: true });
158
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
159
+ const filename = outputFile || `report-${timestamp}.docx`;
160
+ const filepath = path_1.default.isAbsolute(filename)
161
+ ? filename
162
+ : path_1.default.join(reportsDir, filename);
163
+ await promises_1.default.writeFile(filepath, buffer);
164
+ return filepath;
165
+ }
166
+ async function generateJsonReport(scanResult, outputFile) {
167
+ const reportsDir = path_1.default.join(os_1.default.homedir(), ".kramscan", "reports");
168
+ await promises_1.default.mkdir(reportsDir, { recursive: true });
169
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
170
+ const filename = outputFile || `report-${timestamp}.json`;
171
+ const filepath = path_1.default.isAbsolute(filename)
172
+ ? filename
173
+ : path_1.default.join(reportsDir, filename);
174
+ await promises_1.default.writeFile(filepath, JSON.stringify(scanResult, null, 2));
175
+ return filepath;
176
+ }
177
+ async function generateTxtReport(scanResult, outputFile) {
178
+ const lines = [];
179
+ lines.push("=".repeat(60));
180
+ lines.push("SECURITY ASSESSMENT REPORT");
181
+ lines.push("=".repeat(60));
182
+ lines.push("");
183
+ lines.push(`Target: ${scanResult.target}`);
184
+ lines.push(`Date: ${new Date(scanResult.timestamp).toLocaleString()}`);
185
+ lines.push(`Duration: ${(scanResult.duration / 1000).toFixed(2)}s`);
186
+ lines.push("");
187
+ lines.push("EXECUTIVE SUMMARY");
188
+ lines.push("-".repeat(60));
189
+ lines.push(`Total Vulnerabilities: ${scanResult.summary.total} (${scanResult.summary.critical} Critical, ${scanResult.summary.high} High, ${scanResult.summary.medium} Medium, ${scanResult.summary.low} Low, ${scanResult.summary.info} Info)`);
190
+ lines.push("");
191
+ lines.push("SCAN STATISTICS");
192
+ lines.push("-".repeat(60));
193
+ lines.push(`URLs Crawled: ${scanResult.metadata.crawledUrls}`);
194
+ lines.push(`Forms Tested: ${scanResult.metadata.testedForms}`);
195
+ lines.push(`Requests Made: ${scanResult.metadata.requestsMade}`);
196
+ lines.push("");
197
+ lines.push("DETAILED FINDINGS");
198
+ lines.push("-".repeat(60));
199
+ lines.push("");
200
+ scanResult.vulnerabilities.forEach((vuln, i) => {
201
+ lines.push(`${i + 1}. ${vuln.title} [${vuln.severity.toUpperCase()}]`);
202
+ lines.push(` URL: ${vuln.url}`);
203
+ lines.push(` Type: ${vuln.type}`);
204
+ lines.push(` Description: ${vuln.description}`);
205
+ if (vuln.evidence)
206
+ lines.push(` Evidence: ${vuln.evidence}`);
207
+ if (vuln.remediation)
208
+ lines.push(` Remediation: ${vuln.remediation}`);
209
+ if (vuln.cwe)
210
+ lines.push(` CWE: ${vuln.cwe}`);
211
+ lines.push("");
212
+ });
213
+ lines.push("=".repeat(60));
214
+ lines.push("End of Report");
215
+ lines.push("=".repeat(60));
216
+ const reportsDir = path_1.default.join(os_1.default.homedir(), ".kramscan", "reports");
217
+ await promises_1.default.mkdir(reportsDir, { recursive: true });
218
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
219
+ const filename = outputFile || `report-${timestamp}.txt`;
220
+ const filepath = path_1.default.isAbsolute(filename)
221
+ ? filename
222
+ : path_1.default.join(reportsDir, filename);
223
+ await promises_1.default.writeFile(filepath, lines.join("\n"));
224
+ return filepath;
225
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerScanCommand(program: Command): void;