From 0ff9079a87cc6f66dbfc704faf9dcbc60bc2625c Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Fri, 22 May 2026 14:22:22 -0500 Subject: [PATCH] fix(mcp): make session-marker symlink resistance work on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O_NOFOLLOW is undefined on Windows (libuv ignores it), so the bitwise-OR silently dropped it and markSessionConsulted would follow a pre-planted symlink at the tmp marker path — the CWE-59 gap #280 closed on POSIX but not Windows. Add a cross-platform lstatSync isSymbolicLink() refuse-check before openSync (O_NOFOLLOW stays as the atomic, TOCTOU-free guard on POSIX). The existing Session-marker-symlink-resistance test now passes on Windows. Refs #280 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/mcp/tools.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index deb8dfdc..16df373d 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -11,6 +11,7 @@ import { constants as fsConstants, closeSync, existsSync, + lstatSync, openSync, readFileSync, writeSync, @@ -224,6 +225,16 @@ function markSessionConsulted(sessionId: string): void { try { const hash = createHash('md5').update(sessionId).digest('hex').slice(0, 16); const markerPath = join(tmpdir(), `codegraph-consulted-${hash}`); + // Refuse to follow a pre-planted symlink at the marker path (CWE-59). + // O_NOFOLLOW (below) is the atomic, TOCTOU-free guard on POSIX, but it is + // `undefined` on Windows (libuv ignores it), so the bitwise-OR silently + // drops it and openSync would follow the link. This lstat check closes that + // gap cross-platform; ENOENT (path is free) falls through to create it. + try { + if (lstatSync(markerPath).isSymbolicLink()) return; + } catch { + // No existing entry (or stat failed) — nothing to refuse; proceed. + } // O_NOFOLLOW makes openSync throw ELOOP if markerPath is already a symlink. // O_CREAT + O_TRUNC keep the original "create-or-overwrite" semantics, and // mode 0o600 prevents readback by other local users (the marker payload is