a4befd9382fadf413884d2215012535d5d667063 braney Mon Aug 17 13:46:45 2026 -0700 hgApi, hgTracks: tighten callback parameter validation, refs #38126 #38057 Add isValidJsonpCallback() and apply it to the callback-name paths in apiOut() and the hgTracks jsonp output, so only C-symbol dotted names are echoed back. diff --git src/lib/cheapcgi.c src/lib/cheapcgi.c index 5e972091198..cb73dcd9463 100644 --- src/lib/cheapcgi.c +++ src/lib/cheapcgi.c @@ -2962,15 +2962,46 @@ char *s = NULL; for (el = elList; el != NULL; el = el->next) { if (firstTime) firstTime = FALSE; else dyStringAppendC(dy, '&'); dyStringAppend(dy, el->name); dyStringAppendC(dy, '='); s = cgiEncode(el->val); dyStringAppend(dy, s); freez(&s); } hashElFreeList(&elList); } + +boolean isValidJsonpCallback(char *s) +/* Return TRUE if s is safe to use as a JSONP callback name: non-empty, not + * too long, and every dot-separated segment is a C symbol (letters, digits, + * underscore, not starting with a digit). This rejects anything with + * parentheses, spaces, operators, or other characters that would let an + * attacker turn a same-origin JSONP response into arbitrary script. */ +{ +if (isEmpty(s)) + return FALSE; +if (strlen(s) > 128) + return FALSE; +char *dupe = cloneString(s); +boolean ok = TRUE; +char *seg = dupe; +char *dot; +while (seg != NULL) + { + dot = strchr(seg, '.'); + if (dot != NULL) + *dot = 0; + if (!isSymbolString(seg)) + { + ok = FALSE; + break; + } + seg = (dot != NULL) ? dot + 1 : NULL; + } +freeMem(dupe); +return ok; +}