21805a85e551d9688edb34683f955c8a41710e80 jcasper Wed Mar 18 22:17:44 2026 -0700 Added a #defined-out version of cgiDecode that's much faster for large cart variables. Came up once years ago but was never put into practice. refs #37262 diff --git src/lib/cheapcgi.c src/lib/cheapcgi.c index 288b1c4c02e..798a498a25b 100644 --- src/lib/cheapcgi.c +++ src/lib/cheapcgi.c @@ -6,30 +6,33 @@ #include "common.h" #include "hash.h" #include "cheapcgi.h" #include "portable.h" #include "linefile.h" #include "errAbort.h" #include "filePath.h" #include "htmshell.h" #include "dystring.h" #ifndef GBROWSE #include "mime.h" #endif /* GBROWSE */ #include +// FAST_CGI_DECODE can be defined in cheapcgi.h to try a faster decode process that +// also limits variable/value lengths and applies encoding to variable names + //============ javascript inline-separation routines =============== // One of the main services that CSP (Content Security Policy) provides // is protecting from reflected and stored XSS attacks by disabling all inline javacript, // both in script tags, and in inline event handlers. The separated javascript // can be either added back to the end of the html page with a nonce or sha hashid, // or it can be saved to a temp file in trash and then included as a non-inline, off-page .js. struct dyString *jsInlineLines = NULL; void jsInlineInit() /* init if needed */ { if (!jsInlineLines) { @@ -743,35 +746,49 @@ namePt = str; while (isNotEmpty(namePt)) { dataPt = strchr(namePt, '='); if (dataPt == NULL) errAbort("Mangled Cookie input string: no = in '%s' (offset %d in complete cookie string: '%s')", namePt, (int)(namePt - str), getenv("HTTP_COOKIE")); *dataPt++ = 0; nextNamePt = strchr(dataPt, ';'); if (nextNamePt != NULL) { *nextNamePt++ = 0; if (*nextNamePt == ' ') nextNamePt++; } +#ifndef FAST_CGI_DECODE cgiDecode(dataPt,dataPt,strlen(dataPt)); AllocVar(el); el->val = dataPt; slAddHead(&list, el); hashAddSaveName(hash, namePt, el, &el->name); +#else + int dataSize = strlen(dataPt); + int nameSize = strlen(namePt); + if ((dataSize <= CGI_VAR_SIZE_LIMIT) && (nameSize < CGI_VAR_NAME_LIMIT)) + { + cgiDecode(namePt,namePt,nameSize); + cgiDecode(dataPt,dataPt,dataSize); + AllocVar(el); + el->val = dataPt; + slAddHead(&list, el); + hashAddSaveName(hash, namePt, el, &el->name); + } +#endif // FAST_CGI_DECODE namePt = nextNamePt; } haveCookiesHash = TRUE; slReverse(&list); *retList = list; *retHash = hash; } char *findCookieData(char *varName) /* Get the string associated with varName from the cookie string. */ { struct hashEl *hel; char *firstResult; @@ -870,53 +887,92 @@ next = el->next; cgiDictionaryFree(&el); } *pList = NULL; } boolean cgiParseNext(char **pInput, char **retVar, char **retVal) /* Parse out next var/val in a var=val&var=val... cgi formatted string * This will insert zeroes and other things into string. * Usage: * char *pt = cgiStringStart; * char *var, *val * while (cgiParseNext(&pt, &var, &val)) * printf("%s\t%s\n", var, val); */ { +#ifndef FAST_CGI_DECODE char *var = *pInput; if (var == NULL || var[0] == 0) return FALSE; char *val = strchr(var, '='); if (val == NULL) errAbort("Mangled CGI input string %s", var); *val++ = 0; char *end = strchr(val, '&'); if (end == NULL) end = strchr(val, ';'); // For DAS if (end == NULL) { end = val + strlen(val); *pInput = NULL; } else { *pInput = end+1; *end = 0; } *retVar = var; *retVal = val; cgiDecode(val,val,end-val); +#else +char *val = NULL; +char *var = NULL; +int varLength = 0; +int valLength = 0; +do + { + var = *pInput; + if (var == NULL || var[0] == 0) + { + *retVar = *retVal = NULL; + return FALSE; + } + val = strchr(var, '='); + if (val == NULL) + errAbort("Mangled CGI input string %s", var); + *val++ = 0; + char *end = strchr(val, '&'); + if (end == NULL) + end = strchr(val, ';'); // For DAS + if (end == NULL) + { + end = val + strlen(val); + *pInput = NULL; + } + else + { + *pInput = end+1; + *end = 0; + } + *retVar = var; + *retVal = val; + valLength = end-val; + } while ((varLength > CGI_VAR_NAME_LIMIT) || (valLength > CGI_VAR_SIZE_LIMIT)); + // skip variables that are too big +cgiDecode(var,var,valLength); +cgiDecode(val,val,valLength); +#endif // FAST_CGI_DECODE return TRUE; } void cgiSetMaxLogLen(int l) /* set the size of variable values that are dumped to stderr. Default is 0, which means no logging */ { logCgiVarMaxLen = l; } void cgiParseInputAbort(char *input, struct hash **retHash, struct cgiVar **retList) /* Parse cgi-style input into a hash table and list. This will alter * the input data. The hash table will contain references back * into input, so please don't free input until you're done with * the hash. Prints message aborts if there's an error. @@ -940,36 +996,48 @@ dataPt = strchr(namePt, '='); if (dataPt == NULL) { errAbort("Mangled CGI input string %s", namePt); } *dataPt++ = 0; nextNamePt = strchr(dataPt, '&'); if (nextNamePt == NULL) nextNamePt = strchr(dataPt, ';'); /* Accomodate DAS. */ if (nextNamePt != NULL) *nextNamePt++ = 0; if (logMsg && dataPt && strlen(dataPt) < logCgiVarMaxLen) dyStringPrintf(logMsg, "%s=%s ", namePt, dataPt); // if dataPt is empty string, still print it, could be important +#ifndef FAST_CGI_DECODE cgiDecode(namePt,namePt,strlen(namePt)); /* for unusual ct names */ cgiDecode(dataPt,dataPt,strlen(dataPt)); AllocVar(el); el->val = dataPt; slAddHead(&list, el); hashAddSaveName(hash, namePt, el, &el->name); +#else + if ((strlen(namePt) < CGI_VAR_NAME_LIMIT) && (strlen(dataPt) < CGI_VAR_SIZE_LIMIT)) + { + cgiDecode(namePt,namePt,strlen(namePt)); /* for unusual ct names */ + cgiDecode(dataPt,dataPt,strlen(dataPt)); + AllocVar(el); + el->val = dataPt; + slAddHead(&list, el); + hashAddSaveName(hash, namePt, el, &el->name); + } +#endif // FAST_CGI_DECODE namePt = nextNamePt; } if (logMsg) { char *logStr = dyStringCannibalize(&logMsg); fprintf(stderr, "CGIVARS %s\n", logStr); freez(&logStr); } slReverse(&list); *retList = list; *retHash = hash;