[ruby/shellwords] NUL char cannot be in shell words
authorNobuyoshi Nakada <[email protected]>
Wed, 3 Jul 2024 10:43:07 +0000 (3 19:43 +0900)
committergit <[email protected]>
Wed, 4 Dec 2024 07:48:59 +0000 (4 07:48 +0000)
https://github.com/ruby/shellwords/commit/2c7ae1b76c

lib/shellwords.rb
test/test_shellwords.rb

index f03abd9..7f78403 100644 (file)
@@ -73,6 +73,9 @@ module Shellwords
   #   argv = Shellwords.split('here are "two words"')
   #   argv #=> ["here", "are", "two words"]
   #
+  # +line+ must not contain NUL characters because of nature of
+  # +exec+ system call.
+  #
   # Note, however, that this is not a command line parser.  Shell
   # metacharacters except for the single and double quotes and
   # backslash are not treated as such.
@@ -87,9 +90,14 @@ module Shellwords
   def shellsplit(line)
     words = []
     field = String.new
-    line.scan(/\G\s*(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|\z)?/m) do
+    line.scan(/\G\s*(?>([^\0\s\\\'\"]+)|'([^\0\']*)'|"((?:[^\0\"\\]|\\[^\0])*)"|(\\[^\0]?)|(\S))(\s|\z)?/m) do
       |word, sq, dq, esc, garbage, sep|
-      raise ArgumentError, "Unmatched quote: #{line.inspect}" if garbage
+      if garbage
+        b = $~.begin(0)
+        line = $~[0]
+        line = "..." + line if b > 0
+        raise ArgumentError, "#{garbage == "\0" ? 'Nul character' : 'Unmatched quote'} at #{b}: #{line}"
+      end
       # 2.2.3 Double-Quotes:
       #
       #   The <backslash> shall retain its special meaning as an
@@ -118,6 +126,9 @@ module Shellwords
   # command line.  +str+ can be a non-string object that responds to
   # +to_s+.
   #
+  # +str+ must not contain NUL characters because of nature of +exec+
+  # system call.
+  #
   # Note that a resulted string should be used unquoted and is not
   # intended for use in double quotes nor in single quotes.
   #
@@ -150,6 +161,9 @@ module Shellwords
     # An empty argument will be skipped, so return empty quotes.
     return "''".dup if str.empty?
 
+    # Shellwords cannot contain NUL characters.
+    raise ArgumentError, "NUL character" if str.index("\0")
+
     str = str.dup
 
     # Treat multibyte characters as is.  It is the caller's responsibility
@@ -175,6 +189,7 @@ module Shellwords
   # All elements are joined into a single string with fields separated by a
   # space, where each element is escaped for the Bourne shell and stringified
   # using +to_s+.
+  # See also Shellwords.shellescape.
   #
   #   ary = ["There's", "a", "time", "and", "place", "for", "everything"]
   #   argv = Shellwords.join(ary)
index 86971b7..0020235 100644 (file)
@@ -128,4 +128,13 @@ class TestShellwords < Test::Unit::TestCase
     # used as shell meta-character that needs to be escaped.
     assert_equal "\\あ\\い", "あい".shellescape
   end
+
+  def test_nul_char
+    assert_raise(ArgumentError) do
+      shellescape("\0")
+    end
+    assert_raise(ArgumentError) do
+      shelljoin(["\0"])
+    end
+  end
 end