kramscan 0.2.0 → 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.
Files changed (35) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +81 -54
  3. package/dist/cli.js +6 -0
  4. package/dist/commands/dev.d.ts +2 -0
  5. package/dist/commands/dev.js +236 -0
  6. package/dist/commands/gate.d.ts +2 -0
  7. package/dist/commands/gate.js +109 -0
  8. package/dist/commands/scan.js +1 -0
  9. package/dist/commands/scans.js +4 -0
  10. package/dist/core/config-schema.js +1 -1
  11. package/dist/core/config.js +3 -3
  12. package/dist/core/diff-engine.d.ts +12 -0
  13. package/dist/core/diff-engine.js +47 -0
  14. package/dist/core/scan-index.d.ts +1 -0
  15. package/dist/core/scanner.js +7 -1
  16. package/dist/core/server-probe.d.ts +20 -0
  17. package/dist/core/server-probe.js +109 -0
  18. package/dist/core/vulnerability-detector.d.ts +6 -0
  19. package/dist/core/vulnerability-detector.js +21 -0
  20. package/dist/plugins/index.d.ts +5 -0
  21. package/dist/plugins/index.js +11 -1
  22. package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.d.ts +10 -0
  23. package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.js +67 -0
  24. package/dist/plugins/vulnerabilities/CookieSecurityPlugin.d.ts +10 -0
  25. package/dist/plugins/vulnerabilities/CookieSecurityPlugin.js +91 -0
  26. package/dist/plugins/vulnerabilities/DebugEndpointPlugin.d.ts +15 -0
  27. package/dist/plugins/vulnerabilities/DebugEndpointPlugin.js +222 -0
  28. package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.d.ts +13 -0
  29. package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.js +110 -0
  30. package/dist/plugins/vulnerabilities/OpenRedirectPlugin.d.ts +10 -0
  31. package/dist/plugins/vulnerabilities/OpenRedirectPlugin.js +69 -0
  32. package/dist/reports/PdfGenerator.js +26 -1
  33. package/dist/utils/theme.d.ts +1 -0
  34. package/dist/utils/theme.js +7 -1
  35. package/package.json +6 -3
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Akram Shaikh
3
+ Copyright (c) 2026 Akram Shaikh
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -42,15 +42,17 @@ Web security is complex and often fragmented. Developers rely on multiple disjoi
42
42
  ## ✨ Key Features
43
43
  | Feature | Description |
44
44
  | :--- | :--- |
45
- | 🔍 **Automated Vulnerability Engine** | Detects XSS, SQL Injection, CSRF, insecure headers, and more using Puppeteer-powered crawling. |
46
- | 🔌 **Modular Plugin System** | Extensible architecture for custom vulnerability detection plugins. Built-in plugins for common vulnerabilities. |
45
+ | 🔍 **Automated Vulnerability Engine** | Detects XSS, SQL Injection, CSRF, insecure headers, CORS misconfigs, open redirects, and more. |
46
+ | 🔌 **10 Built-in Security Plugins** | CORS, debug endpoints, directory traversal, cookie auditing, open redirects, sensitive data, and more. |
47
+ | 🛠️ **Dev Mode (Watch Scanner)** | Watch your localhost for file changes and auto re-scan with diff reports (new vs resolved vulns). |
48
+ | 🚧 **CI/CD Security Gate** | `kramscan gate` exits with code 1 if vulnerabilities exceed your threshold. Plug into any pipeline. |
47
49
  | 🤖 **Interactive AI Agent** | A conversational security assistant with **Autonomous Verification** skills to confirm findings live. |
48
50
  | 🧠 **Multi-Provider AI Analysis** | Supports OpenAI, Anthropic, Google Gemini, Mistral, OpenRouter, and more for results auditing. |
49
51
  | 📝 **AI Executive Summaries** | Automatically generates business-oriented summaries for Word, JSON, and TXT reports. |
50
52
  | 📊 **Event-Driven Feedback** | Real-time progress updates with dynamic spinners and live vulnerability alerts during scanning. |
51
53
  | 📄 **Professional Reporting** | Generates detailed PDF, DOCX, TXT, and JSON reports with remediation steps and error tracking. |
52
54
  | 🌐 **Headless Browser Testing** | Renders modern SPAs (Single Page Applications) to find vulnerabilities in dynamic content. |
53
- | ⚡ **Smarter User Flow** | Revamped interactive menu and post-scan "Golden Path" prompts for a guided experience. |
55
+ | ⚡ **Smarter User Flow** | Interactive menu and post-scan "Golden Path" prompts for a guided experience. |
54
56
  | 🛡️ **Error Resilience** | Robust configuration defaults and graceful recovery if individual URLs or plugins fail. |
55
57
 
56
58
  <br />
@@ -82,19 +84,24 @@ graph LR
82
84
 
83
85
  ### Plugin Architecture
84
86
 
85
- KramScan now features a modular plugin system that makes extending vulnerability detection effortless:
87
+ KramScan is built on a modular plugin system that makes extending vulnerability detection effortless:
86
88
 
87
89
  ```
88
90
  src/plugins/
89
- ├── types.ts # Base interfaces and types
90
- ├── PluginManager.ts # Plugin orchestration
91
- ├── index.ts # Plugin exports
92
- └── vulnerabilities/ # Built-in plugins
93
- ├── XSSPlugin.ts
94
- ├── SQLInjectionPlugin.ts
95
- ├── SecurityHeadersPlugin.ts
96
- ├── SensitiveDataPlugin.ts
97
- └── CSRFPlugin.ts
91
+ ├── types.ts # Base interfaces and types
92
+ ├── PluginManager.ts # Plugin orchestration
93
+ ├── index.ts # Plugin exports
94
+ └── vulnerabilities/ # Built-in plugins
95
+ ├── XSSPlugin.ts # Cross-Site Scripting
96
+ ├── SQLInjectionPlugin.ts # SQL Injection
97
+ ├── SecurityHeadersPlugin.ts # Missing security headers
98
+ ├── SensitiveDataPlugin.ts # Exposed secrets & API keys
99
+ ├── CSRFPlugin.ts # Cross-Site Request Forgery
100
+ ├── CORSAnalyzerPlugin.ts # CORS misconfiguration
101
+ ├── DebugEndpointPlugin.ts # Exposed debug/dev endpoints
102
+ ├── DirectoryTraversalPlugin.ts # Path traversal / LFI
103
+ ├── CookieSecurityPlugin.ts # Insecure cookie flags
104
+ └── OpenRedirectPlugin.ts # Open redirect detection
98
105
  ```
99
106
 
100
107
  **Creating a custom plugin:**
@@ -183,10 +190,10 @@ You can provide API keys via environment variables (useful for CI/CD) instead of
183
190
  KramScan automatically detects API keys in your environment variables. During `kramscan onboard`, the tool will identify and pre-configure providers like OpenAI, Anthropic, and Gemini if their keys are found in your session.
184
191
 
185
192
  ### AI-Powered Context-Aware Payloads
186
- The scanning engine now utilizes AI to generate payloads tailored to the specific context of your application, significantly increasing detection rates against filtered inputs and complex WAFs.
193
+ The scanning engine utilizes AI to generate payloads tailored to the specific context of your application, significantly increasing detection rates against filtered inputs and complex WAFs.
187
194
 
188
195
  ### Autonomous Finding Verification
189
- The `kramscan agent` can now independently verify reported vulnerabilities using non-destructive, context-aware payloads to differentiate between theoretical findings and exploitable risks.
196
+ The `kramscan agent` independently verifies reported vulnerabilities using non-destructive, context-aware payloads to differentiate between theoretical findings and exploitable risks.
190
197
 
191
198
  <br />
192
199
 
@@ -225,18 +232,64 @@ kramscan scan https://example.com
225
232
 
226
233
  ## 📖 Usage & Commands
227
234
 
228
- | Command | Description | Status |
229
- | :--- | :--- | :---: |
230
- | `kramscan` | Launch the interactive dashboard menu with smart argument prompting. | ✅ Stable |
231
- | `kramscan scan <url>` | Run a comprehensive vulnerability scan with post-scan prompts. | ✅ Stable |
232
- | `kramscan agent` | Start the AI security assistant with autonomous verification skills. | ✅ Stable |
233
- | `kramscan analyze` | AI-powered analysis with proactive onboarding redirection. | Stable |
234
- | `kramscan report` | Generate professional reports with optional AI executive summaries. | ✅ Stable |
235
- | `kramscan onboard` | Smart setup wizard with environment key detection. | ✅ Stable |
236
- | `kramscan doctor` | Verify environment health and check for Docker dependencies. | ✅ Stable |
237
- | `kramscan config` | View and edit current configuration with robust schema defaults. | ✅ Stable |
238
- | `kramscan scans` | List and inspect recent scans from the persistent index. | ✅ Stable |
239
- | `kramscan ai` | AI helpers (model listing and connectivity test). | Stable |
235
+ | Command | Description |
236
+ | :--- | :--- |
237
+ | `kramscan` | Launch the interactive dashboard menu with smart argument prompting. |
238
+ | `kramscan scan <url>` | Run a comprehensive vulnerability scan with post-scan prompts. |
239
+ | `kramscan dev [url]` | Watch-mode localhost scanner with diff reports and desktop notifications. |
240
+ | `kramscan gate <url>` | CI/CD security quality gate — exits with code 1 on threshold breach. |
241
+ | `kramscan agent` | Start the AI security assistant with autonomous verification skills. |
242
+ | `kramscan analyze` | AI-powered analysis with proactive onboarding redirection. |
243
+ | `kramscan report` | Generate professional reports with optional AI executive summaries. |
244
+ | `kramscan onboard` | Smart setup wizard with environment key detection. |
245
+ | `kramscan doctor` | Verify environment health and check for Docker dependencies. |
246
+ | `kramscan config` | View and edit current configuration with robust schema defaults. |
247
+ | `kramscan scans` | List and inspect recent scans from the persistent index. |
248
+ | `kramscan ai` | AI helpers (model listing and connectivity test). |
249
+
250
+ <br />
251
+
252
+ ### 🛠️ Dev Mode — Localhost Watch Scanner
253
+
254
+ Scan your local dev server continuously. KramScan watches for file changes and **auto re-scans**, showing a diff of new vs. resolved vulnerabilities:
255
+
256
+ ```bash
257
+ # Watch-mode with port shorthand
258
+ kramscan dev --port 3000
259
+
260
+ # Full URL with notifications
261
+ kramscan dev http://localhost:3000 --watch-dir ./src --notify
262
+
263
+ # Single scan (no watching)
264
+ kramscan dev http://localhost:8080 --no-watch --fail-on high
265
+ ```
266
+
267
+ **How it works:**
268
+ 1. Probes your server until it's ready (auto-detects Express, Next.js, Django, etc.)
269
+ 2. Runs an initial security scan
270
+ 3. Watches `--watch-dir` for file changes (debounced)
271
+ 4. Re-scans and shows only **new** and **resolved** vulnerabilities
272
+
273
+ ### 🚧 CI/CD Security Gate
274
+
275
+ Block deployments with vulnerabilities above your threshold:
276
+
277
+ ```bash
278
+ # Fail if any high+ vulnerabilities found
279
+ kramscan gate http://localhost:3000 --fail-on high
280
+
281
+ # JSON output for pipeline processing
282
+ kramscan gate $APP_URL --fail-on medium --json
283
+
284
+ # Allow up to 3 low-severity findings
285
+ kramscan gate http://staging.example.com --fail-on low --max-vulns 3
286
+ ```
287
+
288
+ **Pipeline example (GitHub Actions):**
289
+ ```yaml
290
+ - name: Security Gate
291
+ run: npx kramscan gate http://localhost:3000 --fail-on high
292
+ ```
240
293
 
241
294
  <br />
242
295
 
@@ -272,7 +325,7 @@ kramscan scan https://example.com --no-pdf
272
325
  ```
273
326
 
274
327
  ### Error Tracking and Recovery
275
- KramScan now features comprehensive error handling:
328
+ KramScan features comprehensive error handling:
276
329
 
277
330
  - **Continue on Failure**: Scan continues even if individual URLs fail to load
278
331
  - **Plugin Error Isolation**: If one vulnerability plugin fails, others continue working
@@ -327,33 +380,7 @@ Agent: Scan complete! Found 2 High severity issues.
327
380
 
328
381
  ---
329
382
 
330
- <br />
331
383
 
332
- ## 🗺️ Roadmap
333
-
334
- - [x] Core vulnerability scanner (XSS, SQLi, CSRF, headers)
335
- - [x] Multi-provider AI analysis engine
336
- - [x] Interactive AI agent mode
337
- - [x] Professional report generation (DOCX, TXT, JSON)
338
- - [x] Configuration wizard & management
339
- - [x] **Plugin system for custom scan modules** ✅
340
- - [x] **PDF report generation** ✅
341
- - [x] **Event-driven progress feedback** ✅
342
- - [x] **Error resilience and recovery** ✅
343
- - [x] **Zod schema validation** ✅
344
- - [x] **AI Executive Summaries** ✅
345
- - [x] **Autonomous Verification Agent** ✅
346
- - [x] **Smarter Interactive Flows** ✅
347
- - [ ] CI/CD integration (GitHub Actions, GitLab CI)
348
- - [ ] Web-based dashboard UI
349
- - [ ] SARIF export format
350
- - [ ] OWASP ZAP integration
351
-
352
- <br />
353
-
354
- ---
355
-
356
- <br />
357
384
 
358
385
  ## 🔒 Security & Privacy
359
386
  - **Local Execution:** All scanning logic runs locally on your machine.
package/dist/cli.js CHANGED
@@ -51,6 +51,8 @@ const doctor_1 = require("./commands/doctor");
51
51
  const agent_1 = require("./commands/agent");
52
52
  const scans_1 = require("./commands/scans");
53
53
  const ai_1 = require("./commands/ai");
54
+ const dev_1 = require("./commands/dev");
55
+ const gate_1 = require("./commands/gate");
54
56
  const config_2 = require("./core/config");
55
57
  const theme_1 = require("./utils/theme");
56
58
  let verboseMode = false;
@@ -70,6 +72,8 @@ const menuChoices = [
70
72
  { label: "Agent", value: "agent", description: "AI-powered interactive security assistant", icon: "🤖", status: "active" },
71
73
  { label: "Onboard", value: "onboard", description: "First-time setup wizard", icon: "⚡", status: "active" },
72
74
  { label: "Scan", value: "scan", description: "Scan a target URL for vulnerabilities", icon: "🔍", status: "active" },
75
+ { label: "Dev", value: "dev", description: "Watch-mode scanning for localhost dev servers", icon: "🛠️", status: "active" },
76
+ { label: "Gate", value: "gate", description: "CI/CD security quality gate", icon: "🚧", status: "active" },
73
77
  { label: "Analyze", value: "analyze", description: "Deep AI analysis of scan results", icon: "🧠", status: "active" },
74
78
  { label: "Report", value: "report", description: "Generate a professional report", icon: "📄", status: "active" },
75
79
  { label: "Config", value: "config", description: "View or edit your configuration", icon: "⚙️", status: "active" },
@@ -220,6 +224,8 @@ function createProgram() {
220
224
  (0, agent_1.registerAgentCommand)(program);
221
225
  (0, scans_1.registerScansCommand)(program);
222
226
  (0, ai_1.registerAiCommand)(program);
227
+ (0, dev_1.registerDevCommand)(program);
228
+ (0, gate_1.registerGateCommand)(program);
223
229
  // Version subcommand with detailed environment info
224
230
  program
225
231
  .command("version")
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDevCommand(program: Command): void;
@@ -0,0 +1,236 @@
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.registerDevCommand = registerDevCommand;
7
+ const scanner_1 = require("../core/scanner");
8
+ const server_probe_1 = require("../core/server-probe");
9
+ const diff_engine_1 = require("../core/diff-engine");
10
+ const theme_1 = require("../utils/theme");
11
+ const logger_1 = require("../utils/logger");
12
+ const theme_2 = require("../utils/theme");
13
+ const path_1 = __importDefault(require("path"));
14
+ const promises_1 = __importDefault(require("fs/promises"));
15
+ function registerDevCommand(program) {
16
+ program
17
+ .command("dev [url]")
18
+ .description("Watch-mode security scanning for localhost development servers")
19
+ .option("--port <number>", "Shorthand for http://localhost:<port>")
20
+ .option("--watch-dir <path>", "Directory to watch for file changes", "./src")
21
+ .option("--debounce <ms>", "Debounce time before re-scanning (ms)", "2000")
22
+ .option("--profile <name>", "Scan profile: quick|balanced", "quick")
23
+ .option("--notify", "Enable desktop notifications for critical findings")
24
+ .option("--fail-on <severity>", "Exit with code 1 if severity threshold met")
25
+ .option("--no-watch", "Run a single scan without watching (useful for CI)")
26
+ .action(async (url, options) => {
27
+ // Resolve target URL
28
+ const targetUrl = url || (options.port ? `http://localhost:${options.port}` : null);
29
+ if (!targetUrl) {
30
+ console.log("");
31
+ console.log(theme_1.theme.error("✗ No target URL specified."));
32
+ console.log(theme_1.theme.gray(" Usage: kramscan dev http://localhost:3000"));
33
+ console.log(theme_1.theme.gray(" or: kramscan dev --port 3000"));
34
+ console.log("");
35
+ process.exit(1);
36
+ }
37
+ const isLocal = (0, server_probe_1.isLocalhost)(targetUrl);
38
+ console.log("");
39
+ console.log(theme_1.theme.brand.bold("🛠️ KramScan Dev Mode"));
40
+ console.log(theme_1.theme.gray("─".repeat(50)));
41
+ console.log("");
42
+ console.log(theme_1.theme.white("Target:"), theme_1.theme.cyan(targetUrl));
43
+ console.log(theme_1.theme.white("Profile:"), theme_1.theme.cyan(options.profile));
44
+ if (isLocal) {
45
+ console.log(theme_1.theme.white("Environment:"), theme_1.theme.green("localhost (dev mode)"));
46
+ }
47
+ console.log("");
48
+ // Probe server readiness
49
+ const probeSpinner = logger_1.logger.spinner(`Waiting for ${targetUrl} to be ready...`);
50
+ const probeResult = await (0, server_probe_1.probeServer)(targetUrl, { timeout: 30000 });
51
+ if (!probeResult.reachable) {
52
+ probeSpinner.fail(`Server at ${targetUrl} is not responding`);
53
+ console.log("");
54
+ console.log(theme_1.theme.warning("⚠️ Make sure your dev server is running:"));
55
+ console.log(theme_1.theme.gray(" • npm run dev"));
56
+ console.log(theme_1.theme.gray(" • yarn dev"));
57
+ console.log(theme_1.theme.gray(" • python manage.py runserver"));
58
+ console.log("");
59
+ process.exit(1);
60
+ }
61
+ probeSpinner.succeed(`Server ready! (${probeResult.responseTime}ms` +
62
+ `${probeResult.framework ? `, ${probeResult.framework}` : ""}` +
63
+ `${probeResult.server ? `, ${probeResult.server}` : ""})`);
64
+ // Run initial scan
65
+ let previousResult = null;
66
+ const runScan = async (isRescan = false) => {
67
+ const scanSpinner = logger_1.logger.spinner(isRescan ? "Re-scanning after code change..." : "Running initial security scan...");
68
+ try {
69
+ const scanner = new scanner_1.Scanner(true);
70
+ const scanOptions = {
71
+ depth: 2,
72
+ timeout: 10000,
73
+ headless: true,
74
+ maxPages: 15,
75
+ maxLinksPerPage: 30,
76
+ profile: options.profile,
77
+ };
78
+ const result = await scanner.scan(targetUrl, scanOptions);
79
+ scanSpinner.succeed(isRescan
80
+ ? `Re-scan complete: ${result.summary.total} vulnerabilities`
81
+ : `Initial scan complete: ${result.summary.total} vulnerabilities`);
82
+ await scanner.close();
83
+ if (isRescan && previousResult) {
84
+ // Show diff
85
+ const diff = (0, diff_engine_1.diffScanResults)(previousResult, result);
86
+ displayDiff(diff);
87
+ }
88
+ else {
89
+ // Show full summary for initial scan
90
+ (0, theme_1.displayScanSummary)({
91
+ target: result.target,
92
+ duration: result.duration,
93
+ metadata: result.metadata,
94
+ summary: result.summary,
95
+ vulnerabilities: result.vulnerabilities,
96
+ score: result.score,
97
+ filepath: "(dev mode — results in memory)",
98
+ });
99
+ }
100
+ // Desktop notification for critical/high findings
101
+ if (options.notify && result.summary.critical + result.summary.high > 0) {
102
+ try {
103
+ // node-notifier is optional — skip if not installed
104
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
105
+ const notifier = require("node-notifier");
106
+ notifier.notify({
107
+ title: "⚠️ KramScan Security Alert",
108
+ message: `Found ${result.summary.critical} critical, ${result.summary.high} high vulnerabilities on ${targetUrl}`,
109
+ sound: true,
110
+ });
111
+ }
112
+ catch {
113
+ // node-notifier not installed, skip silently
114
+ }
115
+ }
116
+ // Check fail threshold
117
+ if (options.failOn) {
118
+ const shouldFail = checkThreshold(result, options.failOn);
119
+ if (shouldFail && !options.watch) {
120
+ console.log(theme_1.theme.error(`\n✗ Security gate failed: found vulnerabilities at or above '${options.failOn}' severity.\n`));
121
+ process.exit(1);
122
+ }
123
+ }
124
+ return result;
125
+ }
126
+ catch (err) {
127
+ scanSpinner.fail(`Scan failed: ${err.message}`);
128
+ return previousResult;
129
+ }
130
+ };
131
+ // Initial scan
132
+ previousResult = await runScan(false);
133
+ // Watch mode
134
+ if (options.watch !== false) {
135
+ const watchDir = path_1.default.resolve(options.watchDir);
136
+ try {
137
+ await promises_1.default.access(watchDir);
138
+ }
139
+ catch {
140
+ console.log(theme_1.theme.warning(`⚠️ Watch directory not found: ${watchDir}`));
141
+ console.log(theme_1.theme.gray(" Falling back to current directory."));
142
+ }
143
+ console.log("");
144
+ console.log(theme_1.theme.brand("👁️ Watching for changes..."));
145
+ console.log(theme_1.theme.gray(` Directory: ${watchDir}`));
146
+ console.log(theme_1.theme.gray(` Debounce: ${options.debounce}ms`));
147
+ console.log(theme_1.theme.gray(" Press Ctrl+C to stop."));
148
+ console.log("");
149
+ // Use fs.watch with recursive option (Node.js 19+)
150
+ let debounceTimer = null;
151
+ let scanning = false;
152
+ try {
153
+ const watcher = promises_1.default.watch(watchDir, { recursive: true });
154
+ for await (const event of watcher) {
155
+ if (scanning)
156
+ continue;
157
+ // Ignore node_modules, dist, .git, etc.
158
+ const filename = event.filename || "";
159
+ if (filename.includes("node_modules") ||
160
+ filename.includes("dist") ||
161
+ filename.includes(".git") ||
162
+ filename.includes(".next") ||
163
+ filename.includes("__pycache__")) {
164
+ continue;
165
+ }
166
+ if (debounceTimer)
167
+ clearTimeout(debounceTimer);
168
+ debounceTimer = setTimeout(async () => {
169
+ scanning = true;
170
+ console.log("");
171
+ console.log(theme_1.theme.dim(`📝 Change detected: ${filename}`));
172
+ previousResult = await runScan(true);
173
+ scanning = false;
174
+ }, parseInt(options.debounce, 10));
175
+ }
176
+ }
177
+ catch (err) {
178
+ console.log(theme_1.theme.warning(`⚠️ File watching failed: ${err.message}`));
179
+ console.log(theme_1.theme.gray(" Make sure the watch directory exists and is readable."));
180
+ }
181
+ }
182
+ });
183
+ }
184
+ function displayDiff(diff) {
185
+ console.log("");
186
+ console.log(theme_1.theme.brightWhite.bold("🔄 Scan Diff Report"));
187
+ console.log(theme_1.theme.gray("─".repeat(50)));
188
+ if (diff.newVulnerabilities.length === 0 && diff.resolvedVulnerabilities.length === 0) {
189
+ console.log(theme_1.theme.gray(" No changes since last scan."));
190
+ console.log(theme_1.theme.gray(` ${diff.unchangedCount} vulnerabilities unchanged.`));
191
+ }
192
+ else {
193
+ // New vulnerabilities
194
+ if (diff.newVulnerabilities.length > 0) {
195
+ console.log("");
196
+ console.log(theme_1.theme.error(` 🆕 ${diff.newVulnerabilities.length} New Vulnerabilities`));
197
+ for (const v of diff.newVulnerabilities) {
198
+ const color = (0, theme_2.getSeverityColor)(v.severity);
199
+ console.log(color(` [${v.severity.toUpperCase()}] ${v.title}`));
200
+ console.log(theme_1.theme.gray(` ${v.url}`));
201
+ }
202
+ }
203
+ // Resolved vulnerabilities
204
+ if (diff.resolvedVulnerabilities.length > 0) {
205
+ console.log("");
206
+ console.log(theme_1.theme.success(` ✅ ${diff.resolvedVulnerabilities.length} Resolved`));
207
+ for (const v of diff.resolvedVulnerabilities) {
208
+ console.log(theme_1.theme.green(` ✓ ${v.title}`));
209
+ }
210
+ }
211
+ console.log("");
212
+ console.log(theme_1.theme.gray(` Total: ${diff.previousTotal} → ${diff.currentTotal} `) +
213
+ (diff.currentTotal < diff.previousTotal
214
+ ? theme_1.theme.green(`(↓ ${diff.previousTotal - diff.currentTotal})`)
215
+ : diff.currentTotal > diff.previousTotal
216
+ ? theme_1.theme.error(`(↑ ${diff.currentTotal - diff.previousTotal})`)
217
+ : theme_1.theme.gray("(no change)")));
218
+ }
219
+ console.log("");
220
+ }
221
+ function checkThreshold(result, failOn) {
222
+ const severityLevels = {
223
+ critical: 4,
224
+ high: 3,
225
+ medium: 2,
226
+ low: 1,
227
+ info: 0,
228
+ };
229
+ const threshold = severityLevels[failOn.toLowerCase()] ?? 3;
230
+ for (const v of result.vulnerabilities) {
231
+ if ((severityLevels[v.severity] ?? 0) >= threshold) {
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerGateCommand(program: Command): void;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerGateCommand = registerGateCommand;
4
+ const scanner_1 = require("../core/scanner");
5
+ const server_probe_1 = require("../core/server-probe");
6
+ const theme_1 = require("../utils/theme");
7
+ const logger_1 = require("../utils/logger");
8
+ function registerGateCommand(program) {
9
+ program
10
+ .command("gate <url>")
11
+ .description("CI/CD security quality gate — scan and exit with code 1 if thresholds are breached")
12
+ .option("--fail-on <severity>", "Minimum severity to fail on (critical|high|medium|low)", "high")
13
+ .option("--max-vulns <number>", "Maximum allowed vulnerabilities before failing", "0")
14
+ .option("--profile <name>", "Scan profile: quick|balanced|deep", "quick")
15
+ .option("--timeout <ms>", "Maximum scan duration", "60000")
16
+ .option("--json", "Output results as JSON")
17
+ .action(async (url, options) => {
18
+ const jsonMode = options.json === true;
19
+ if (!jsonMode) {
20
+ console.log("");
21
+ console.log(theme_1.theme.brand.bold("🚧 KramScan Security Gate"));
22
+ console.log(theme_1.theme.gray("─".repeat(50)));
23
+ console.log("");
24
+ }
25
+ // Probe server
26
+ if (!jsonMode) {
27
+ const probeSpinner = logger_1.logger.spinner(`Checking server at ${url}...`);
28
+ const probeResult = await (0, server_probe_1.probeServer)(url, { timeout: 10000, maxAttempts: 5 });
29
+ if (!probeResult.reachable) {
30
+ probeSpinner.fail(`Server at ${url} is not responding`);
31
+ if (jsonMode) {
32
+ console.log(JSON.stringify({ error: "Server unreachable", passed: false }));
33
+ }
34
+ process.exit(1);
35
+ }
36
+ probeSpinner.succeed(`Server ready (${probeResult.responseTime}ms)`);
37
+ }
38
+ // Run scan
39
+ const scanSpinner = jsonMode ? null : logger_1.logger.spinner("Running security scan...");
40
+ try {
41
+ const scanner = new scanner_1.Scanner(true);
42
+ const scanOptions = {
43
+ depth: 2,
44
+ timeout: parseInt(options.timeout, 10) || 60000,
45
+ headless: true,
46
+ maxPages: 20,
47
+ maxLinksPerPage: 30,
48
+ profile: options.profile,
49
+ };
50
+ const result = await scanner.scan(url, scanOptions);
51
+ await scanner.close();
52
+ if (scanSpinner)
53
+ scanSpinner.succeed(`Scan complete: ${result.summary.total} vulnerabilities`);
54
+ // Evaluate threshold
55
+ const severityLevels = {
56
+ critical: 4, high: 3, medium: 2, low: 1, info: 0,
57
+ };
58
+ const threshold = severityLevels[options.failOn.toLowerCase()] ?? 3;
59
+ const maxVulns = parseInt(options.maxVulns, 10) || 0;
60
+ const vulnsAboveThreshold = result.vulnerabilities.filter((v) => (severityLevels[v.severity] ?? 0) >= threshold);
61
+ const passed = vulnsAboveThreshold.length <= maxVulns;
62
+ if (jsonMode) {
63
+ console.log(JSON.stringify({
64
+ passed,
65
+ total: result.summary.total,
66
+ threshold: options.failOn,
67
+ vulnsAboveThreshold: vulnsAboveThreshold.length,
68
+ maxAllowed: maxVulns,
69
+ summary: result.summary,
70
+ vulnerabilities: vulnsAboveThreshold,
71
+ }, null, 2));
72
+ }
73
+ else {
74
+ console.log("");
75
+ if (passed) {
76
+ console.log(theme_1.theme.success.bold("✅ SECURITY GATE: PASSED"));
77
+ console.log(theme_1.theme.gray(` ${result.summary.total} total vulnerabilities found`));
78
+ console.log(theme_1.theme.gray(` ${vulnsAboveThreshold.length} at or above '${options.failOn}' severity (max allowed: ${maxVulns})`));
79
+ }
80
+ else {
81
+ console.log(theme_1.theme.error.bold("❌ SECURITY GATE: FAILED"));
82
+ console.log(theme_1.theme.error(` ${vulnsAboveThreshold.length} vulnerabilities at or above '${options.failOn}' severity (max allowed: ${maxVulns})`));
83
+ console.log("");
84
+ for (const v of vulnsAboveThreshold.slice(0, 10)) {
85
+ const color = v.severity === "critical" ? theme_1.theme.critical : theme_1.theme.high;
86
+ console.log(color(` [${v.severity.toUpperCase()}] ${v.title}`));
87
+ console.log(theme_1.theme.gray(` ${v.url}`));
88
+ if (v.remediation) {
89
+ console.log(theme_1.theme.dim(` Fix: ${v.remediation}`));
90
+ }
91
+ }
92
+ if (vulnsAboveThreshold.length > 10) {
93
+ console.log(theme_1.theme.gray(` ... and ${vulnsAboveThreshold.length - 10} more`));
94
+ }
95
+ }
96
+ console.log("");
97
+ }
98
+ process.exit(passed ? 0 : 1);
99
+ }
100
+ catch (err) {
101
+ if (scanSpinner)
102
+ scanSpinner.fail(`Scan failed: ${err.message}`);
103
+ if (jsonMode) {
104
+ console.log(JSON.stringify({ error: err.message, passed: false }));
105
+ }
106
+ process.exit(1);
107
+ }
108
+ });
109
+ }
@@ -226,6 +226,7 @@ function registerScanCommand(program) {
226
226
  metadata: result.metadata,
227
227
  summary: result.summary,
228
228
  vulnerabilities: result.vulnerabilities,
229
+ score: result.score,
229
230
  filepath,
230
231
  pdfPath,
231
232
  });
@@ -30,6 +30,10 @@ function registerScansCommand(program) {
30
30
  console.log(chalk_1.default.gray(" PDF :"), chalk_1.default.white(entry.pdfPath));
31
31
  }
32
32
  console.log(chalk_1.default.gray(" Findings:"), chalk_1.default.white(`${entry.summary.total} total (${entry.summary.critical}C ${entry.summary.high}H ${entry.summary.medium}M ${entry.summary.low}L ${entry.summary.info}I)`));
33
+ if (entry.score !== undefined) {
34
+ const scoreColor = entry.score > 80 ? chalk_1.default.green : (entry.score > 50 ? chalk_1.default.yellow : chalk_1.default.red);
35
+ console.log(chalk_1.default.gray(" Score :"), scoreColor(`${entry.score}/100`));
36
+ }
33
37
  console.log("");
34
38
  }
35
39
  });
@@ -39,7 +39,7 @@ exports.ConfigSchema = zod_1.z.object({
39
39
  scan: zod_1.z.object({
40
40
  defaultTimeout: zod_1.z.number().int().min(1000).default(30000),
41
41
  maxThreads: zod_1.z.number().int().min(1).max(20).default(5),
42
- userAgent: zod_1.z.string().default("KramScan/0.1.1"),
42
+ userAgent: zod_1.z.string().default("KramScan/0.2.0"),
43
43
  followRedirects: zod_1.z.boolean().default(true),
44
44
  verifySSL: zod_1.z.boolean().default(true),
45
45
  rateLimitPerSecond: zod_1.z.number().int().min(1).max(100).default(5),
@@ -64,7 +64,7 @@ const defaults = {
64
64
  enabled: false,
65
65
  },
66
66
  scan: {
67
- defaultTimeout: 60,
67
+ defaultTimeout: 30000,
68
68
  maxThreads: 5,
69
69
  userAgent: `KramScan/${theme_1.CLI_VERSION}`,
70
70
  followRedirects: true,
@@ -81,8 +81,8 @@ const defaults = {
81
81
  severityThreshold: "low",
82
82
  },
83
83
  skills: {
84
- sqli: { enabled: true, timeout: 120 },
85
- xss: { enabled: true, timeout: 90 },
84
+ sqli: { enabled: true, timeout: 120000 },
85
+ xss: { enabled: true, timeout: 90000 },
86
86
  headers: { enabled: true },
87
87
  csrf: { enabled: true },
88
88
  idor: { enabled: true },
@@ -0,0 +1,12 @@
1
+ import { Vulnerability, ScanResult } from "./vulnerability-detector";
2
+ export interface ScanDiff {
3
+ newVulnerabilities: Vulnerability[];
4
+ resolvedVulnerabilities: Vulnerability[];
5
+ unchangedCount: number;
6
+ previousTotal: number;
7
+ currentTotal: number;
8
+ }
9
+ /**
10
+ * Compares two scan results and produces a diff of new and resolved vulnerabilities.
11
+ */
12
+ export declare function diffScanResults(previous: ScanResult, current: ScanResult): ScanDiff;