mirror of
https://github.com/github/spec-kit.git
synced 2026-03-19 03:43:07 +00:00
fix(scripts): encode residual JSON control chars as \uXXXX instead of stripping (#1872)
* fix(scripts): encode residual control chars as \uXXXX instead of stripping
json_escape() was silently deleting control characters (U+0000-U+001F)
that were not individually handled (\n, \t, \r, \b, \f). Per RFC 8259,
these must be encoded as \uXXXX sequences to preserve data integrity.
Replace the tr -d strip with a char-by-char loop that emits proper
\uXXXX escapes for any remaining control characters.
* fix(scripts): address Copilot review on json_escape control char loop
- Set LC_ALL=C for the entire loop (not just printf) so that ${#s} and
${s:$i:1} operate on bytes deterministically across locales
- Fix comment: U+0000 (NUL) cannot exist in bash strings, range is
U+0001-U+001F; adjust code guard accordingly (code >= 1)
- Emit directly to stdout instead of accumulating in a variable,
avoiding quadratic string concatenation on longer inputs
* perf(scripts): use printf -v to avoid subshell in json_escape loop
Replace code=$(printf ...) with printf -v code to assign the character
code without spawning a subshell on every byte, reducing overhead for
longer inputs.
This commit is contained in:
@@ -171,9 +171,21 @@ json_escape() {
|
|||||||
s="${s//$'\r'/\\r}"
|
s="${s//$'\r'/\\r}"
|
||||||
s="${s//$'\b'/\\b}"
|
s="${s//$'\b'/\\b}"
|
||||||
s="${s//$'\f'/\\f}"
|
s="${s//$'\f'/\\f}"
|
||||||
# Strip remaining control characters (U+0000–U+001F) not individually escaped above
|
# Escape any remaining U+0001-U+001F control characters as \uXXXX.
|
||||||
s=$(printf '%s' "$s" | tr -d '\000-\007\013\016-\037')
|
# (U+0000/NUL cannot appear in bash strings and is excluded.)
|
||||||
printf '%s' "$s"
|
# LC_ALL=C ensures ${#s} counts bytes and ${s:$i:1} yields single bytes,
|
||||||
|
# so multi-byte UTF-8 sequences (first byte >= 0xC0) pass through intact.
|
||||||
|
local LC_ALL=C
|
||||||
|
local i char code
|
||||||
|
for (( i=0; i<${#s}; i++ )); do
|
||||||
|
char="${s:$i:1}"
|
||||||
|
printf -v code '%d' "'$char" 2>/dev/null || code=256
|
||||||
|
if (( code >= 1 && code <= 31 )); then
|
||||||
|
printf '\\u%04x' "$code"
|
||||||
|
else
|
||||||
|
printf '%s' "$char"
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||||
|
|||||||
Reference in New Issue
Block a user