summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorMichał Zieliński <michal@zielinscy.org.pl>2013-12-02 18:34:32 +0100
committerMichał Zieliński <michal@zielinscy.org.pl>2013-12-07 22:56:02 +0100
commite7e8c7706240c4bef17533da63df988d228ba127 (patch)
tree9841b117a79e64f00f6e6165e77ee3a5ec0d65e2 /lib
parent5dcfa97fb959eda3cb8a41e2bd39e998c30052c8 (diff)
downloadNim-e7e8c7706240c4bef17533da63df988d228ba127.tar.gz
Make quoteIfContainsWhite quote argument, so it can be safely passed to shell.
On Windows put it in double quotes and escape double quotes using backslash.
On Posix put it in single quotes and escape single quotes using '"'"'.

This commit changes what quoteIfContainsWhite does, but before that change it
was used incorrectly all over standard library, which caused security issues.
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/strutils.nim51
1 files changed, 43 insertions, 8 deletions
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index a4aa81578..0299f5cd2 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -709,14 +709,6 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
     if result != -1: return
   return -1
 

-proc quoteIfContainsWhite*(s: string): string =

-  ## returns ``'"' & s & '"'`` if `s` contains a space and does not

-  ## start with a quote, else returns `s`

-  if find(s, {' ', '\t'}) >= 0 and s[0] != '"':

-    result = '"' & s & '"'

-  else:

-    result = s

-

 proc contains*(s: string, c: char): bool {.noSideEffect.} =

   ## Same as ``find(s, c) >= 0``.

   return find(s, c) >= 0

@@ -780,6 +772,49 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   # copy the rest:

   add result, substr(s, i)

 

+proc quoteIfContainsWhite*(s: string): string {.noSideEffect.} =

+  ## Quote s, so it can be safely passed to shell.

+  when defined(Windows):

+    # based on Python's subprocess.list2cmdline

+    # see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx

+    let needQuote = {' ', '\t'} in s or s.len == 0

+

+    result = ""

+    var backslashBuff = ""

+    if needQuote:

+      result.add("\"")

+

+    for c in s:

+      if c == '\\':

+        backslashBuff.add(c)

+      elif c == '\"':

+        result.add(backslashBuff)

+        result.add(backslashBuff)

+        backslashBuff.setLen(0)

+        result.add("\\\"")

+      else:

+        if backslashBuff.len != 0:

+          result.add(backslashBuff)

+          backslashBuff.setLen(0)

+        result.add(c)

+

+    if needQuote:

+      result.add("\"")

+

+  else:

+    # based on Python's pipes.quote

+    const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
+                         '0'..'9', 'A'..'Z', 'a'..'z'}

+    if s.len == 0:

+      return "''"

+

+    let safe = s.allCharsInSet(safeUnixChars)

+

+    if safe:

+      return s

+    else:

+      return "'" & s.replace("'", "'\"'\"'") & "'"

+

 proc delete*(s: var string, first, last: int) {.noSideEffect,

   rtl, extern: "nsuDelete".} =

   ## Deletes in `s` the characters at position `first` .. `last`. This modifies