Skip to content

Commit dd0e5ac

Browse files
Houseofmvpsclaude
andcommitted
fix: replace new Function() with safe regex parser in config loader
Removes dynamic code execution (eval/Function) flagged as supply chain risk by Socket.dev. Config fields are now extracted via typed regex without executing user file content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a1156fb commit dd0e5ac

2 files changed

Lines changed: 55 additions & 15 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codesight",
3-
"version": "1.13.0",
3+
"version": "1.13.1",
44
"description": "See your codebase clearly. Universal AI context generator that maps routes, schema, components, dependencies, and more for Claude Code, Cursor, Copilot, Codex, and any AI coding tool.",
55
"main": "dist/index.js",
66
"bin": {

src/config.ts

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,53 @@ export async function loadConfig(root: string): Promise<CodesightConfig> {
6565
return {};
6666
}
6767

68+
function safeParseConfigText(content: string): CodesightConfig {
69+
const config: CodesightConfig = {};
70+
const match = content.match(/export\s+default\s+(\{[\s\S]*\})\s*;?\s*$/m);
71+
if (!match) return config;
72+
const body = match[1];
73+
74+
function extractString(field: string): string | undefined {
75+
const m = body.match(new RegExp(`\\b${field}\\s*:\\s*['"\`]([^'"\`]*?)['"\`]`));
76+
return m ? m[1] : undefined;
77+
}
78+
function extractNumber(field: string): number | undefined {
79+
const m = body.match(new RegExp(`\\b${field}\\s*:\\s*(\\d+)`));
80+
return m ? parseInt(m[1], 10) : undefined;
81+
}
82+
function extractBoolean(field: string): boolean | undefined {
83+
const m = body.match(new RegExp(`\\b${field}\\s*:\\s*(true|false)`));
84+
return m ? m[1] === "true" : undefined;
85+
}
86+
function extractStringArray(field: string): string[] | undefined {
87+
const m = body.match(new RegExp(`\\b${field}\\s*:\\s*\\[([^\\]]*?)\\]`));
88+
if (!m) return undefined;
89+
const items = m[1].match(/['"`]([^'"`]*?)['"`]/g);
90+
return items ? items.map((s) => s.slice(1, -1)) : [];
91+
}
92+
93+
const maxDepth = extractNumber("maxDepth");
94+
if (maxDepth !== undefined) config.maxDepth = maxDepth;
95+
const outputDir = extractString("outputDir");
96+
if (outputDir !== undefined) config.outputDir = outputDir;
97+
const profile = extractString("profile");
98+
if (profile !== undefined) config.profile = profile as CodesightConfig["profile"];
99+
const blastRadiusDepth = extractNumber("blastRadiusDepth");
100+
if (blastRadiusDepth !== undefined) config.blastRadiusDepth = blastRadiusDepth;
101+
const hotFileThreshold = extractNumber("hotFileThreshold");
102+
if (hotFileThreshold !== undefined) config.hotFileThreshold = hotFileThreshold;
103+
const maxTokens = extractNumber("maxTokens");
104+
if (maxTokens !== undefined) config.maxTokens = maxTokens;
105+
const collapseCrud = extractBoolean("collapseCrud");
106+
if (collapseCrud !== undefined) config.collapseCrud = collapseCrud;
107+
const disableDetectors = extractStringArray("disableDetectors");
108+
if (disableDetectors !== undefined) config.disableDetectors = disableDetectors;
109+
const ignorePatterns = extractStringArray("ignorePatterns");
110+
if (ignorePatterns !== undefined) config.ignorePatterns = ignorePatterns;
111+
112+
return config;
113+
}
114+
68115
async function loadTsConfig(configPath: string, _root: string): Promise<CodesightConfig> {
69116
// Strategy 1: try tsx via dynamic import of the .ts file directly
70117
// (works if tsx or ts-node is installed)
@@ -73,22 +120,15 @@ async function loadTsConfig(configPath: string, _root: string): Promise<Codesigh
73120
return (module.default || module) as CodesightConfig;
74121
} catch {}
75122

76-
// Strategy 2: read as text and extract JSON-like config
77-
// (fallback for when no TS loader is available)
123+
// Strategy 2: read as text and extract known fields with safe regex parsing
124+
// (fallback for when no TS loader is available — avoids dynamic code execution)
78125
const content = await readFile(configPath, "utf-8");
126+
const parsed = safeParseConfigText(content);
127+
if (Object.keys(parsed).length > 0) return parsed;
79128

80-
// Try to extract the config object from simple export default { ... }
81-
const match = content.match(/export\s+default\s+({[\s\S]*})\s*;?\s*$/m);
82-
if (match) {
83-
try {
84-
// Use Function constructor to evaluate the object literal
85-
// Safe here since this is user's own config file in their project
86-
const fn = new Function(`return (${match[1]})`);
87-
return fn() as CodesightConfig;
88-
} catch {}
89-
}
90-
91-
console.warn(` Warning: cannot load codesight.config.ts (install tsx for TS config support)`);
129+
console.warn(
130+
` Warning: cannot load codesight.config.ts (install tsx for full TS config support, or use codesight.config.json)`
131+
);
92132
return {};
93133
}
94134

0 commit comments

Comments
 (0)