Skip to content

fix: Properly escape file path in chmod command (#1527)#1732

Merged
thomhurst merged 1 commit into
mainfrom
fix/1527-file-path-escaping
Jan 1, 2026
Merged

fix: Properly escape file path in chmod command (#1527)#1732
thomhurst merged 1 commit into
mainfrom
fix/1527-file-path-escaping

Conversation

@thomhurst

Copy link
Copy Markdown
Owner

Summary

  • Fixed security/reliability issue where file paths containing special characters (spaces, quotes, etc.) could cause the chmod command to fail or behave unexpectedly
  • The path is now properly escaped by wrapping it in single quotes and escaping any embedded single quotes with '\''

Changes

Before:

await _bash.Command(new BashCommandOptions($"chmod u+x {options.Path}"), cancellationToken)

After:

var escapedPath = options.Path.Replace("'", "'\''");
await _bash.Command(new BashCommandOptions($"chmod u+x '{escapedPath}'"), cancellationToken)

Test plan

  • Verify build passes in CI
  • Test with file paths containing spaces (e.g., /path/to/my script.sh)
  • Test with file paths containing single quotes (e.g., /path/to/it's working.sh)

Fixes #1527

🤖 Generated with Claude Code

The chmod command was constructed with an unsanitized file path which
could fail or be dangerous with special characters. This change wraps
the path in single quotes and escapes any embedded single quotes with
the standard '\'' technique.

Fixes #1527

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 1, 2026 22:00
@thomhurst

Copy link
Copy Markdown
Owner Author

Summary

Adds proper shell escaping for file paths in the chmod command to prevent command injection vulnerabilities.

Critical Issues

None found ✅

Analysis

The fix correctly addresses a command injection vulnerability in the chmod command execution:

Why this fix is needed:

  • Line 31 uses BashCommandOptions with string interpolation: $"chmod u+x {options.Path}"
  • This creates a bash command string passed via -c, where the entire string is treated as a single argument to bash
  • CliWrap's argument escaping doesn't protect against injection within the command string itself
  • Example attack: if options.Path is "/tmp/file'; rm -rf /; echo '", the original code would execute arbitrary commands

Why the fix is correct:

  • The PR escapes single quotes using the bash-safe pattern: ''' (close quote, escaped quote, open quote)
  • Wraps the path in single quotes to prevent word splitting and glob expansion
  • This is the standard and secure way to handle untrusted input in bash commands

Why FromFile on line 33 doesn't need the same fix:

  • BashFileOptions passes the path as a structured CLI argument (not string interpolation)
  • CliWrap's WithArguments() properly escapes this argument when invoking bash
  • The path becomes a properly escaped argument to the bash executable, not part of an interpolated command string

Verdict

APPROVE - No critical issues

This is a good security fix that properly addresses command injection in the chmod execution path while correctly leaving the FromFile call unchanged (since it uses safe argument passing).

Copilot AI 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.

Pull request overview

This PR fixes a security and reliability issue in the FileInstaller.cs where file paths containing special shell characters (spaces, quotes, etc.) could cause the chmod command to fail or execute incorrectly. The fix properly escapes single quotes in file paths before interpolating them into a bash command string.

Key Changes

  • Added shell escaping logic for file paths used in the chmod command to prevent command injection and ensure paths with special characters work correctly
  • The path is now wrapped in single quotes with embedded single quotes escaped using the '\'' pattern

}

await _bash.Command(new BashCommandOptions($"chmod u+x {options.Path}"), cancellationToken).ConfigureAwait(false);
var escapedPath = options.Path.Replace("'", "'\\''");

Copilot AI Jan 1, 2026

Copy link

Choose a reason for hiding this comment

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

The escaping logic is duplicated from the existing EscapeShellArgument utility method in PredefinedInstallers.cs (line 194). Consider extracting this method to a shared utility class to maintain consistency and follow the DRY principle. The existing method at PredefinedInstallers.cs:194-199 uses the same escaping pattern and includes documentation explaining the approach.

Suggested change
var escapedPath = options.Path.Replace("'", "'\\''");
var escapedPath = PredefinedInstallers.EscapeShellArgument(options.Path);

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +32
var escapedPath = options.Path.Replace("'", "'\\''");
await _bash.Command(new BashCommandOptions($"chmod u+x '{escapedPath}'"), cancellationToken).ConfigureAwait(false);

Copilot AI Jan 1, 2026

Copy link

Choose a reason for hiding this comment

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

This fix for shell escaping lacks test coverage. The existing BashTests.cs file covers basic bash command execution but doesn't test file paths with special characters (spaces, quotes, etc.). Consider adding tests that verify the chmod command works correctly with file paths containing spaces and single quotes, as described in the PR test plan.

Copilot uses AI. Check for mistakes.
@thomhurst thomhurst merged commit bf815da into main Jan 1, 2026
17 of 18 checks passed
@thomhurst thomhurst deleted the fix/1527-file-path-escaping branch January 1, 2026 22:11
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.

Code smell: FileInstaller constructs command with unsanitized file path

2 participants