Skip to content

feat: add ALLOWED_PATHS environment variable to restrict file system access#350

Open
feraudet wants to merge 1 commit intositeboon:mainfrom
feraudet:feature/allowed-paths
Open

feat: add ALLOWED_PATHS environment variable to restrict file system access#350
feraudet wants to merge 1 commit intositeboon:mainfrom
feraudet:feature/allowed-paths

Conversation

@feraudet
Copy link

@feraudet feraudet commented Jan 28, 2026

Summary

Implements #348 - Add ALLOWED_PATHS environment variable to restrict which directories can be accessed through the UI.

Changes

  • Add getAllowedPaths() function in server/routes/projects.js
  • Filter projects in getProjects() based on allowed paths
  • Restrict /api/browse-filesystem endpoint to allowed paths
  • Add startup logging for ALLOWED_PATHS configuration

Configuration

# Single directory
ALLOWED_PATHS=/Users/username/projects/my-app

# Multiple directories
ALLOWED_PATHS=/home/user/client-a,/home/user/client-b

Technical Notes

Uses a getter function getAllowedPaths() instead of a constant to work around ES Modules hoisting (imports run before .env is loaded).

Test plan

  • Set ALLOWED_PATHS to a single directory - only that project appears
  • Set multiple paths - only those projects appear
  • Remove variable - all projects visible (default behavior)
  • Try browsing outside allowed paths - returns 403

Closes #348

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added environment-based path restrictions to control which directories users can access in the workspace
    • API endpoints now report restriction status and list accessible paths in responses
  • Bug Fixes

    • Improved shell compatibility for macOS environments

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

Warning

Rate limit exceeded

@feraudet has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 18 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Introduces ALLOWED_PATHS environment variable to restrict filesystem and workspace access to specific directories. Enforces path restrictions across the file browser API, project listing, and workspace validation layers with early 403 rejection when access falls outside allowed paths.

Changes

Cohort / File(s) Summary
Core ALLOWED_PATHS Logic
server/routes/projects.js
Added getAllowedPaths() export to read and cache comma-separated paths from environment variable. Integrated allowed-path validation into validateWorkspacePath with early rejection before symlink checks. Exports deprecated ALLOWED_PATHS constant for backward compatibility.
Filesystem Browsing Enforcement
server/index.js
Extended /api/browse-filesystem endpoint to enforce ALLOWED_PATHS: defaults target path to first allowed path when none provided, validates target is within an allowed path (returns 403 with allowed paths listing if outside), augments response with restricted and allowedPaths fields. Updated macOS shell integration to use absolute path /bin/bash.
Project Filtering
server/projects.js
Added import of getAllowedPaths() and filtering logic in getProjects to skip projects whose paths fall outside allowed paths when ALLOWED_PATHS is configured.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops with glee,
Paths now cordoned, wild and free!
ALLOWED_PATHS shall guard the way,
No sneaky symlinks lead astray—
Secure and neat, hip-hip-hooray! 🌿

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning One out-of-scope change detected: updating WebSocket shell to use /bin/bash instead of bash for macOS. While potentially beneficial, this fix is not mentioned in issue #348 requirements. Remove or document the /bin/bash macOS PTY change separately, as it is unrelated to ALLOWED_PATHS functionality and should be addressed in a dedicated issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding ALLOWED_PATHS environment variable to restrict file system access, which is the core objective.
Linked Issues check ✅ Passed All core coding requirements from issue #348 are implemented: ALLOWED_PATHS support in projects filtering, workspace validation, /api/browse-filesystem restrictions with 403 responses, startup logging, and comma-separated path parsing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/index.js (1)

492-573: Symlink escape possible in /api/browse-filesystem allowed-path check.
path.resolve is lexical; a symlink inside an allowed directory can point outside and still pass the check. Resolve both the target and allowed paths with realpath before comparison.

🔒 Suggested fix
-        if (allowedPaths && allowedPaths.length > 0) {
-            const isAllowed = allowedPaths.some(allowedPath => {
-                const resolved = path.resolve(allowedPath);
-                return targetPath === resolved || targetPath.startsWith(resolved + path.sep);
-            });
+        if (allowedPaths && allowedPaths.length > 0) {
+            const resolvedTarget = await fs.promises.realpath(targetPath)
+                .catch(() => path.resolve(targetPath));
+            let isAllowed = false;
+            for (const allowedPath of allowedPaths) {
+                const resolvedAllowed = await fs.promises.realpath(allowedPath)
+                    .catch(() => path.resolve(allowedPath));
+                if (resolvedTarget === resolvedAllowed || resolvedTarget.startsWith(resolvedAllowed + path.sep)) {
+                    isAllowed = true;
+                    break;
+                }
+            }
             if (!isAllowed) {
                 return res.status(403).json({
                     error: `Access restricted to: ${allowedPaths.join(', ')}`,
                     allowedPaths: allowedPaths
                 });
             }
+            targetPath = resolvedTarget;
         }
🤖 Fix all issues with AI agents
In `@server/projects.js`:
- Around line 428-438: Extract the path-check logic into a shared isAllowedPath
helper (used in the Claude-discovered block that currently checks
actualProjectDir and in the manual-projects block later) that: 1) reads
ALLOWED_PATHS via getAllowedPaths(), 2) resolves both the candidate project path
and each allowedPath to their real paths (e.g., using fs.realpathSync or async
equivalent) to prevent symlink escape, and 3) returns true only if the real
project path equals or is a child of a real allowedPath; replace the inline
checks in the current actualProjectDir block and the manual-projects addition to
call isAllowedPath and skip projects when it returns false.

In `@server/routes/projects.js`:
- Around line 43-44: Remove the dead exported constant ALLOWED_PATHS: delete the
export declaration for ALLOWED_PATHS and any associated comment so the module
only exposes the real accessors (getAllowedPaths) and uses
process.env.ALLOWED_PATHS; ensure no other code relies on the removed symbol and
run tests to confirm nothing imports ALLOWED_PATHS.

…access

Implements path-based access restrictions for enhanced security:

- Add getAllowedPaths() function to read and cache ALLOWED_PATHS env var
- Add isPathAllowed() function with symlink protection using realpath
- Update /api/browse-filesystem to enforce ALLOWED_PATHS restrictions
- Filter projects in getProjects() based on allowed paths
- Add startup logging for ALLOWED_PATHS configuration
- Default to first allowed path when browsing without explicit path

The ALLOWED_PATHS variable accepts comma-separated directory paths.
When set, only projects within these paths are accessible.

Closes siteboon#348

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@feraudet feraudet force-pushed the feature/allowed-paths branch from 3776f19 to 7df4dfd Compare January 28, 2026 10:40
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.

feat: add ALLOWED_PATHS environment variable to restrict file system access

1 participant