mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-08 08:53:43 +08:00
Escape control bytes in properties-JSON array items (raw newlines from multi-line C params)
The array appenders (param_types/param_names/decorators/base_classes) in both props-builder twins escaped only quote and backslash, so items sliced from multi-line C parameter declarations carried raw newline/tab bytes — invalid inside JSON strings (the remaining 118 malformed kernel rows after the truncation fix; e.g. param_types:["struct\n\t\t..."]). Array items now go through the full escape function, and both escapers degrade any other raw control byte (e.g. form feed) to a space. Reproduced via the prod CLI on a multi-line-param fixture (nl_fn json_valid=0 pre-fix); the sweep regression test gained that fixture (61st case).
This commit is contained in:
@@ -103,7 +103,9 @@ static int def_json_escape_char(char *buf, size_t avail, char ch) {
|
||||
break;
|
||||
default:
|
||||
if (avail >= SKIP_ONE) {
|
||||
buf[0] = ch;
|
||||
/* Any other raw control byte (e.g. form feed) is invalid inside a
|
||||
* JSON string — degrade to a space. */
|
||||
buf[0] = ((unsigned char)ch < 0x20) ? ' ' : ch;
|
||||
}
|
||||
return SKIP_ONE;
|
||||
}
|
||||
@@ -194,14 +196,11 @@ static void append_json_str_array(char *buf, size_t bufsize, size_t *pos, const
|
||||
if (p < bufsize - SKIP_ONE) {
|
||||
buf[p++] = '"';
|
||||
}
|
||||
/* Full escaping (not just quote/backslash): items like C param types
|
||||
* sliced from multi-line declarations carry raw \n/\t bytes, which are
|
||||
* invalid inside JSON strings. */
|
||||
for (const char *s = arr[i]; *s && p < bufsize - PD_ESC_SPACE; s++) {
|
||||
if (*s == '"' || *s == '\\') {
|
||||
buf[p++] = '\\';
|
||||
if (p >= bufsize - PD_ESC_SPACE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[p++] = *s;
|
||||
p += (size_t)def_json_escape_char(buf + p, bufsize - p - PD_ESC_SPACE, *s);
|
||||
}
|
||||
if (p < bufsize - SKIP_ONE) {
|
||||
buf[p++] = '"';
|
||||
|
||||
@@ -145,7 +145,9 @@ static int json_escape_char(char *buf, size_t avail, char ch) {
|
||||
break;
|
||||
default:
|
||||
if (avail >= SKIP_ONE) {
|
||||
buf[0] = ch;
|
||||
/* Any other raw control byte (e.g. form feed) is invalid inside a
|
||||
* JSON string — degrade to a space. */
|
||||
buf[0] = ((unsigned char)ch < 0x20) ? ' ' : ch;
|
||||
}
|
||||
return SKIP_ONE;
|
||||
}
|
||||
@@ -237,14 +239,11 @@ static void append_json_str_array(char *buf, size_t bufsize, size_t *pos, const
|
||||
if (p < bufsize - SKIP_ONE) {
|
||||
buf[p++] = '"';
|
||||
}
|
||||
/* Full escaping (not just quote/backslash): items like C param types
|
||||
* sliced from multi-line declarations carry raw \n/\t bytes, which are
|
||||
* invalid inside JSON strings. */
|
||||
for (const char *s = arr[i]; *s && p < bufsize - PP_ESC_SPACE; s++) {
|
||||
if (*s == '"' || *s == '\\') {
|
||||
buf[p++] = '\\';
|
||||
if (p >= bufsize - PP_ESC_SPACE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[p++] = *s;
|
||||
p += (size_t)json_escape_char(buf + p, bufsize - p - PP_ESC_SPACE, *s);
|
||||
}
|
||||
if (p < bufsize - SKIP_ONE) {
|
||||
buf[p++] = '"';
|
||||
|
||||
+18
-1
@@ -506,6 +506,23 @@ TEST(pipeline_def_props_valid_json_when_oversized) {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* Multi-line parameter declarations: param_types items then carry raw
|
||||
* newline/tab bytes from the source slice. The array appender's inline
|
||||
* escape loop only handled quote/backslash, so the raw control bytes made
|
||||
* the JSON invalid (118 Linux-kernel rows of this shape). */
|
||||
{
|
||||
char path[512];
|
||||
snprintf(path, sizeof(path), "%s/sweep_nl.c", g_tmpdir);
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) {
|
||||
teardown_test_repo();
|
||||
FAIL("failed to write sweep_nl.c");
|
||||
}
|
||||
fprintf(f, "int sweep_fn_nl(struct\n\t\t\t\treally_long_struct_name *a,\n"
|
||||
" enum\n\tweird_enum b) { return 0; }\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/test_huge_props.db", g_tmpdir);
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(g_tmpdir, db_path, CBM_MODE_FULL);
|
||||
@@ -539,7 +556,7 @@ TEST(pipeline_def_props_valid_json_when_oversized) {
|
||||
yyjson_doc_free(doc);
|
||||
checked++;
|
||||
}
|
||||
ASSERT_EQ(checked, 60); /* all sweep functions present */
|
||||
ASSERT_EQ(checked, 61); /* all sweep functions present (60 sizes + nl case) */
|
||||
|
||||
cbm_store_free_nodes(funcs, func_count);
|
||||
cbm_store_close(s);
|
||||
|
||||
Reference in New Issue
Block a user