Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Change exec(args) to exec(shelljoin(string))
Browse files Browse the repository at this point in the history
* fixes unicode problems on windows
  • Loading branch information
Ransom Briggs committed Dec 2, 2015
1 parent aae9bf3 commit 78869d7
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 1 deletion.
33 changes: 32 additions & 1 deletion lib/heroku/jsplugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,45 @@ def self.copy_ca_cert

def self.run(topic, command, args)
cmd = command ? "#{topic}:#{command}" : topic
exec self.bin, cmd, *args

# calling exec with multiple arguments mangles multibyte characters on windows
exec shelljoin([self.bin, cmd, *args])
end

def self.spawn(topic, command, args)
cmd = command ? "#{topic}:#{command}" : topic
system self.bin, cmd, *args
end

# see https://github.com/ruby/ruby/blob/v2_2_3/lib/shellwords.rb#L149
def self.shelljoin(array)
array.map { |arg| shellescape(arg) }.join(' ')
end

# see https://github.com/ruby/ruby/blob/v2_2_3/lib/shellwords.rb#L93
def self.shellescape(str)
str = str.to_s

return "''" if str.empty?

str = str.dup

begin
# attempt to convert the duplicated string to utf-8
str.encode!('utf-8')

# escape while still preserving unicode characters
str.gsub!(/([^\p{L}\p{N}_\-.,:\/@\n])/, "\\\\\\1")
rescue Encoding::UndefinedConversionError
# if it cannot be converted then fall back to normal behavior
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
end

str.gsub!(/\n/, "'\n'")

return str
end

def self.arch
case RbConfig::CONFIG['host_cpu']
when /x86_64/
Expand Down
58 changes: 58 additions & 0 deletions spec/heroku/jsplugin_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require "spec_helper"
require "heroku/jsplugin"

module Heroku
describe JSPlugin do
context "shellescape" do
it "should to_s the arguments" do
expect(Heroku::JSPlugin.shellescape(3)).to eq("3")
end

it "should return single quotes for an empty string" do
expect(Heroku::JSPlugin.shellescape('')).to eq("''")
end

it "escape newlines properly" do
expect(Heroku::JSPlugin.shellescape("\n")).to eq("'\n'")
end

it "escapes bad shell commands" do
expect(Heroku::JSPlugin.shellescape("`$()|;&><'\"")).to eq("\\`\\$\\(\\)\\|\\;\\&\\>\\<\\'\\\"")
end

it "passes options through" do
expect(Heroku::JSPlugin.shellescape("-f")).to eq("-f")
end

it "passes multi byte characters through" do
expect(Heroku::JSPlugin.shellescape("あい")).to eq("あい")
end

it "passes ascci numbers and letters through without escaping" do
expect(Heroku::JSPlugin.shellescape("Aa0")).to eq("Aa0")
end

it "passes multi byte characters through" do
expect(Heroku::JSPlugin.shellescape("あい")).to eq("あい")
end

it "passes multi byte numbers through" do
expect(Heroku::JSPlugin.shellescape("Ⅲ")).to eq("Ⅲ")
end

it "does not fail on things that cannot be converted to utf-8" do
expected_bad_encoding = "\u0412".force_encoding("ASCII-8BIT")
expected_bad_encoding.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")

bad_encoding = "\u0412".force_encoding("ASCII-8BIT")
expect(Heroku::JSPlugin.shellescape(bad_encoding)).to eq(expected_bad_encoding)
end
end

context "shelljoin" do
it "escapes bad shell commands" do
expect(Heroku::JSPlugin.shelljoin(["`$()|;", "&><'\""])).to eq("\\`\\$\\(\\)\\|\\; \\&\\>\\<\\'\\\"")
end
end
end
end

0 comments on commit 78869d7

Please sign in to comment.