Skip to content

fix(runtime): decide path containment with relative(), not a string prefix - #518

Merged
DavidBabinec merged 1 commit into
mainfrom
fix/path-containment-separator
Sep 10, 2026
Merged

fix(runtime): decide path containment with relative(), not a string prefix#518
DavidBabinec merged 1 commit into
mainfrom
fix/path-containment-separator

Conversation

@DavidBabinec

Copy link
Copy Markdown
Contributor

The bug

The runtime package server tested containment with absPath.startsWith(${nodeModulesDir}/). The separator is hard-coded to POSIX, so on Windows hosts, where resolve() yields backslashes, the check was false for every legitimate path and the endpoint 404'd all runtime package assets. Windows is a supported target: the release workflow ships a windows-x64 server build.

It fails closed, so this is an availability bug rather than a bypass. The code is wrong either way.

The fix

Containment now goes through a shared isPathWithin helper that decides with path.relative(), which is separator-correct on both platforms. assertPathWithin delegates to it, and the .. test compares a whole segment so a child legitimately named ..foo is no longer read as an escape.

The same sweep found the inverse spelling in virtualSiteWorkspace, a prefix check with no separator at all, which would accept a sibling directory whose name merely begins with the root. Routed through the same helper.

Reported as GHSA-hwp9-vc7h-gvvf.

Verification

bun test   # 6851 pass, 0 fail
bun run build && bun run lint   # clean

Notes

New unit tests cover the sibling-prefix and ..foo cases, which are observable on POSIX. The Windows separator behaviour itself comes from path.relative() being platform-native.

…refix

The runtime package server tested containment with
absPath.startsWith(`${nodeModulesDir}/`). The separator is hard-coded to
POSIX, so on Windows hosts, where resolve() yields backslashes, the check
was false for every legitimate path and the endpoint 404'd all runtime
package assets. It fails closed, so this is an availability bug rather
than a bypass, but the code is wrong either way.

Containment now goes through a shared isPathWithin helper that decides
with path.relative(), which is separator-correct on both platforms.
assertPathWithin delegates to it, and the '..' test compares a whole
segment so a child legitimately named '..foo' is no longer read as an
escape.

The same sweep found the inverse spelling in virtualSiteWorkspace, a
prefix check with no separator at all, which would accept a sibling
directory whose name merely begins with the root. Routed through the same
helper.

Reported as GHSA-hwp9-vc7h-gvvf.
@DavidBabinec
DavidBabinec merged commit ef4abbf into main Sep 10, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant