@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.
- package/README.md +105 -0
- package/bin/musubix-security-mcp.js +12 -0
- package/bin/musubix-security.js +12 -0
- package/dist/analysis/dependency-auditor.d.ts +30 -0
- package/dist/analysis/dependency-auditor.d.ts.map +1 -0
- package/dist/analysis/dependency-auditor.js +325 -0
- package/dist/analysis/dependency-auditor.js.map +1 -0
- package/dist/analysis/index.d.ts +9 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +9 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/secret-detector.d.ts +44 -0
- package/dist/analysis/secret-detector.d.ts.map +1 -0
- package/dist/analysis/secret-detector.js +465 -0
- package/dist/analysis/secret-detector.js.map +1 -0
- package/dist/analysis/taint-analyzer.d.ts +62 -0
- package/dist/analysis/taint-analyzer.d.ts.map +1 -0
- package/dist/analysis/taint-analyzer.js +519 -0
- package/dist/analysis/taint-analyzer.js.map +1 -0
- package/dist/analysis/vulnerability-scanner.d.ts +58 -0
- package/dist/analysis/vulnerability-scanner.d.ts.map +1 -0
- package/dist/analysis/vulnerability-scanner.js +417 -0
- package/dist/analysis/vulnerability-scanner.js.map +1 -0
- package/dist/cli/commands.d.ts +15 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +405 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/ast-parser.d.ts +87 -0
- package/dist/infrastructure/ast-parser.d.ts.map +1 -0
- package/dist/infrastructure/ast-parser.js +273 -0
- package/dist/infrastructure/ast-parser.js.map +1 -0
- package/dist/infrastructure/cache.d.ts +100 -0
- package/dist/infrastructure/cache.d.ts.map +1 -0
- package/dist/infrastructure/cache.js +288 -0
- package/dist/infrastructure/cache.js.map +1 -0
- package/dist/infrastructure/config-loader.d.ts +35 -0
- package/dist/infrastructure/config-loader.d.ts.map +1 -0
- package/dist/infrastructure/config-loader.js +358 -0
- package/dist/infrastructure/config-loader.js.map +1 -0
- package/dist/infrastructure/file-scanner.d.ts +94 -0
- package/dist/infrastructure/file-scanner.d.ts.map +1 -0
- package/dist/infrastructure/file-scanner.js +189 -0
- package/dist/infrastructure/file-scanner.js.map +1 -0
- package/dist/infrastructure/index.d.ts +9 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +9 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +34 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +88 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +88 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +443 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/services/fix-generator.d.ts +56 -0
- package/dist/services/fix-generator.d.ts.map +1 -0
- package/dist/services/fix-generator.js +346 -0
- package/dist/services/fix-generator.js.map +1 -0
- package/dist/services/fix-verifier.d.ts +62 -0
- package/dist/services/fix-verifier.d.ts.map +1 -0
- package/dist/services/fix-verifier.js +224 -0
- package/dist/services/fix-verifier.js.map +1 -0
- package/dist/services/index.d.ts +9 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +13 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/report-generator.d.ts +87 -0
- package/dist/services/report-generator.d.ts.map +1 -0
- package/dist/services/report-generator.js +463 -0
- package/dist/services/report-generator.js.map +1 -0
- package/dist/services/security-service.d.ts +151 -0
- package/dist/services/security-service.d.ts.map +1 -0
- package/dist/services/security-service.js +279 -0
- package/dist/services/security-service.js.map +1 -0
- package/dist/types/config.d.ts +188 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +89 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/dependency.d.ts +266 -0
- package/dist/types/dependency.d.ts.map +1 -0
- package/dist/types/dependency.js +7 -0
- package/dist/types/dependency.js.map +1 -0
- package/dist/types/fix.d.ts +213 -0
- package/dist/types/fix.d.ts.map +1 -0
- package/dist/types/fix.js +7 -0
- package/dist/types/fix.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/secret.d.ts +151 -0
- package/dist/types/secret.d.ts.map +1 -0
- package/dist/types/secret.js +91 -0
- package/dist/types/secret.js.map +1 -0
- package/dist/types/taint.d.ts +182 -0
- package/dist/types/taint.d.ts.map +1 -0
- package/dist/types/taint.js +24 -0
- package/dist/types/taint.js.map +1 -0
- package/dist/types/vulnerability.d.ts +136 -0
- package/dist/types/vulnerability.d.ts.map +1 -0
- package/dist/types/vulnerability.js +7 -0
- package/dist/types/vulnerability.js.map +1 -0
- 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
|