Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

### Security and Correctness

- **Security:** Resolve symlink aliases component-by-component before applying later `..` segments in the exported root-path validator, and validate raw `root()` inputs before normalized I/O, preventing alias-dependent traversal from being approved under a different lexical path.
- **Security — `resolveRootPath()` / `resolveRootPathSync()`:** Published releases through 0.4.7 validated a lexically normalized path spelling, so a caller-supplied path traversing an in-root symlink could pass validation while resolving outside the root. Version 0.5 fixes this with component-wise alias resolution. `root()` handles were **not** affected: their operations have contained this case since `5ddca80`, so exposure is limited to direct users of these two exported helpers.
- Prefer macOS 15.4's `O_RESOLVE_BENEATH` for native opens, retain the guarded component walk on older kernels, and apply an `F_GETPATH` post-open escape detector to both routes without claiming rename-race atomicity.
- Report open containment explicitly: native `openBeneath()` returns `{ fd, containment }` with `kernel-atomic` on Linux and `best-effort` on macOS/Windows, while JavaScript root open/read/writable results report `best-effort`.
- Serialize async `jsonStore` writes and read-modify-write updates in-process by canonical store path before taking the cross-process sidecar lock, preventing overlapping `write`, `update`, and `updateOr` calls from silently losing updates; reject nested same-path mutations with typed `store-reentrant-update` errors. Thanks @yetval for reporting this.
Expand Down
5 changes: 5 additions & 0 deletions docs/migrating-to-0.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ optional.
import fs-safe, but native-only features and missing JS archive decoders fail
with actionable errors.

If you call `resolveRootPath()` or `resolveRootPathSync()` directly, upgrade to
0.5: versions through 0.4.7 could approve an in-root symlink traversal that
resolved outside the root. `root()` handles were not affected. See the
[affected versions and exposure](security-model.md#affected-versions-exposure).

## 2. Replace Python helper configuration

Change startup configuration before the first filesystem operation:
Expand Down
13 changes: 13 additions & 0 deletions docs/security-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@

The same shape exists in other languages: Go's [`os.Root` / `OpenInRoot`](https://go.dev/blog/osroot) and Rust's [`cap-std`](https://github.com/bytecodealliance/cap-std) both expose a root handle whose operations refuse to escape it. `fs-safe` is the Node-side equivalent: a single `root()` capability that carries the boundary across every read, write, move, and remove, instead of leaving each call site to redo `path.resolve(...).startsWith(...)` and hope.

## Affected versions / exposure

In published releases through 0.4.7, the exported `resolveRootPath()` and
`resolveRootPathSync()` helpers validated a lexically normalized path spelling.
A caller-supplied path traversing an in-root symlink could therefore pass
validation while resolving outside the root. Version 0.5 fixes this with
component-wise alias resolution, resolving each alias before applying later
path components.

`root()` handles were **not** affected: their operations have contained this
case since `5ddca80`. Exposure is limited to consumers that call
`resolveRootPath()` or `resolveRootPathSync()` directly.

## Threat model

You hand a `root()` boundary to a piece of code that takes caller-controlled relative paths. The library defends against a caller that:
Expand Down
Loading