# fix\(server\): refuse symlinked theme files on Windows too · PR \#9580 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/9580)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: windows-tests/theme-nofollow

Target branch: windows-tests/server-paths

Head commit: ad9b7df33d4e7ac221ddc39f20377f7d68825b16

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/9580)

[Commits](https://git.cafe/pingdotgg/t3code/pull/9580/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/9580/diffs?format=markdown)

## Description

---


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](https://github.com/pingdotgg/t3code/pull/9564); the manual Windows lane comes from [#9538](https://github.com/pingdotgg/t3code/pull/9538).

Model: Claude Fable 5 in Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. -->
> [!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's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized ad9b7df.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->

<!-- CURSOR_SUMMARY -->
---

> [!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.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ad9b7df33d4e7ac221ddc39f20377f7d68825b16. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->


