| Author | Andy Green <andy@warmcat.com> 2026-09-06 07:41 UTC | | Committer | Andy Green <andy@warmcat.com> 2026-09-06 07:41 UTC | | Tree | 86777c08b3c40c1b22ed535aa46023efab7c0eab 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__);
|