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.
Files changed (91) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +419 -236
  3. package/dist/agent/confirmation.d.ts +5 -1
  4. package/dist/agent/confirmation.js +29 -9
  5. package/dist/agent/context.js +2 -3
  6. package/dist/agent/orchestrator.d.ts +2 -0
  7. package/dist/agent/orchestrator.js +50 -8
  8. package/dist/agent/prompts/system.d.ts +1 -1
  9. package/dist/agent/prompts/system.js +5 -7
  10. package/dist/agent/skills/health-check.js +22 -2
  11. package/dist/agent/skills/index.d.ts +1 -0
  12. package/dist/agent/skills/index.js +3 -1
  13. package/dist/agent/skills/verify-finding.d.ts +17 -0
  14. package/dist/agent/skills/verify-finding.js +91 -0
  15. package/dist/agent/skills/web-scan.js +46 -0
  16. package/dist/cli.js +156 -149
  17. package/dist/commands/agent.js +38 -38
  18. package/dist/commands/ai.d.ts +2 -0
  19. package/dist/commands/ai.js +112 -0
  20. package/dist/commands/analyze.js +103 -54
  21. package/dist/commands/config.js +55 -29
  22. package/dist/commands/dev.d.ts +2 -0
  23. package/dist/commands/dev.js +236 -0
  24. package/dist/commands/doctor.js +20 -15
  25. package/dist/commands/gate.d.ts +2 -0
  26. package/dist/commands/gate.js +109 -0
  27. package/dist/commands/onboard.js +188 -141
  28. package/dist/commands/report.js +68 -76
  29. package/dist/commands/scan.js +262 -81
  30. package/dist/commands/scans.d.ts +2 -0
  31. package/dist/commands/scans.js +55 -0
  32. package/dist/core/ai-client.d.ts +6 -1
  33. package/dist/core/ai-client.js +80 -12
  34. package/dist/core/ai-payloads.d.ts +17 -0
  35. package/dist/core/ai-payloads.js +54 -0
  36. package/dist/core/config-schema.d.ts +197 -0
  37. package/dist/core/config-schema.js +68 -0
  38. package/dist/core/config-schema.test.d.ts +1 -0
  39. package/dist/core/config-schema.test.js +151 -0
  40. package/dist/core/config.d.ts +8 -31
  41. package/dist/core/config.js +71 -14
  42. package/dist/core/diff-engine.d.ts +12 -0
  43. package/dist/core/diff-engine.js +47 -0
  44. package/dist/core/errors.d.ts +71 -0
  45. package/dist/core/errors.js +162 -0
  46. package/dist/core/scan-index.d.ts +20 -0
  47. package/dist/core/scan-index.js +52 -0
  48. package/dist/core/scan-storage.d.ts +11 -0
  49. package/dist/core/scan-storage.js +69 -0
  50. package/dist/core/scanner.d.ts +95 -13
  51. package/dist/core/scanner.js +342 -248
  52. package/dist/core/server-probe.d.ts +20 -0
  53. package/dist/core/server-probe.js +109 -0
  54. package/dist/core/vulnerability-detector.d.ts +9 -0
  55. package/dist/core/vulnerability-detector.js +46 -15
  56. package/dist/core/vulnerability-detector.test.d.ts +1 -0
  57. package/dist/core/vulnerability-detector.test.js +210 -0
  58. package/dist/index.js +3 -0
  59. package/dist/plugins/PluginManager.d.ts +27 -0
  60. package/dist/plugins/PluginManager.js +166 -0
  61. package/dist/plugins/index.d.ts +12 -0
  62. package/dist/plugins/index.js +29 -0
  63. package/dist/plugins/types.d.ts +55 -0
  64. package/dist/plugins/types.js +25 -0
  65. package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.d.ts +10 -0
  66. package/dist/plugins/vulnerabilities/CORSAnalyzerPlugin.js +67 -0
  67. package/dist/plugins/vulnerabilities/CSRFPlugin.d.ts +8 -0
  68. package/dist/plugins/vulnerabilities/CSRFPlugin.js +34 -0
  69. package/dist/plugins/vulnerabilities/CookieSecurityPlugin.d.ts +10 -0
  70. package/dist/plugins/vulnerabilities/CookieSecurityPlugin.js +91 -0
  71. package/dist/plugins/vulnerabilities/DebugEndpointPlugin.d.ts +15 -0
  72. package/dist/plugins/vulnerabilities/DebugEndpointPlugin.js +222 -0
  73. package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.d.ts +13 -0
  74. package/dist/plugins/vulnerabilities/DirectoryTraversalPlugin.js +110 -0
  75. package/dist/plugins/vulnerabilities/OpenRedirectPlugin.d.ts +10 -0
  76. package/dist/plugins/vulnerabilities/OpenRedirectPlugin.js +69 -0
  77. package/dist/plugins/vulnerabilities/SQLInjectionPlugin.d.ts +11 -0
  78. package/dist/plugins/vulnerabilities/SQLInjectionPlugin.js +109 -0
  79. package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.d.ts +11 -0
  80. package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.js +63 -0
  81. package/dist/plugins/vulnerabilities/SensitiveDataPlugin.d.ts +9 -0
  82. package/dist/plugins/vulnerabilities/SensitiveDataPlugin.js +32 -0
  83. package/dist/plugins/vulnerabilities/XSSPlugin.d.ts +15 -0
  84. package/dist/plugins/vulnerabilities/XSSPlugin.js +81 -0
  85. package/dist/reports/PdfGenerator.d.ts +36 -0
  86. package/dist/reports/PdfGenerator.js +404 -0
  87. package/dist/utils/logger.d.ts +33 -1
  88. package/dist/utils/logger.js +127 -8
  89. package/dist/utils/theme.d.ts +56 -0
  90. package/dist/utils/theme.js +201 -0
  91. package/package.json +6 -3
@@ -0,0 +1,17 @@
1
+ import { AIClient } from "./ai-client";
2
+ export type PayloadType = "xss" | "sqli" | "cmdi" | "lfi";
3
+ export interface ContextInfo {
4
+ htmlContext?: string;
5
+ tagName?: string;
6
+ attributeName?: string;
7
+ parameterName: string;
8
+ url: string;
9
+ }
10
+ export declare class PayloadGenerator {
11
+ private aiClient;
12
+ constructor(aiClient: AIClient);
13
+ /**
14
+ * Generates a list of contextual payloads based on the provided HTML context.
15
+ */
16
+ generatePayloads(type: PayloadType, context: ContextInfo): Promise<string[]>;
17
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PayloadGenerator = void 0;
4
+ class PayloadGenerator {
5
+ aiClient;
6
+ constructor(aiClient) {
7
+ this.aiClient = aiClient;
8
+ }
9
+ /**
10
+ * Generates a list of contextual payloads based on the provided HTML context.
11
+ */
12
+ async generatePayloads(type, context) {
13
+ const systemPrompt = `You are an expert penetration tester. Your task is to generate 5 highly effective, specialized payloads to test for ${type.toUpperCase()} vulnerabilities.
14
+ Focus on bypass techniques relevant to the provided HTML/parameter context.
15
+ Format your response as a simple JSON array of strings: ["payload1", "payload2", ...]
16
+ Do not include any explanations, just the JSON array.`;
17
+ const prompt = `
18
+ Generate 5 ${type.toUpperCase()} payloads for:
19
+ Parameter: ${context.parameterName}
20
+ URL: ${context.url}
21
+ ${context.htmlContext ? `HTML Context: ${context.htmlContext}` : ""}
22
+ ${context.tagName ? `Inside Tag: ${context.tagName}` : ""}
23
+ ${context.attributeName ? `Inside Attribute: ${context.attributeName}` : ""}
24
+
25
+ Consider:
26
+ 1. Filter bypasses (WAF, sanitizers).
27
+ 2. Escape sequences if inside a string or attribute.
28
+ 3. Breaking out of tags if applicable.
29
+ 4. Non-standard encoding (URL/HTML entities) if likely needed.
30
+ `;
31
+ try {
32
+ const response = await this.aiClient.analyze(prompt, systemPrompt);
33
+ const content = response.content.trim();
34
+ // Try to parse JSON array from the response
35
+ const match = content.match(/\[.*\]/s);
36
+ if (match) {
37
+ const payloads = JSON.parse(match[0]);
38
+ if (Array.isArray(payloads)) {
39
+ return payloads.map(p => String(p));
40
+ }
41
+ }
42
+ // Fallback: split by newlines if JSON parsing fails
43
+ return content.split("\n")
44
+ .map(line => line.replace(/^[\d.-]\s*/, "").trim())
45
+ .filter(line => line.length > 0)
46
+ .slice(0, 5);
47
+ }
48
+ catch (error) {
49
+ console.error(`Error generating AI payloads: ${error.message}`);
50
+ return [];
51
+ }
52
+ }
53
+ }
54
+ exports.PayloadGenerator = PayloadGenerator;
@@ -0,0 +1,197 @@
1
+ import { z } from "zod";
2
+ export declare const AiProviderNameSchema: z.ZodEnum<["openai", "anthropic", "gemini", "openrouter", "mistral", "kimi", "groq"]>;
3
+ export declare const ReportFormatSchema: z.ZodEnum<["word", "txt", "json"]>;
4
+ export declare const SeverityThresholdSchema: z.ZodEnum<["info", "low", "medium", "high", "critical"]>;
5
+ export declare const ScanProfileSchema: z.ZodObject<{
6
+ depth: z.ZodNumber;
7
+ timeout: z.ZodNumber;
8
+ maxPages: z.ZodNumber;
9
+ maxLinksPerPage: z.ZodNumber;
10
+ }, "strip", z.ZodTypeAny, {
11
+ depth: number;
12
+ timeout: number;
13
+ maxPages: number;
14
+ maxLinksPerPage: number;
15
+ }, {
16
+ depth: number;
17
+ timeout: number;
18
+ maxPages: number;
19
+ maxLinksPerPage: number;
20
+ }>;
21
+ export type ScanProfile = z.infer<typeof ScanProfileSchema>;
22
+ export declare const defaultScanProfiles: Record<string, ScanProfile>;
23
+ export declare const ConfigSchema: z.ZodObject<{
24
+ ai: z.ZodObject<{
25
+ provider: z.ZodDefault<z.ZodEnum<["openai", "anthropic", "gemini", "openrouter", "mistral", "kimi", "groq"]>>;
26
+ apiKey: z.ZodDefault<z.ZodString>;
27
+ defaultModel: z.ZodDefault<z.ZodString>;
28
+ enabled: z.ZodDefault<z.ZodBoolean>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ provider: "openai" | "anthropic" | "gemini" | "openrouter" | "mistral" | "kimi" | "groq";
31
+ apiKey: string;
32
+ defaultModel: string;
33
+ enabled: boolean;
34
+ }, {
35
+ provider?: "openai" | "anthropic" | "gemini" | "openrouter" | "mistral" | "kimi" | "groq" | undefined;
36
+ apiKey?: string | undefined;
37
+ defaultModel?: string | undefined;
38
+ enabled?: boolean | undefined;
39
+ }>;
40
+ scan: z.ZodObject<{
41
+ defaultTimeout: z.ZodDefault<z.ZodNumber>;
42
+ maxThreads: z.ZodDefault<z.ZodNumber>;
43
+ userAgent: z.ZodDefault<z.ZodString>;
44
+ followRedirects: z.ZodDefault<z.ZodBoolean>;
45
+ verifySSL: z.ZodDefault<z.ZodBoolean>;
46
+ rateLimitPerSecond: z.ZodDefault<z.ZodNumber>;
47
+ strictScope: z.ZodDefault<z.ZodBoolean>;
48
+ profiles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
49
+ depth: z.ZodNumber;
50
+ timeout: z.ZodNumber;
51
+ maxPages: z.ZodNumber;
52
+ maxLinksPerPage: z.ZodNumber;
53
+ }, "strip", z.ZodTypeAny, {
54
+ depth: number;
55
+ timeout: number;
56
+ maxPages: number;
57
+ maxLinksPerPage: number;
58
+ }, {
59
+ depth: number;
60
+ timeout: number;
61
+ maxPages: number;
62
+ maxLinksPerPage: number;
63
+ }>>>;
64
+ defaultProfile: z.ZodDefault<z.ZodString>;
65
+ }, "strip", z.ZodTypeAny, {
66
+ defaultTimeout: number;
67
+ maxThreads: number;
68
+ userAgent: string;
69
+ followRedirects: boolean;
70
+ verifySSL: boolean;
71
+ rateLimitPerSecond: number;
72
+ strictScope: boolean;
73
+ profiles: Record<string, {
74
+ depth: number;
75
+ timeout: number;
76
+ maxPages: number;
77
+ maxLinksPerPage: number;
78
+ }>;
79
+ defaultProfile: string;
80
+ }, {
81
+ defaultTimeout?: number | undefined;
82
+ maxThreads?: number | undefined;
83
+ userAgent?: string | undefined;
84
+ followRedirects?: boolean | undefined;
85
+ verifySSL?: boolean | undefined;
86
+ rateLimitPerSecond?: number | undefined;
87
+ strictScope?: boolean | undefined;
88
+ profiles?: Record<string, {
89
+ depth: number;
90
+ timeout: number;
91
+ maxPages: number;
92
+ maxLinksPerPage: number;
93
+ }> | undefined;
94
+ defaultProfile?: string | undefined;
95
+ }>;
96
+ report: z.ZodObject<{
97
+ defaultFormat: z.ZodDefault<z.ZodEnum<["word", "txt", "json"]>>;
98
+ companyName: z.ZodDefault<z.ZodString>;
99
+ includeScreenshots: z.ZodDefault<z.ZodBoolean>;
100
+ severityThreshold: z.ZodDefault<z.ZodEnum<["info", "low", "medium", "high", "critical"]>>;
101
+ }, "strip", z.ZodTypeAny, {
102
+ defaultFormat: "word" | "txt" | "json";
103
+ companyName: string;
104
+ includeScreenshots: boolean;
105
+ severityThreshold: "info" | "low" | "medium" | "high" | "critical";
106
+ }, {
107
+ defaultFormat?: "word" | "txt" | "json" | undefined;
108
+ companyName?: string | undefined;
109
+ includeScreenshots?: boolean | undefined;
110
+ severityThreshold?: "info" | "low" | "medium" | "high" | "critical" | undefined;
111
+ }>;
112
+ skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
113
+ enabled: z.ZodDefault<z.ZodBoolean>;
114
+ timeout: z.ZodOptional<z.ZodNumber>;
115
+ }, "strip", z.ZodTypeAny, {
116
+ enabled: boolean;
117
+ timeout?: number | undefined;
118
+ }, {
119
+ timeout?: number | undefined;
120
+ enabled?: boolean | undefined;
121
+ }>>>;
122
+ proxy: z.ZodOptional<z.ZodString>;
123
+ }, "strip", z.ZodTypeAny, {
124
+ ai: {
125
+ provider: "openai" | "anthropic" | "gemini" | "openrouter" | "mistral" | "kimi" | "groq";
126
+ apiKey: string;
127
+ defaultModel: string;
128
+ enabled: boolean;
129
+ };
130
+ scan: {
131
+ defaultTimeout: number;
132
+ maxThreads: number;
133
+ userAgent: string;
134
+ followRedirects: boolean;
135
+ verifySSL: boolean;
136
+ rateLimitPerSecond: number;
137
+ strictScope: boolean;
138
+ profiles: Record<string, {
139
+ depth: number;
140
+ timeout: number;
141
+ maxPages: number;
142
+ maxLinksPerPage: number;
143
+ }>;
144
+ defaultProfile: string;
145
+ };
146
+ report: {
147
+ defaultFormat: "word" | "txt" | "json";
148
+ companyName: string;
149
+ includeScreenshots: boolean;
150
+ severityThreshold: "info" | "low" | "medium" | "high" | "critical";
151
+ };
152
+ skills: Record<string, {
153
+ enabled: boolean;
154
+ timeout?: number | undefined;
155
+ }>;
156
+ proxy?: string | undefined;
157
+ }, {
158
+ ai: {
159
+ provider?: "openai" | "anthropic" | "gemini" | "openrouter" | "mistral" | "kimi" | "groq" | undefined;
160
+ apiKey?: string | undefined;
161
+ defaultModel?: string | undefined;
162
+ enabled?: boolean | undefined;
163
+ };
164
+ scan: {
165
+ defaultTimeout?: number | undefined;
166
+ maxThreads?: number | undefined;
167
+ userAgent?: string | undefined;
168
+ followRedirects?: boolean | undefined;
169
+ verifySSL?: boolean | undefined;
170
+ rateLimitPerSecond?: number | undefined;
171
+ strictScope?: boolean | undefined;
172
+ profiles?: Record<string, {
173
+ depth: number;
174
+ timeout: number;
175
+ maxPages: number;
176
+ maxLinksPerPage: number;
177
+ }> | undefined;
178
+ defaultProfile?: string | undefined;
179
+ };
180
+ report: {
181
+ defaultFormat?: "word" | "txt" | "json" | undefined;
182
+ companyName?: string | undefined;
183
+ includeScreenshots?: boolean | undefined;
184
+ severityThreshold?: "info" | "low" | "medium" | "high" | "critical" | undefined;
185
+ };
186
+ skills?: Record<string, {
187
+ timeout?: number | undefined;
188
+ enabled?: boolean | undefined;
189
+ }> | undefined;
190
+ proxy?: string | undefined;
191
+ }>;
192
+ export type Config = z.infer<typeof ConfigSchema>;
193
+ export type AiProviderName = z.infer<typeof AiProviderNameSchema>;
194
+ export type ReportFormat = z.infer<typeof ReportFormatSchema>;
195
+ export declare function validateConfig(config: unknown): Config;
196
+ export declare function validateScanProfile(profile: unknown): ScanProfile;
197
+ export { defaultScanProfiles as scanProfiles };
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanProfiles = exports.ConfigSchema = exports.defaultScanProfiles = exports.ScanProfileSchema = exports.SeverityThresholdSchema = exports.ReportFormatSchema = exports.AiProviderNameSchema = void 0;
4
+ exports.validateConfig = validateConfig;
5
+ exports.validateScanProfile = validateScanProfile;
6
+ const zod_1 = require("zod");
7
+ // ─── Zod Schemas ───────────────────────────────────────────────────
8
+ exports.AiProviderNameSchema = zod_1.z.enum([
9
+ "openai",
10
+ "anthropic",
11
+ "gemini",
12
+ "openrouter",
13
+ "mistral",
14
+ "kimi",
15
+ "groq",
16
+ ]);
17
+ exports.ReportFormatSchema = zod_1.z.enum(["word", "txt", "json"]);
18
+ exports.SeverityThresholdSchema = zod_1.z.enum(["info", "low", "medium", "high", "critical"]);
19
+ exports.ScanProfileSchema = zod_1.z.object({
20
+ depth: zod_1.z.number().int().min(1).max(5),
21
+ timeout: zod_1.z.number().int().min(1000),
22
+ maxPages: zod_1.z.number().int().min(1),
23
+ maxLinksPerPage: zod_1.z.number().int().min(1),
24
+ });
25
+ // Default scan profiles
26
+ exports.defaultScanProfiles = {
27
+ quick: { depth: 1, timeout: 15000, maxPages: 10, maxLinksPerPage: 20 },
28
+ balanced: { depth: 2, timeout: 30000, maxPages: 30, maxLinksPerPage: 50 },
29
+ deep: { depth: 3, timeout: 60000, maxPages: 100, maxLinksPerPage: 100 },
30
+ };
31
+ exports.scanProfiles = exports.defaultScanProfiles;
32
+ exports.ConfigSchema = zod_1.z.object({
33
+ ai: zod_1.z.object({
34
+ provider: exports.AiProviderNameSchema.default("openai"),
35
+ apiKey: zod_1.z.string().default(""),
36
+ defaultModel: zod_1.z.string().default("gpt-4"),
37
+ enabled: zod_1.z.boolean().default(false),
38
+ }),
39
+ scan: zod_1.z.object({
40
+ defaultTimeout: zod_1.z.number().int().min(1000).default(30000),
41
+ maxThreads: zod_1.z.number().int().min(1).max(20).default(5),
42
+ userAgent: zod_1.z.string().default("KramScan/0.2.0"),
43
+ followRedirects: zod_1.z.boolean().default(true),
44
+ verifySSL: zod_1.z.boolean().default(true),
45
+ rateLimitPerSecond: zod_1.z.number().int().min(1).max(100).default(5),
46
+ strictScope: zod_1.z.boolean().default(true),
47
+ profiles: zod_1.z.record(exports.ScanProfileSchema).default(exports.defaultScanProfiles),
48
+ defaultProfile: zod_1.z.string().default("balanced"),
49
+ }),
50
+ report: zod_1.z.object({
51
+ defaultFormat: exports.ReportFormatSchema.default("word"),
52
+ companyName: zod_1.z.string().default("Your Company"),
53
+ includeScreenshots: zod_1.z.boolean().default(false),
54
+ severityThreshold: exports.SeverityThresholdSchema.default("low"),
55
+ }),
56
+ skills: zod_1.z.record(zod_1.z.object({
57
+ enabled: zod_1.z.boolean().default(true),
58
+ timeout: zod_1.z.number().int().optional(),
59
+ })).default({}),
60
+ proxy: zod_1.z.string().optional(),
61
+ });
62
+ // Validation function
63
+ function validateConfig(config) {
64
+ return exports.ConfigSchema.parse(config);
65
+ }
66
+ function validateScanProfile(profile) {
67
+ return exports.ScanProfileSchema.parse(profile);
68
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_schema_1 = require("./config-schema");
4
+ describe("config-schema", () => {
5
+ // ─── validateConfig ────────────────────────────────────────────
6
+ describe("validateConfig", () => {
7
+ const validConfig = {
8
+ ai: {
9
+ provider: "openai",
10
+ apiKey: "sk-test",
11
+ defaultModel: "gpt-4",
12
+ enabled: true,
13
+ },
14
+ scan: {
15
+ defaultTimeout: 30000,
16
+ maxThreads: 5,
17
+ userAgent: "KramScan/0.1.0",
18
+ followRedirects: true,
19
+ verifySSL: true,
20
+ rateLimitPerSecond: 5,
21
+ strictScope: true,
22
+ profiles: {
23
+ quick: { depth: 1, timeout: 15000, maxPages: 10, maxLinksPerPage: 20 },
24
+ },
25
+ defaultProfile: "balanced",
26
+ },
27
+ report: {
28
+ defaultFormat: "word",
29
+ companyName: "Test Corp",
30
+ includeScreenshots: false,
31
+ severityThreshold: "low",
32
+ },
33
+ skills: {
34
+ sqli: { enabled: true },
35
+ xss: { enabled: true },
36
+ },
37
+ };
38
+ it("should accept a valid config", () => {
39
+ const result = (0, config_schema_1.validateConfig)(validConfig);
40
+ expect(result.ai.provider).toBe("openai");
41
+ expect(result.scan.maxThreads).toBe(5);
42
+ });
43
+ it("should reject invalid AI provider", () => {
44
+ expect(() => (0, config_schema_1.validateConfig)({
45
+ ...validConfig,
46
+ ai: { ...validConfig.ai, provider: "invalid_provider" },
47
+ })).toThrow();
48
+ });
49
+ it("should reject invalid report format", () => {
50
+ expect(() => (0, config_schema_1.validateConfig)({
51
+ ...validConfig,
52
+ report: { ...validConfig.report, defaultFormat: "pdf" },
53
+ })).toThrow();
54
+ });
55
+ it("should reject timeout below minimum", () => {
56
+ expect(() => (0, config_schema_1.validateConfig)({
57
+ ...validConfig,
58
+ scan: { ...validConfig.scan, defaultTimeout: 500 },
59
+ })).toThrow();
60
+ });
61
+ it("should reject maxThreads above maximum", () => {
62
+ expect(() => (0, config_schema_1.validateConfig)({
63
+ ...validConfig,
64
+ scan: { ...validConfig.scan, maxThreads: 25 },
65
+ })).toThrow();
66
+ });
67
+ it("should reject maxThreads below minimum", () => {
68
+ expect(() => (0, config_schema_1.validateConfig)({
69
+ ...validConfig,
70
+ scan: { ...validConfig.scan, maxThreads: 0 },
71
+ })).toThrow();
72
+ });
73
+ it("should accept all valid AI providers", () => {
74
+ const providers = ["openai", "anthropic", "gemini", "openrouter", "mistral", "kimi", "groq"];
75
+ for (const provider of providers) {
76
+ expect(() => (0, config_schema_1.validateConfig)({
77
+ ...validConfig,
78
+ ai: { ...validConfig.ai, provider },
79
+ })).not.toThrow();
80
+ }
81
+ });
82
+ it("should accept all valid severity thresholds", () => {
83
+ const thresholds = ["info", "low", "medium", "high", "critical"];
84
+ for (const threshold of thresholds) {
85
+ expect(() => (0, config_schema_1.validateConfig)({
86
+ ...validConfig,
87
+ report: { ...validConfig.report, severityThreshold: threshold },
88
+ })).not.toThrow();
89
+ }
90
+ });
91
+ });
92
+ // ─── validateScanProfile ───────────────────────────────────────
93
+ describe("validateScanProfile", () => {
94
+ it("should accept a valid scan profile", () => {
95
+ const profile = (0, config_schema_1.validateScanProfile)({
96
+ depth: 2,
97
+ timeout: 30000,
98
+ maxPages: 50,
99
+ maxLinksPerPage: 30,
100
+ });
101
+ expect(profile.depth).toBe(2);
102
+ expect(profile.maxPages).toBe(50);
103
+ });
104
+ it("should reject depth below minimum", () => {
105
+ expect(() => (0, config_schema_1.validateScanProfile)({
106
+ depth: 0,
107
+ timeout: 30000,
108
+ maxPages: 50,
109
+ maxLinksPerPage: 30,
110
+ })).toThrow();
111
+ });
112
+ it("should reject depth above maximum", () => {
113
+ expect(() => (0, config_schema_1.validateScanProfile)({
114
+ depth: 6,
115
+ timeout: 30000,
116
+ maxPages: 50,
117
+ maxLinksPerPage: 30,
118
+ })).toThrow();
119
+ });
120
+ it("should reject timeout below minimum", () => {
121
+ expect(() => (0, config_schema_1.validateScanProfile)({
122
+ depth: 2,
123
+ timeout: 500,
124
+ maxPages: 50,
125
+ maxLinksPerPage: 30,
126
+ })).toThrow();
127
+ });
128
+ it("should reject maxPages below minimum", () => {
129
+ expect(() => (0, config_schema_1.validateScanProfile)({
130
+ depth: 2,
131
+ timeout: 30000,
132
+ maxPages: 0,
133
+ maxLinksPerPage: 30,
134
+ })).toThrow();
135
+ });
136
+ it("should accept boundary values", () => {
137
+ expect(() => (0, config_schema_1.validateScanProfile)({
138
+ depth: 1,
139
+ timeout: 1000,
140
+ maxPages: 1,
141
+ maxLinksPerPage: 1,
142
+ })).not.toThrow();
143
+ expect(() => (0, config_schema_1.validateScanProfile)({
144
+ depth: 5,
145
+ timeout: 120000,
146
+ maxPages: 1000,
147
+ maxLinksPerPage: 500,
148
+ })).not.toThrow();
149
+ });
150
+ });
151
+ });
@@ -1,33 +1,7 @@
1
- export type AiProviderName = "openai" | "anthropic" | "gemini" | "openrouter" | "mistral" | "kimi";
2
- export type ReportFormat = "word" | "txt" | "json";
3
- export interface Config {
4
- ai: {
5
- provider: AiProviderName;
6
- apiKey: string;
7
- defaultModel: string;
8
- enabled: boolean;
9
- };
10
- scan: {
11
- defaultTimeout: number;
12
- maxThreads: number;
13
- userAgent: string;
14
- followRedirects: boolean;
15
- verifySSL: boolean;
16
- rateLimitPerSecond: number;
17
- strictScope: boolean;
18
- };
19
- report: {
20
- defaultFormat: ReportFormat;
21
- companyName: string;
22
- includeScreenshots: boolean;
23
- severityThreshold: "info" | "low" | "medium" | "high" | "critical";
24
- };
25
- skills: Record<string, {
26
- enabled: boolean;
27
- timeout?: number;
28
- }>;
29
- proxy?: string;
30
- }
1
+ import { Config, ScanProfile } from "./config-schema";
2
+ export type { AiProviderName, ReportFormat, Config } from "./config-schema";
3
+ export { defaultScanProfiles as scanProfiles, validateConfig, validateScanProfile, ScanProfile } from "./config-schema";
4
+ export * from "./errors";
31
5
  export declare function isDebugEnabled(): boolean;
32
6
  declare class ConfigStore {
33
7
  private configPath;
@@ -40,10 +14,13 @@ declare class ConfigStore {
40
14
  set(key: string, value: unknown): Promise<void>;
41
15
  private save;
42
16
  setConfig(config: Config): Promise<void>;
17
+ getScanProfile(name: string): ScanProfile | undefined;
18
+ addScanProfile(name: string, profile: ScanProfile): Promise<void>;
43
19
  }
44
20
  export declare function getConfigStore(): ConfigStore;
45
21
  export declare function getConfig(): Promise<Config>;
46
22
  export declare function getConfigValue(key: string): Promise<unknown>;
47
23
  export declare function setConfigValue(key: string, value: unknown): Promise<void>;
48
24
  export declare function setConfig(config: Config): Promise<void>;
49
- export {};
25
+ export declare function getScanProfile(name: string): Promise<ScanProfile | undefined>;
26
+ export declare function addScanProfile(name: string, profile: ScanProfile): Promise<void>;