mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
fix(csharp): cap .csproj read via stream, not stat-then-read, to clear CodeQL TOCTOU (#1908)
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).
This commit is contained in:
parent
33fc24de91
commit
3ae4d4ecd4
1 changed files with 16 additions and 3 deletions
|
|
@ -324,6 +324,19 @@ export async function scanCSharpProject(repoRoot: string): Promise<CSharpProject
|
|||
return { configs, declaredNamespaces, rootNamespaces, truncated };
|
||||
}
|
||||
|
||||
/** Read up to `maxBytes` of a UTF-8 text file via a length-capped stream. Bounds
|
||||
* the read on untrusted input WITHOUT a stat-then-read filesystem race — a
|
||||
* `.cs` is streamed the same way (see `collectDeclaredNamespaces`). A file
|
||||
* smaller than the cap is read in full; a larger one is read only up to it. */
|
||||
async function readFileTextCapped(filePath: string, maxBytes: number): Promise<string> {
|
||||
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<CSharpProjectConfig | null> {
|
||||
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');
|
||||
// `<RootNamespace>` 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, '/');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue