fix(server): refuse symlinked theme files on Windows too#9580

merged1
windows-tests/server-paths
windows-tests/theme-nofollow
openedSep 4, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 3d ago

readThemeFileGuarded relied on O_NOFOLLOW to reject a symlinked theme file. Windows has no such flag: the constant is undefined, OR-ing it in is a no-op, and the open followed the link, so a symlink planted in the themes directory published whatever it pointed at. Check with lstat first where the flag is missing; the descriptor-bound checks after the open still narrow what a swap in that window can reach.

Part of the Windows test-suite stack rooted at #9564; the manual Windows lane comes from #9538.

Model: Claude Fable 5 in Claude Code.

πŸ€– Generated with Claude Code

Note

Reject symlinked theme files on platforms without O_NOFOLLOW in readThemeFileGuarded

Adds an lstatSync pre-open check that returns null for symlinked theme files on platforms like Windows where O_NOFOLLOW is unavailable. Unavailable O_NOFOLLOW and O_NONBLOCK constants are now substituted with zero instead of causing open failures.

Macroscope summarized ad9b7df.


Note

Low Risk Small, localized hardening of local theme file reads with no changes to auth, APIs, or core business logic.

Overview readThemeFileGuarded no longer follows symlinked theme files on Windows, where O_NOFOLLOW and O_NONBLOCK are missing and were previously OR’d into openSync as no-ops.

When O_NOFOLLOW is undefined, the path is checked with lstatSync first and symlink targets are rejected by returning null, matching Unix behavior from the open flag. Open flags now use ?? 0 so undefined constants do not break the open call. Comments note the small TOCTOU window on Windows and that post-open fstat checks still limit what gets read.

Reviewed by Cursor Bugbot for commit ad9b7df33d4e7ac221ddc39f20377f7d68825b16. Bugbot is set up for automated code reviews on this repo. Configure here.

Synced from GitHub

Thread transfer impact

βœ… Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wireβ€”13.6 KiBβ€”15.1 KiBβœ…
CodexThread snapshot wireβ€”7.0 KiBβ€”7.3 KiBβœ…
CodexLive turn WebSocket wireβ€”6.5 KiBβ€”7.8 KiBβœ…
CodexLive turn WebSocket decodedβ€”57.0 KiBβ€”66.4 KiBβœ…
CodexLive turn messagesβ€”9β€”21βœ…
ClaudeTotal thread wireβ€”13.8 KiBβ€”15.1 KiBβœ…
ClaudeThread snapshot wireβ€”7.0 KiBβ€”7.3 KiBβœ…
ClaudeLive turn WebSocket wireβ€”6.7 KiBβ€”7.8 KiBβœ…
ClaudeLive turn WebSocket decodedβ€”59.3 KiBβ€”66.4 KiBβœ…
ClaudeLive turn messagesβ€”10β€”21βœ…

Baseline: unavailable Β· PR result: ad9b7df Β· Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable β€” This single-file fix hardens theme reads against symlink-based arbitrary file exposure on Windows and changes server behavior for symlinked theme paths. Because the change has security implications, it warrants human review despite its narrow scope.

No code changes detected at ad9b7df. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

Added 1 commit

  • fix(server): refuse symlinked theme files on Windows tooad9b7df