denoland / denoland/std

std/path: utility to check/prevent exploit in paths by setting boundaries

Open
#6,079 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.