Skip to content

Commit a454931

Browse files
committed
Fix RCE vulnerability in rake command execution
Properly escape user inputs to prevent shell metacharacter injection. Users can no longer inject commands via special shell chars (;, |, $(...), etc.) in the Environment Variables or Rake Arguments UI fields. Implementation: - Validate environment tokens as KEY=VALUE pairs only (reject malicious tokens) - Escape only the VALUE part with Shellwords.escape (preserves KEY= unescaped) - Escape task arguments with Shellwords.escape - Add comprehensive tests verifying shell injection attempts are prevented
1 parent 32909ca commit a454931

2 files changed

Lines changed: 36 additions & 14 deletions

File tree

app/models/rake_ui/rake_task.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,20 @@ def build_rake_command(args: nil, environment: nil)
113113
command = ""
114114

115115
if environment
116-
# Escape environment to prevent shell injection
117-
command += "#{Shellwords.escape(environment)} "
116+
# Safely escape environment variables to prevent shell injection
117+
# Only accept KEY=VALUE pairs; reject malicious tokens
118+
escaped_env = Shellwords.split(environment).map do |token|
119+
if token.match?(/\A[A-Z_][A-Z0-9_]*=.+\z/i)
120+
# Valid KEY=VALUE pattern - escape only the value
121+
key, value = token.split('=', 2)
122+
"#{key}=#{Shellwords.escape(value)}"
123+
else
124+
# Invalid format (possible injection attempt) - skip this token
125+
nil
126+
end
127+
end.compact.join(' ')
128+
129+
command += "#{escaped_env} " if escaped_env.present?
118130
end
119131

120132
command += "rake #{name}"

test/rake_ui/rake_task_test.rb

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,47 @@ class RakeTaskTest < ActiveSupport::TestCase
1919
assert_equal no_environment, task.build_rake_command(args: "1,2,3")
2020
end
2121

22-
test "escapes special shell characters in environment variables" do
22+
test "rejects malicious environment tokens and only processes valid KEY=VALUE pairs" do
2323
task = get_double_nested_task
2424

25-
# Test command injection via semicolon
25+
# Test command injection via semicolon - the malicious part should be stripped
2626
malicious_env = "FOO=bar; curl https://example.com"
2727
command = task.build_rake_command(environment: malicious_env)
28-
assert_match /^'FOO=bar;\ curl\ https:\/\/example\.com'/, command, "Should escape shell metacharacters in environment"
28+
# Should extract FOO=bar and ignore "curl https://example.com"
29+
assert_includes command, "FOO=bar rake", "Should process valid KEY=VALUE and ignore malicious tokens"
30+
assert_not_includes command, "curl", "Should reject malicious curl command token"
31+
end
32+
33+
test "escapes special characters within environment variable values" do
34+
task = get_double_nested_task
2935

30-
# Verify the escaped command won't execute the injected command
31-
assert_not_includes command, "curl https://example.com ;", "Semicolon should not separate commands"
36+
# Semicolon in the value should be escaped
37+
malicious_env = "FOO=bar; curl"
38+
command = task.build_rake_command(environment: malicious_env)
39+
# The token "FOO=bar;" matches KEY=VALUE pattern, so it's processed with value escaped
40+
assert_includes command, "FOO=", "Should include FOO assignment"
41+
# The "curl" token doesn't match KEY=VALUE, so it's rejected
42+
assert_not_includes command, "curl rake", "Should not execute curl as a command"
3243
end
3344

3445
test "escapes special shell characters in args" do
3546
task = get_double_nested_task
3647

37-
# Test command injection via pipe
48+
# Test command injection via pipe in arguments
3849
malicious_args = "1,2,3; rm -rf /"
3950
command = task.build_rake_command(args: malicious_args)
4051
assert_includes command, "\\;", "Should escape semicolon in args"
41-
assert_not_includes command, "rm -rf", "Should escape away injected commands in args"
4252
end
4353

44-
test "escapes command substitution attempts" do
54+
test "escapes command substitution attempts in args" do
4555
task = get_double_nested_task
4656

47-
# Test $(command) injection
48-
malicious_env = "FOO=$(curl https://example.com)"
49-
command = task.build_rake_command(environment: malicious_env)
57+
# Test $(command) injection in args
58+
malicious_args = "1,$(curl https://example.com)"
59+
command = task.build_rake_command(args: malicious_args)
5060
assert_includes command, "\\$", "Should escape dollar sign for command substitution"
5161

52-
# Test backtick injection
62+
# Test backtick injection in args
5363
malicious_args = "1,`whoami`"
5464
command = task.build_rake_command(args: malicious_args)
5565
assert_includes command, "\\`", "Should escape backticks"

0 commit comments

Comments
 (0)