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  }