Files
bun.sh/test/js/node/path/normalize.test.js
robobun 5971bf67ef fix: buffer allocation for path operations with very long paths (#23819)
## Summary

Fixed an off-by-one error in buffer allocation for several path module
functions when handling paths longer than `PATH_SIZE` (typically 4096
bytes on most platforms).

## Changes

- `normalizeJS_T`: Added +1 to buffer allocation for null terminator
- `relativeJS_T`: Added +1 to buffer allocation for null terminator  
- `toNamespacedPathJS_T`: Added +9 bytes (8 for possible UNC prefix + 1
for null terminator)

## Test plan

- Added tests for `path.normalize()` with paths up to 100,000 characters
- Added tests for `path.relative()` with very long paths
- All existing path tests continue to pass

The issue occurred because when a path is exactly equal to or longer
than `PATH_SIZE`, the buffer was allocated with size equal to the path
length, but then a null terminator was written at `buf[bufSize]`, which
was out of bounds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-10-20 19:28:34 -07:00

66 lines
4.1 KiB
JavaScript

import { describe, test } from "bun:test";
import assert from "node:assert";
import path from "node:path";
describe("path.normalize", () => {
test("win32", () => {
assert.strictEqual(path.win32.normalize("./fixtures///b/../b/c.js"), "fixtures\\b\\c.js");
assert.strictEqual(path.win32.normalize("/foo/../../../bar"), "\\bar");
assert.strictEqual(path.win32.normalize("a//b//../b"), "a\\b");
assert.strictEqual(path.win32.normalize("a//b//./c"), "a\\b\\c");
assert.strictEqual(path.win32.normalize("a//b//."), "a\\b");
assert.strictEqual(path.win32.normalize("//server/share/dir/file.ext"), "\\\\server\\share\\dir\\file.ext");
assert.strictEqual(path.win32.normalize("/a/b/c/../../../x/y/z"), "\\x\\y\\z");
assert.strictEqual(path.win32.normalize("C:"), "C:.");
assert.strictEqual(path.win32.normalize("C:..\\abc"), "C:..\\abc");
assert.strictEqual(path.win32.normalize("C:..\\..\\abc\\..\\def"), "C:..\\..\\def");
assert.strictEqual(path.win32.normalize("C:\\."), "C:\\");
assert.strictEqual(path.win32.normalize("file:stream"), "file:stream");
assert.strictEqual(path.win32.normalize("bar\\foo..\\..\\"), "bar\\");
assert.strictEqual(path.win32.normalize("bar\\foo..\\.."), "bar");
assert.strictEqual(path.win32.normalize("bar\\foo..\\..\\baz"), "bar\\baz");
assert.strictEqual(path.win32.normalize("bar\\foo..\\"), "bar\\foo..\\");
assert.strictEqual(path.win32.normalize("bar\\foo.."), "bar\\foo..");
assert.strictEqual(path.win32.normalize("..\\foo..\\..\\..\\bar"), "..\\..\\bar");
assert.strictEqual(path.win32.normalize("..\\...\\..\\.\\...\\..\\..\\bar"), "..\\..\\bar");
assert.strictEqual(path.win32.normalize("../../../foo/../../../bar"), "..\\..\\..\\..\\..\\bar");
assert.strictEqual(path.win32.normalize("../../../foo/../../../bar/../../"), "..\\..\\..\\..\\..\\..\\");
assert.strictEqual(path.win32.normalize("../foobar/barfoo/foo/../../../bar/../../"), "..\\..\\");
assert.strictEqual(path.win32.normalize("../.../../foobar/../../../bar/../../baz"), "..\\..\\..\\..\\baz");
assert.strictEqual(path.win32.normalize("foo/bar\\baz"), "foo\\bar\\baz");
});
test("posix", () => {
assert.strictEqual(path.posix.normalize("./fixtures///b/../b/c.js"), "fixtures/b/c.js");
assert.strictEqual(path.posix.normalize("/foo/../../../bar"), "/bar");
assert.strictEqual(path.posix.normalize("a//b//../b"), "a/b");
assert.strictEqual(path.posix.normalize("a//b//./c"), "a/b/c");
assert.strictEqual(path.posix.normalize("a//b//."), "a/b");
assert.strictEqual(path.posix.normalize("/a/b/c/../../../x/y/z"), "/x/y/z");
assert.strictEqual(path.posix.normalize("///..//./foo/.//bar"), "/foo/bar");
assert.strictEqual(path.posix.normalize("bar/foo../../"), "bar/");
assert.strictEqual(path.posix.normalize("bar/foo../.."), "bar");
assert.strictEqual(path.posix.normalize("bar/foo../../baz"), "bar/baz");
assert.strictEqual(path.posix.normalize("bar/foo../"), "bar/foo../");
assert.strictEqual(path.posix.normalize("bar/foo.."), "bar/foo..");
assert.strictEqual(path.posix.normalize("../foo../../../bar"), "../../bar");
assert.strictEqual(path.posix.normalize("../.../.././.../../../bar"), "../../bar");
assert.strictEqual(path.posix.normalize("../../../foo/../../../bar"), "../../../../../bar");
assert.strictEqual(path.posix.normalize("../../../foo/../../../bar/../../"), "../../../../../../");
assert.strictEqual(path.posix.normalize("../foobar/barfoo/foo/../../../bar/../../"), "../../");
assert.strictEqual(path.posix.normalize("../.../../foobar/../../../bar/../../baz"), "../../../../baz");
assert.strictEqual(path.posix.normalize("foo/bar\\baz"), "foo/bar\\baz");
});
test("very long paths", () => {
// Regression test: buffer overflow with paths longer than PATH_SIZE
// This used to panic with "index out of bounds" because the buffer
// didn't account for the null terminator
for (const len of [4096, 10000, 50000, 98340, 100000]) {
const longPath = "a".repeat(len);
assert.strictEqual(path.normalize(longPath), longPath);
assert.strictEqual(path.normalize(longPath).length, len);
}
});
});