From c38da7dd66c7cd0f6a320faaf05b681e10fe06b7 Mon Sep 17 00:00:00 2001 From: olaservo Date: Thu, 19 Feb 2026 08:56:55 -0700 Subject: [PATCH] fix(filesystem): add symlink startup test and clarify catch block comment Follow-up to #3254 (symlinked allowed directories fix). - Add integration test in startup-validation.test.ts that spawns the server with a symlinked directory argument, verifying the startup code correctly stores both path forms - Clarify catch block comment documenting the design tradeoff when a directory doesn't exist at startup (only unresolved form is stored) Co-Authored-By: Claude Opus 4.6 --- .../__tests__/startup-validation.test.ts | 26 +++++++++++++++++++ src/filesystem/index.ts | 7 +++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/filesystem/__tests__/startup-validation.test.ts b/src/filesystem/__tests__/startup-validation.test.ts index 3be283df74..6c8b595012 100644 --- a/src/filesystem/__tests__/startup-validation.test.ts +++ b/src/filesystem/__tests__/startup-validation.test.ts @@ -84,6 +84,32 @@ describe('Startup Directory Validation', () => { expect(result.stderr).toContain('Error: None of the specified directories are accessible'); }); + // Test for macOS /tmp -> /private/tmp symlink issue (GitHub issue #3253) + // Verifies that the startup code stores both the symlink path and its + // resolved target in allowedDirectories, so requests through either form work. + it('should start successfully with symlinked allowed directory', async () => { + const actualDir = path.join(testDir, 'actual-target'); + await fs.mkdir(actualDir, { recursive: true }); + + const symlinkDir = path.join(testDir, 'symlink-dir'); + try { + await fs.symlink(actualDir, symlinkDir); + } catch (error) { + // Skip on systems without symlink permissions (e.g., restricted Windows) + if ((error as NodeJS.ErrnoException).code === 'EPERM') { + return; + } + throw error; + } + + // Start server with the symlink path (not the resolved path) + const result = await spawnServer([symlinkDir]); + + // Server should start without errors + expect(result.stderr).toContain('Secure MCP Filesystem Server running on stdio'); + expect(result.stderr).not.toContain('Error:'); + }); + it('should warn when path is not a directory', async () => { const filePath = path.join(testDir, 'not-a-directory.txt'); await fs.writeFile(filePath, 'content'); diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index a515df7c61..ae99d88dfa 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -59,8 +59,11 @@ let allowedDirectories = (await Promise.all( } return [normalizedResolved]; } catch (error) { - // If we can't resolve (doesn't exist), use the normalized absolute path - // This allows configuring allowed dirs that will be created later + // Directory doesn't exist yet - store only the unresolved path. + // This allows configuring allowed dirs that will be created later. + // Note: if this path later appears as a symlink, only the unresolved + // form will be in allowedDirectories. Full symlink support requires + // the directory to exist at startup so both forms can be resolved. return [normalizedOriginal]; } })