@nahisaho/musubix-security 1.8.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 (116) hide show
  1. package/README.md +105 -0
  2. package/bin/musubix-security-mcp.js +12 -0
  3. package/bin/musubix-security.js +12 -0
  4. package/dist/analysis/dependency-auditor.d.ts +30 -0
  5. package/dist/analysis/dependency-auditor.d.ts.map +1 -0
  6. package/dist/analysis/dependency-auditor.js +325 -0
  7. package/dist/analysis/dependency-auditor.js.map +1 -0
  8. package/dist/analysis/index.d.ts +9 -0
  9. package/dist/analysis/index.d.ts.map +1 -0
  10. package/dist/analysis/index.js +9 -0
  11. package/dist/analysis/index.js.map +1 -0
  12. package/dist/analysis/secret-detector.d.ts +44 -0
  13. package/dist/analysis/secret-detector.d.ts.map +1 -0
  14. package/dist/analysis/secret-detector.js +465 -0
  15. package/dist/analysis/secret-detector.js.map +1 -0
  16. package/dist/analysis/taint-analyzer.d.ts +62 -0
  17. package/dist/analysis/taint-analyzer.d.ts.map +1 -0
  18. package/dist/analysis/taint-analyzer.js +519 -0
  19. package/dist/analysis/taint-analyzer.js.map +1 -0
  20. package/dist/analysis/vulnerability-scanner.d.ts +58 -0
  21. package/dist/analysis/vulnerability-scanner.d.ts.map +1 -0
  22. package/dist/analysis/vulnerability-scanner.js +417 -0
  23. package/dist/analysis/vulnerability-scanner.js.map +1 -0
  24. package/dist/cli/commands.d.ts +15 -0
  25. package/dist/cli/commands.d.ts.map +1 -0
  26. package/dist/cli/commands.js +405 -0
  27. package/dist/cli/commands.js.map +1 -0
  28. package/dist/cli/index.d.ts +6 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +6 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/index.d.ts +42 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +66 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/infrastructure/ast-parser.d.ts +87 -0
  37. package/dist/infrastructure/ast-parser.d.ts.map +1 -0
  38. package/dist/infrastructure/ast-parser.js +273 -0
  39. package/dist/infrastructure/ast-parser.js.map +1 -0
  40. package/dist/infrastructure/cache.d.ts +100 -0
  41. package/dist/infrastructure/cache.d.ts.map +1 -0
  42. package/dist/infrastructure/cache.js +288 -0
  43. package/dist/infrastructure/cache.js.map +1 -0
  44. package/dist/infrastructure/config-loader.d.ts +35 -0
  45. package/dist/infrastructure/config-loader.d.ts.map +1 -0
  46. package/dist/infrastructure/config-loader.js +358 -0
  47. package/dist/infrastructure/config-loader.js.map +1 -0
  48. package/dist/infrastructure/file-scanner.d.ts +94 -0
  49. package/dist/infrastructure/file-scanner.d.ts.map +1 -0
  50. package/dist/infrastructure/file-scanner.js +189 -0
  51. package/dist/infrastructure/file-scanner.js.map +1 -0
  52. package/dist/infrastructure/index.d.ts +9 -0
  53. package/dist/infrastructure/index.d.ts.map +1 -0
  54. package/dist/infrastructure/index.js +9 -0
  55. package/dist/infrastructure/index.js.map +1 -0
  56. package/dist/mcp/index.d.ts +7 -0
  57. package/dist/mcp/index.d.ts.map +1 -0
  58. package/dist/mcp/index.js +7 -0
  59. package/dist/mcp/index.js.map +1 -0
  60. package/dist/mcp/server.d.ts +34 -0
  61. package/dist/mcp/server.d.ts.map +1 -0
  62. package/dist/mcp/server.js +88 -0
  63. package/dist/mcp/server.js.map +1 -0
  64. package/dist/mcp/tools.d.ts +88 -0
  65. package/dist/mcp/tools.d.ts.map +1 -0
  66. package/dist/mcp/tools.js +443 -0
  67. package/dist/mcp/tools.js.map +1 -0
  68. package/dist/services/fix-generator.d.ts +56 -0
  69. package/dist/services/fix-generator.d.ts.map +1 -0
  70. package/dist/services/fix-generator.js +346 -0
  71. package/dist/services/fix-generator.js.map +1 -0
  72. package/dist/services/fix-verifier.d.ts +62 -0
  73. package/dist/services/fix-verifier.d.ts.map +1 -0
  74. package/dist/services/fix-verifier.js +224 -0
  75. package/dist/services/fix-verifier.js.map +1 -0
  76. package/dist/services/index.d.ts +9 -0
  77. package/dist/services/index.d.ts.map +1 -0
  78. package/dist/services/index.js +13 -0
  79. package/dist/services/index.js.map +1 -0
  80. package/dist/services/report-generator.d.ts +87 -0
  81. package/dist/services/report-generator.d.ts.map +1 -0
  82. package/dist/services/report-generator.js +463 -0
  83. package/dist/services/report-generator.js.map +1 -0
  84. package/dist/services/security-service.d.ts +151 -0
  85. package/dist/services/security-service.d.ts.map +1 -0
  86. package/dist/services/security-service.js +279 -0
  87. package/dist/services/security-service.js.map +1 -0
  88. package/dist/types/config.d.ts +188 -0
  89. package/dist/types/config.d.ts.map +1 -0
  90. package/dist/types/config.js +89 -0
  91. package/dist/types/config.js.map +1 -0
  92. package/dist/types/dependency.d.ts +266 -0
  93. package/dist/types/dependency.d.ts.map +1 -0
  94. package/dist/types/dependency.js +7 -0
  95. package/dist/types/dependency.js.map +1 -0
  96. package/dist/types/fix.d.ts +213 -0
  97. package/dist/types/fix.d.ts.map +1 -0
  98. package/dist/types/fix.js +7 -0
  99. package/dist/types/fix.js.map +1 -0
  100. package/dist/types/index.d.ts +14 -0
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/index.js +8 -0
  103. package/dist/types/index.js.map +1 -0
  104. package/dist/types/secret.d.ts +151 -0
  105. package/dist/types/secret.d.ts.map +1 -0
  106. package/dist/types/secret.js +91 -0
  107. package/dist/types/secret.js.map +1 -0
  108. package/dist/types/taint.d.ts +182 -0
  109. package/dist/types/taint.d.ts.map +1 -0
  110. package/dist/types/taint.js +24 -0
  111. package/dist/types/taint.js.map +1 -0
  112. package/dist/types/vulnerability.d.ts +136 -0
  113. package/dist/types/vulnerability.d.ts.map +1 -0
  114. package/dist/types/vulnerability.js +7 -0
  115. package/dist/types/vulnerability.js.map +1 -0
  116. package/package.json +87 -0
@@ -0,0 +1,288 @@
1
+ /**
2
+ * @fileoverview Caching infrastructure
3
+ * @module @nahisaho/musubix-security/infrastructure/cache
4
+ * @trace REQ-SEC-CACHE-001
5
+ */
6
+ import { writeFile, readFile, mkdir, unlink, readdir, stat } from 'node:fs/promises';
7
+ import { join, dirname } from 'node:path';
8
+ import { createHash } from 'node:crypto';
9
+ /**
10
+ * Memory cache implementation
11
+ */
12
+ export class MemoryCache {
13
+ cache = new Map();
14
+ stats = { hits: 0, misses: 0 };
15
+ maxSizeBytes;
16
+ defaultTtlSeconds;
17
+ currentSize = 0;
18
+ constructor(options = { strategy: 'memory' }) {
19
+ this.maxSizeBytes = options.maxSizeBytes ?? 100 * 1024 * 1024; // 100MB
20
+ this.defaultTtlSeconds = options.ttlSeconds ?? 3600; // 1 hour
21
+ }
22
+ async get(key) {
23
+ const entry = this.cache.get(key);
24
+ if (!entry) {
25
+ this.stats.misses++;
26
+ return undefined;
27
+ }
28
+ // Check expiration
29
+ if (Date.now() > entry.expiresAt) {
30
+ this.cache.delete(key);
31
+ this.currentSize -= entry.size;
32
+ this.stats.misses++;
33
+ return undefined;
34
+ }
35
+ this.stats.hits++;
36
+ return entry.value;
37
+ }
38
+ async set(key, value, ttlSeconds) {
39
+ const serialized = JSON.stringify(value);
40
+ const size = Buffer.byteLength(serialized);
41
+ const ttl = ttlSeconds ?? this.defaultTtlSeconds;
42
+ // Remove existing entry if present
43
+ const existing = this.cache.get(key);
44
+ if (existing) {
45
+ this.currentSize -= existing.size;
46
+ }
47
+ // Evict entries if needed
48
+ while (this.currentSize + size > this.maxSizeBytes && this.cache.size > 0) {
49
+ this.evictOldest();
50
+ }
51
+ const entry = {
52
+ key,
53
+ value,
54
+ createdAt: Date.now(),
55
+ expiresAt: Date.now() + ttl * 1000,
56
+ size,
57
+ };
58
+ this.cache.set(key, entry);
59
+ this.currentSize += size;
60
+ }
61
+ async has(key) {
62
+ const entry = this.cache.get(key);
63
+ if (!entry)
64
+ return false;
65
+ if (Date.now() > entry.expiresAt) {
66
+ this.cache.delete(key);
67
+ this.currentSize -= entry.size;
68
+ return false;
69
+ }
70
+ return true;
71
+ }
72
+ async delete(key) {
73
+ const entry = this.cache.get(key);
74
+ if (entry) {
75
+ this.currentSize -= entry.size;
76
+ this.cache.delete(key);
77
+ return true;
78
+ }
79
+ return false;
80
+ }
81
+ async clear() {
82
+ this.cache.clear();
83
+ this.currentSize = 0;
84
+ this.stats = { hits: 0, misses: 0 };
85
+ }
86
+ getStats() {
87
+ const total = this.stats.hits + this.stats.misses;
88
+ return {
89
+ hits: this.stats.hits,
90
+ misses: this.stats.misses,
91
+ size: this.currentSize,
92
+ entryCount: this.cache.size,
93
+ hitRate: total > 0 ? this.stats.hits / total : 0,
94
+ };
95
+ }
96
+ evictOldest() {
97
+ let oldest;
98
+ let oldestTime = Infinity;
99
+ for (const [key, entry] of this.cache) {
100
+ if (entry.createdAt < oldestTime) {
101
+ oldestTime = entry.createdAt;
102
+ oldest = key;
103
+ }
104
+ }
105
+ if (oldest) {
106
+ const entry = this.cache.get(oldest);
107
+ this.currentSize -= entry.size;
108
+ this.cache.delete(oldest);
109
+ }
110
+ }
111
+ }
112
+ /**
113
+ * File-based cache implementation
114
+ */
115
+ export class FileCache {
116
+ stats = { hits: 0, misses: 0 };
117
+ cacheDir;
118
+ defaultTtlSeconds;
119
+ initialized = false;
120
+ constructor(options) {
121
+ if (!options.cacheDir) {
122
+ throw new Error('cacheDir is required for file cache');
123
+ }
124
+ this.cacheDir = options.cacheDir;
125
+ this.defaultTtlSeconds = options.ttlSeconds ?? 3600;
126
+ // maxSizeBytes is stored for future use when implementing cache eviction
127
+ }
128
+ async ensureDir() {
129
+ if (this.initialized)
130
+ return;
131
+ await mkdir(this.cacheDir, { recursive: true });
132
+ this.initialized = true;
133
+ }
134
+ getCachePath(key) {
135
+ const hash = createHash('sha256').update(key).digest('hex');
136
+ return join(this.cacheDir, `${hash}.json`);
137
+ }
138
+ async get(key) {
139
+ await this.ensureDir();
140
+ const path = this.getCachePath(key);
141
+ try {
142
+ const content = await readFile(path, 'utf-8');
143
+ const entry = JSON.parse(content);
144
+ if (Date.now() > entry.expiresAt) {
145
+ await unlink(path).catch(() => { });
146
+ this.stats.misses++;
147
+ return undefined;
148
+ }
149
+ this.stats.hits++;
150
+ return entry.value;
151
+ }
152
+ catch {
153
+ this.stats.misses++;
154
+ return undefined;
155
+ }
156
+ }
157
+ async set(key, value, ttlSeconds) {
158
+ await this.ensureDir();
159
+ const path = this.getCachePath(key);
160
+ const ttl = ttlSeconds ?? this.defaultTtlSeconds;
161
+ const entry = {
162
+ key,
163
+ value,
164
+ createdAt: Date.now(),
165
+ expiresAt: Date.now() + ttl * 1000,
166
+ size: 0,
167
+ };
168
+ const content = JSON.stringify(entry, null, 2);
169
+ entry.size = Buffer.byteLength(content);
170
+ await mkdir(dirname(path), { recursive: true });
171
+ await writeFile(path, content, 'utf-8');
172
+ }
173
+ async has(key) {
174
+ const value = await this.get(key);
175
+ return value !== undefined;
176
+ }
177
+ async delete(key) {
178
+ const path = this.getCachePath(key);
179
+ try {
180
+ await unlink(path);
181
+ return true;
182
+ }
183
+ catch {
184
+ return false;
185
+ }
186
+ }
187
+ async clear() {
188
+ await this.ensureDir();
189
+ try {
190
+ const files = await readdir(this.cacheDir);
191
+ await Promise.all(files
192
+ .filter((f) => f.endsWith('.json'))
193
+ .map((f) => unlink(join(this.cacheDir, f)).catch(() => { })));
194
+ }
195
+ catch {
196
+ // Directory might not exist
197
+ }
198
+ this.stats = { hits: 0, misses: 0 };
199
+ }
200
+ async getStats() {
201
+ await this.ensureDir();
202
+ let totalSize = 0;
203
+ let entryCount = 0;
204
+ try {
205
+ const files = await readdir(this.cacheDir);
206
+ for (const file of files) {
207
+ if (file.endsWith('.json')) {
208
+ const stats = await stat(join(this.cacheDir, file)).catch(() => null);
209
+ if (stats) {
210
+ totalSize += stats.size;
211
+ entryCount++;
212
+ }
213
+ }
214
+ }
215
+ }
216
+ catch {
217
+ // Directory might not exist
218
+ }
219
+ const total = this.stats.hits + this.stats.misses;
220
+ return {
221
+ hits: this.stats.hits,
222
+ misses: this.stats.misses,
223
+ size: totalSize,
224
+ entryCount,
225
+ hitRate: total > 0 ? this.stats.hits / total : 0,
226
+ };
227
+ }
228
+ }
229
+ /**
230
+ * No-op cache implementation
231
+ */
232
+ export class NoopCache {
233
+ async get(_key) {
234
+ return undefined;
235
+ }
236
+ async set(_key, _value) {
237
+ // No-op
238
+ }
239
+ async has(_key) {
240
+ return false;
241
+ }
242
+ async delete(_key) {
243
+ return false;
244
+ }
245
+ async clear() {
246
+ // No-op
247
+ }
248
+ getStats() {
249
+ return {
250
+ hits: 0,
251
+ misses: 0,
252
+ size: 0,
253
+ entryCount: 0,
254
+ hitRate: 0,
255
+ };
256
+ }
257
+ }
258
+ /**
259
+ * Create a cache instance based on options
260
+ */
261
+ export function createCache(options) {
262
+ switch (options.strategy) {
263
+ case 'memory':
264
+ return new MemoryCache(options);
265
+ case 'file':
266
+ return new FileCache(options);
267
+ case 'none':
268
+ return new NoopCache();
269
+ default:
270
+ return new MemoryCache(options);
271
+ }
272
+ }
273
+ /**
274
+ * Generate a cache key from multiple parts
275
+ */
276
+ export function cacheKey(...parts) {
277
+ return parts
278
+ .filter((p) => p !== undefined && p !== null)
279
+ .map((p) => String(p))
280
+ .join(':');
281
+ }
282
+ /**
283
+ * Generate a hash for a file's content
284
+ */
285
+ export function contentHash(content) {
286
+ return createHash('sha256').update(content).digest('hex').slice(0, 16);
287
+ }
288
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/infrastructure/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @fileoverview Configuration loader using cosmiconfig
3
+ * @module @nahisaho/musubix-security/infrastructure/config-loader
4
+ * @trace REQ-SEC-CONFIG-001, REQ-SEC-CONFIG-002
5
+ */
6
+ import { z } from 'zod';
7
+ import type { SecurityConfig } from '../types/index.js';
8
+ /**
9
+ * Configuration loader result
10
+ */
11
+ export interface ConfigLoadResult {
12
+ config: SecurityConfig;
13
+ filepath?: string;
14
+ isEmpty: boolean;
15
+ }
16
+ /**
17
+ * Load configuration asynchronously
18
+ */
19
+ export declare function loadConfig(searchFrom?: string): Promise<ConfigLoadResult>;
20
+ /**
21
+ * Load configuration synchronously
22
+ */
23
+ export declare function loadConfigSync(searchFrom?: string): ConfigLoadResult;
24
+ /**
25
+ * Validate a configuration object
26
+ */
27
+ export declare function validateConfig(config: unknown): {
28
+ valid: boolean;
29
+ errors?: string[];
30
+ };
31
+ /**
32
+ * Get config schema for documentation
33
+ */
34
+ export declare function getConfigSchema(): z.ZodType<any>;
35
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/infrastructure/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AA2LxD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAyFD;;GAEG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiD/E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,gBAAgB,CA4CpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAUrF;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAEhD"}
@@ -0,0 +1,358 @@
1
+ /**
2
+ * @fileoverview Configuration loader using cosmiconfig
3
+ * @module @nahisaho/musubix-security/infrastructure/config-loader
4
+ * @trace REQ-SEC-CONFIG-001, REQ-SEC-CONFIG-002
5
+ */
6
+ import { cosmiconfig, cosmiconfigSync } from 'cosmiconfig';
7
+ import { z } from 'zod';
8
+ import { DEFAULT_CONFIG, CONFIG_FILE_LOCATIONS, ENV_PREFIX } from '../types/config.js';
9
+ /**
10
+ * Zod schema for severity
11
+ */
12
+ const SeveritySchema = z.enum(['critical', 'high', 'medium', 'low']);
13
+ /**
14
+ * Zod schema for scan options
15
+ */
16
+ const ScanOptionsSchema = z.object({
17
+ severityFilter: z.array(SeveritySchema).optional(),
18
+ rulesets: z.array(z.enum(['owasp-top-10', 'cwe-top-25', 'custom'])).optional(),
19
+ excludePatterns: z.array(z.string()).optional(),
20
+ maxFileSize: z.number().positive().optional(),
21
+ incremental: z.boolean().optional(),
22
+ customRulesDir: z.string().optional(),
23
+ }).optional();
24
+ /**
25
+ * Zod schema for taint options
26
+ */
27
+ const TaintOptionsSchema = z.object({
28
+ customSources: z.array(z.object({
29
+ pattern: z.string(),
30
+ category: z.string(),
31
+ description: z.string(),
32
+ })).optional(),
33
+ customSinks: z.array(z.object({
34
+ pattern: z.string(),
35
+ category: z.string(),
36
+ severity: SeveritySchema,
37
+ description: z.string(),
38
+ })).optional(),
39
+ additionalSanitizers: z.array(z.object({
40
+ functionName: z.string(),
41
+ sinkCategories: z.array(z.string()),
42
+ })).optional(),
43
+ maxPathDepth: z.number().positive().optional(),
44
+ interprocedural: z.boolean().optional(),
45
+ trackAsync: z.boolean().optional(),
46
+ excludePatterns: z.array(z.string()).optional(),
47
+ }).optional();
48
+ /**
49
+ * Zod schema for fix options
50
+ */
51
+ const FixOptionsSchema = z.object({
52
+ preferredStrategies: z.array(z.string()).optional(),
53
+ useAI: z.boolean().optional(),
54
+ aiModel: z.string().optional(),
55
+ generateAlternatives: z.boolean().optional(),
56
+ maxAlternatives: z.number().positive().optional(),
57
+ preserveStyle: z.boolean().optional(),
58
+ targetFramework: z.string().optional(),
59
+ }).optional();
60
+ /**
61
+ * Zod schema for secret options
62
+ */
63
+ const SecretOptionsSchema = z.object({
64
+ customPatterns: z.array(z.any()).optional(),
65
+ disablePatterns: z.array(z.string()).optional(),
66
+ excludePatterns: z.array(z.string()).optional(),
67
+ ignoreTestValues: z.boolean().optional(),
68
+ maxFileSize: z.number().positive().optional(),
69
+ verify: z.boolean().optional(),
70
+ entropyThreshold: z.number().min(0).max(8).optional(),
71
+ }).optional();
72
+ /**
73
+ * Zod schema for audit options
74
+ */
75
+ const AuditOptionsSchema = z.object({
76
+ includeDevDependencies: z.boolean().optional(),
77
+ minSeverity: SeveritySchema.optional(),
78
+ sources: z.array(z.string()).optional(),
79
+ ignoreVulnerabilities: z.array(z.string()).optional(),
80
+ ignorePackages: z.array(z.string()).optional(),
81
+ maxDepth: z.number().positive().optional(),
82
+ suggestUpgrades: z.boolean().optional(),
83
+ checkBreaking: z.boolean().optional(),
84
+ registryUrl: z.string().url().optional(),
85
+ }).optional();
86
+ /**
87
+ * Zod schema for report config
88
+ */
89
+ const ReportConfigSchema = z.object({
90
+ format: z.enum(['json', 'sarif', 'markdown', 'html', 'csv']).optional(),
91
+ outputPath: z.string().optional(),
92
+ includeCodeSnippets: z.boolean().optional(),
93
+ includeFixes: z.boolean().optional(),
94
+ includeTaintPaths: z.boolean().optional(),
95
+ groupBy: z.enum(['file', 'type', 'severity']).optional(),
96
+ maxPerFile: z.number().positive().optional(),
97
+ includeSummary: z.boolean().optional(),
98
+ templatePath: z.string().optional(),
99
+ }).optional();
100
+ /**
101
+ * Zod schema for knowledge graph config
102
+ */
103
+ const KnowledgeGraphConfigSchema = z.object({
104
+ mode: z.enum(['local', 'global', 'hybrid', 'disabled']).optional(),
105
+ localDbPath: z.string().optional(),
106
+ globalEndpoint: z.string().url().optional(),
107
+ autoLearn: z.boolean().optional(),
108
+ namespace: z.string().optional(),
109
+ maxCachedPatterns: z.number().positive().optional(),
110
+ }).optional();
111
+ /**
112
+ * Zod schema for AI config
113
+ */
114
+ const AIConfigSchema = z.object({
115
+ enabled: z.boolean().optional(),
116
+ provider: z.enum(['vscode-lm', 'openai', 'anthropic']).optional(),
117
+ model: z.string().optional(),
118
+ maxTokens: z.number().positive().optional(),
119
+ temperature: z.number().min(0).max(2).optional(),
120
+ useForFixes: z.boolean().optional(),
121
+ useForExplanation: z.boolean().optional(),
122
+ }).optional();
123
+ /**
124
+ * Zod schema for cache config
125
+ */
126
+ const CacheConfigSchema = z.object({
127
+ strategy: z.enum(['memory', 'file', 'none']).optional(),
128
+ cacheDir: z.string().optional(),
129
+ ttlSeconds: z.number().positive().optional(),
130
+ maxSizeMB: z.number().positive().optional(),
131
+ cacheAST: z.boolean().optional(),
132
+ cachePatterns: z.boolean().optional(),
133
+ }).optional();
134
+ /**
135
+ * Zod schema for CI config
136
+ */
137
+ const CIConfigSchema = z.object({
138
+ failOnSeverity: SeveritySchema.optional(),
139
+ failOnCount: z.number().nonnegative().optional(),
140
+ failOnNewOnly: z.boolean().optional(),
141
+ baselinePath: z.string().optional(),
142
+ sarifOutput: z.boolean().optional(),
143
+ sarifPath: z.string().optional(),
144
+ prComments: z.boolean().optional(),
145
+ platform: z.enum(['github', 'gitlab', 'azure-devops', 'jenkins']).optional(),
146
+ }).optional();
147
+ /**
148
+ * Complete security config schema
149
+ */
150
+ const SecurityConfigSchema = z.object({
151
+ version: z.literal('1.0').optional(),
152
+ projectRoot: z.string().optional(),
153
+ scan: ScanOptionsSchema,
154
+ taint: TaintOptionsSchema,
155
+ fix: FixOptionsSchema,
156
+ secret: SecretOptionsSchema,
157
+ audit: AuditOptionsSchema,
158
+ sbom: z.object({
159
+ format: z.enum(['cyclonedx', 'spdx']).optional(),
160
+ includeDevDependencies: z.boolean().optional(),
161
+ includeVulnerabilities: z.boolean().optional(),
162
+ includeLicenses: z.boolean().optional(),
163
+ outputPath: z.string().optional(),
164
+ }).optional(),
165
+ licensePolicy: z.object({
166
+ allowed: z.array(z.string()).optional(),
167
+ denied: z.array(z.string()).optional(),
168
+ requireApproval: z.array(z.string()).optional(),
169
+ }).optional(),
170
+ report: ReportConfigSchema,
171
+ knowledgeGraph: KnowledgeGraphConfigSchema,
172
+ ai: AIConfigSchema,
173
+ cache: CacheConfigSchema,
174
+ ci: CIConfigSchema,
175
+ severityFilter: z.array(SeveritySchema).optional(),
176
+ excludePatterns: z.array(z.string()).optional(),
177
+ customRulesDir: z.string().optional(),
178
+ verbose: z.boolean().optional(),
179
+ debug: z.boolean().optional(),
180
+ });
181
+ /**
182
+ * Load configuration from environment variables
183
+ */
184
+ function loadEnvConfig() {
185
+ const config = {};
186
+ // Helper to get env var with prefix
187
+ const getEnv = (key) => process.env[`${ENV_PREFIX}${key}`];
188
+ // Verbose mode
189
+ const verbose = getEnv('VERBOSE');
190
+ if (verbose) {
191
+ config.verbose = verbose === 'true' || verbose === '1';
192
+ }
193
+ // Debug mode
194
+ const debug = getEnv('DEBUG');
195
+ if (debug) {
196
+ config.debug = debug === 'true' || debug === '1';
197
+ }
198
+ // Severity filter
199
+ const severityFilter = getEnv('SEVERITY_FILTER');
200
+ if (severityFilter) {
201
+ config.severityFilter = severityFilter.split(',').map((s) => s.trim());
202
+ }
203
+ // Project root
204
+ const projectRoot = getEnv('PROJECT_ROOT');
205
+ if (projectRoot) {
206
+ config.projectRoot = projectRoot;
207
+ }
208
+ // AI enabled
209
+ const aiEnabled = getEnv('AI_ENABLED');
210
+ if (aiEnabled) {
211
+ config.ai = { enabled: aiEnabled === 'true' || aiEnabled === '1' };
212
+ }
213
+ // KG mode
214
+ const kgMode = getEnv('KG_MODE');
215
+ if (kgMode) {
216
+ config.knowledgeGraph = { mode: kgMode };
217
+ }
218
+ // Report format
219
+ const reportFormat = getEnv('REPORT_FORMAT');
220
+ if (reportFormat) {
221
+ config.report = { format: reportFormat };
222
+ }
223
+ // CI fail severity
224
+ const ciFailSeverity = getEnv('CI_FAIL_SEVERITY');
225
+ if (ciFailSeverity) {
226
+ config.ci = { failOnSeverity: ciFailSeverity };
227
+ }
228
+ return config;
229
+ }
230
+ /**
231
+ * Deep merge two objects
232
+ */
233
+ function deepMerge(target, source) {
234
+ const result = { ...target };
235
+ for (const key of Object.keys(source)) {
236
+ const sourceValue = source[key];
237
+ const targetValue = target[key];
238
+ if (sourceValue !== undefined &&
239
+ sourceValue !== null &&
240
+ typeof sourceValue === 'object' &&
241
+ !Array.isArray(sourceValue) &&
242
+ typeof targetValue === 'object' &&
243
+ !Array.isArray(targetValue)) {
244
+ result[key] = deepMerge(targetValue, sourceValue);
245
+ }
246
+ else if (sourceValue !== undefined) {
247
+ result[key] = sourceValue;
248
+ }
249
+ }
250
+ return result;
251
+ }
252
+ /**
253
+ * Load configuration asynchronously
254
+ */
255
+ export async function loadConfig(searchFrom) {
256
+ const explorer = cosmiconfig('musubix-security', {
257
+ searchPlaces: CONFIG_FILE_LOCATIONS,
258
+ });
259
+ try {
260
+ const result = searchFrom
261
+ ? await explorer.search(searchFrom)
262
+ : await explorer.search();
263
+ if (result && !result.isEmpty) {
264
+ // Validate loaded config
265
+ const parsed = SecurityConfigSchema.safeParse(result.config);
266
+ if (!parsed.success) {
267
+ console.error('Configuration validation error:', parsed.error.format());
268
+ throw new Error(`Invalid configuration: ${parsed.error.message}`);
269
+ }
270
+ // Merge with defaults and env
271
+ const envConfig = loadEnvConfig();
272
+ const fileConfig = parsed.data;
273
+ const mergedConfig = deepMerge(deepMerge(DEFAULT_CONFIG, fileConfig), envConfig);
274
+ return {
275
+ config: mergedConfig,
276
+ filepath: result.filepath,
277
+ isEmpty: false,
278
+ };
279
+ }
280
+ // No config file found, use defaults with env overrides
281
+ const envConfig = loadEnvConfig();
282
+ const mergedConfig = deepMerge(DEFAULT_CONFIG, envConfig);
283
+ return {
284
+ config: mergedConfig,
285
+ isEmpty: true,
286
+ };
287
+ }
288
+ catch (error) {
289
+ // On error, return defaults with env overrides
290
+ console.error('Error loading configuration:', error);
291
+ const envConfig = loadEnvConfig();
292
+ const mergedConfig = deepMerge(DEFAULT_CONFIG, envConfig);
293
+ return {
294
+ config: mergedConfig,
295
+ isEmpty: true,
296
+ };
297
+ }
298
+ }
299
+ /**
300
+ * Load configuration synchronously
301
+ */
302
+ export function loadConfigSync(searchFrom) {
303
+ const explorer = cosmiconfigSync('musubix-security', {
304
+ searchPlaces: CONFIG_FILE_LOCATIONS,
305
+ });
306
+ try {
307
+ const result = searchFrom
308
+ ? explorer.search(searchFrom)
309
+ : explorer.search();
310
+ if (result && !result.isEmpty) {
311
+ const parsed = SecurityConfigSchema.safeParse(result.config);
312
+ if (!parsed.success) {
313
+ throw new Error(`Invalid configuration: ${parsed.error.message}`);
314
+ }
315
+ const envConfig = loadEnvConfig();
316
+ const fileConfig = parsed.data;
317
+ const mergedConfig = deepMerge(deepMerge(DEFAULT_CONFIG, fileConfig), envConfig);
318
+ return {
319
+ config: mergedConfig,
320
+ filepath: result.filepath,
321
+ isEmpty: false,
322
+ };
323
+ }
324
+ const envConfig = loadEnvConfig();
325
+ const mergedConfig = deepMerge(DEFAULT_CONFIG, envConfig);
326
+ return {
327
+ config: mergedConfig,
328
+ isEmpty: true,
329
+ };
330
+ }
331
+ catch (error) {
332
+ console.error('Error loading configuration:', error);
333
+ const envConfig = loadEnvConfig();
334
+ const mergedConfig = deepMerge(DEFAULT_CONFIG, envConfig);
335
+ return {
336
+ config: mergedConfig,
337
+ isEmpty: true,
338
+ };
339
+ }
340
+ }
341
+ /**
342
+ * Validate a configuration object
343
+ */
344
+ export function validateConfig(config) {
345
+ const result = SecurityConfigSchema.safeParse(config);
346
+ if (result.success) {
347
+ return { valid: true };
348
+ }
349
+ const errors = result.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
350
+ return { valid: false, errors };
351
+ }
352
+ /**
353
+ * Get config schema for documentation
354
+ */
355
+ export function getConfigSchema() {
356
+ return SecurityConfigSchema;
357
+ }
358
+ //# sourceMappingURL=config-loader.js.map