Skip to content

fix(webui): replace statSync with lstatSync in workspace /raw and /read endpoints (issue #15) - #35

Merged
labtgbot merged 1 commit into
mainfrom
issue-15-webui-symlink-bypass
Jun 15, 2026
Merged

fix(webui): replace statSync with lstatSync in workspace /raw and /read endpoints (issue #15)#35
labtgbot merged 1 commit into
mainfrom
issue-15-webui-symlink-bypass

Conversation

@xdevrobot

Copy link
Copy Markdown
Collaborator

Summary

Fixes CWE-377 / CWE-22 (Symlink TOCTOU Bypass) — The WebUI workspace /raw and /read endpoints used statSync which follows symlinks. An attacker could create a symlink in the workspace pointing to arbitrary host files (e.g., /etc/shadow, ~/.ssh/id_rsa), bypassing the workspace boundary enforced by validatePath's lstatSync check.

Root Cause

TOCTOU race condition:

  1. validatePath passes with lstatSymlink (symlinks not yet present)
  2. Attacker swaps file with symlink
  3. statSync in /raw or /read follows the symlink → serves arbitrary file contents

Changes

src/webui/routes/workspace.ts

  • Replaced statSync import with lstatSync from node:fs
  • /raw endpoint: use lstatSync instead of statSync + explicit isSymbolicLink() rejection
  • /read endpoint: use lstatSync instead of statSync + explicit isSymbolicLink() rejection
  • listDir helper: use lstatSync instead of statSync
  • getWorkspaceStats helper: use lstatSync instead of statSync

src/webui/tests/workspace-raw.test.ts

  • Updated mock from statSync to lstatSync
  • All existing tests updated to include isSymbolicLink: () => false in lstatSync mock returns
  • Added symlink rejection test for /raw endpoint (403 on isSymbolicLink)
  • Added new describe block for /read endpoint with:
    • Symlink rejection test (403 on isSymbolicLink)
    • Regular file read test (200 with correct content)

Security Impact

  • Prevents symlink-based path traversal via TOCTOU race
  • Defense in depth: validatePath already blocks symlinks at validation time, but the endpoints now independently verify as well
  • All 4 statSync usages in workspace.ts now use lstatSync

Closes #15

🤖 Generated with Claude Code

…ad endpoints (issue #15)

Prevents CWE-377 symlink TOCTOU bypass — statSync follows symlinks,
allowing an attacker to swap a validated file with a symlink after the
validatePass check. lstatSymlink does not follow symlinks, and an
explicit isSymbolicLink() check is added as a second guard.
@labtgbot
labtgbot merged commit 1a126bc into main Jun 15, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HIGH][CWE-377] WebUI /raw and /read endpoints use statSync instead of lstatSync symlink bypass

2 participants