mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix(glob): fix typo that caused patterns like .*/* to escape cwd boundary (#24939)
## Summary - Fixed a typo in `makeComponent` that incorrectly identified 2-character patterns starting with `.` (like `.*`) as `..` (DotBack) patterns - The condition checked `pattern[component.start] == '.'` twice instead of checking both characters at positions 0 and 1 - This caused patterns like `.*/*` to be parsed as `../` + `*`, making the glob walker traverse into parent directories Fixes #24936 ## Test plan - [x] Added tests in `test/js/bun/glob/scan.test.ts` that verify patterns like `.*/*` and `.*/**/*.ts` don't escape the cwd boundary - [x] Tests fail with system bun (bug reproduced) and pass with the fix - [x] All existing glob tests pass (169 tests) 🤖 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>
This commit is contained in:
@@ -1431,12 +1431,12 @@ pub fn GlobWalker_(
|
||||
if (component.len == 0) return null;
|
||||
|
||||
out: {
|
||||
if (component.len == 1 and pattern[component.start] == '.') {
|
||||
if (bun.strings.eqlComptime(pattern[component.start .. component.start + component.len], ".")) {
|
||||
component.syntax_hint = .Dot;
|
||||
has_relative_patterns.* = true;
|
||||
break :out;
|
||||
}
|
||||
if (component.len == 2 and pattern[component.start] == '.' and pattern[component.start] == '.') {
|
||||
if (bun.strings.eqlComptime(pattern[component.start .. component.start + component.len], "..")) {
|
||||
component.syntax_hint = .DotBack;
|
||||
has_relative_patterns.* = true;
|
||||
break :out;
|
||||
|
||||
@@ -665,6 +665,78 @@ describe("absolute path pattern", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/24936
|
||||
describe("glob scan should not escape cwd boundary", () => {
|
||||
test("pattern .*/* should not match parent directory via ..", async () => {
|
||||
// Create a directory structure where we can verify paths don't escape cwd
|
||||
const tempdir = tempDirWithFiles("glob-cwd-escape", {
|
||||
".hidden": {
|
||||
"file.txt": "hidden file content",
|
||||
},
|
||||
".dotfile": "dot file",
|
||||
"regular": {
|
||||
"file.txt": "regular file",
|
||||
},
|
||||
});
|
||||
|
||||
const glob = new Glob(".*/*");
|
||||
const entries = await Array.fromAsync(
|
||||
glob.scan({
|
||||
cwd: tempdir,
|
||||
onlyFiles: false,
|
||||
dot: true, // Need dot:true to match dotfiles/directories
|
||||
}),
|
||||
);
|
||||
|
||||
// All entries should be within the cwd - none should start with ../
|
||||
for (const entry of entries) {
|
||||
expect(entry.startsWith("../")).toBe(false);
|
||||
expect(entry.startsWith("..\\")).toBe(false);
|
||||
expect(entry.includes("/../")).toBe(false);
|
||||
expect(entry.includes("\\..\\")).toBe(false);
|
||||
}
|
||||
|
||||
// Should match .hidden/file.txt but not escape to parent
|
||||
expect(entries.sort()).toEqual([`.hidden${path.sep}file.txt`].sort());
|
||||
});
|
||||
|
||||
test("pattern .*/**/*.ts should not escape cwd", async () => {
|
||||
const tempdir = tempDirWithFiles("glob-cwd-escape-ts", {
|
||||
".config": {
|
||||
"settings.ts": "export default {}",
|
||||
"nested": {
|
||||
"deep.ts": "export const x = 1",
|
||||
},
|
||||
},
|
||||
"src": {
|
||||
"index.ts": "console.log('hi')",
|
||||
},
|
||||
});
|
||||
|
||||
const glob = new Glob(".*/**/*.ts");
|
||||
const entries = await Array.fromAsync(
|
||||
glob.scan({
|
||||
cwd: tempdir,
|
||||
onlyFiles: true,
|
||||
dot: true, // Need dot:true to match dotfiles/directories
|
||||
}),
|
||||
);
|
||||
|
||||
// All entries should be within the cwd
|
||||
for (const entry of entries) {
|
||||
expect(entry.startsWith("../")).toBe(false);
|
||||
expect(entry.startsWith("..\\")).toBe(false);
|
||||
expect(entry.includes("/../")).toBe(false);
|
||||
expect(entry.includes("\\..\\")).toBe(false);
|
||||
}
|
||||
|
||||
// Should match files in .config but not escape to parent
|
||||
expect(entries.sort()).toEqual(
|
||||
[`.config${path.sep}settings.ts`, `.config${path.sep}nested${path.sep}deep.ts`].sort(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("glob.scan wildcard fast path", async () => {
|
||||
test("works", async () => {
|
||||
const tempdir = tempDirWithFiles("glob-scan-wildcard-fast-path", {
|
||||
|
||||
Reference in New Issue
Block a user