kramscan 0.1.1 → 0.3.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/LICENSE +1 -1
- package/README.md +419 -236
- package/dist/agent/confirmation.d.ts +5 -1
- package/dist/agent/confirmation.js +29 -9
- package/dist/agent/context.js +2 -3
- package/dist/agent/orchestrator.d.ts +2 -0
- package/dist/agent/orchestrator.js +50 -8
- package/dist/agent/prompts/system.d.ts +1 -1
- package/dist/agent/prompts/system.js +5 -7
- package/dist/agent/skills/health-check.js +22 -2
- package/dist/agent/skills/index.d.ts +1 -0
- package/dist/agent/skills/index.js +3 -1
- 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.js +46 -0
- package/dist/cli.js +156 -149
- package/dist/commands/agent.js +38 -38
- package/dist/commands/ai.d.ts +2 -0
- package/dist/commands/ai.js +112 -0
- package/dist/commands/analyze.js +103 -54
- package/dist/commands/config.js +55 -29
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.js +236 -0
- package/dist/commands/doctor.js +20 -15
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +109 -0
- package/dist/commands/onboard.js +188 -141
- package/dist/commands/report.js +68 -76
- package/dist/commands/scan.js +262 -81
- package/dist/commands/scans.d.ts +2 -0
- package/dist/commands/scans.js +55 -0
- package/dist/core/ai-client.d.ts +6 -1
- package/dist/core/ai-client.js +80 -12
- 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 +8 -31
- package/dist/core/config.js +71 -14
- package/dist/core/diff-engine.d.ts +12 -0
- package/dist/core/diff-engine.js +47 -0
- package/dist/core/errors.d.ts +71 -0
- package/dist/core/errors.js +162 -0
- package/dist/core/scan-index.d.ts +20 -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 +95 -13
- package/dist/core/scanner.js +342 -248
- package/dist/core/server-probe.d.ts +20 -0
- package/dist/core/server-probe.js +109 -0
- package/dist/core/vulnerability-detector.d.ts +9 -0
- package/dist/core/vulnerability-detector.js +46 -15
- 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 +12 -0
- package/dist/plugins/index.js +29 -0
- package/dist/plugins/types.d.ts +55 -0
- package/dist/plugins/types.js +25 -0
- package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.d.ts +10 -0
- package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.js +67 -0
- package/dist/plugins/vulnerabilities/CSRFPlugin.d.ts +8 -0
- package/dist/plugins/vulnerabilities/CSRFPlugin.js +34 -0
- package/dist/plugins/vulnerabilities/CookieSecurityPlugin.d.ts +10 -0
- package/dist/plugins/vulnerabilities/CookieSecurityPlugin.js +91 -0
- package/dist/plugins/vulnerabilities/DebugEndpointPlugin.d.ts +15 -0
- package/dist/plugins/vulnerabilities/DebugEndpointPlugin.js +222 -0
- package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.d.ts +13 -0
- package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.js +110 -0
- package/dist/plugins/vulnerabilities/OpenRedirectPlugin.d.ts +10 -0
- package/dist/plugins/vulnerabilities/OpenRedirectPlugin.js +69 -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 +404 -0
- package/dist/utils/logger.d.ts +33 -1
- package/dist/utils/logger.js +127 -8
- package/dist/utils/theme.d.ts +56 -0
- package/dist/utils/theme.js +201 -0
- package/package.json +6 -3
package/dist/utils/logger.js
CHANGED
|
@@ -3,25 +3,97 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.logger = void 0;
|
|
6
|
+
exports.structuredLogger = exports.logger = exports.LogLevel = void 0;
|
|
7
|
+
exports.createChildLogger = createChildLogger;
|
|
7
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
9
|
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
var LogLevel;
|
|
11
|
+
(function (LogLevel) {
|
|
12
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
13
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
14
|
+
LogLevel[LogLevel["SUCCESS"] = 2] = "SUCCESS";
|
|
15
|
+
LogLevel[LogLevel["WARN"] = 3] = "WARN";
|
|
16
|
+
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
|
|
17
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
18
|
+
function getLogLevel() {
|
|
19
|
+
const envLevel = process.env.LOG_LEVEL?.toUpperCase();
|
|
20
|
+
switch (envLevel) {
|
|
21
|
+
case "DEBUG":
|
|
22
|
+
return LogLevel.DEBUG;
|
|
23
|
+
case "INFO":
|
|
24
|
+
return LogLevel.INFO;
|
|
25
|
+
case "WARN":
|
|
26
|
+
case "WARNING":
|
|
27
|
+
return LogLevel.WARN;
|
|
28
|
+
case "ERROR":
|
|
29
|
+
return LogLevel.ERROR;
|
|
30
|
+
default:
|
|
31
|
+
return process.env.KRAMSCAN_DEBUG === "1" || process.env.KRAMSCAN_DEBUG === "true"
|
|
32
|
+
? LogLevel.DEBUG
|
|
33
|
+
: LogLevel.INFO;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function debugEnabled() {
|
|
37
|
+
return getLogLevel() <= LogLevel.DEBUG;
|
|
38
|
+
}
|
|
39
|
+
function isJsonOutput() {
|
|
40
|
+
return process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true";
|
|
41
|
+
}
|
|
42
|
+
function shouldLog(level) {
|
|
43
|
+
return level >= getLogLevel();
|
|
44
|
+
}
|
|
45
|
+
function formatLogEntry(level, message, context) {
|
|
46
|
+
return {
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
level,
|
|
49
|
+
message,
|
|
50
|
+
context,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function outputLog(entry) {
|
|
54
|
+
if (isJsonOutput()) {
|
|
55
|
+
console.log(JSON.stringify(entry));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const { timestamp, level, message, ...rest } = entry;
|
|
59
|
+
let output = message;
|
|
60
|
+
if (process.env.LOG_INCLUDE_CONTEXT === "true" && Object.keys(rest).length > 0) {
|
|
61
|
+
output += ` ${JSON.stringify(rest)}`;
|
|
62
|
+
}
|
|
63
|
+
console.log(output);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Simple human-readable logger (original behavior)
|
|
9
67
|
exports.logger = {
|
|
10
68
|
info: (message) => {
|
|
11
|
-
|
|
69
|
+
if (shouldLog(LogLevel.INFO)) {
|
|
70
|
+
console.log(chalk_1.default.blue("i"), message);
|
|
71
|
+
}
|
|
12
72
|
},
|
|
13
73
|
success: (message) => {
|
|
14
|
-
|
|
74
|
+
if (shouldLog(LogLevel.SUCCESS)) {
|
|
75
|
+
console.log(chalk_1.default.green("✓"), message);
|
|
76
|
+
}
|
|
15
77
|
},
|
|
16
78
|
warn: (message) => {
|
|
17
|
-
|
|
79
|
+
if (shouldLog(LogLevel.WARN)) {
|
|
80
|
+
console.log(chalk_1.default.yellow("⚠"), message);
|
|
81
|
+
}
|
|
18
82
|
},
|
|
19
83
|
error: (message) => {
|
|
20
|
-
|
|
84
|
+
if (shouldLog(LogLevel.ERROR)) {
|
|
85
|
+
console.log(chalk_1.default.red("✗"), message);
|
|
86
|
+
}
|
|
21
87
|
},
|
|
22
|
-
debug: (message) => {
|
|
23
|
-
if (
|
|
24
|
-
|
|
88
|
+
debug: (message, context) => {
|
|
89
|
+
if (debugEnabled()) {
|
|
90
|
+
if (isJsonOutput()) {
|
|
91
|
+
const entry = formatLogEntry("DEBUG", message, context);
|
|
92
|
+
console.log(JSON.stringify(entry));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log(chalk_1.default.gray("→"), message, context ? JSON.stringify(context) : "");
|
|
96
|
+
}
|
|
25
97
|
}
|
|
26
98
|
},
|
|
27
99
|
spinner: (text) => {
|
|
@@ -32,3 +104,50 @@ exports.logger = {
|
|
|
32
104
|
}).start();
|
|
33
105
|
},
|
|
34
106
|
};
|
|
107
|
+
// Structured JSON logger for log aggregation systems
|
|
108
|
+
exports.structuredLogger = {
|
|
109
|
+
debug: (message, context) => {
|
|
110
|
+
if (shouldLog(LogLevel.DEBUG)) {
|
|
111
|
+
outputLog(formatLogEntry("DEBUG", message, context));
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
info: (message, context) => {
|
|
115
|
+
if (shouldLog(LogLevel.INFO)) {
|
|
116
|
+
outputLog(formatLogEntry("INFO", message, context));
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
warn: (message, context) => {
|
|
120
|
+
if (shouldLog(LogLevel.WARN)) {
|
|
121
|
+
outputLog(formatLogEntry("WARN", message, context));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
error: (message, context) => {
|
|
125
|
+
if (shouldLog(LogLevel.ERROR)) {
|
|
126
|
+
outputLog(formatLogEntry("ERROR", message, context));
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
// Log with custom level
|
|
130
|
+
log: (level, message, context) => {
|
|
131
|
+
if (shouldLog(level)) {
|
|
132
|
+
const levelName = LogLevel[level];
|
|
133
|
+
outputLog(formatLogEntry(levelName, message, context));
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
// Convenience function to create a child logger with additional context
|
|
138
|
+
function createChildLogger(defaultContext) {
|
|
139
|
+
return {
|
|
140
|
+
debug: (message, context) => {
|
|
141
|
+
exports.structuredLogger.debug(message, { ...defaultContext, ...context });
|
|
142
|
+
},
|
|
143
|
+
info: (message, context) => {
|
|
144
|
+
exports.structuredLogger.info(message, { ...defaultContext, ...context });
|
|
145
|
+
},
|
|
146
|
+
warn: (message, context) => {
|
|
147
|
+
exports.structuredLogger.warn(message, { ...defaultContext, ...context });
|
|
148
|
+
},
|
|
149
|
+
error: (message, context) => {
|
|
150
|
+
exports.structuredLogger.error(message, { ...defaultContext, ...context });
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const CLI_VERSION: string;
|
|
2
|
+
export declare const theme: {
|
|
3
|
+
brand: import("chalk").ChalkInstance;
|
|
4
|
+
error: import("chalk").ChalkInstance;
|
|
5
|
+
success: import("chalk").ChalkInstance;
|
|
6
|
+
warning: import("chalk").ChalkInstance;
|
|
7
|
+
info: import("chalk").ChalkInstance;
|
|
8
|
+
dim: import("chalk").ChalkInstance;
|
|
9
|
+
gray: import("chalk").ChalkInstance;
|
|
10
|
+
white: import("chalk").ChalkInstance;
|
|
11
|
+
cyan: import("chalk").ChalkInstance;
|
|
12
|
+
brightWhite: import("chalk").ChalkInstance;
|
|
13
|
+
brightCyan: import("chalk").ChalkInstance;
|
|
14
|
+
brightMagenta: import("chalk").ChalkInstance;
|
|
15
|
+
brightBlue: import("chalk").ChalkInstance;
|
|
16
|
+
brightGreen: import("chalk").ChalkInstance;
|
|
17
|
+
brightYellow: import("chalk").ChalkInstance;
|
|
18
|
+
brightRed: import("chalk").ChalkInstance;
|
|
19
|
+
critical: import("chalk").ChalkInstance;
|
|
20
|
+
high: import("chalk").ChalkInstance;
|
|
21
|
+
medium: import("chalk").ChalkInstance;
|
|
22
|
+
low: import("chalk").ChalkInstance;
|
|
23
|
+
infoSeverity: import("chalk").ChalkInstance;
|
|
24
|
+
yellow: import("chalk").ChalkInstance;
|
|
25
|
+
green: import("chalk").ChalkInstance;
|
|
26
|
+
};
|
|
27
|
+
export declare function printBanner(): void;
|
|
28
|
+
export declare function printInfo(): void;
|
|
29
|
+
export declare function getSeverityColor(severity: string): (text: string) => string;
|
|
30
|
+
export declare function displayScanSummary(result: {
|
|
31
|
+
target: string;
|
|
32
|
+
duration: number;
|
|
33
|
+
metadata: {
|
|
34
|
+
crawledUrls: number;
|
|
35
|
+
testedForms: number;
|
|
36
|
+
requestsMade: number;
|
|
37
|
+
};
|
|
38
|
+
summary: {
|
|
39
|
+
total: number;
|
|
40
|
+
critical: number;
|
|
41
|
+
high: number;
|
|
42
|
+
medium: number;
|
|
43
|
+
low: number;
|
|
44
|
+
info: number;
|
|
45
|
+
};
|
|
46
|
+
score: number;
|
|
47
|
+
vulnerabilities: Array<{
|
|
48
|
+
severity: string;
|
|
49
|
+
title: string;
|
|
50
|
+
url: string;
|
|
51
|
+
description: string;
|
|
52
|
+
}>;
|
|
53
|
+
filepath: string;
|
|
54
|
+
pdfPath?: string | null;
|
|
55
|
+
}): void;
|
|
56
|
+
export default theme;
|
|
@@ -0,0 +1,201 @@
|
|
|
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.theme = exports.CLI_VERSION = void 0;
|
|
40
|
+
exports.printBanner = printBanner;
|
|
41
|
+
exports.printInfo = printInfo;
|
|
42
|
+
exports.getSeverityColor = getSeverityColor;
|
|
43
|
+
exports.displayScanSummary = displayScanSummary;
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
// Read version from package.json (single source of truth)
|
|
48
|
+
function getPackageVersion() {
|
|
49
|
+
try {
|
|
50
|
+
const pkgPath = path.resolve(__dirname, "..", "..", "package.json");
|
|
51
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
52
|
+
return pkg.version || "0.0.0";
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "0.0.0";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.CLI_VERSION = getPackageVersion();
|
|
59
|
+
exports.theme = {
|
|
60
|
+
// Colors
|
|
61
|
+
brand: chalk_1.default.hex("#00D1FF"),
|
|
62
|
+
error: chalk_1.default.red.bold,
|
|
63
|
+
success: chalk_1.default.green,
|
|
64
|
+
warning: chalk_1.default.yellow,
|
|
65
|
+
info: chalk_1.default.blue,
|
|
66
|
+
dim: chalk_1.default.gray,
|
|
67
|
+
gray: chalk_1.default.gray,
|
|
68
|
+
white: chalk_1.default.white,
|
|
69
|
+
cyan: chalk_1.default.cyan,
|
|
70
|
+
brightWhite: chalk_1.default.white.bold,
|
|
71
|
+
brightCyan: chalk_1.default.cyan.bold,
|
|
72
|
+
brightMagenta: chalk_1.default.magenta.bold,
|
|
73
|
+
brightBlue: chalk_1.default.blue.bold,
|
|
74
|
+
brightGreen: chalk_1.default.green.bold,
|
|
75
|
+
brightYellow: chalk_1.default.yellow.bold,
|
|
76
|
+
brightRed: chalk_1.default.red.bold,
|
|
77
|
+
// Severity colors
|
|
78
|
+
critical: chalk_1.default.red.bold,
|
|
79
|
+
high: chalk_1.default.red,
|
|
80
|
+
medium: chalk_1.default.yellow,
|
|
81
|
+
low: chalk_1.default.blue,
|
|
82
|
+
infoSeverity: chalk_1.default.gray,
|
|
83
|
+
yellow: chalk_1.default.yellow,
|
|
84
|
+
green: chalk_1.default.green,
|
|
85
|
+
};
|
|
86
|
+
// ─── ASCII Art Banner ──────────────────────────────────────────────
|
|
87
|
+
function printBanner() {
|
|
88
|
+
const lines = [
|
|
89
|
+
`██╗ ██╗██████╗ █████╗ ███╗ ███╗███████╗ ██████╗ █████╗ ███╗ ██╗`,
|
|
90
|
+
`██║ ██╔╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗████╗ ██║`,
|
|
91
|
+
`█████╔╝ ██████╔╝███████║██╔████╔██║███████╗██║ ███████║██╔██╗ ██║`,
|
|
92
|
+
`██╔═██╗ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██║ ██╔══██║██║╚██╗██║`,
|
|
93
|
+
`██║ ██╗██║ ██║██║ ██║██║ ╚═╝ ██║███████║╚██████╗██║ ██║██║ ╚████║`,
|
|
94
|
+
`╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝`,
|
|
95
|
+
];
|
|
96
|
+
console.log("");
|
|
97
|
+
lines.forEach((line, i) => {
|
|
98
|
+
const shade = i % 2 === 0 ? exports.theme.brightWhite : exports.theme.dim;
|
|
99
|
+
console.log(` ${shade(line)}`);
|
|
100
|
+
});
|
|
101
|
+
console.log("");
|
|
102
|
+
}
|
|
103
|
+
// ─── Dashboard Info ────────────────────────────────────────────────
|
|
104
|
+
function printInfo() {
|
|
105
|
+
console.log(` ${exports.theme.dim("───────────────────────────────────────────────────────")}`);
|
|
106
|
+
console.log(` ${exports.theme.brightWhite(" KramScan")} ${exports.theme.gray(`v${exports.CLI_VERSION}`)} ${exports.theme.dim("|")} ${exports.theme.cyan("AI-Powered Web Security Scanner")}`);
|
|
107
|
+
console.log(` ${exports.theme.dim("───────────────────────────────────────────────────────")}`);
|
|
108
|
+
console.log("");
|
|
109
|
+
console.log(` ${exports.theme.brightYellow("Tips for getting started:")}`);
|
|
110
|
+
console.log(` ${exports.theme.white("1.")} ${exports.theme.gray("Run")} ${exports.theme.cyan("kramscan onboard")} ${exports.theme.gray("to configure your API keys.")}`);
|
|
111
|
+
console.log(` ${exports.theme.white("2.")} ${exports.theme.gray("Run")} ${exports.theme.cyan("kramscan scan <url>")} ${exports.theme.gray("to scan a target.")}`);
|
|
112
|
+
console.log(` ${exports.theme.white("3.")} ${exports.theme.gray("Run")} ${exports.theme.cyan("kramscan --help")} ${exports.theme.gray("for all commands.")}`);
|
|
113
|
+
console.log("");
|
|
114
|
+
}
|
|
115
|
+
// ─── Severity Color Helper ─────────────────────────────────────────
|
|
116
|
+
function getSeverityColor(severity) {
|
|
117
|
+
const s = severity.toLowerCase();
|
|
118
|
+
if (s === "critical")
|
|
119
|
+
return exports.theme.critical;
|
|
120
|
+
if (s === "high")
|
|
121
|
+
return exports.theme.high;
|
|
122
|
+
if (s === "medium")
|
|
123
|
+
return exports.theme.medium;
|
|
124
|
+
if (s === "low")
|
|
125
|
+
return exports.theme.low;
|
|
126
|
+
return exports.theme.infoSeverity;
|
|
127
|
+
}
|
|
128
|
+
// ─── Scan Summary Display ──────────────────────────────────────────
|
|
129
|
+
function displayScanSummary(result) {
|
|
130
|
+
const { target, duration, metadata, summary, vulnerabilities, filepath, pdfPath, score } = result;
|
|
131
|
+
// Scan Summary
|
|
132
|
+
console.log("");
|
|
133
|
+
console.log(exports.theme.brightWhite.bold("📊 Scan Summary"));
|
|
134
|
+
console.log(exports.theme.gray("─".repeat(50)));
|
|
135
|
+
console.log("");
|
|
136
|
+
console.log(exports.theme.white("Target:"), exports.theme.cyan(target));
|
|
137
|
+
console.log(exports.theme.white("Duration:"), exports.theme.cyan(`${(duration / 1000).toFixed(2)}s`));
|
|
138
|
+
console.log(exports.theme.white("URLs Crawled:"), exports.theme.cyan(metadata.crawledUrls));
|
|
139
|
+
console.log(exports.theme.white("Forms Tested:"), exports.theme.cyan(metadata.testedForms));
|
|
140
|
+
console.log(exports.theme.white("Requests Made:"), exports.theme.cyan(metadata.requestsMade));
|
|
141
|
+
// Security Score Display
|
|
142
|
+
const scoreColor = score > 80 ? exports.theme.success : (score > 50 ? exports.theme.warning : exports.theme.error);
|
|
143
|
+
const scoreLabel = score > 80 ? "EXCELLENT" : (score > 50 ? "FAIR" : "POOR");
|
|
144
|
+
console.log("");
|
|
145
|
+
console.log(` ${exports.theme.white("Security Score:")} ${scoreColor.bold(score + "/100")} ${exports.theme.gray(`(${scoreLabel})`)}`);
|
|
146
|
+
console.log(` ${scoreColor("█".repeat(Math.round(score / 5)) + exports.theme.dim("█".repeat(20 - Math.round(score / 5))))}`);
|
|
147
|
+
console.log("");
|
|
148
|
+
// Vulnerability summary
|
|
149
|
+
console.log(exports.theme.brightWhite.bold("🛡️ Vulnerabilities Found"));
|
|
150
|
+
console.log(exports.theme.gray("─".repeat(50)));
|
|
151
|
+
console.log("");
|
|
152
|
+
if (summary.total === 0) {
|
|
153
|
+
console.log(exports.theme.success("✓ No vulnerabilities found!"));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
if (summary.critical > 0)
|
|
157
|
+
console.log(exports.theme.critical(` ${summary.critical} Critical`), exports.theme.gray("- Immediate action required"));
|
|
158
|
+
if (summary.high > 0)
|
|
159
|
+
console.log(exports.theme.high(` ${summary.high} High`), exports.theme.gray("- Should be fixed soon"));
|
|
160
|
+
if (summary.medium > 0)
|
|
161
|
+
console.log(exports.theme.medium(` ${summary.medium} Medium`), exports.theme.gray("- Fix when possible"));
|
|
162
|
+
if (summary.low > 0)
|
|
163
|
+
console.log(exports.theme.low(` ${summary.low} Low`), exports.theme.gray("- Minor issues"));
|
|
164
|
+
if (summary.info > 0)
|
|
165
|
+
console.log(exports.theme.infoSeverity(` ${summary.info} Info`), exports.theme.gray("- Informational"));
|
|
166
|
+
}
|
|
167
|
+
console.log("");
|
|
168
|
+
console.log(exports.theme.gray("Results saved to:"), exports.theme.white(filepath));
|
|
169
|
+
if (pdfPath) {
|
|
170
|
+
console.log(exports.theme.gray("PDF report saved to:"), exports.theme.white(pdfPath));
|
|
171
|
+
}
|
|
172
|
+
console.log("");
|
|
173
|
+
// Show top vulnerabilities
|
|
174
|
+
if (vulnerabilities.length > 0) {
|
|
175
|
+
console.log(exports.theme.brightWhite.bold("🔴 Top Findings"));
|
|
176
|
+
console.log(exports.theme.gray("─".repeat(50)));
|
|
177
|
+
console.log("");
|
|
178
|
+
const topVulns = vulnerabilities
|
|
179
|
+
.sort((a, b) => {
|
|
180
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
181
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
182
|
+
})
|
|
183
|
+
.slice(0, 5);
|
|
184
|
+
for (const vuln of topVulns) {
|
|
185
|
+
const severityColor = getSeverityColor(vuln.severity);
|
|
186
|
+
console.log(severityColor(`[${vuln.severity.toUpperCase()}]`), exports.theme.brightWhite.bold(vuln.title));
|
|
187
|
+
console.log(exports.theme.gray(` ${vuln.url}`));
|
|
188
|
+
console.log(exports.theme.white(` ${vuln.description}`));
|
|
189
|
+
console.log("");
|
|
190
|
+
}
|
|
191
|
+
if (vulnerabilities.length > 5) {
|
|
192
|
+
console.log(exports.theme.gray(` ... and ${vulnerabilities.length - 5} more`));
|
|
193
|
+
console.log("");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
console.log(exports.theme.cyan("💡 Next steps:"));
|
|
197
|
+
console.log(exports.theme.white(` 1. Run ${exports.theme.cyan(`kramscan analyze ${filepath}`)} for AI-powered insights`));
|
|
198
|
+
console.log(exports.theme.white(" 2. PDF report is generated automatically after the scan"));
|
|
199
|
+
console.log("");
|
|
200
|
+
}
|
|
201
|
+
exports.default = exports.theme;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kramscan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "KramScan CLI — AI-powered web app security testing",
|
|
5
|
-
"author": "Akram Shaikh
|
|
5
|
+
"author": "Akram Shaikh (https://akramshaikh.me)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"security",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"web-security",
|
|
14
14
|
"analysis"
|
|
15
15
|
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
16
19
|
"repository": {
|
|
17
20
|
"type": "git",
|
|
18
21
|
"url": "https://github.com/shaikhakramshakil/kramscan.git"
|
|
@@ -45,7 +48,7 @@
|
|
|
45
48
|
"lint": "eslint src --ext .ts",
|
|
46
49
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
47
50
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
48
|
-
"prepublishOnly": "npm run clean && npm run build"
|
|
51
|
+
"prepublishOnly": "npm test && npm run clean && npm run build"
|
|
49
52
|
},
|
|
50
53
|
"dependencies": {
|
|
51
54
|
"@anthropic-ai/sdk": "^0.31.0",
|