mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
## 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>
89 lines
3.6 KiB
JavaScript
89 lines
3.6 KiB
JavaScript
import { describe, test } from "bun:test";
|
|
import assert from "node:assert";
|
|
import path from "node:path";
|
|
|
|
describe("path.relative", () => {
|
|
test("general", () => {
|
|
const failures = [];
|
|
|
|
const relativeTests = [
|
|
[
|
|
path.win32.relative,
|
|
// Arguments result
|
|
[
|
|
["c:/blah\\blah", "d:/games", "d:\\games"],
|
|
["c:/aaaa/bbbb", "c:/aaaa", ".."],
|
|
["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"],
|
|
["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""],
|
|
["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"],
|
|
["c:/aaaa/", "c:/aaaa/cccc", "cccc"],
|
|
["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"],
|
|
["c:/aaaa/bbbb", "d:\\", "d:\\"],
|
|
["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""],
|
|
["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"],
|
|
["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."],
|
|
["C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"],
|
|
["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"],
|
|
["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"],
|
|
["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"],
|
|
["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."],
|
|
["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"],
|
|
["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"],
|
|
["C:\\baz-quux", "C:\\baz", "..\\baz"],
|
|
["C:\\baz", "C:\\baz-quux", "..\\baz-quux"],
|
|
["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"],
|
|
["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"],
|
|
["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"],
|
|
["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"],
|
|
],
|
|
],
|
|
[
|
|
path.posix.relative,
|
|
// Arguments result
|
|
[
|
|
["/var/lib", "/var", ".."],
|
|
["/var/lib", "/bin", "../../bin"],
|
|
["/var/lib", "/var/lib", ""],
|
|
["/var/lib", "/var/apache", "../apache"],
|
|
["/var/", "/var/lib", "lib"],
|
|
["/", "/var/lib", "var/lib"],
|
|
["/foo/test", "/foo/test/bar/package.json", "bar/package.json"],
|
|
["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."],
|
|
["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"],
|
|
["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"],
|
|
["/baz-quux", "/baz", "../baz"],
|
|
["/baz", "/baz-quux", "../baz-quux"],
|
|
["/page1/page2/foo", "/", "../../.."],
|
|
],
|
|
],
|
|
];
|
|
relativeTests.forEach(test => {
|
|
const relative = test[0];
|
|
test[1].forEach(test => {
|
|
const actual = relative(test[0], test[1]);
|
|
const expected = test[2];
|
|
if (actual !== expected) {
|
|
const os = relative === path.win32.relative ? "win32" : "posix";
|
|
const message = `path.${os}.relative(${test
|
|
.slice(0, 2)
|
|
.map(JSON.stringify)
|
|
.join(",")})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
|
|
failures.push(`\n${message}`);
|
|
}
|
|
});
|
|
});
|
|
assert.strictEqual(failures.length, 0, failures.join(""));
|
|
});
|
|
|
|
test("very long paths", () => {
|
|
// Regression test: buffer overflow with very long paths
|
|
// This used to panic because the buffer didn't account for the null terminator
|
|
const longPath1 = "/home/" + "a".repeat(50000);
|
|
const longPath2 = "/home/" + "b".repeat(50000);
|
|
const result = path.relative(longPath1, longPath2);
|
|
// Should return something like "../bbb...bbb"
|
|
assert.ok(result.startsWith(".."));
|
|
assert.ok(result.includes("b"));
|
|
});
|
|
});
|