Close the remaining malformed edge-props producers

Three stragglers after 6c04ab7/7f78f93, found by edge json_valid audits on elasticsearch: (1) the parallel emit_route_registration twin still interpolated callee/route_path raw into CALLS props; (2) HANDLES props buffers were smaller than escaped handler QNs plus the JSON wrapper, so snprintf truncated the closing brace (escaping alone cannot prevent wrapper truncation - value buffers now 512B inside 1KB props); (3) DATA_FLOWS assembles sliced handler_params/args_json fragments that can break in ways escaping does not cover - the assembled props are now validated with yyjson and fall back to a minimal valid envelope. Verified by reindexing elasticsearch: edge json_valid violations 47 -> expected 0.
This commit is contained in:
Martin Vogel
2026-06-10 17:07:10 +02:00
parent 7f78f936ec
commit 8666a99e29
3 changed files with 28 additions and 8 deletions
+3 -2
View File
@@ -208,8 +208,9 @@ static void handle_route_registration(cbm_pipeline_ctx_t *ctx, const CBMCall *ca
if (hres.qualified_name != NULL && hres.qualified_name[0] != '\0') {
const cbm_gbuf_node_t *handler = cbm_gbuf_find_by_qn(ctx->gbuf, hres.qualified_name);
if (handler != NULL) {
char hprops[CBM_SZ_256];
char esc_h[CBM_SZ_256];
char hprops[CBM_SZ_1K]; /* must exceed escaped value + wrapper or snprintf cuts the
closing brace */
char esc_h[CBM_SZ_512];
cbm_json_escape(esc_h, sizeof(esc_h), hres.qualified_name);
snprintf(hprops, sizeof(hprops), "{\"handler\":\"%s\"}", esc_h);
cbm_gbuf_insert_edge(ctx->gbuf, handler->id, route_id, "HANDLES", hprops);
+11 -6
View File
@@ -507,7 +507,7 @@ static void insert_def_into_gbuf(extract_worker_state_t *ws, const cbm_file_info
cbm_gbuf_upsert_node(ws->local_gbuf, "Route", def->route_path, route_qn,
def->file_path ? def->file_path : fi->rel_path, 0, 0, rprops);
char hprops[CBM_SZ_512];
char esc_h[CBM_SZ_256];
char esc_h[CBM_SZ_512];
cbm_json_escape(esc_h, sizeof(esc_h), def->qualified_name);
snprintf(hprops, sizeof(hprops), "{\"handler\":\"%s\"}", esc_h);
cbm_gbuf_insert_edge(ws->local_gbuf, func_id, route_id, "HANDLES", hprops);
@@ -1279,18 +1279,23 @@ static void emit_route_registration(cbm_gbuf_t *gbuf, const cbm_gbuf_node_t *sou
char rp[CBM_SZ_256];
snprintf(rp, sizeof(rp), "{\"method\":\"%s\"}", method ? method : "ANY");
int64_t rid = cbm_gbuf_upsert_node(gbuf, "Route", route_path, rqn, "", 0, 0, rp);
char props[CBM_SZ_512];
char esc_cn[CBM_SZ_256]; /* sliced source text: escape quotes/newlines */
char esc_rp[CBM_SZ_512];
cbm_json_escape(esc_cn, sizeof(esc_cn), call->callee_name);
cbm_json_escape(esc_rp, sizeof(esc_rp), route_path);
char props[CBM_SZ_1K];
snprintf(props, sizeof(props),
"{\"callee\":\"%s\",\"url_path\":\"%s\",\"via\":\"route_registration\"}",
call->callee_name, route_path);
"{\"callee\":\"%s\",\"url_path\":\"%s\",\"via\":\"route_registration\"}", esc_cn,
esc_rp);
cbm_gbuf_insert_edge(gbuf, source->id, rid, "CALLS", props);
if (handler_ref && handler_ref[0] != '\0') {
cbm_resolution_t hres = cbm_registry_resolve(registry, handler_ref, module_qn, ik, iv, ic);
if (hres.qualified_name && hres.qualified_name[0] != '\0') {
const cbm_gbuf_node_t *h = cbm_gbuf_find_by_qn(main_gbuf, hres.qualified_name);
if (h) {
char hp[CBM_SZ_256];
char esc_h2[CBM_SZ_256];
char hp[CBM_SZ_1K]; /* must exceed escaped value + wrapper or snprintf cuts the
closing brace */
char esc_h2[CBM_SZ_512];
cbm_json_escape(esc_h2, sizeof(esc_h2), hres.qualified_name);
snprintf(hp, sizeof(hp), "{\"handler\":\"%s\"}", esc_h2);
cbm_gbuf_insert_edge(gbuf, h->id, rid, "HANDLES", hp);
+14
View File
@@ -16,6 +16,7 @@
*/
#include "foundation/constants.h"
#include "foundation/str_util.h" // cbm_json_escape
#include <yyjson/yyjson.h> // validate assembled DATA_FLOWS props
enum {
RN_MAX_SCHEME = 12,
@@ -610,6 +611,19 @@ static int try_create_data_flow(cbm_gbuf_t *gb, int64_t caller_id, int64_t handl
if (n > 0 && (size_t)n < sizeof(props) - RN_PROPS_MARGIN) {
finish_data_flow_props(props, sizeof(props), (size_t)n, handler_params, args_json);
}
/* handler_params/args_json are sliced text fragments; truncation or stray
* quotes can leave the assembled JSON malformed, which aborts every
* json_extract consumer downstream. Validate and fall back to the minimal
* envelope rather than persisting invalid properties. */
{
yyjson_doc *vdoc = yyjson_read(props, strlen(props), 0);
if (!vdoc) {
snprintf(props, sizeof(props), "{\"via\":\"%s\",\"edge_type\":\"%s\"}", esc_rname,
edge_type);
} else {
yyjson_doc_free(vdoc);
}
}
cbm_gbuf_insert_edge(gb, caller_id, handler_id, "DATA_FLOWS", props);
return SKIP_ONE;
}