Skip to content

Commit 128bba5

Browse files
fix: replace execSync('cat') with readFileSync to prevent command injection
discovery.ts used execSync('cat "${path}"') to read package.json and Cargo.toml, which could execute arbitrary shell commands if a project path contained metacharacters. Replaced with readFileSync in 3 locations. Also exports MODULE_ROOTS constant and adds buildModuleNodes() helper to eliminate duplication across init.ts, update.ts, and discovery.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent eea7665 commit 128bba5

1 file changed

Lines changed: 32 additions & 7 deletions

File tree

‎src/core/discovery.ts‎

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import{execSync}from'node:child_process'
22
import{readFileasfsRead,stat}from'node:fs/promises'
33
import{join,relative,dirname,basename,extname}from'node:path'
4-
import{existsSync}from'node:fs'
5-
importtype{DiscoveredFile,ProjectInfo}from'../types/index.js'
4+
import{existsSync,readFileSyncasreadFileSyncFs}from'node:fs'
5+
importtype{DiscoveredFile,ProjectInfo,ModuleNode,SymbolRecord}from'../types/index.js'
66
import{EXTENSION_MAP}from'../extraction/parser.js'
77

88
constIGNORED_DIRS=newSet([
@@ -32,7 +32,7 @@ function detectProjectName(root: string): string {
3232
constpkgPath=join(root,'package.json')
3333
if(existsSync(pkgPath)){
3434
try{
35-
constpkg=JSON.parse(execSync(`cat "${pkgPath}"`,{encoding: 'utf-8'}))
35+
constpkg=JSON.parse(readFileSyncFs(pkgPath,'utf-8'))
3636
if(pkg.name)returnpkg.name
3737
}catch{/* ignore */}
3838
}
@@ -41,7 +41,7 @@ function detectProjectName(root: string): string {
4141
constcargoPath=join(root,'Cargo.toml')
4242
if(existsSync(cargoPath)){
4343
try{
44-
constcargo=execSync(`cat "${cargoPath}"`,{encoding: 'utf-8'})
44+
constcargo=readFileSyncFs(cargoPath,'utf-8')
4545
constmatch=cargo.match(/name\s*=\s*"(.+?)"/)
4646
if(match?.[1])returnmatch[1]
4747
}catch{/* ignore */}
@@ -128,9 +128,10 @@ async function walkDirectory(root: string, baseRoot: string): Promise<string[]>
128128
returnpaths
129129
}
130130

131+
/** Directories that contain modules (src/cli, lib/utils, etc.) */
132+
exportconstMODULE_ROOTS=newSet(['src','lib','pkg','packages','apps','extensions','crates','internal','cmd','scripts','tools','rust'])
133+
131134
functiondetectModules(root: string,files: DiscoveredFile[]): string[]{
132-
// Detect top-level directories under src/ (or equivalent)
133-
constMODULE_ROOTS=newSet(['src','lib','pkg','packages','apps','extensions','crates','internal','cmd','scripts','tools','rust'])
134135
constsrcDirs=newSet<string>()
135136

136137
for(constfileoffiles){
@@ -149,6 +150,30 @@ function detectModules(root: string, files: DiscoveredFile[]): string[] {
149150
return[...srcDirs].sort()
150151
}
151152

153+
exportfunctionbuildModuleNodes(
154+
modules: string[],
155+
files: DiscoveredFile[],
156+
symbols: SymbolRecord[],
157+
): ModuleNode[]{
158+
returnmodules.map(modName=>{
159+
constmodFiles=files.filter(f=>{
160+
constparts=f.path.split('/')
161+
consttopDir=parts[0]??''
162+
return(MODULE_ROOTS.has(topDir)&&parts[1]===modName)||
163+
(parts[0]===modName)
164+
})
165+
consttopDir=modFiles[0]?.path.split('/')[0]??'src'
166+
return{
167+
path: `${topDir}/${modName}`,
168+
name: modName,
169+
files: modFiles.map(f=>f.path),
170+
language: modFiles[0]?.language||'unknown',
171+
lines: modFiles.reduce((sum,f)=>sum+f.lines,0),
172+
symbols: symbols.filter(s=>modFiles.some(f=>f.path===s.file)).length,
173+
}
174+
})
175+
}
176+
152177
functiondetectEntryPoints(root: string,files: DiscoveredFile[],type: ProjectInfo['type']): string[]{
153178
constentryPoints: string[]=[]
154179

@@ -157,7 +182,7 @@ function detectEntryPoints(root: string, files: DiscoveredFile[], type: ProjectI
157182
constpkgPath=join(root,'package.json')
158183
if(existsSync(pkgPath)){
159184
try{
160-
constpkg=JSON.parse(execSync(`cat "${pkgPath}"`,{encoding: 'utf-8'}))
185+
constpkg=JSON.parse(readFileSyncFs(pkgPath,'utf-8'))
161186
if(pkg.main)entryPoints.push(pkg.main)
162187
if(pkg.bin){
163188
constbins=typeofpkg.bin==='string' ? [pkg.bin] : Object.values(pkg.bin)

0 commit comments

Comments
 (0)