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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Custom Error Types for KramScan
|
|
4
|
+
* Provides structured error handling with error codes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ReportError = exports.AiError = exports.NetworkError = exports.ConfigError = exports.PluginError = exports.ScannerError = exports.KramScanError = exports.ErrorCode = void 0;
|
|
8
|
+
exports.isRetryable = isRetryable;
|
|
9
|
+
exports.shouldRetry = shouldRetry;
|
|
10
|
+
exports.getRetryDelay = getRetryDelay;
|
|
11
|
+
exports.setupGlobalErrorHandlers = setupGlobalErrorHandlers;
|
|
12
|
+
var ErrorCode;
|
|
13
|
+
(function (ErrorCode) {
|
|
14
|
+
// Scanner errors (SCN-xxx)
|
|
15
|
+
ErrorCode["SCN_INIT_FAILED"] = "SCN_INIT_FAILED";
|
|
16
|
+
ErrorCode["SCN_CRAWL_FAILED"] = "SCN_CRAWL_FAILED";
|
|
17
|
+
ErrorCode["SCN_TIMEOUT"] = "SCN_TIMEOUT";
|
|
18
|
+
ErrorCode["SCN_INVALID_URL"] = "SCN_INVALID_URL";
|
|
19
|
+
ErrorCode["SCN_SCOPE_VIOLATION"] = "SCN_SCOPE_VIOLATION";
|
|
20
|
+
ErrorCode["SCN_BROWSER_ERROR"] = "SCN_BROWSER_ERROR";
|
|
21
|
+
// Plugin errors (PLG-xxx)
|
|
22
|
+
ErrorCode["PLG_INIT_FAILED"] = "PLG_INIT_FAILED";
|
|
23
|
+
ErrorCode["PLG_EXECUTION_FAILED"] = "PLG_EXECUTION_FAILED";
|
|
24
|
+
ErrorCode["PLG_NOT_FOUND"] = "PLG_NOT_FOUND";
|
|
25
|
+
ErrorCode["PLG_DISABLED"] = "PLG_DISABLED";
|
|
26
|
+
ErrorCode["PLG_TIMEOUT"] = "PLG_TIMEOUT";
|
|
27
|
+
// Config errors (CFG-xxx)
|
|
28
|
+
ErrorCode["CFG_INVALID"] = "CFG_INVALID";
|
|
29
|
+
ErrorCode["CFG_NOT_FOUND"] = "CFG_NOT_FOUND";
|
|
30
|
+
ErrorCode["CFG_WRITE_FAILED"] = "CFG_WRITE_FAILED";
|
|
31
|
+
// Network errors (NET-xxx)
|
|
32
|
+
ErrorCode["NET_REQUEST_FAILED"] = "NET_REQUEST_FAILED";
|
|
33
|
+
ErrorCode["NET_RATE_LIMITED"] = "NET_RATE_LIMITED";
|
|
34
|
+
ErrorCode["NET_SSL_ERROR"] = "NET_SSL_ERROR";
|
|
35
|
+
// AI errors (AI-xxx)
|
|
36
|
+
ErrorCode["AI_INIT_FAILED"] = "AI_INIT_FAILED";
|
|
37
|
+
ErrorCode["AI_REQUEST_FAILED"] = "AI_REQUEST_FAILED";
|
|
38
|
+
ErrorCode["AI_QUOTA_EXCEEDED"] = "AI_QUOTA_EXCEEDED";
|
|
39
|
+
// Report errors (RPT-xxx)
|
|
40
|
+
ErrorCode["RPT_GENERATION_FAILED"] = "RPT_GENERATION_FAILED";
|
|
41
|
+
ErrorCode["RPT_INVALID_FORMAT"] = "RPT_INVALID_FORMAT";
|
|
42
|
+
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
|
|
43
|
+
class KramScanError extends Error {
|
|
44
|
+
code;
|
|
45
|
+
statusCode;
|
|
46
|
+
retryable;
|
|
47
|
+
context;
|
|
48
|
+
timestamp;
|
|
49
|
+
constructor(message, options) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = "KramScanError";
|
|
52
|
+
this.code = options.code;
|
|
53
|
+
this.statusCode = options.statusCode;
|
|
54
|
+
this.retryable = options.retryable ?? false;
|
|
55
|
+
this.context = options.context;
|
|
56
|
+
this.timestamp = new Date().toISOString();
|
|
57
|
+
// Maintains proper stack trace in V8 environments
|
|
58
|
+
if (Error.captureStackTrace) {
|
|
59
|
+
Error.captureStackTrace(this, KramScanError);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
toJSON() {
|
|
63
|
+
return {
|
|
64
|
+
name: this.name,
|
|
65
|
+
message: this.message,
|
|
66
|
+
code: this.code,
|
|
67
|
+
statusCode: this.statusCode,
|
|
68
|
+
retryable: this.retryable,
|
|
69
|
+
context: this.context,
|
|
70
|
+
timestamp: this.timestamp,
|
|
71
|
+
stack: this.stack,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.KramScanError = KramScanError;
|
|
76
|
+
// Convenience error classes for common scenarios
|
|
77
|
+
class ScannerError extends KramScanError {
|
|
78
|
+
constructor(message, code = ErrorCode.SCN_CRAWL_FAILED, context) {
|
|
79
|
+
super(message, { code, retryable: true, context });
|
|
80
|
+
this.name = "ScannerError";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.ScannerError = ScannerError;
|
|
84
|
+
class PluginError extends KramScanError {
|
|
85
|
+
constructor(message, code = ErrorCode.PLG_EXECUTION_FAILED, context) {
|
|
86
|
+
super(message, { code, retryable: false, context });
|
|
87
|
+
this.name = "PluginError";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.PluginError = PluginError;
|
|
91
|
+
class ConfigError extends KramScanError {
|
|
92
|
+
constructor(message, code = ErrorCode.CFG_INVALID, context) {
|
|
93
|
+
super(message, { code, retryable: false, context });
|
|
94
|
+
this.name = "ConfigError";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.ConfigError = ConfigError;
|
|
98
|
+
class NetworkError extends KramScanError {
|
|
99
|
+
constructor(message, code = ErrorCode.NET_REQUEST_FAILED, context) {
|
|
100
|
+
super(message, { code, retryable: true, context });
|
|
101
|
+
this.name = "NetworkError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.NetworkError = NetworkError;
|
|
105
|
+
class AiError extends KramScanError {
|
|
106
|
+
constructor(message, code = ErrorCode.AI_REQUEST_FAILED, context) {
|
|
107
|
+
super(message, { code, retryable: true, context });
|
|
108
|
+
this.name = "AiError";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.AiError = AiError;
|
|
112
|
+
class ReportError extends KramScanError {
|
|
113
|
+
constructor(message, code = ErrorCode.RPT_GENERATION_FAILED, context) {
|
|
114
|
+
super(message, { code, retryable: false, context });
|
|
115
|
+
this.name = "ReportError";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ReportError = ReportError;
|
|
119
|
+
const defaultErrorHandlerConfig = {
|
|
120
|
+
maxRetries: 3,
|
|
121
|
+
baseDelay: 1000,
|
|
122
|
+
maxDelay: 10000,
|
|
123
|
+
retryableCodes: [
|
|
124
|
+
ErrorCode.SCN_CRAWL_FAILED,
|
|
125
|
+
ErrorCode.SCN_TIMEOUT,
|
|
126
|
+
ErrorCode.SCN_BROWSER_ERROR,
|
|
127
|
+
ErrorCode.NET_REQUEST_FAILED,
|
|
128
|
+
ErrorCode.PLG_EXECUTION_FAILED,
|
|
129
|
+
ErrorCode.AI_REQUEST_FAILED,
|
|
130
|
+
],
|
|
131
|
+
};
|
|
132
|
+
function isRetryable(error, config = defaultErrorHandlerConfig) {
|
|
133
|
+
if (!error.retryable)
|
|
134
|
+
return false;
|
|
135
|
+
return config.retryableCodes.includes(error.code);
|
|
136
|
+
}
|
|
137
|
+
function shouldRetry(error, attempt, config = defaultErrorHandlerConfig) {
|
|
138
|
+
return attempt < config.maxRetries && isRetryable(error, config);
|
|
139
|
+
}
|
|
140
|
+
function getRetryDelay(error, attempt, config = defaultErrorHandlerConfig) {
|
|
141
|
+
const delay = Math.min(config.baseDelay * Math.pow(2, attempt), config.maxDelay);
|
|
142
|
+
// Add jitter to prevent thundering herd
|
|
143
|
+
const jitter = Math.random() * 0.3 * delay;
|
|
144
|
+
return Math.floor(delay + jitter);
|
|
145
|
+
}
|
|
146
|
+
// Global error handler for uncaught errors
|
|
147
|
+
function setupGlobalErrorHandlers() {
|
|
148
|
+
process.on("uncaughtException", (error) => {
|
|
149
|
+
console.error("[FATAL] Uncaught Exception:");
|
|
150
|
+
console.error(error.message);
|
|
151
|
+
if (error.stack) {
|
|
152
|
+
console.error(error.stack);
|
|
153
|
+
}
|
|
154
|
+
process.exit(1);
|
|
155
|
+
});
|
|
156
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
157
|
+
console.error("[FATAL] Unhandled Promise Rejection:");
|
|
158
|
+
console.error("Reason:", reason);
|
|
159
|
+
console.error("Promise:", promise);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ScanIndexEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
target: string;
|
|
4
|
+
hostname: string;
|
|
5
|
+
timestamp: string;
|
|
6
|
+
jsonPath: string;
|
|
7
|
+
pdfPath?: string;
|
|
8
|
+
summary: {
|
|
9
|
+
total: number;
|
|
10
|
+
critical: number;
|
|
11
|
+
high: number;
|
|
12
|
+
medium: number;
|
|
13
|
+
low: number;
|
|
14
|
+
info: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function addScanToIndex(entry: Omit<ScanIndexEntry, "id">): Promise<ScanIndexEntry>;
|
|
18
|
+
export declare function listScans(limit?: number): Promise<ScanIndexEntry[]>;
|
|
19
|
+
export declare function getLatestScan(): Promise<ScanIndexEntry | null>;
|
|
@@ -0,0 +1,52 @@
|
|
|
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.addScanToIndex = addScanToIndex;
|
|
7
|
+
exports.listScans = listScans;
|
|
8
|
+
exports.getLatestScan = getLatestScan;
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
function getIndexPath() {
|
|
13
|
+
return path_1.default.join(os_1.default.homedir(), ".kramscan", "scans", "index.json");
|
|
14
|
+
}
|
|
15
|
+
async function readIndex() {
|
|
16
|
+
const indexPath = getIndexPath();
|
|
17
|
+
try {
|
|
18
|
+
const raw = await promises_1.default.readFile(indexPath, "utf-8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (Array.isArray(parsed)) {
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function writeIndex(entries) {
|
|
30
|
+
const indexPath = getIndexPath();
|
|
31
|
+
await promises_1.default.mkdir(path_1.default.dirname(indexPath), { recursive: true });
|
|
32
|
+
await promises_1.default.writeFile(indexPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
33
|
+
}
|
|
34
|
+
async function addScanToIndex(entry) {
|
|
35
|
+
const hostname = entry.hostname || "unknown";
|
|
36
|
+
const id = `${new Date(entry.timestamp).getTime()}-${hostname}-${Math.random().toString(36).slice(2, 8)}`;
|
|
37
|
+
const full = { ...entry, id };
|
|
38
|
+
const existing = await readIndex();
|
|
39
|
+
const merged = [full, ...existing]
|
|
40
|
+
.filter((e, idx, arr) => arr.findIndex((x) => x.jsonPath === e.jsonPath) === idx)
|
|
41
|
+
.slice(0, 500);
|
|
42
|
+
await writeIndex(merged);
|
|
43
|
+
return full;
|
|
44
|
+
}
|
|
45
|
+
async function listScans(limit = 20) {
|
|
46
|
+
const entries = await readIndex();
|
|
47
|
+
return entries.slice(0, Math.max(1, limit));
|
|
48
|
+
}
|
|
49
|
+
async function getLatestScan() {
|
|
50
|
+
const entries = await readIndex();
|
|
51
|
+
return entries.length > 0 ? entries[0] : null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ResolvedScanFile {
|
|
2
|
+
filepath: string;
|
|
3
|
+
filename: string;
|
|
4
|
+
isLatest: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function getKramScanHome(): string;
|
|
7
|
+
export declare function getScansDirectory(): string;
|
|
8
|
+
export declare function getReportsDirectory(): string;
|
|
9
|
+
export declare function ensureScansDirectory(): Promise<string>;
|
|
10
|
+
export declare function ensureReportsDirectory(): Promise<string>;
|
|
11
|
+
export declare function resolveScanFile(scanFile?: string): Promise<ResolvedScanFile>;
|
|
@@ -0,0 +1,69 @@
|
|
|
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.getKramScanHome = getKramScanHome;
|
|
7
|
+
exports.getScansDirectory = getScansDirectory;
|
|
8
|
+
exports.getReportsDirectory = getReportsDirectory;
|
|
9
|
+
exports.ensureScansDirectory = ensureScansDirectory;
|
|
10
|
+
exports.ensureReportsDirectory = ensureReportsDirectory;
|
|
11
|
+
exports.resolveScanFile = resolveScanFile;
|
|
12
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
13
|
+
const os_1 = __importDefault(require("os"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
function getKramScanHome() {
|
|
16
|
+
return path_1.default.join(os_1.default.homedir(), ".kramscan");
|
|
17
|
+
}
|
|
18
|
+
function getScansDirectory() {
|
|
19
|
+
return path_1.default.join(getKramScanHome(), "scans");
|
|
20
|
+
}
|
|
21
|
+
function getReportsDirectory() {
|
|
22
|
+
return path_1.default.join(getKramScanHome(), "reports");
|
|
23
|
+
}
|
|
24
|
+
async function ensureScansDirectory() {
|
|
25
|
+
const scanDir = getScansDirectory();
|
|
26
|
+
await promises_1.default.mkdir(scanDir, { recursive: true });
|
|
27
|
+
return scanDir;
|
|
28
|
+
}
|
|
29
|
+
async function ensureReportsDirectory() {
|
|
30
|
+
const reportsDir = getReportsDirectory();
|
|
31
|
+
await promises_1.default.mkdir(reportsDir, { recursive: true });
|
|
32
|
+
return reportsDir;
|
|
33
|
+
}
|
|
34
|
+
async function resolveScanFile(scanFile) {
|
|
35
|
+
if (scanFile) {
|
|
36
|
+
const filepath = path_1.default.isAbsolute(scanFile)
|
|
37
|
+
? scanFile
|
|
38
|
+
: path_1.default.join(process.cwd(), scanFile);
|
|
39
|
+
return {
|
|
40
|
+
filepath,
|
|
41
|
+
filename: path_1.default.basename(filepath),
|
|
42
|
+
isLatest: false,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const scanDir = await ensureScansDirectory();
|
|
46
|
+
const entries = await promises_1.default.readdir(scanDir, { withFileTypes: true });
|
|
47
|
+
const files = entries
|
|
48
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
49
|
+
.map((entry) => entry.name);
|
|
50
|
+
if (files.length === 0) {
|
|
51
|
+
throw new Error("No scan results found. Run 'kramscan scan <url>' first.");
|
|
52
|
+
}
|
|
53
|
+
const filesWithStats = await Promise.all(files.map(async (filename) => {
|
|
54
|
+
const filepath = path_1.default.join(scanDir, filename);
|
|
55
|
+
const stats = await promises_1.default.stat(filepath);
|
|
56
|
+
return {
|
|
57
|
+
filename,
|
|
58
|
+
filepath,
|
|
59
|
+
mtimeMs: stats.mtimeMs,
|
|
60
|
+
};
|
|
61
|
+
}));
|
|
62
|
+
filesWithStats.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
63
|
+
const latest = filesWithStats[0];
|
|
64
|
+
return {
|
|
65
|
+
filepath: latest.filepath,
|
|
66
|
+
filename: latest.filename,
|
|
67
|
+
isLatest: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
package/dist/core/scanner.d.ts
CHANGED
|
@@ -1,21 +1,118 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { ScanResult, Vulnerability } from "./vulnerability-detector";
|
|
3
|
+
export interface ScanEventMap {
|
|
4
|
+
"scan:start": {
|
|
5
|
+
target: string;
|
|
6
|
+
options: ScanOptions;
|
|
7
|
+
};
|
|
8
|
+
"scan:complete": {
|
|
9
|
+
result: ScanResult;
|
|
10
|
+
};
|
|
11
|
+
"scan:error": {
|
|
12
|
+
error: Error;
|
|
13
|
+
};
|
|
14
|
+
"crawl:start": {
|
|
15
|
+
url: string;
|
|
16
|
+
depth: number;
|
|
17
|
+
};
|
|
18
|
+
"crawl:page": {
|
|
19
|
+
url: string;
|
|
20
|
+
crawledCount: number;
|
|
21
|
+
maxPages: number;
|
|
22
|
+
};
|
|
23
|
+
"crawl:complete": {
|
|
24
|
+
url: string;
|
|
25
|
+
};
|
|
26
|
+
"crawl:error": {
|
|
27
|
+
url: string;
|
|
28
|
+
error: Error;
|
|
29
|
+
};
|
|
30
|
+
"form:test": {
|
|
31
|
+
url: string;
|
|
32
|
+
formCount: number;
|
|
33
|
+
};
|
|
34
|
+
"vuln:found": {
|
|
35
|
+
vulnerability: Vulnerability;
|
|
36
|
+
};
|
|
37
|
+
"plugin:execute": {
|
|
38
|
+
plugin: string;
|
|
39
|
+
url: string;
|
|
40
|
+
duration: number;
|
|
41
|
+
};
|
|
42
|
+
"progress": {
|
|
43
|
+
stage: string;
|
|
44
|
+
current: number;
|
|
45
|
+
total: number;
|
|
46
|
+
message?: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
2
49
|
export interface ScanOptions {
|
|
3
50
|
depth?: number;
|
|
4
51
|
timeout?: number;
|
|
5
52
|
headless?: boolean;
|
|
53
|
+
maxPages?: number;
|
|
54
|
+
maxLinksPerPage?: number;
|
|
55
|
+
include?: string[];
|
|
56
|
+
exclude?: string[];
|
|
57
|
+
strictScope?: boolean;
|
|
58
|
+
profile?: string;
|
|
59
|
+
useAiPayloads?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface ScanError {
|
|
62
|
+
url: string;
|
|
63
|
+
error: string;
|
|
64
|
+
plugin?: string;
|
|
6
65
|
}
|
|
7
|
-
export declare class Scanner {
|
|
66
|
+
export declare class Scanner extends EventEmitter {
|
|
8
67
|
private browser;
|
|
9
68
|
private detector;
|
|
10
69
|
private visitedUrls;
|
|
11
70
|
private crawledUrls;
|
|
12
71
|
private testedForms;
|
|
13
72
|
private requestsMade;
|
|
14
|
-
|
|
73
|
+
private headersChecked;
|
|
74
|
+
private rateLimiter;
|
|
75
|
+
private retryConfig;
|
|
76
|
+
private maxConcurrency;
|
|
77
|
+
private strictScope;
|
|
78
|
+
private baseOrigin;
|
|
79
|
+
private maxPages;
|
|
80
|
+
private maxLinksPerPage;
|
|
81
|
+
private includePatterns;
|
|
82
|
+
private excludePatterns;
|
|
83
|
+
private userAgent;
|
|
84
|
+
private scanErrors;
|
|
85
|
+
private pluginErrors;
|
|
86
|
+
private usePlugins;
|
|
87
|
+
private useAiPayloads;
|
|
88
|
+
private payloadGenerator;
|
|
89
|
+
constructor(usePlugins?: boolean);
|
|
90
|
+
private registerDefaultPlugins;
|
|
91
|
+
emit<K extends keyof ScanEventMap>(event: K, data: ScanEventMap[K]): boolean;
|
|
92
|
+
on<K extends keyof ScanEventMap>(event: K, listener: (data: ScanEventMap[K]) => void): this;
|
|
93
|
+
once<K extends keyof ScanEventMap>(event: K, listener: (data: ScanEventMap[K]) => void): this;
|
|
94
|
+
getScanErrors(): ScanError[];
|
|
95
|
+
getPluginErrors(): Map<string, Array<{
|
|
96
|
+
url: string;
|
|
97
|
+
error: string;
|
|
98
|
+
}>>;
|
|
99
|
+
private initializeScanSettings;
|
|
100
|
+
private resetScanState;
|
|
15
101
|
initialize(options?: ScanOptions): Promise<void>;
|
|
16
102
|
scan(targetUrl: string, options?: ScanOptions): Promise<ScanResult>;
|
|
103
|
+
private applyRateLimit;
|
|
104
|
+
private withRetry;
|
|
105
|
+
private createInstrumentedPage;
|
|
106
|
+
private runInIsolatedPage;
|
|
17
107
|
private crawl;
|
|
18
|
-
private
|
|
108
|
+
private runPlugins;
|
|
109
|
+
private processPluginResults;
|
|
110
|
+
private testUrlParametersWithPlugins;
|
|
111
|
+
private testFormsWithPlugins;
|
|
112
|
+
private runLegacyDetection;
|
|
113
|
+
private testFormsLegacy;
|
|
114
|
+
private testUrlParametersLegacy;
|
|
115
|
+
private runWithConcurrency;
|
|
19
116
|
private testXSS;
|
|
20
117
|
private testSQLi;
|
|
21
118
|
private buildTestUrl;
|