Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions doc/api/path.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,29 @@ added: v0.11.2
* `path` {string}
* Returns: {boolean}

The `path.isAbsolute()` method determines if `path` is an absolute path.
The `path.isAbsolute()` method determines if the literal (non-resolved) `path` is an
absolute path. Therefore, it’s likely that you want to run it through `path.normalize()`
if your plan is to use it as a subpath in order to mitigate path traversals. For example:

```js
// Normalizing the subpath mitigates traversing above the mount directory.
const mySubpath = path.normalize('/foo/../..');
if (path.isAbsolute(mySubpath)) {
const myPath = path.join(MOUNT_DIR, mySubpath)
}
Copy link
Copy Markdown
Contributor

@aduh95 aduh95 Feb 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand why you would call path.isAbsolute at all.

Suggested change
The `path.isAbsolute()` method determines if the literal (non-resolved) `path` is an
absolute path. Therefore, it’s likely that you want to run it through `path.normalize()`
if your plan is to use it as a subpath in order to mitigate path traversals. For example:
```js
// Normalizing the subpath mitigates traversing above the mount directory.
const mySubpath = path.normalize('/foo/../..');
if (path.isAbsolute(mySubpath)) {
const myPath = path.join(MOUNT_DIR, mySubpath)
}
The `path.isAbsolute()` method determines if `path` is an absolute path. Being an
absolute path doesn't not guarentee it would be safe to treat as a subpath:
```js
const MOUNT_DIR = '/home/example/';
const userProvidedPath = '/foo/../../../etc/passwd';
console.log(path.isAbsolute(userProvidedPath)); // true
console.log(path.join(MOUNT_DIR, userProvidedPath)); // /etc/passwd
// Instead, call `path.normalize`:
console.log(path.join(MOUNT_DIR, path.normalize(`/${userProvidedPath}`))); // /home/example/etc/passwd

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flow control, e.g.

const mySubpath = path.normalize('/foo/../..')
if (!path.isAbsolute(mySubpath))
  throw 'FORBIDDEN'
const myPath = path.join(MOUNT_DIR, mySubpath)

I agree it‘s not needed.

Copy link
Copy Markdown
Contributor Author

@ericfortis ericfortis Feb 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, instead of documenting the behavior, a better approach could be adding path.isAbsoluteSubpath(). That way, when autocompleting, users can tell there's something not obvious about path.isAbsolute().

Something like,

function isAbsoluteSubpath(path) {
  const dpath = decodeURIComponent(path)
  if (!dpath.startsWith('/'))
    return false
  let nOutside = 0
  for (const part of dpath.split('/')) {
    if (part === '')
      continue
    if (part === '..')
      nOutside++
    else
      nOutside--
    if (nOutside > 0)
      return false
  }
  return true
}

function is(path, expected) {
  console.assert(expected === isAbsoluteSubpath(path), path)
}

;[
  '.',
  './',
  '..',
  '/..',
  '../',
  '/home//../../..',
  '/home/user/../../..'
].forEach(p => {
  is(p, false)
  is(encodeURIComponent(p), false)
})

;[
  '/',
  '/home/user/docs',
  '/home/user/./docs',
  '/home/user//docs'
].forEach(p => {
  is(p, true)
  is(encodeURIComponent(p), true)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the current behavior is confusing, let's update the doc to make them less confusing. Having clearer docs doesn't mean we can't add another API, that'd be a false dichotomy to think we can do one and not the other.

Would you like to take my suggestion so this PR can move forward?

```


If the given `path` is a zero-length string, `false` will be returned.

For example, on POSIX:

```js
path.isAbsolute('/foo/bar'); // true
path.isAbsolute('/baz/..'); // true
path.isAbsolute('qux/'); // false
path.isAbsolute('.'); // false
path.isAbsolute('/foo/bar'); // true
path.isAbsolute('/baz/..'); // true
path.isAbsolute('/baz/../..'); // true (doesn’t resolve)
path.isAbsolute('qux/'); // false
path.isAbsolute('.'); // false
```

On Windows:
Expand Down