From bf703b6c29e76b13bb8d8cf8079f18df4fc321bd Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 8 Aug 2026 20:48:46 +0200 Subject: [PATCH] fix(runner): reject NUL bytes in file paths --- api/src/validation.test.ts | 8 ++++++++ api/src/validation.ts | 3 +++ 2 files changed, 11 insertions(+) diff --git a/api/src/validation.test.ts b/api/src/validation.test.ts index 0596c2f..a756685 100644 --- a/api/src/validation.test.ts +++ b/api/src/validation.test.ts @@ -126,6 +126,10 @@ describe('validateFilePath', () => { expect(() => validateFilePath('.', submissionDir)).toThrow(ValidationError); }); + it('rejects NUL bytes in file names', () => { + expect(() => validateFilePath('file\0.txt', submissionDir)).toThrow(/NUL/); + }); + it('rejects path traversal with .. segments', () => { expect(() => validateFilePath('../etc/passwd', submissionDir)).toThrow(ValidationError); expect(() => validateFilePath('a/../../escape', submissionDir)).toThrow(ValidationError); @@ -188,4 +192,8 @@ describe('isValidFilePath', () => { expect(isValidFilePath('', submissionDir)).toBe(false); expect(isValidFilePath('a'.repeat(MAX_LEN + 10), submissionDir)).toBe(false); }); + + it('returns false for paths containing NUL bytes', () => { + expect(isValidFilePath('file\0.txt', submissionDir)).toBe(false); + }); }); diff --git a/api/src/validation.ts b/api/src/validation.ts index 2e937ba..27e1b4a 100644 --- a/api/src/validation.ts +++ b/api/src/validation.ts @@ -48,6 +48,9 @@ export function validateFilePath(name: string, submissionDir: string): void { if (!name || name === '.') { throw new ValidationError('File path must not be empty'); } + if (name.includes('\0')) { + throw new ValidationError('File path must not contain NUL bytes'); + } /* Reject absolute paths up front. `path.resolve(submissionDir, name)` * ignores `submissionDir` when `name` is absolute, so an absolute path * that happens to point inside `submissionDir` (e.g. the exact on-disk