Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / terminal-core.js
Author[]Andy Green <andy@warmcat.com> 2026-09-06 07:41 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-06 07:41 UTC
Tree86777c08b3c40c1b22ed535aa46023efab7c0eab   Raw Patch
 
web: harden SQL literals in browser query paths, fixes F-007
web: harden SQL literals in browser query paths, fixes F-007

The overview filters interpolated browser-supplied project / ref /
event-uuid values into double-quoted SQL string literals after
lws_sql_purify(), which escapes single quotes only -- double quotes
passed through, so the predicate logic of the events SELECT could be
altered (eg OR-ed conditions), defeating that connection's own sidebar
scoping.  Use single-quoted literals like the rest of the file, which
lws_sql_purify() actually makes safe.

Also purify the db-derived uuid interpolations that had no escaping at
all: one_task->uuid in the taskinfo artifacts filter (filt widened so a
fully-quoted 64-char uuid cannot truncate the composed clause),
pss->sub_task_uuid in the log-batch filter, and e->uuid in the watcher
filter.  These ids are server-minted hex today; purifying keeps the
literal safe if that invariant ever weakens.
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 7491697..3e0422c 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -336,7 +336,7 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub, i struct lwsac *query_ac = NULL, *runs_ac = NULL, *art_ac = NULL; sai_task_t *one_task = NULL; lws_struct_serialize_t *js; - char esc[256], filt[128]; + char esc[256], filt[192]; lws_dll2_owner_t owner; sqlite3 *pdb = NULL; lws_dll2_owner_t o; @@ -502,12 +502,15 @@ saiw_pss_schedule_taskinfo(struct pss *pss, const char *task_uuid, int logsub, i pss->vhd->sqlite3_path_lhs, event_uuid, 0, &pdb)) { + /* uuid is db-derived, purify keeps the literal safe anyway */ + lws_sql_purify(esc, one_task->uuid, sizeof(esc)); + if (run_idx >= 0) lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d", - one_task->uuid, run_idx); + esc, run_idx); else lws_snprintf(filt, sizeof(filt), " and (task_uuid == '%s') and run=%d", - one_task->uuid, one_task->run); + esc, one_task->run); if (lws_struct_sq3_deserialize(pdb, filt, NULL, lsm_schema_sq3_map_artifact, @@ -1215,16 +1218,19 @@ saiw_broadcast_logs_batch(struct vhd *vhd, struct pss *pss) //if (pss->log_cache_index == pss->log_cache_size) { sqlite3 *pdb = NULL; - char esc[256]; + char esc[256], pesc[132]; int sr; sai_task_uuid_to_event_uuid(event_uuid, pss->sub_task_uuid); lwsac_free(&pss->logs_ac); + /* uuid is db-derived, purify keeps the literal safe anyway */ + lws_sql_purify(pesc, pss->sub_task_uuid, sizeof(pesc)); + lws_snprintf(esc, sizeof(esc), "and task_uuid='%s' and run=%d and timestamp > %llu", - pss->sub_task_uuid, pss->sub_run, + pesc, pss->sub_run, (unsigned long long)pss->sub_timestamp); // lwsl_notice("%s: collecting logs %s\n", __func__, esc); @@ -1479,7 +1485,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) */ lws_sql_purify(esc, pss->specific_project, sizeof(esc) - 1); lws_snprintf(filt, sizeof(filt), - " and state != %d and repo_name=\"%s\"", + " and state != %d and repo_name='%s'", SAIES_DELETED, esc); n = -1; } else { @@ -1504,7 +1510,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) lws_sql_purify(esc, pss->event_tasks_uuid, sizeof(esc) - 1); lws_snprintf(filt + fl, sizeof(filt) - fl, - " and uuid=\"%s\"", esc); + " and uuid='%s'", esc); n = -1; } @@ -1518,7 +1524,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) lws_sql_purify(esc, pss->selected_project, sizeof(esc) - 1); lws_snprintf(filt + fl, sizeof(filt) - fl, - " and repo_name=\"%s\"", esc); + " and repo_name='%s'", esc); n = -100; } @@ -1527,7 +1533,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) lws_sql_purify(esc, pss->selected_ref, sizeof(esc) - 1); lws_snprintf(filt + fl, sizeof(filt) - fl, - " and ref=\"%s\"", esc); + " and ref='%s'", esc); if (n == -6) n = -100; } @@ -1608,10 +1614,12 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) } { - char wfilt[128]; + char wfilt[128], wesc[70]; struct lwsac *ac_watchers = NULL; lws_dll2_owner_clear(&e->watcher_owner); - lws_snprintf(wfilt, sizeof(wfilt), " and event_hash='%s'", e->uuid); + /* uuid is db-derived, purify keeps the literal safe anyway */ + lws_sql_purify(wesc, e->uuid, sizeof(wesc)); + lws_snprintf(wfilt, sizeof(wfilt), " and event_hash='%s'", wesc); if (lws_struct_sq3_deserialize(vhd->pdb, wfilt, "created", lsm_schema_sq3_map_watcher, &e->watcher_owner, &ac_watchers, 0, 0) < 0) lwsl_err("%s: watchers deserialize failed\n", __func__);
Page fetched -1s ago, creation time: 3ms (vhost etag hits: 0%, cache hits: 0%)