github.com/TeaOSLab/EdgeNode@v1.3.8/internal/waf/injectionutils/libinjection/src/libinjection_xss.c (about) 1 2 #include "libinjection.h" 3 #include "libinjection_xss.h" 4 #include "libinjection_html5.h" 5 6 #include <assert.h> 7 #include <stdio.h> 8 9 typedef enum attribute { 10 TYPE_NONE 11 , TYPE_BLACK /* ban always */ 12 , TYPE_ATTR_URL /* attribute value takes a URL-like object */ 13 , TYPE_STYLE 14 , TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */ 15 } attribute_t; 16 17 18 static attribute_t is_black_attr(const char* s, size_t len, int strictMode); 19 static int is_black_tag(const char* s, size_t len, int strictMode); 20 static int is_black_url(const char* s, size_t len); 21 static int cstrcasecmp_with_null(const char *a, const char *b, size_t n); 22 static int html_decode_char_at(const char* src, size_t len, size_t* consumed); 23 static int htmlencode_startswith(const char *a/* prefix */, const char *b /* src */, size_t n); 24 25 26 typedef struct stringtype { 27 const char* name; 28 attribute_t atype; 29 } stringtype_t; 30 31 32 static const int gsHexDecodeMap[256] = { 33 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 34 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 35 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 36 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 37 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256, 38 256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256, 39 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 40 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 41 256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256, 42 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 43 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 44 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 45 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 46 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 47 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 48 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 49 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 50 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 51 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 52 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 53 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 54 256, 256, 256, 256 55 }; 56 57 static int html_decode_char_at(const char* src, size_t len, size_t* consumed) 58 { 59 int val = 0; 60 size_t i; 61 int ch; 62 63 if (len == 0 || src == NULL) { 64 *consumed = 0; 65 return -1; 66 } 67 68 *consumed = 1; 69 if (*src != '&' || len < 2) { 70 return (unsigned char)(*src); 71 } 72 73 74 if (*(src+1) != '#') { 75 /* normally this would be for named entities 76 * but for this case we don't actually care 77 */ 78 return '&'; 79 } 80 81 if (*(src+2) == 'x' || *(src+2) == 'X') { 82 ch = (unsigned char) (*(src+3)); 83 ch = gsHexDecodeMap[ch]; 84 if (ch == 256) { 85 /* degenerate case '&#[?]' */ 86 return '&'; 87 } 88 val = ch; 89 i = 4; 90 while (i < len) { 91 ch = (unsigned char) src[i]; 92 if (ch == ';') { 93 *consumed = i + 1; 94 return val; 95 } 96 ch = gsHexDecodeMap[ch]; 97 if (ch == 256) { 98 *consumed = i; 99 return val; 100 } 101 val = (val * 16) + ch; 102 if (val > 0x1000FF) { 103 return '&'; 104 } 105 ++i; 106 } 107 *consumed = i; 108 return val; 109 } else { 110 i = 2; 111 ch = (unsigned char) src[i]; 112 if (ch < '0' || ch > '9') { 113 return '&'; 114 } 115 val = ch - '0'; 116 i += 1; 117 while (i < len) { 118 ch = (unsigned char) src[i]; 119 if (ch == ';') { 120 *consumed = i + 1; 121 return val; 122 } 123 if (ch < '0' || ch > '9') { 124 *consumed = i; 125 return val; 126 } 127 val = (val * 10) + (ch - '0'); 128 if (val > 0x1000FF) { 129 return '&'; 130 } 131 ++i; 132 } 133 *consumed = i; 134 return val; 135 } 136 } 137 138 /* 139 * These were mostly extracted from: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/WebCore/dom/EventNames.h 140 * 141 * view-source: 142 * data: 143 * javascript: 144 * events: 145 */ 146 static stringtype_t BLACKATTREVENT[] = { 147 { "ABORT", TYPE_BLACK } 148 , { "ACTIVATE", TYPE_BLACK } 149 , { "ACTIVE", TYPE_BLACK } 150 , { "ADDSOURCEBUFFER", TYPE_BLACK } 151 , { "ADDSTREAM", TYPE_BLACK } 152 , { "ADDTRACK", TYPE_BLACK } 153 , { "AFTERPRINT", TYPE_BLACK } 154 , { "ANIMATIONCANCEL", TYPE_BLACK } 155 , { "ANIMATIONEND", TYPE_BLACK } 156 , { "ANIMATIONITERATION", TYPE_BLACK } 157 , { "ANIMATIONSTART", TYPE_BLACK } 158 , { "AUDIOEND", TYPE_BLACK } 159 , { "AUDIOPROCESS", TYPE_BLACK } 160 , { "AUDIOSTART", TYPE_BLACK } 161 , { "AUTOCOMPLETEERROR", TYPE_BLACK } 162 , { "AUTOCOMPLETE", TYPE_BLACK } 163 , { "BEFOREACTIVATE", TYPE_BLACK } 164 , { "BEFORECOPY", TYPE_BLACK } 165 , { "BEFORECUT", TYPE_BLACK } 166 , { "BEFOREINPUT", TYPE_BLACK } 167 , { "BEFORELOAD", TYPE_BLACK } 168 , { "BEFOREPASTE", TYPE_BLACK } 169 , { "BEFOREPRINT", TYPE_BLACK } 170 , { "BEFOREUNLOAD", TYPE_BLACK } 171 , { "BEGINEVENT", TYPE_BLACK } 172 , { "BLOCKED", TYPE_BLACK } 173 , { "BLUR", TYPE_BLACK } 174 , { "BOUNDARY", TYPE_BLACK } 175 , { "BUFFEREDAMOUNTLOW", TYPE_BLACK } 176 , { "CACHED", TYPE_BLACK } 177 , { "CANCEL", TYPE_BLACK } 178 , { "CANPLAYTHROUGH", TYPE_BLACK } 179 , { "CANPLAY", TYPE_BLACK } 180 , { "CHANGE", TYPE_BLACK } 181 , { "CHARGINGCHANGE", TYPE_BLACK } 182 , { "CHARGINGTIMECHANGE", TYPE_BLACK } 183 , { "CHECKING", TYPE_BLACK } 184 , { "CLICK", TYPE_BLACK } 185 , { "CLOSE", TYPE_BLACK } 186 , { "COMPLETE", TYPE_BLACK } 187 , { "COMPOSITIONEND", TYPE_BLACK } 188 , { "COMPOSITIONSTART", TYPE_BLACK } 189 , { "COMPOSITIONUPDATE", TYPE_BLACK } 190 , { "CONNECTING", TYPE_BLACK } 191 , { "CONNECTIONSTATECHANGE", TYPE_BLACK } 192 , { "CONNECT", TYPE_BLACK } 193 , { "CONTEXTMENU", TYPE_BLACK } 194 , { "CONTROLLERCHANGE", TYPE_BLACK } 195 , { "COPY", TYPE_BLACK } 196 , { "CUECHANGE", TYPE_BLACK } 197 , { "CUT", TYPE_BLACK } 198 , { "DATAAVAILABLE", TYPE_BLACK } 199 , { "DATACHANNEL", TYPE_BLACK } 200 , { "DBLCLICK", TYPE_BLACK } 201 , { "DEVICECHANGE", TYPE_BLACK } 202 , { "DEVICEMOTION", TYPE_BLACK } 203 , { "DEVICEORIENTATION", TYPE_BLACK } 204 , { "DISCHARGINGTIMECHANGE", TYPE_BLACK } 205 , { "DISCONNECT", TYPE_BLACK } 206 , { "DOMACTIVATE", TYPE_BLACK } 207 , { "DOMCHARACTERDATAMODIFIED", TYPE_BLACK } 208 , { "DOMCONTENTLOADED", TYPE_BLACK } 209 , { "DOMFOCUSIN", TYPE_BLACK } 210 , { "DOMFOCUSOUT", TYPE_BLACK } 211 , { "DOMNODEINSERTEDINTODOCUMENT", TYPE_BLACK } 212 , { "DOMNODEINSERTED", TYPE_BLACK } 213 , { "DOMNODEREMOVEDFROMDOCUMENT", TYPE_BLACK } 214 , { "DOMNODEREMOVED", TYPE_BLACK } 215 , { "DOMSUBTREEMODIFIED", TYPE_BLACK } 216 , { "DOWNLOADING", TYPE_BLACK } 217 , { "DRAGEND", TYPE_BLACK } 218 , { "DRAGENTER", TYPE_BLACK } 219 , { "DRAGLEAVE", TYPE_BLACK } 220 , { "DRAGOVER", TYPE_BLACK } 221 , { "DRAGSTART", TYPE_BLACK } 222 , { "DRAG", TYPE_BLACK } 223 , { "DROP", TYPE_BLACK } 224 , { "DURATIONCHANGE", TYPE_BLACK } 225 , { "EMPTIED", TYPE_BLACK } 226 , { "ENCRYPTED", TYPE_BLACK } 227 , { "ENDED", TYPE_BLACK } 228 , { "ENDEVENT", TYPE_BLACK } 229 , { "END", TYPE_BLACK } 230 , { "ENTERPICTUREINPICTURE", TYPE_BLACK } 231 , { "ENTER", TYPE_BLACK } 232 , { "ERROR", TYPE_BLACK } 233 , { "EXIT", TYPE_BLACK } 234 , { "FETCH", TYPE_BLACK } 235 , { "FINISH", TYPE_BLACK } 236 , { "FOCUSIN", TYPE_BLACK } 237 , { "FOCUSOUT", TYPE_BLACK } 238 , { "FOCUS", TYPE_BLACK } 239 , { "FORMCHANGE", TYPE_BLACK } 240 , { "FORMINPUT", TYPE_BLACK } 241 , { "GAMEPADCONNECTED", TYPE_BLACK } 242 , { "GAMEPADDISCONNECTED", TYPE_BLACK } 243 , { "GESTURECHANGE", TYPE_BLACK } 244 , { "GESTUREEND", TYPE_BLACK } 245 , { "GESTURESCROLLEND", TYPE_BLACK } 246 , { "GESTURESCROLLSTART", TYPE_BLACK } 247 , { "GESTURESCROLLUPDATE", TYPE_BLACK } 248 , { "GESTURESTART", TYPE_BLACK } 249 , { "GESTURETAPDOWN", TYPE_BLACK } 250 , { "GESTURETAP", TYPE_BLACK } 251 , { "GOTPOINTERCAPTURE", TYPE_BLACK } 252 , { "HASHCHANGE", TYPE_BLACK } 253 , { "ICECANDIDATEERROR", TYPE_BLACK } 254 , { "ICECANDIDATE", TYPE_BLACK } 255 , { "ICECONNECTIONSTATECHANGE", TYPE_BLACK } 256 , { "ICEGATHERINGSTATECHANGE", TYPE_BLACK } 257 , { "INACTIVE", TYPE_BLACK } 258 , { "INPUTSOURCESCHANGE", TYPE_BLACK } 259 , { "INPUT", TYPE_BLACK } 260 , { "INSTALL", TYPE_BLACK } 261 , { "INVALID", TYPE_BLACK } 262 , { "KEYDOWN", TYPE_BLACK } 263 , { "KEYPRESS", TYPE_BLACK } 264 , { "KEYSTATUSESCHANGE", TYPE_BLACK } 265 , { "KEYUP", TYPE_BLACK } 266 , { "LANGUAGECHANGE", TYPE_BLACK } 267 , { "LEAVEPICTUREINPICTURE", TYPE_BLACK } 268 , { "LEVELCHANGE", TYPE_BLACK } 269 , { "LOADEDDATA", TYPE_BLACK } 270 , { "LOADEDMETADATA", TYPE_BLACK } 271 , { "LOADEND", TYPE_BLACK } 272 , { "LOADINGDONE", TYPE_BLACK } 273 , { "LOADINGERROR", TYPE_BLACK } 274 , { "LOADING", TYPE_BLACK } 275 , { "LOADSTART", TYPE_BLACK } 276 , { "LOAD", TYPE_BLACK } 277 , { "LOSTPOINTERCAPTURE", TYPE_BLACK } 278 , { "MARK", TYPE_BLACK } 279 , { "MERCHANTVALIDATION", TYPE_BLACK } 280 , { "MESSAGEERROR", TYPE_BLACK } 281 , { "MESSAGE", TYPE_BLACK } 282 , { "MOUSEDOWN", TYPE_BLACK } 283 , { "MOUSEENTER", TYPE_BLACK } 284 , { "MOUSELEAVE", TYPE_BLACK } 285 , { "MOUSEMOVE", TYPE_BLACK } 286 , { "MOUSEOUT", TYPE_BLACK } 287 , { "MOUSEOVER", TYPE_BLACK } 288 , { "MOUSEUP", TYPE_BLACK } 289 , { "MOUSEWHEEL", TYPE_BLACK } 290 , { "MUTE", TYPE_BLACK } 291 , { "NEGOTIATIONNEEDED", TYPE_BLACK } 292 , { "NEXTTRACK", TYPE_BLACK } 293 , { "NOMATCH", TYPE_BLACK } 294 , { "NOUPDATE", TYPE_BLACK } 295 , { "OBSOLETE", TYPE_BLACK } 296 , { "OFFLINE", TYPE_BLACK } 297 , { "ONLINE", TYPE_BLACK } 298 , { "OPEN", TYPE_BLACK } 299 , { "ORIENTATIONCHANGE", TYPE_BLACK } 300 , { "OVERCONSTRAINED", TYPE_BLACK } 301 , { "OVERFLOWCHANGED", TYPE_BLACK } 302 , { "PAGEHIDE", TYPE_BLACK } 303 , { "PAGESHOW", TYPE_BLACK } 304 , { "PASTE", TYPE_BLACK } 305 , { "PAUSE", TYPE_BLACK } 306 , { "PAYERDETAILCHANGE", TYPE_BLACK } 307 , { "PAYMENTAUTHORIZED", TYPE_BLACK } 308 , { "PAYMENTMETHODCHANGE", TYPE_BLACK } 309 , { "PAYMENTMETHODSELECTED", TYPE_BLACK } 310 , { "PLAYING", TYPE_BLACK } 311 , { "PLAY", TYPE_BLACK } 312 , { "POINTERCANCEL", TYPE_BLACK } 313 , { "POINTERDOWN", TYPE_BLACK } 314 , { "POINTERENTER", TYPE_BLACK } 315 , { "POINTERLEAVE", TYPE_BLACK } 316 , { "POINTERLOCKCHANGE", TYPE_BLACK } 317 , { "POINTERLOCKERROR", TYPE_BLACK } 318 , { "POINTERMOVE", TYPE_BLACK } 319 , { "POINTEROUT", TYPE_BLACK } 320 , { "POINTEROVER", TYPE_BLACK } 321 , { "POINTERUP", TYPE_BLACK } 322 , { "POPSTATE", TYPE_BLACK } 323 , { "PREVIOUSTRACK", TYPE_BLACK } 324 , { "PROCESSORERROR", TYPE_BLACK } 325 , { "PROGRESS", TYPE_BLACK } 326 , { "PROPERTYCHANGE", TYPE_BLACK } 327 , { "RATECHANGE", TYPE_BLACK } 328 , { "READYSTATECHANGE", TYPE_BLACK } 329 , { "REJECTIONHANDLED", TYPE_BLACK } 330 , { "REMOVESOURCEBUFFER", TYPE_BLACK } 331 , { "REMOVESTREAM", TYPE_BLACK } 332 , { "REMOVETRACK", TYPE_BLACK } 333 , { "REMOVE", TYPE_BLACK } 334 , { "RESET", TYPE_BLACK } 335 , { "RESIZE", TYPE_BLACK } 336 , { "RESOURCETIMINGBUFFERFULL", TYPE_BLACK } 337 , { "RESULT", TYPE_BLACK } 338 , { "RESUME", TYPE_BLACK } 339 , { "SCROLL", TYPE_BLACK } 340 , { "SEARCH", TYPE_BLACK } 341 , { "SECURITYPOLICYVIOLATION", TYPE_BLACK } 342 , { "SEEKED", TYPE_BLACK } 343 , { "SEEKING", TYPE_BLACK } 344 , { "SELECTEND", TYPE_BLACK } 345 , { "SELECTIONCHANGE", TYPE_BLACK } 346 , { "SELECTSTART", TYPE_BLACK } 347 , { "SELECT", TYPE_BLACK } 348 , { "SHIPPINGADDRESSCHANGE", TYPE_BLACK } 349 , { "SHIPPINGCONTACTSELECTED", TYPE_BLACK } 350 , { "SHIPPINGMETHODSELECTED", TYPE_BLACK } 351 , { "SHIPPINGOPTIONCHANGE", TYPE_BLACK } 352 , { "SHOW", TYPE_BLACK } 353 , { "SIGNALINGSTATECHANGE", TYPE_BLACK } 354 , { "SLOTCHANGE", TYPE_BLACK } 355 , { "SOUNDEND", TYPE_BLACK } 356 , { "SOUNDSTART", TYPE_BLACK } 357 , { "SOURCECLOSE", TYPE_BLACK } 358 , { "SOURCEENDED", TYPE_BLACK } 359 , { "SOURCEOPEN", TYPE_BLACK } 360 , { "SPEECHEND", TYPE_BLACK } 361 , { "SPEECHSTART", TYPE_BLACK } 362 , { "SQUEEZEEND", TYPE_BLACK } 363 , { "SQUEEZESTART", TYPE_BLACK } 364 , { "SQUEEZE", TYPE_BLACK } 365 , { "STALLED", TYPE_BLACK } 366 , { "STARTED", TYPE_BLACK } 367 , { "START", TYPE_BLACK } 368 , { "STATECHANGE", TYPE_BLACK } 369 , { "STOP", TYPE_BLACK } 370 , { "STORAGE", TYPE_BLACK } 371 , { "SUBMIT", TYPE_BLACK } 372 , { "SUCCESS", TYPE_BLACK } 373 , { "SUSPEND", TYPE_BLACK } 374 , { "TEXTINPUT", TYPE_BLACK } 375 , { "TIMEOUT", TYPE_BLACK } 376 , { "TIMEUPDATE", TYPE_BLACK } 377 , { "TOGGLE", TYPE_BLACK } 378 , { "TOGGLE", TYPE_BLACK } 379 , { "TONECHANGE", TYPE_BLACK } 380 , { "TOUCHCANCEL", TYPE_BLACK } 381 , { "TOUCHEND", TYPE_BLACK } 382 , { "TOUCHFORCECHANGE", TYPE_BLACK } 383 , { "TOUCHMOVE", TYPE_BLACK } 384 , { "TOUCHSTART", TYPE_BLACK } 385 , { "TRACK", TYPE_BLACK } 386 , { "TRANSITIONCANCEL", TYPE_BLACK } 387 , { "TRANSITIONEND", TYPE_BLACK } 388 , { "TRANSITIONRUN", TYPE_BLACK } 389 , { "TRANSITIONSTART", TYPE_BLACK } 390 , { "UNCAPTUREDERROR", TYPE_BLACK } 391 , { "UNHANDLEDREJECTION", TYPE_BLACK } 392 , { "UNLOAD", TYPE_BLACK } 393 , { "UNMUTE", TYPE_BLACK } 394 , { "UPDATEEND", TYPE_BLACK } 395 , { "UPDATEFOUND", TYPE_BLACK } 396 , { "UPDATEREADY", TYPE_BLACK } 397 , { "UPDATESTART", TYPE_BLACK } 398 , { "UPDATE", TYPE_BLACK } 399 , { "UPGRADENEEDED", TYPE_BLACK } 400 , { "VALIDATEMERCHANT", TYPE_BLACK } 401 , { "VERSIONCHANGE", TYPE_BLACK } 402 , { "VISIBILITYCHANGE", TYPE_BLACK } 403 , { "VOLUMECHANGE", TYPE_BLACK } 404 , { "WAITINGFORKEY", TYPE_BLACK } 405 , { "WAITING", TYPE_BLACK } 406 , { "WEBGLCONTEXTCHANGED", TYPE_BLACK } 407 , { "WEBGLCONTEXTCREATIONERROR", TYPE_BLACK } 408 , { "WEBGLCONTEXTLOST", TYPE_BLACK } 409 , { "WEBGLCONTEXTRESTORED", TYPE_BLACK } 410 , { "WEBKITANIMATIONEND", TYPE_BLACK } 411 , { "WEBKITANIMATIONITERATION", TYPE_BLACK } 412 , { "WEBKITANIMATIONSTART", TYPE_BLACK } 413 , { "WEBKITBEFORETEXTINSERTED", TYPE_BLACK } 414 , { "WEBKITBEGINFULLSCREEN", TYPE_BLACK } 415 , { "WEBKITCURRENTPLAYBACKTARGETISWIRELESSCHANGED", TYPE_BLACK } 416 , { "WEBKITENDFULLSCREEN", TYPE_BLACK } 417 , { "WEBKITFULLSCREENCHANGE", TYPE_BLACK } 418 , { "WEBKITFULLSCREENERROR", TYPE_BLACK } 419 , { "WEBKITKEYADDED", TYPE_BLACK } 420 , { "WEBKITKEYERROR", TYPE_BLACK } 421 , { "WEBKITKEYMESSAGE", TYPE_BLACK } 422 , { "WEBKITMOUSEFORCECHANGED", TYPE_BLACK } 423 , { "WEBKITMOUSEFORCEDOWN", TYPE_BLACK } 424 , { "WEBKITMOUSEFORCEUP", TYPE_BLACK } 425 , { "WEBKITMOUSEFORCEWILLBEGIN", TYPE_BLACK } 426 , { "WEBKITNEEDKEY", TYPE_BLACK } 427 , { "WEBKITNETWORKINFOCHANGE", TYPE_BLACK } 428 , { "WEBKITPLAYBACKTARGETAVAILABILITYCHANGED", TYPE_BLACK } 429 , { "WEBKITPRESENTATIONMODECHANGED", TYPE_BLACK } 430 , { "WEBKITREGIONOVERSETCHANGE", TYPE_BLACK } 431 , { "WEBKITREMOVESOURCEBUFFER", TYPE_BLACK } 432 , { "WEBKITSOURCECLOSE", TYPE_BLACK } 433 , { "WEBKITSOURCEENDED", TYPE_BLACK } 434 , { "WEBKITSOURCEOPEN", TYPE_BLACK } 435 , { "WEBKITSPEECHCHANGE", TYPE_BLACK } 436 , { "WEBKITTRANSITIONEND", TYPE_BLACK } 437 , { "WEBKITWILLREVEALBOTTOM", TYPE_BLACK } 438 , { "WEBKITWILLREVEALLEFT", TYPE_BLACK } 439 , { "WEBKITWILLREVEALRIGHT", TYPE_BLACK } 440 , { "WEBKITWILLREVEALTOP", TYPE_BLACK } 441 , { "WHEEL", TYPE_BLACK } 442 , { "WRITEEND", TYPE_BLACK } 443 , { "WRITESTART", TYPE_BLACK } 444 , { "WRITE", TYPE_BLACK } 445 , { "ZOOM", TYPE_BLACK } 446 , { NULL, TYPE_NONE } 447 }; 448 449 /* 450 * view-source: 451 * data: 452 * javascript: 453 */ 454 static stringtype_t STRICT_BLACKATTR[] = { 455 { "ACTION", TYPE_ATTR_URL } /* form */ 456 , { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */ 457 , { "BY", TYPE_ATTR_URL } /* SVG */ 458 , { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */ 459 , { "DATAFORMATAS", TYPE_BLACK } /* IE */ 460 , { "DATASRC", TYPE_BLACK } /* IE */ 461 , { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */ 462 , { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */ 463 , { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */ 464 , { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */ 465 , { "FROM", TYPE_ATTR_URL } /* SVG */ 466 , { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */ 467 , { "HREF", TYPE_ATTR_URL } 468 , { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */ 469 , { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */ 470 , { "SRC", TYPE_ATTR_URL } 471 , { "STYLE", TYPE_STYLE } 472 , { "TO", TYPE_ATTR_URL } /* SVG */ 473 , { "VALUES", TYPE_ATTR_URL } /* SVG */ 474 , { "XLINK:HREF", TYPE_ATTR_URL } 475 , { NULL, TYPE_NONE } 476 }; 477 478 static stringtype_t BLACKATTR[] = { 479 { "ACTION", TYPE_ATTR_URL } /* form */ 480 , { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */ 481 , { "BY", TYPE_ATTR_URL } /* SVG */ 482 , { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */ 483 , { "DATAFORMATAS", TYPE_BLACK } /* IE */ 484 , { "DATASRC", TYPE_BLACK } /* IE */ 485 , { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */ 486 , { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */ 487 , { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */ 488 , { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */ 489 , { "FROM", TYPE_ATTR_URL } /* SVG */ 490 , { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */ 491 , { "HREF", TYPE_ATTR_URL } 492 , { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */ 493 , { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */ 494 , { "SRC", TYPE_ATTR_URL } 495 , { "TO", TYPE_ATTR_URL } /* SVG */ 496 , { "VALUES", TYPE_ATTR_URL } /* SVG */ 497 , { "XLINK:HREF", TYPE_ATTR_URL } 498 , { NULL, TYPE_NONE } 499 }; 500 501 502 503 /* xmlns */ 504 /* `xml-stylesheet` > <eval>, <if expr=> */ 505 506 /* 507 static const char* BLACKATTR[] = { 508 "ATTRIBUTENAME", 509 "BACKGROUND", 510 "DATAFORMATAS", 511 "HREF", 512 "SCROLL", 513 "SRC", 514 "STYLE", 515 "SRCDOC", 516 NULL 517 }; 518 */ 519 520 // GoEdge: change BLACKTAG to STRICT_BLACKTAG 521 static const char* STRICT_BLACKTAG[] = { 522 "APPLET" 523 , "AUDIO" 524 , "BASE" 525 , "COMMENT" /* IE http://html5sec.org/#38 */ 526 , "EMBED" 527 , "FORM" 528 , "FRAME" 529 , "FRAMESET" 530 , "HANDLER" /* Opera SVG, effectively a script tag */ 531 , "IFRAME" 532 , "IMPORT" 533 , "ISINDEX" 534 , "LINK" 535 , "LISTENER" 536 /* , "MARQUEE" */ 537 , "META" 538 , "NOSCRIPT" 539 , "OBJECT" 540 , "SCRIPT" 541 , "STYLE" 542 , "VIDEO" 543 , "VMLFRAME" 544 , "XML" 545 , "XSS" 546 , NULL 547 }; 548 549 static const char* BLACKTAG[] = { 550 "APPLET" 551 /* , "AUDIO" */ 552 , "BASE" 553 , "COMMENT" /* IE http://html5sec.org/#38 */ 554 , "EMBED" 555 /* , "FORM" */ 556 , "FRAME" 557 , "FRAMESET" 558 , "HANDLER" /* Opera SVG, effectively a script tag */ 559 , "IFRAME" 560 , "IMPORT" 561 , "ISINDEX" 562 , "LINK" 563 , "LISTENER" 564 /* , "MARQUEE" */ 565 , "META" 566 , "NOSCRIPT" 567 , "OBJECT" 568 , "SCRIPT" 569 , "STYLE" 570 /* , "VIDEO" */ 571 , "VMLFRAME" 572 , "XSS" 573 , NULL 574 }; 575 576 577 static int cstrcasecmp_with_null(const char *a, const char *b, size_t n) 578 { 579 char ca; 580 char cb; 581 /* printf("Comparing to %s %.*s\n", a, (int)n, b); */ 582 while (n-- > 0) { 583 cb = *b++; 584 if (cb == '\0') continue; 585 586 ca = *a++; 587 588 if (cb >= 'a' && cb <= 'z') { 589 cb -= 0x20; 590 } 591 /* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */ 592 if (ca != cb) { 593 return 1; 594 } 595 } 596 597 if (*a == 0) { 598 /* printf(" MATCH \n"); */ 599 return 0; 600 } else { 601 return 1; 602 } 603 } 604 605 /* 606 * Does an HTML encoded binary string (const char*, length) start with 607 * a all uppercase c-string (null terminated), case insensitive! 608 * 609 * also ignore any embedded nulls in the HTML string! 610 * 611 * return 1 if match / starts with 612 * return 0 if not 613 */ 614 static int htmlencode_startswith(const char *a, const char *b, size_t n) 615 { 616 size_t consumed; 617 int cb; 618 int first = 1; 619 /* printf("Comparing %s with %.*s\n", a,(int)n,b); */ 620 while (n > 0) { 621 if (*a == 0) { 622 /* printf("Match EOL!\n"); */ 623 return 1; 624 } 625 cb = html_decode_char_at(b, n, &consumed); 626 b += consumed; 627 n -= consumed; 628 629 if (first && cb <= 32) { 630 /* ignore all leading whitespace and control characters */ 631 continue; 632 } 633 first = 0; 634 635 if (cb == 0) { 636 /* always ignore null characters in user input */ 637 continue; 638 } 639 640 if (cb == 10) { 641 /* always ignore vertical tab characters in user input */ 642 /* who allows this?? */ 643 continue; 644 } 645 646 if (cb >= 'a' && cb <= 'z') { 647 /* upcase */ 648 cb -= 0x20; 649 } 650 651 if (*a != (char) cb) { 652 /* printf(" %c != %c\n", *a, cb); */ 653 /* mismatch */ 654 return 0; 655 } 656 a++; 657 } 658 659 return (*a == 0) ? 1 : 0; 660 } 661 662 static int is_black_tag(const char* s, size_t len, int strictMode) 663 { 664 const char** black; 665 666 if (len < 3) { 667 return 0; 668 } 669 670 if (strictMode == 1) { 671 black = STRICT_BLACKTAG; 672 } else { 673 black = BLACKTAG; 674 } 675 while (*black != NULL) { 676 if (cstrcasecmp_with_null(*black, s, len) == 0) { 677 /* printf("Got black tag %s\n", *black); */ 678 return 1; 679 } 680 black += 1; 681 } 682 683 /* anything SVG related */ 684 if ((s[0] == 's' || s[0] == 'S') && 685 (s[1] == 'v' || s[1] == 'V') && 686 (s[2] == 'g' || s[2] == 'G')) { 687 /* printf("Got SVG tag \n"); */ 688 return 1; 689 } 690 691 /* Anything XSL(t) related */ 692 if ((s[0] == 'x' || s[0] == 'X') && 693 (s[1] == 's' || s[1] == 'S') && 694 (s[2] == 'l' || s[2] == 'L')) { 695 /* printf("Got XSL tag\n"); */ 696 return 1; 697 } 698 699 return 0; 700 } 701 702 static attribute_t is_black_attr(const char* s, size_t len, int strictMode) 703 { 704 stringtype_t* black; 705 706 if (len < 2) { 707 return TYPE_NONE; 708 } 709 710 if (len >= 5) { 711 712 /* JavaScript on.* event handlers */ 713 if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) { 714 black = BLACKATTREVENT; 715 const char *s_without_on = &s[2]; // start comparing from the third char 716 while (black->name != NULL) { 717 if (cstrcasecmp_with_null(black->name, s_without_on, strlen(black->name)) == 0) { 718 /* printf("Got banned attribute name %s\n", black->name); */ 719 return black->atype; 720 } 721 black += 1; 722 } 723 } 724 725 726 /* XMLNS can be used to create arbitrary tags */ 727 // goedge: commented for photo uploading 728 //if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) { 729 /* printf("Got XMLNS and XLINK tags\n"); */ 730 // return TYPE_BLACK; 731 //} 732 } 733 734 if (strictMode == 1) { 735 black = STRICT_BLACKATTR; 736 } else { 737 black = BLACKATTR; 738 } 739 while (black->name != NULL) { 740 if (cstrcasecmp_with_null(black->name, s, len) == 0) { 741 /* printf("Got banned attribute name %s\n", black->name); */ 742 return black->atype; 743 } 744 black += 1; 745 } 746 747 return TYPE_NONE; 748 } 749 750 static int is_black_url(const char* s, size_t len) 751 { 752 753 static const char* data_url = "DATA"; 754 static const char* viewsource_url = "VIEW-SOURCE"; 755 756 /* obsolete but interesting signal */ 757 static const char* vbscript_url = "VBSCRIPT"; 758 759 /* covers JAVA, JAVASCRIPT, + colon */ 760 static const char* javascript_url = "JAVA"; 761 762 /* skip whitespace */ 763 while (len > 0 && (*s <= 32 || *s >= 127)) { 764 /* 765 * HEY: this is a signed character. 766 * We are intentionally skipping high-bit characters too 767 * since they are not ASCII, and Opera sometimes uses UTF-8 whitespace. 768 * 769 * Also in EUC-JP some of the high bytes are just ignored. 770 */ 771 ++s; 772 --len; 773 } 774 775 if (htmlencode_startswith(data_url, s, len)) { 776 return 1; 777 } 778 779 if (htmlencode_startswith(viewsource_url, s, len)) { 780 return 1; 781 } 782 783 if (htmlencode_startswith(javascript_url, s, len)) { 784 return 1; 785 } 786 787 if (htmlencode_startswith(vbscript_url, s, len)) { 788 return 1; 789 } 790 return 0; 791 } 792 793 int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode) 794 { 795 h5_state_t h5; 796 attribute_t attr = TYPE_NONE; 797 798 libinjection_h5_init(&h5, s, len, (enum html5_flags) flags); 799 while (libinjection_h5_next(&h5)) { 800 if (h5.token_type != ATTR_VALUE) { 801 attr = TYPE_NONE; 802 } 803 804 if (h5.token_type == DOCTYPE) { 805 return 1; 806 } else if (h5.token_type == TAG_NAME_OPEN) { 807 if (is_black_tag(h5.token_start, h5.token_len, strictMode)) { 808 return 1; 809 } 810 } else if (h5.token_type == ATTR_NAME) { 811 attr = is_black_attr(h5.token_start, h5.token_len, strictMode); 812 } else if (h5.token_type == ATTR_VALUE) { 813 /* 814 * IE6,7,8 parsing works a bit differently so 815 * a whole <script> or other black tag might be hiding 816 * inside an attribute value under HTML 5 parsing 817 * See http://html5sec.org/#102 818 * to avoid doing a full reparse of the value, just 819 * look for "<". This probably need adjusting to 820 * handle escaped characters 821 */ 822 /* 823 if (memchr(h5.token_start, '<', h5.token_len) != NULL) { 824 return 1; 825 } 826 */ 827 828 switch (attr) { 829 case TYPE_NONE: 830 break; 831 case TYPE_BLACK: 832 return 1; 833 case TYPE_ATTR_URL: 834 if (is_black_url(h5.token_start, h5.token_len)) { 835 return 1; 836 } 837 break; 838 case TYPE_STYLE: 839 return 1; 840 case TYPE_ATTR_INDIRECT: 841 /* an attribute name is specified in a _value_ */ 842 if (is_black_attr(h5.token_start, h5.token_len, strictMode)) { 843 return 1; 844 } 845 break; 846 /* 847 default: 848 assert(0); 849 */ 850 } 851 attr = TYPE_NONE; 852 } else if (h5.token_type == TAG_COMMENT) { 853 /* IE uses a "`" as a tag ending char */ 854 // goedge: commented for photo uploading 855 /**if (memchr(h5.token_start, '`', h5.token_len) != NULL) { 856 return 1; 857 }**/ 858 859 /* IE conditional comment */ 860 if (h5.token_len > 3) { 861 if (h5.token_start[0] == '[' && 862 (h5.token_start[1] == 'i' || h5.token_start[1] == 'I') && 863 (h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) { 864 return 1; 865 } 866 if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') && 867 (h5.token_start[1] == 'm' || h5.token_start[1] == 'M') && 868 (h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) { 869 return 1; 870 } 871 } 872 873 if (h5.token_len > 5) { 874 /* IE <?import pseudo-tag */ 875 if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) { 876 return 1; 877 } 878 879 /* XML Entity definition */ 880 if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) { 881 return 1; 882 } 883 } 884 } 885 } 886 return 0; 887 } 888 889 890 /* 891 * wrapper 892 * 893 * 894 * const char* s: input string, may contain nulls, does not need to be null-terminated. 895 * size_t len: input string length. 896 * 897 * 898 */ 899 int libinjection_xss(const char* s, size_t slen, int strictMode) 900 { 901 if (libinjection_is_xss(s, slen, DATA_STATE, strictMode)) { 902 return 1; 903 } 904 if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE, strictMode)) { 905 return 1; 906 } 907 if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE, strictMode)) { 908 return 1; 909 } 910 if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE, strictMode)) { 911 return 1; 912 } 913 if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE, strictMode)) { 914 return 1; 915 } 916 917 return 0; 918 }