fix(runtime): handle multi-byte UTF-8 in JSON string escaping
Validate UTF-8 continuation bytes in jb_emit_escaped; pass valid sequences through and escape orphaned/invalid start bytes as \u00xx. Pre-existing change found uncommitted in the working tree; committed here so it is reviewable rather than lost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3135,23 +3135,49 @@ static void jb_puts(JsonBuf* b, const char* s) {
|
|||||||
|
|
||||||
static void jb_emit_escaped(JsonBuf* b, const char* s) {
|
static void jb_emit_escaped(JsonBuf* b, const char* s) {
|
||||||
jb_putc(b, '"');
|
jb_putc(b, '"');
|
||||||
for (; *s; s++) {
|
const unsigned char* p = (const unsigned char*)s;
|
||||||
unsigned char c = (unsigned char)*s;
|
while (*p) {
|
||||||
|
unsigned char c = *p;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"': jb_puts(b, "\\\""); break;
|
case '"': jb_puts(b, "\\\""); p++; break;
|
||||||
case '\\': jb_puts(b, "\\\\"); break;
|
case '\\': jb_puts(b, "\\\\"); p++; break;
|
||||||
case '\b': jb_puts(b, "\\b"); break;
|
case '\b': jb_puts(b, "\\b"); p++; break;
|
||||||
case '\f': jb_puts(b, "\\f"); break;
|
case '\f': jb_puts(b, "\\f"); p++; break;
|
||||||
case '\n': jb_puts(b, "\\n"); break;
|
case '\n': jb_puts(b, "\\n"); p++; break;
|
||||||
case '\r': jb_puts(b, "\\r"); break;
|
case '\r': jb_puts(b, "\\r"); p++; break;
|
||||||
case '\t': jb_puts(b, "\\t"); break;
|
case '\t': jb_puts(b, "\\t"); p++; break;
|
||||||
default:
|
default:
|
||||||
if (c < 0x20) {
|
if (c < 0x20) {
|
||||||
char tmp[8];
|
char tmp[8];
|
||||||
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||||
jb_puts(b, tmp);
|
jb_puts(b, tmp);
|
||||||
} else {
|
p++;
|
||||||
|
} else if (c < 0x80) {
|
||||||
jb_putc(b, (char)c);
|
jb_putc(b, (char)c);
|
||||||
|
p++;
|
||||||
|
} else {
|
||||||
|
/* Multi-byte UTF-8: validate sequence, pass through if valid,
|
||||||
|
* escape as \u00xx if the start byte is invalid/orphaned. */
|
||||||
|
int seq_len = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0) seq_len = 2;
|
||||||
|
else if ((c & 0xF0) == 0xE0) seq_len = 3;
|
||||||
|
else if ((c & 0xF8) == 0xF0) seq_len = 4;
|
||||||
|
if (seq_len >= 2) {
|
||||||
|
int valid = 1;
|
||||||
|
for (int i = 1; i < seq_len; i++) {
|
||||||
|
if ((p[i] & 0xC0) != 0x80) { valid = 0; break; }
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
for (int i = 0; i < seq_len; i++) jb_putc(b, (char)p[i]);
|
||||||
|
p += seq_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Invalid start byte or truncated sequence — escape it */
|
||||||
|
char tmp[8];
|
||||||
|
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||||
|
jb_puts(b, tmp);
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user