Skip to content

fix(bash): normalize allowlist path segments#307

Open
ThunderTr77 wants to merge 3 commits into
TouchAI-org:mainfrom
ThunderTr77:fix/bash-allowlist-path-normalization
Open

fix(bash): normalize allowlist path segments#307
ThunderTr77 wants to merge 3 commits into
TouchAI-org:mainfrom
ThunderTr77:fix/bash-allowlist-path-normalization

Conversation

@ThunderTr77

Copy link
Copy Markdown
Contributor

Summary

  • Normalize Bash tool working directory paths by resolving . and .. segments before comparing them with the allowed-directory list.
  • Keep the existing allowlist behavior for matching the exact allowed directory or a child directory.
  • Add a regression test covering a parent-segment escape attempt.

Related issue or RFC

Related to https://github.com/TouchAI-org/TouchAI

AI assistance disclosure

  • Tool(s) used: local development assistant
  • Scope of assistance: bug discovery, test-first patch, local verification, and PR preparation
  • Human review or rewrite performed: reviewed the diff and command outputs before submission
  • Architecture or boundary impact: no new boundary or architecture changes

Testing evidence

  • corepack pnpm --filter @touchai/desktop exec vitest run --configLoader runner tests/services/BuiltInToolService/tools/bash/helper.test.ts - failed before the fix, then passed after the fix.
  • corepack pnpm --filter @touchai/desktop exec vitest run --configLoader runner tests/services/BuiltInToolService/tools/bash - passed, 56 tests.
  • corepack pnpm --dir apps/desktop exec eslint src/services/BuiltInToolService/tools/bash/helper.ts tests/services/BuiltInToolService/tools/bash/helper.test.ts - passed.
  • corepack pnpm --dir apps/desktop exec prettier --check src/services/BuiltInToolService/tools/bash/helper.ts tests/services/BuiltInToolService/tools/bash/helper.test.ts - passed.
  • corepack pnpm --dir apps/desktop run test:typecheck - passed.
  • Full pnpm test:pr was not run locally because the direct pnpm shim is not available on this machine; focused checks were run through corepack pnpm.

Risk notes

  • AgentService, runtime, MCP, or schema impact: none.
  • database baseline or migration impact: none.
  • release or packaging impact: none.

Screenshots or recordings

Not applicable; tool logic change only.

Checklist

  • The PR title follows Conventional Commits and is valid for squash merge.
  • This PR is either ready for review or explicitly marked as a Draft PR.
  • I did not use [WIP] or similar title prefixes.
  • If AI materially assisted this PR, I disclosed the tools and scope and I personally reviewed every affected change.
  • I can explain the why, what, and how of this change without relying on an AI tool.
  • If this touches AgentService, runtime, MCP, or schema boundaries, there is an accepted RFC.
  • If this changes architecture or adds a new cross-boundary abstraction, there is an accepted RFC.
  • I ran pnpm test:pr for this code PR, or this is a docs-only change.
  • If I changed Rust behavior or tests, I reviewed pnpm test:coverage:rust or relied on CI coverage evidence.
  • If I changed desktop startup/window/search/popup/settings/E2E paths, I ran pnpm test:e2e locally or documented why CI is the first valid proof.
  • I added tests or explained why tests are not appropriate.
  • I updated docs when behavior changed.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @ThunderTr77, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bf978132-1cc6-4da8-a14d-4ec426d1218f

📥 Commits

Reviewing files that changed from the base of the PR and between a62fba5 and a4a534d.

📒 Files selected for processing (2)
  • apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts
  • apps/desktop/tests/services/BuiltInToolService/tools/bash/helper.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: CodeQL (rust)
  • GitHub Check: Desktop E2E Smoke (Windows)
  • GitHub Check: Frontend Tests
  • GitHub Check: Rust Checks
  • GitHub Check: Frontend Quality
🔇 Additional comments (2)
apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts (1)

31-37: LGTM!

apps/desktop/tests/services/BuiltInToolService/tools/bash/helper.test.ts (1)

112-122: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved Windows path normalization: trims input, standardizes separators, normalizes drive-letter and root semantics, collapses redundant segments, and resolves "."/ ".." so paths are consistently normalized.
    • Strengthened working-directory validation to reject paths that escape the allowed scope.
  • Tests

    • Added tests covering working-directory validation scenarios that attempt to escape allowed directories.

Walkthrough

normalizeDirectoryPath now performs full Windows-aware path normalization (drive prefixes, backslashes, collapse separators, resolve ./..) and tests were added to ensure resolveCommandContext rejects working directories that escape the allowed scope via parent segments.

Changes

Path Traversal Protection via Normalization

Layer / File(s) Summary
Path normalization enhancement
apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts
normalizeDirectoryPath expanded from simple slash replacement to full Windows-aware normalization: trims input, converts / to \, strips trailing separators, detects drive-letter/prefix and root semantics, lowercases the non-prefix body, collapses redundant separators, and resolves . and .. segments.
Path traversal security tests
apps/desktop/tests/services/BuiltInToolService/tools/bash/helper.test.ts
Two new Vitest cases assert resolveCommandContext rejects workingDirectory values that escape allowedWorkingDirectories (e.g., D:/allowed/../other and ../../foo) with “Working directory is outside the allowed scope”.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hop through slashes, dots, and drives,
Sniffing where the safe path lives,
I fold up .. and tidy \,
So stray paths cannot run or shove —
Securely home each working dir thrives.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows Conventional Commits format and accurately describes the main change: normalizing path segments in the bash tool's allowlist path handling.
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, explaining the path normalization fix, related testing, and providing proper AI assistance disclosure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts`:
- Around line 31-38: The normalizeDirectoryPath function incorrectly collapses
consecutive ".." in relative paths; update its ".." handling so it only pops a
previous segment when resolvedSegments is non-empty and the last resolved
segment is not another ".." (i.e., change the condition from
resolvedSegments.length > 0 to resolvedSegments.length > 0 &&
resolvedSegments[resolvedSegments.length - 1] !== '..'); otherwise (and when no
prefix/root) push the ".." segment as before. Ensure this change in
normalizeDirectoryPath (and re-run tests for isWithinAllowedDirectory which
relies on it) preserves absolute/path-with-prefix behavior while keeping
relative "../.." sequences intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e89637cf-fe80-41c8-991c-6bef5cb5b1e0

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9fba1 and a62fba5.

📒 Files selected for processing (2)
  • apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts
  • apps/desktop/tests/services/BuiltInToolService/tools/bash/helper.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Desktop E2E Smoke (Windows)
  • GitHub Check: Frontend Tests
  • GitHub Check: Rust Checks
  • GitHub Check: Frontend Quality
  • GitHub Check: CodeQL (rust)
  • GitHub Check: CodeQL (javascript-typescript)
🧰 Additional context used
🪛 OpenGrep (1.22.0)
apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts

[ERROR] 20-20: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (1)
apps/desktop/tests/services/BuiltInToolService/tools/bash/helper.test.ts (1)

97-110: LGTM!

Comment thread apps/desktop/src/services/BuiltInToolService/tools/bash/helper.ts
const normalizedSeparators = path.trim().replace(/\//g, '\\').replace(/\\+$/, '');
const driveMatch = /^([a-zA-Z]:)(?:\\(.*))?$/.exec(normalizedSeparators);
const hasRoot = normalizedSeparators.startsWith('\\');
const prefix = driveMatch?.[1]?.toLowerCase() ?? (hasRoot ? '\\' : '');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Potential allowlist bypass for UNC paths: \\server\share is the root of a UNC path, but this normalizer stores the prefix as only \ and then treats both server and share as ordinary segments. That lets .. pop the share name, which does not match Windows path semantics.

For example, with allowedWorkingDirectories: ['\\server\share'], a requested working directory like \\server\other\..\share\secret is normalized here as \\server\share\secret and passes the allowlist, while Windows resolves it under \\server\other\share\secret, outside the allowed share. Could we preserve \\server\share as the UNC prefix/root when resolving .., and add a resolveCommandContext test for this case?

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.

2 participants