std/path: utility to check/prevent exploit in paths by setting boundaries
- Dominant language
- TypeScript
- Stars
- 3.6k
- Forks
- 681
- PR merge metrics
- No merged PRs in 30d
Description
**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:
```ts
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 `serveDir` from `@std/http` anyways)
- having a standard and tested solution would increase the overall security of apps relying on `@std` as 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
```ts
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
```ts
function chroot(root:string, path:string) {
const resolved = resolve(root, path)
return resolved.includes("..") ? root : path
}
```
### 3. A combination of both with configurable behavior
```ts
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.
Contributor guide
Assessment
This issue has not been assessed yet.