[feature] add arbitrary code execution to strformat (#17694)

* changed parser to ignore ':' within parens
* Update strformat.nim
* Update lib/pure/strformat.nim

Co-authored-by: flywind <xzsflywind@gmail.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
shirleyquirk 2021-04-11 12:07:23 +01:00 • committed by GitHub
commit a5b30c94c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -74,6 +74,20 @@ runnableExamples:
assert fmt"{123.456:13e}" == " 1.234560e+02" assert fmt"{123.456:13e}" == " 1.234560e+02"
##[ ##[
# Expressions
]##
runnableExamples:
let x = 3.14
assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
assert fmt"""{(block:
var res: string
for i in 1..15:
res.add (if i mod 15 == 0: "FizzBuzz"
elif i mod 5 == 0: "Buzz"
elif i mod 3 == 0: "Fizz"
else: $i) & " "
res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz "
##[
# Debugging strings # Debugging strings
`fmt"{expr=}"` expands to `fmt"expr={expr}"` namely the text of the expression, `fmt"{expr=}"` expands to `fmt"expr={expr}"` namely the text of the expression,
@ -557,7 +571,7 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
# XXX: https://github.com/nim-lang/Nim/issues/8405 # XXX: https://github.com/nim-lang/Nim/issues/8405
# When compiling with -d:useNimRtl, certain procs such as `count` from the strutils # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
# module are not accessible at compile-time: # module are not accessible at compile-time:
let expectedGrowth = when defined(useNimRtl): 0 else: count(f, '{') * 10 let expectedGrowth = when defined(useNimRtl): 0 else: count(f, openChar) * 10
result.add newVarStmt(res, newCall(bindSym"newStringOfCap", result.add newVarStmt(res, newCall(bindSym"newStringOfCap",
newLit(f.len + expectedGrowth))) newLit(f.len + expectedGrowth)))
var strlit = "" var strlit = ""
@ -573,8 +587,12 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
strlit = "" strlit = ""
var subexpr = "" var subexpr = ""
while i < f.len and f[i] != closeChar and f[i] != ':': var inParens = 0
if f[i] == '=': while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens!=0):
case f[i]
of '(': inc inParens
of ')': dec inParens
of '=':
let start = i let start = i
inc i inc i
i += f.skipWhitespace(i) i += f.skipWhitespace(i)
@ -582,9 +600,10 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i])) result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i]))
else: else:
subexpr.add f[start ..< i] subexpr.add f[start ..< i]
else: continue
subexpr.add f[i] else: discard
inc i subexpr.add f[i]
inc i
var x: NimNode var x: NimNode
try: try:
@ -608,11 +627,11 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
doAssert false, "invalid format string: missing '}'" doAssert false, "invalid format string: missing '}'"
result.add newCall(formatSym, res, x, newLit(options)) result.add newCall(formatSym, res, x, newLit(options))
elif f[i] == closeChar: elif f[i] == closeChar:
if f[i+1] == closeChar: if i<f.len-1 and f[i+1] == closeChar:
strlit.add closeChar strlit.add closeChar
inc i, 2 inc i, 2
else: else:
doAssert false, "invalid format string: '}' instead of '}}'" doAssert false, "invalid format string: '$1' instead of '$1$1'" % $closeChar
inc i inc i
else: else:
strlit.add f[i] strlit.add f[i]