Skip to content

fix: enforce symlink-aware filesystem roots - #326

Open
antfubot wants to merge 4 commits into
mainfrom
fix/symlink-containment
Open

fix: enforce symlink-aware filesystem roots#326
antfubot wants to merge 4 commits into
mainfrom
fix/symlink-containment

Conversation

@antfubot

@antfubot antfubot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Why

Static serving and asset RPC operations proved containment only from normalized path strings. Filesystem operations then follow symlinks, so a symlink inside an allowed root could redirect reads, writes, deletes, renames, or editor-opening outside that root. This adds canonical (symlink-resolved) containment checks on top of the existing lexical guards, closing deterministic, pre-existing symlink escapes.

Implements plans/007-enforce-symlink-containment.md.

What

  • Static serving (serve-static.ts): canonicalizes the served root once per handler and requires every candidate file's canonical path to stay beneath it (direct hits, index candidates, .html/.htm extension candidates, and SPA fallback). An escaping symlink now reads as a 404 miss; an in-root symlink whose canonical target stays inside the root still serves.
  • Asset reads (read-text, read-image-meta): allow an in-root symlink only when its canonical target remains inside the canonical managed root.
  • Asset mutations (upload, mkdir, rename, delete): reject every pre-existing symlink path component (including in-root ones), walking existing components before creation and re-checking after directory creation and immediately before the mutating I/O.
  • Scanner (list): configures the glob not to follow symbolic links and omits symlink entries from returned AssetInfo.
  • Open service: applies canonical containment to allowed-root validation — allows canonical in-root targets, rejects canonical escapes. Reuses the existing DP_ASSETS_0001 / DS_OPEN_0002 diagnostics.

Scope & threat model

These canonical checks close pre-existing symlink escapes; they do not defeat a concurrent local process replacing path components between validation and I/O (documented in the plan's maintenance notes). Remote asset paths, upload quotas/content validation, and filesystem sandboxing outside configured roots are out of scope.

Verification

Targeted suites, typechecks, lint, and the API snapshot guard pass for the in-scope packages (devframe, @devframes/plugin-assets, @devframes/service-open), including new regression tests for final-component and ancestor-directory symlinks across both static handlers, every asset read/mutation family, and the open service. New symlink tests skip on Windows (where symlink creation needs privileges hosted CI lacks). The repo-wide pnpm build gate fails only on unrelated, out-of-scope packages due to sandbox environment limits (a write-only leftover artifact in the git plugin's Next.js output; resource-limited exits in hub-ui/json-render-ui).

This PR was created with the help of an agent.

@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
devframe Skipped Skipped Sep 1, 2026 8:15am UTC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant