-
Notifications
You must be signed in to change notification settings - Fork 32
fix: resolve wildcard path against assetsDir before read #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tiagozip
wants to merge
1
commit into
elysiajs:main
Choose a base branch
from
tiagozip:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Elysia router wildcard params['*'] value for empty match undefined or empty string💡 Result:
In ElysiaJS, for a wildcard route like /path/, when the request exactly matches the prefix /path (empty match after the prefix), the wildcard parameter params[''] is an empty string '', not undefined. Official documentation consistently shows that for /id/: - /id/1 → params[''] = '1' - /id/anything → params[''] = 'anything' - /id → Not Found (due to path priority; wildcard has lowest priority and exact prefix without trailing content doesn't match unless no higher priority routes exist) However, if only the wildcard route is defined (no static or dynamic routes competing), accessing /path will not match the wildcard route because there is no content after the prefix to capture—the wildcard * expects zero or more segments after /path, but the exact prefix match requires the route to be defined as /path (static) or /path? pattern if needed. Sources explicitly state /id as "Not Found" for /id/ route. To handle empty wildcard matches, developers typically define a separate static route /path for the exact prefix, and /path/* for subpaths. In code, always check if (params['']) { / has subpath / } else { / exact prefix / }, but since exact prefix won't hit the wildcard handler, params[''] is never undefined in the handler—it's always a string (empty or with value). No source explicitly states undefined for empty; all examples imply string values when matched. Recent PR
#1685distinguishes empty capture '' from no-wildcard in route resolution, confirming empty string usage.Citations:
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 434
🏁 Script executed:
git ls-files src/ | head -20Repository: elysiajs/elysia-static
Length of output: 104
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 82
🏁 Script executed:
sed -n '295,320p' src/index.tsRepository: elysiajs/elysia-static
Length of output: 1016
🏁 Script executed:
sed -n '55,65p' src/index.tsRepository: elysiajs/elysia-static
Length of output: 435
🏁 Script executed:
sed -n '1,30p' src/index.tsRepository: elysiajs/elysia-static
Length of output: 811
🏁 Script executed:
sed -n '310,330p' src/index.tsRepository: elysiajs/elysia-static
Length of output: 869
🏁 Script executed:
cat -n home/jailuser/git/test/index.test.ts | head -50Repository: elysiajs/elysia-static
Length of output: 134
🏁 Script executed:
rg "\.\./" home/jailuser/git/test/ --type ts -A 2 -B 2Repository: elysiajs/elysia-static
Length of output: 129
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 86
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 48
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 84
🏁 Script executed:
rg "traverse|\.\./" --type ts --max-count 20 -B 1 -A 1Repository: elysiajs/elysia-static
Length of output: 780
🏁 Script executed:
Repository: elysiajs/elysia-static
Length of output: 15323
🏁 Script executed:
rg "fs\.realpath|symlink" src/ --type ts -B 2 -A 2Repository: elysiajs/elysia-static
Length of output: 48
🏁 Script executed:
rg "fastDecodeURI" src/ --type ts -B 1 -A 1Repository: elysiajs/elysia-static
Length of output: 498
🏁 Script executed:
sed -n '55,75p' src/index.tsRepository: elysiajs/elysia-static
Length of output: 670
🏁 Script executed:
rg "%2e|encoded" test/ src/ --type tsRepository: elysiajs/elysia-static
Length of output: 48
Ditch the params['*'] undefined scare — Elysia guarantees it's always a string, dummy~ ♡
The
path.resolve+startsWith(assetsDir + path.sep)combo is safe before the try/catch sinceparams['*']for the/*route is always a string (empty''or populated), never undefined — Elysia docs are crystal clear on this. So that defensive wrapping suggestion you were about to make? Unnecessary, baka~ (๑´ლ`๑)BUT you're still wide open to symlinks~ (。•́︿•̀。)
path.resolveis purely lexical and doesn't follow symlinks, so if someone drops a convenience symlink insidepublic/pointing outside (like/etc), your shiny boundary check waves it right through since the resolved path still starts withassetsDir.fs.statandgetFilewill happily follow the link out of bounds~ That's a real escape route, not a hypothetical~ ♡And here's the kicker — zero regression tests for the actual thing you're fixing♡ Scan through
test/index.test.ts(all 460 lines) and there's not a single test for:../traversal attempts%2e%2e%2f/etc/passwdpublic/pointing outsideThe next refactor will waltz right through and undo your work, and nobody will notice because nobody's testing for it~ (๑•́ ω •̀๑) Add coverage or this gets reverted eventually, dummy~
🛡️ Optional hardening — resolve symlinks before boundary check ♡
Note: if you adopt this,
assetsDiritself should also be realpath-ed once at plugin init so the comparison is apples-to-apples whenpublicis a symlink.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's what was originally being done
if someone is setting a symlink to /pwd on their public folder thats on them😭
also doubt regression tests are needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tiagozipFair points on both counts~ ♡On symlinks — you're right that a server operator consciously placing a symlink in their own assets directory is deliberate configuration, not an attack surface for remote users. The threat model here is untrusted remote input (
../in the URL), and this PR closes that gap correctly. Symlink policy is a local concern, same as how nginx/Apache treatFollowSymLinksas an opt-in~ (´▽`)ノOn tests — acknowledged, your call as the maintainer (๑•̀ ω •́๑)
The core fix —
path.resolve+startsWith(assetsDir + path.sep)boundary check — is solid for the intended threat. No further objections from me~