deno_std
deno_std copied to clipboard
std/path: utility to check/prevent exploit in paths by setting boundaries
Is your feature request related to a problem? Please describe.
A simple function that asserts a path is descending (i.e. not trying to go up ../), or that "virtually" change the root directory (similar to fsRoot from @std/http).
This is useful when dev are importing or resolving path dynamically from user-provided input, to ensure that they're not trying to leave unexpected boundaries.
Examples:
const { default: plugin } = await import(import.meta.resolve(`./plugins/${user_provided_plugin_name}.ts`))
const file = await Deno.open(`/data/images/${user_provided_input}`)
Side notes:
- as shown above, it isn't necessarly for http serving (probably dev would use
serveDirfrom@std/httpanyways) - having a standard and tested solution would increase the overall security of apps relying on
@stdas devs wouldn't have to manually implement it - it should be both posix/windows compatible, and cover any edge-case
- maybe it could be url compatible
- several implementations are offered below, I think it'd make sense to make it configurable
- sometimes you'd just want to throw to reject the user that tried to exploit a path provided input
- or maybe you'd like to "sandbox" the user inside a specific subtree, where it's not possible to go further up
Describe the solution you'd like
NB: examples below are just snippets, they don't cover any edge cases
1. Throws if using ascending fragments
function assertDescending(path: string) {
if (path.includes("../")
throw new RangeError("Not allowed to go above in tree directory")
return path
}
2. "Virtually" change the root directory
function chroot(root:string, path:string) {
const resolved = resolve(root, path)
return resolved.includes("..") ? root : path
}
3. A combination of both with configurable behavior
function assertsOrChroot(path:string, {root, throws}:{root:string, throws?:boolean}) {
if (throws)
return assertDescending(path)
return chroot(root, path)
}
Describe alternatives you've considered
relative, resolve, normalize helps, but you still need to implement manually the logic anyway
Could also copy the logic from @std/http but it's limited to posix and there is special handling for routing in the batch.
I don't think it's hard to implement, or to use combined use of functions to achieve it, but a single function would be way more convenient and less prone to errors leading to exploit.