nre: fix replacement string parser OOB access, numeric refs, and unterminated named refs (#25560)

1. A trailing `$` at the end of a replacement string could read out of
bounds via `how[i + 1]`; this now raises `ValueError` instead.

2. Numeric capture parsing used `id += (id * 10) + digit` instead of `id
= (id * 10) + digit`, so multi-digit refs were parsed incorrectly (e.g.
`$12` resolved as capture 13 instead of 12).

4. Unterminated named replacement syntax (e.g. `${foo)` is now rejected
with ValueError instead of being accepted and parsed inconsistently.

Found and fixed by GPT 5.3 Codex.

(cherry picked from commit 9b2b286baf)
This commit is contained in:
Raka Hourianto
2026-02-28 09:39:16 +03:00
committed by narimiran
parent 7b8ef1a901
commit 5eb96d40ee
2 changed files with 12 additions and 1 deletions

View File

@@ -15,6 +15,9 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
val.add(how[i])
i += 1
else:
if i + 1 >= how.len:
raise newException(ValueError, "Syntax error in format string at " & $i)
if how[i + 1] == '$':
val.add('$')
i += 2
@@ -27,7 +30,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
i += 1
var id {.inject.} = 0
while i < how.len and how[i] in {'0'..'9'}:
id += (id * 10) + (ord(how[i]) - ord('0'))
id = (id * 10) + (ord(how[i]) - ord('0'))
i += 1
val.add(idgetter)
lastNum = id + 1
@@ -44,6 +47,8 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
while i < how.len and how[i] != '}':
name.add(how[i])
i += 1
if i >= how.len or how[i] != '}':
raise newException(ValueError, "Syntax error in format string at " & $i)
i += 1
val.add(namegetter)
else: