From 3ae4d4ecd4b8fe136482fe738f5c8e852e656b92 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sat, 30 May 2026 06:13:10 +0000 Subject: [PATCH] fix(csharp): cap .csproj read via stream, not stat-then-read, to clear CodeQL TOCTOU (#1908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeQL js/file-system-race flagged the fs.stat + fs.readFile size guard in readCsprojConfig as a check-then-use filesystem race. Replace it with a length-capped createReadStream (readFileTextCapped) — same memory bound on untrusted input, no stat-then-read race, and consistent with the streamed .cs scan. Behavior is unchanged for real .csproj files (parity 2/2). --- .../src/core/ingestion/language-config.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/gitnexus/src/core/ingestion/language-config.ts b/gitnexus/src/core/ingestion/language-config.ts index 4776d897e..89da0e69d 100644 --- a/gitnexus/src/core/ingestion/language-config.ts +++ b/gitnexus/src/core/ingestion/language-config.ts @@ -324,6 +324,19 @@ export async function scanCSharpProject(repoRoot: string): Promise { + const parts: string[] = []; + const stream = createReadStream(filePath, { encoding: 'utf-8', end: maxBytes }); + for await (const part of stream) { + parts.push(part as string); + } + return parts.join(''); +} + async function readCsprojConfig( csprojPath: string, fileName: string, @@ -332,9 +345,9 @@ async function readCsprojConfig( maxFileSizeBytes: number, ): Promise { try { - const stat = await fs.stat(csprojPath); - if (stat.size > maxFileSizeBytes) return null; // oversized .csproj → skip unread - const content = await fs.readFile(csprojPath, 'utf-8'); + // `` sits in the first PropertyGroup near the top, so a + // capped read captures any real .csproj while bounding pathological input. + const content = await readFileTextCapped(csprojPath, maxFileSizeBytes); const nsMatch = content.match(CSHARP_ROOT_NAMESPACE_RE); const rootNamespace = nsMatch ? nsMatch[1].trim() : fileName.replace(/\.csproj$/, ''); const projectDir = path.relative(repoRoot, dir).replace(/\\/g, '/');