rack-libinjection 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +55 -0
  3. data/CHANGELOG.md +112 -0
  4. data/GET_STARTED.md +418 -0
  5. data/LICENSE-libinjection.txt +33 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +68 -0
  8. data/SECURITY.md +65 -0
  9. data/ext/libinjection/extconf.rb +113 -0
  10. data/ext/libinjection/libinjection_ext.c +1132 -0
  11. data/ext/libinjection/vendor/libinjection/.vendored +5 -0
  12. data/ext/libinjection/vendor/libinjection/COPYING +33 -0
  13. data/ext/libinjection/vendor/libinjection/MIGRATION.md +393 -0
  14. data/ext/libinjection/vendor/libinjection/README.md +251 -0
  15. data/ext/libinjection/vendor/libinjection/src/libinjection.h +70 -0
  16. data/ext/libinjection/vendor/libinjection/src/libinjection_error.h +26 -0
  17. data/ext/libinjection/vendor/libinjection/src/libinjection_html5.c +830 -0
  18. data/ext/libinjection/vendor/libinjection/src/libinjection_html5.h +56 -0
  19. data/ext/libinjection/vendor/libinjection/src/libinjection_sqli.c +2342 -0
  20. data/ext/libinjection/vendor/libinjection/src/libinjection_sqli.h +297 -0
  21. data/ext/libinjection/vendor/libinjection/src/libinjection_sqli_data.h +9651 -0
  22. data/ext/libinjection/vendor/libinjection/src/libinjection_xss.c +1203 -0
  23. data/ext/libinjection/vendor/libinjection/src/libinjection_xss.h +23 -0
  24. data/lib/libinjection/version.rb +6 -0
  25. data/lib/libinjection.rb +31 -0
  26. data/lib/rack/libinjection.rb +586 -0
  27. data/lib/rack-libinjection.rb +3 -0
  28. data/samples/README.md +67 -0
  29. data/samples/libinjection_detect_raw_hot_path.rb +161 -0
  30. data/samples/rack_all_surfaces_hot_path.rb +198 -0
  31. data/samples/rack_params_hot_path.rb +166 -0
  32. data/samples/rack_query_hot_path.rb +176 -0
  33. data/samples/results/.gitkeep +0 -0
  34. data/script/fuzz_smoke.rb +39 -0
  35. data/script/vendor_libs.rb +227 -0
  36. data/test/test_helper.rb +7 -0
  37. data/test/test_libinjection.rb +223 -0
  38. data/test/test_middleware.rb +404 -0
  39. metadata +148 -0
@@ -0,0 +1,1203 @@
1
+
2
+ #include "libinjection_xss.h"
3
+ #include "libinjection.h"
4
+ #include "libinjection_html5.h"
5
+
6
+ #include <stdio.h>
7
+
8
+ #define IS_HEX_ENTITY_PREFIX(src) (*(src + 2) == 'x' || *(src + 2) == 'X')
9
+
10
+ typedef enum attribute {
11
+ TYPE_NONE,
12
+ TYPE_BLACK, /* ban always */
13
+ TYPE_ATTR_URL, /* attribute value takes a URL-like object */
14
+ TYPE_STYLE,
15
+ TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
16
+ } attribute_t;
17
+
18
+ static attribute_t is_black_attr(const char *s, size_t len);
19
+ static int is_black_tag(const char *s, size_t len);
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 */,
24
+ const char *b /* src */, size_t n);
25
+
26
+ typedef struct stringtype {
27
+ const char *name;
28
+ attribute_t atype;
29
+ } stringtype_t;
30
+
31
+ static const int gsHexDecodeMap[256] = {
32
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
33
+ 256, 256, 256, 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, 256, 256, 256,
35
+ 256, 256, 256, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
36
+ 256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256,
37
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
38
+ 256, 256, 256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256, 256,
39
+ 256, 256, 256, 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, 256, 256, 256,
41
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
42
+ 256, 256, 256, 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, 256, 256, 256,
44
+ 256, 256, 256, 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, 256, 256, 256,
46
+ 256, 256, 256, 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, 256, 256, 256,
48
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
49
+ 256};
50
+
51
+ /**
52
+ * @brief Decode HTML character entities in numeric form.
53
+ *
54
+ * This function decodes HTML character entities in numeric form, both decimal
55
+ * (e.g., &#65;) and hexadecimal (e.g., &#x41;). It processes the input string
56
+ * and returns the corresponding character code, updating the number of
57
+ * characters consumed during decoding.
58
+ *
59
+ * Note that this function does not handle named entities (e.g., &amp;).
60
+ *
61
+ * Note: The function assumes that the input string is well-formed and does not
62
+ * perform extensive validation. This means it allows sequences without ';', eg
63
+ * '&#65' and decodes to 'A'.
64
+ *
65
+ * @param src The input string containing the HTML character entity.
66
+ * @param len The length of the input string.
67
+ * @param consumed Pointer to a size_t variable where the number of characters
68
+ * consumed will be stored.
69
+ * @return The decoded character code, or -1 on error.
70
+ */
71
+ static int html_decode_char_at(const char *src, size_t len, size_t *consumed) {
72
+ int val = 0;
73
+ size_t i;
74
+ int ch;
75
+
76
+ if (len == 0 || src == NULL) {
77
+ *consumed = 0;
78
+ return -1;
79
+ }
80
+
81
+ *consumed = 1;
82
+ /*
83
+ * check if it starts with '&' and
84
+ * len >=3 or src[2] == 'x'/'X' and len >=4
85
+ * if not, return the character itself
86
+ */
87
+ if (*src != '&' || len < 3 || (IS_HEX_ENTITY_PREFIX(src) && len < 4)) {
88
+ return (unsigned char)(*src);
89
+ }
90
+
91
+ /*
92
+ * check if the second character is '#'
93
+ * if not, return '&' (we don't handle named entities here)
94
+ */
95
+ if (*(src + 1) != '#') {
96
+ /* normally this would be for named entities
97
+ * but for this case we don't actually care
98
+ */
99
+ return '&';
100
+ }
101
+
102
+ // if there's a hex prefix
103
+ if (IS_HEX_ENTITY_PREFIX(src)) {
104
+ ch = (unsigned char)(*(src + 3));
105
+ ch = gsHexDecodeMap[ch];
106
+ if (ch == 256) {
107
+ /* degenerate case '&#[?]' */
108
+ return '&';
109
+ }
110
+ val = ch;
111
+ i = 4;
112
+ while (i < len) {
113
+ ch = (unsigned char)src[i];
114
+ if (ch == ';') {
115
+ *consumed = i + 1;
116
+ return val;
117
+ }
118
+ ch = gsHexDecodeMap[ch];
119
+ if (ch == 256) {
120
+ *consumed = i;
121
+ return val;
122
+ }
123
+ val = (val * 16) + ch;
124
+ if (val > 0x1000FF) {
125
+ return '&';
126
+ }
127
+ ++i;
128
+ }
129
+ *consumed = i;
130
+ return val;
131
+ } else {
132
+ i = 2;
133
+ ch = (unsigned char)src[i];
134
+ if (ch < '0' || ch > '9') {
135
+ return '&';
136
+ }
137
+ val = ch - '0';
138
+ i += 1;
139
+ while (i < len) {
140
+ ch = (unsigned char)src[i];
141
+ if (ch == ';') {
142
+ *consumed = i + 1;
143
+ return val;
144
+ }
145
+ if (ch < '0' || ch > '9') {
146
+ *consumed = i;
147
+ return val;
148
+ }
149
+ val = (val * 10) + (ch - '0');
150
+ if (val > 0x1000FF) {
151
+ return '&';
152
+ }
153
+ ++i;
154
+ }
155
+ *consumed = i;
156
+ return val;
157
+ }
158
+ }
159
+
160
+ /*
161
+ * These were extracted from multiple browser sources:
162
+ * - WebKit:
163
+ * https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/EventNames.json
164
+ * - Chromium/Blink:
165
+ * https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/dom/global_event_handlers.idl
166
+ * - Firefox/Gecko:
167
+ * https://github.com/mozilla-firefox/firefox/blob/main/dom/events/EventNameList.h
168
+ * - W3C/WHATWG specifications where applicable
169
+ *
170
+ * view-source:
171
+ * data:
172
+ * javascript:
173
+ * events:
174
+ */
175
+ static stringtype_t BLACKATTREVENT[] = {
176
+ {"ABORT", TYPE_BLACK},
177
+ {"ACCESSKEYNOTFOUND",
178
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
179
+ */
180
+ {"ACTIVATE", TYPE_BLACK},
181
+ {"ACTIVE", TYPE_BLACK},
182
+ {"ADDSOURCEBUFFER", TYPE_BLACK},
183
+ {"ADDSTREAM", TYPE_BLACK},
184
+ {"ADDTRACK", TYPE_BLACK},
185
+ {"AFTERPAINT",
186
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
187
+ */
188
+ {"AFTERPRINT", TYPE_BLACK},
189
+ {"AFTERSCRIPTEXECUTE",
190
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
191
+ */
192
+ {"ANIMATIONCANCEL", TYPE_BLACK},
193
+ {"ANIMATIONEND", TYPE_BLACK},
194
+ {"ANIMATIONITERATION", TYPE_BLACK},
195
+ {"ANIMATIONSTART", TYPE_BLACK},
196
+ {"AUDIOEND", TYPE_BLACK},
197
+ {"AUDIOCOMPLETE",
198
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
199
+ */
200
+ {"AUDIOPROCESS", TYPE_BLACK},
201
+ {"AUDIOSTART", TYPE_BLACK},
202
+ {"AUTOCOMPLETE", TYPE_BLACK},
203
+ {"AUTOCOMPLETEERROR", TYPE_BLACK},
204
+ {"AUXCLICK", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl,
205
+ WebKit: EventNames.json */
206
+ {"BACKGROUNDFETCHABORT", TYPE_BLACK},
207
+ {"BACKGROUNDFETCHCLICK", TYPE_BLACK},
208
+ {"BACKGROUNDFETCHFAIL", TYPE_BLACK},
209
+ {"BACKGROUNDFETCHSUCCESS", TYPE_BLACK},
210
+ {"BEFOREACTIVATE", TYPE_BLACK},
211
+ {"BEFORECOPY", TYPE_BLACK},
212
+ {"BEFORECUT", TYPE_BLACK},
213
+ {"BEFOREINPUT", TYPE_BLACK},
214
+ {"BEFORELOAD", TYPE_BLACK},
215
+ {"BEFOREMATCH",
216
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, WebKit:
217
+ EventNames.json, Firefox: EventNameList.h */
218
+ {"BEFOREPASTE", TYPE_BLACK},
219
+ {"BEFOREPRINT", TYPE_BLACK},
220
+ {"BEFORESCRIPTEXECUTE",
221
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
222
+ */
223
+ {"BEFORETOGGLE", TYPE_BLACK},
224
+ {"BEFOREUNLOAD", TYPE_BLACK},
225
+ {"BEGINEVENT", TYPE_BLACK},
226
+ {"BLOCKED", TYPE_BLACK},
227
+ {"BLUR", TYPE_BLACK},
228
+ {"BOUNDARY", TYPE_BLACK},
229
+ {"BUFFEREDAMOUNTLOW", TYPE_BLACK},
230
+ {"BUFFEREDCHANGE", TYPE_BLACK},
231
+ {"CACHED", TYPE_BLACK},
232
+ {"CANCEL", TYPE_BLACK},
233
+ {"CANPLAY", TYPE_BLACK},
234
+ {"CANPLAYTHROUGH", TYPE_BLACK},
235
+ {"CHANGE", TYPE_BLACK},
236
+ {"CHARGINGCHANGE", TYPE_BLACK},
237
+ {"CHARGINGTIMECHANGE", TYPE_BLACK},
238
+ {"CHECKING", TYPE_BLACK},
239
+ {"CLICK", TYPE_BLACK},
240
+ {"CLOSE", TYPE_BLACK},
241
+ {"CLOSING", TYPE_BLACK},
242
+ {"COMPLETE", TYPE_BLACK},
243
+ {"COMPOSITIONEND", TYPE_BLACK},
244
+ {"COMPOSITIONSTART", TYPE_BLACK},
245
+ {"COMPOSITIONCHANGE",
246
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
247
+ */
248
+ {"COMPOSITIONUPDATE", TYPE_BLACK},
249
+ {"COMMAND", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, WebKit:
250
+ EventNames.json, Firefox: EventNameList.h */
251
+ {"CONFIGURATIONCHANGE", TYPE_BLACK},
252
+ {"CONNECT", TYPE_BLACK},
253
+ {"CONNECTING", TYPE_BLACK},
254
+ {"CONNECTIONSTATECHANGE", TYPE_BLACK},
255
+ {"CONTENTVISIBILITYAUTOSTATECHANGE", TYPE_BLACK},
256
+ {"CONTEXTLOST",
257
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, WebKit:
258
+ EventNames.json, Firefox: EventNameList.h */
259
+ {"CONTEXTMENU", TYPE_BLACK},
260
+ {"CONTEXTRESTORED",
261
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, WebKit:
262
+ EventNames.json, Firefox: EventNameList.h */
263
+ {"CONTROLLERCHANGE", TYPE_BLACK},
264
+ {"COOKIECHANGE", TYPE_BLACK},
265
+ {"COORDINATORSTATECHANGE", TYPE_BLACK},
266
+ {"COPY", TYPE_BLACK},
267
+ {"COUPONCODECHANGED", TYPE_BLACK},
268
+ {"CUECHANGE", TYPE_BLACK},
269
+ {"CURRENTENTRYCHANGE", TYPE_BLACK},
270
+ {"CUT", TYPE_BLACK},
271
+ {"DATAAVAILABLE", TYPE_BLACK},
272
+ {"DATACHANNEL", TYPE_BLACK},
273
+ {"DBLCLICK", TYPE_BLACK},
274
+ {"DEQUEUE", TYPE_BLACK},
275
+ {"DEVICECHANGE", TYPE_BLACK},
276
+ {"DEVICELIGHT",
277
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
278
+ */
279
+ {"DEVICEMOTION", TYPE_BLACK},
280
+ {"DEVICEORIENTATION", TYPE_BLACK},
281
+ {"DEVICEORIENTATIONABSOLUTE",
282
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
283
+ */
284
+ {"DISCHARGINGTIMECHANGE", TYPE_BLACK},
285
+ {"DISCONNECT", TYPE_BLACK},
286
+ {"DISPOSE", TYPE_BLACK},
287
+ {"DOMACTIVATE", TYPE_BLACK},
288
+ {"DOMCHARACTERDATAMODIFIED", TYPE_BLACK},
289
+ {"DOMCONTENTLOADED", TYPE_BLACK},
290
+ {"DOMNODEINSERTED", TYPE_BLACK},
291
+ {"DOMNODEINSERTEDINTODOCUMENT", TYPE_BLACK},
292
+ {"DOMNODEREMOVED", TYPE_BLACK},
293
+ {"DOMNODEREMOVEDFROMDOCUMENT", TYPE_BLACK},
294
+ {"DOMSUBTREEMODIFIED", TYPE_BLACK},
295
+ {"DOWNLOADING", TYPE_BLACK},
296
+ {"DRAG", TYPE_BLACK},
297
+ {"DRAGEND", TYPE_BLACK},
298
+ {"DRAGENTER", TYPE_BLACK},
299
+ {"DRAGLEAVE", TYPE_BLACK},
300
+ {"DRAGEXIT", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl,
301
+ Firefox: EventNameList.h */
302
+ {"DRAGOVER", TYPE_BLACK},
303
+ {"DRAGSTART", TYPE_BLACK},
304
+ {"DROP", TYPE_BLACK},
305
+ {"DURATIONCHANGE", TYPE_BLACK},
306
+ {"EMPTIED", TYPE_BLACK},
307
+ {"ENCRYPTED", TYPE_BLACK},
308
+ {"EDGEUICANCELED",
309
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
310
+ */
311
+ {"EDGEUICOMPLETED",
312
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
313
+ */
314
+ {"EDGEUISTARTED",
315
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
316
+ */
317
+ {"EDITORBEFOREINPUT",
318
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
319
+ */
320
+ {"EDITORINPUT",
321
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
322
+ */
323
+ {"END", TYPE_BLACK},
324
+ {"ENDED", TYPE_BLACK},
325
+ {"ENDEVENT", TYPE_BLACK},
326
+ {"ENDSTREAMING", TYPE_BLACK},
327
+ {"ENTER", TYPE_BLACK},
328
+ {"ENTERPICTUREINPICTURE", TYPE_BLACK},
329
+ {"ERROR", TYPE_BLACK},
330
+ {"EXIT", TYPE_BLACK},
331
+ {"FENCEDTREECLICK",
332
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl */
333
+ {"FETCH", TYPE_BLACK},
334
+ {"FINISH", TYPE_BLACK},
335
+ {"FOCUS", TYPE_BLACK},
336
+ {"FOCUSIN", TYPE_BLACK},
337
+ {"FOCUSOUT", TYPE_BLACK},
338
+ {"FORMCHANGE",
339
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
340
+ */
341
+ {"FORMCHECKBOXSTATECHANGE",
342
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
343
+ */
344
+ {"FORMDATA", TYPE_BLACK},
345
+ {"FORMINVALID",
346
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
347
+ */
348
+ {"FORMRADIOSTATECHANGE",
349
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
350
+ */
351
+ {"FORMRESET",
352
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
353
+ */
354
+ {"FORMSELECT",
355
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
356
+ */
357
+ {"FORMSUBMIT",
358
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
359
+ */
360
+ {"FULLSCREENCHANGE", TYPE_BLACK},
361
+ {"FULLSCREENERROR", TYPE_BLACK},
362
+ {"GAMEPADAXISMOVE",
363
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
364
+ */
365
+ {"GAMEPADBUTTONDOWN",
366
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
367
+ */
368
+ {"GAMEPADBUTTONUP",
369
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
370
+ */
371
+ {"GAMEPADCONNECTED", TYPE_BLACK},
372
+ {"GAMEPADDISCONNECTED", TYPE_BLACK},
373
+ {"GATHERINGSTATECHANGE", TYPE_BLACK},
374
+ {"GESTURECHANGE", TYPE_BLACK},
375
+ {"GESTUREEND", TYPE_BLACK},
376
+ {"GESTURESCROLLEND", TYPE_BLACK},
377
+ {"GESTURESCROLLSTART", TYPE_BLACK},
378
+ {"GESTURESCROLLUPDATE", TYPE_BLACK},
379
+ {"GESTURESTART", TYPE_BLACK},
380
+ {"GESTURETAP", TYPE_BLACK},
381
+ {"GESTURETAPDOWN", TYPE_BLACK},
382
+ {"GOTPOINTERCAPTURE", TYPE_BLACK},
383
+ {"HASHCHANGE", TYPE_BLACK},
384
+ {"ICECANDIDATE", TYPE_BLACK},
385
+ {"ICECANDIDATEERROR", TYPE_BLACK},
386
+ {"ICECONNECTIONSTATECHANGE", TYPE_BLACK},
387
+ {"ICEGATHERINGSTATECHANGE", TYPE_BLACK},
388
+ {"IMAGEABORT",
389
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
390
+ */
391
+ {"INACTIVE", TYPE_BLACK},
392
+ {"INPUT", TYPE_BLACK},
393
+ {"INPUTSOURCESCHANGE", TYPE_BLACK},
394
+ {"INSTALL", TYPE_BLACK},
395
+ {"INVALID", TYPE_BLACK},
396
+ {"INVOKE", TYPE_BLACK},
397
+ {"KEYDOWN", TYPE_BLACK},
398
+ {"KEYPRESS", TYPE_BLACK},
399
+ {"KEYSTATUSESCHANGE", TYPE_BLACK},
400
+ {"KEYUP", TYPE_BLACK},
401
+ {"LANGUAGECHANGE", TYPE_BLACK},
402
+ {"LEAVEPICTUREINPICTURE", TYPE_BLACK},
403
+ {"LEGACYATTRMODIFIED",
404
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
405
+ */
406
+ {"LEGACYCHARACTERDATAMODIFIED",
407
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
408
+ */
409
+ {"LEGACYDOMACTIVATE",
410
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
411
+ */
412
+ {"LEGACYDOMFOCUSIN",
413
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
414
+ */
415
+ {"LEGACYDOMFOCUSOUT",
416
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
417
+ */
418
+ {"LEGACYMOUSELINEORPAGESCROLL",
419
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
420
+ */
421
+ {"LEGACYMOUSEPIXELSCROLL",
422
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
423
+ */
424
+ {"LEGACYNODEINSERTED",
425
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
426
+ */
427
+ {"LEGACYNODEINSERTEDINTODOCUMENT",
428
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
429
+ */
430
+ {"LEGACYNODEREMOVED",
431
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
432
+ */
433
+ {"LEGACYNODEREMOVEDFROMDOCUMENT",
434
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
435
+ */
436
+ {"LEGACYSUBTREEMODIFIED",
437
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
438
+ */
439
+ {"LEGACYTEXTINPUT",
440
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
441
+ */
442
+ {"LEVELCHANGE", TYPE_BLACK},
443
+ {"LOAD", TYPE_BLACK},
444
+ {"LOADEDDATA", TYPE_BLACK},
445
+ {"LOADEDMETADATA", TYPE_BLACK},
446
+ {"LOADEND", TYPE_BLACK},
447
+ {"LOADING", TYPE_BLACK},
448
+ {"LOADINGDONE", TYPE_BLACK},
449
+ {"LOADINGERROR", TYPE_BLACK},
450
+ {"LOADSTART", TYPE_BLACK},
451
+ {"LOSTPOINTERCAPTURE", TYPE_BLACK},
452
+ {"MAGNIFYGESTURE",
453
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
454
+ */
455
+ {"MAGNIFYGESTURESTART",
456
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
457
+ */
458
+ {"MAGNIFYGESTUREUPDATE",
459
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
460
+ */
461
+ {"MARK", TYPE_BLACK},
462
+ {"MEDIARECORDERDATAAVAILABLE",
463
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
464
+ */
465
+ {"MEDIARECORDERSTOP",
466
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
467
+ */
468
+ {"MEDIARECORDERWARNING",
469
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
470
+ */
471
+ {"MERCHANTVALIDATION", TYPE_BLACK},
472
+ {"MESSAGE", TYPE_BLACK},
473
+ {"MESSAGEERROR", TYPE_BLACK},
474
+ {"MOUSEDOUBLECLICK",
475
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
476
+ */
477
+ {"MOUSEDOWN", TYPE_BLACK},
478
+ {"MOUSEENTER", TYPE_BLACK},
479
+ {"MOUSEEXPLOREBYTOUCH",
480
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
481
+ */
482
+ {"MOUSEHITTEST",
483
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
484
+ */
485
+ {"MOUSELEAVE", TYPE_BLACK},
486
+ {"MOUSELONGTAP",
487
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
488
+ */
489
+ {"MOUSEMOVE", TYPE_BLACK},
490
+ {"MOUSEOUT", TYPE_BLACK},
491
+ {"MOUSEOVER", TYPE_BLACK},
492
+ {"MOUSEUP", TYPE_BLACK},
493
+ {"MOUSEWHEEL", TYPE_BLACK},
494
+ {"MOZFULLSCREENCHANGE",
495
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
496
+ */
497
+ {"MOZFULLSCREENERROR",
498
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
499
+ */
500
+ {"MOZPOINTERLOCKCHANGE",
501
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
502
+ */
503
+ {"MOZPOINTERLOCKERROR",
504
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
505
+ */
506
+ {"MOZVISUALRESIZE",
507
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
508
+ */
509
+ {"MOZVISUALSCROLL",
510
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
511
+ */
512
+ {"MUTE", TYPE_BLACK},
513
+ {"NAVIGATE", TYPE_BLACK},
514
+ {"NAVIGATEERROR", TYPE_BLACK},
515
+ {"NAVIGATESUCCESS", TYPE_BLACK},
516
+ {"NEGOTIATIONNEEDED", TYPE_BLACK},
517
+ {"NEXTTRACK", TYPE_BLACK},
518
+ {"NOMATCH", TYPE_BLACK},
519
+ {"NOTIFICATIONCLICK", TYPE_BLACK},
520
+ {"NOTIFICATIONCLOSE", TYPE_BLACK},
521
+ {"NOUPDATE", TYPE_BLACK},
522
+ {"OBSOLETE", TYPE_BLACK},
523
+ {"OFFLINE", TYPE_BLACK},
524
+ {"ONLINE", TYPE_BLACK},
525
+ {"OPEN", TYPE_BLACK},
526
+ {"ORIENTATIONCHANGE", TYPE_BLACK},
527
+ {"OVERFLOWCHANGED", TYPE_BLACK},
528
+ {"OVERSCROLL", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl */
529
+ {"PAGEHIDE", TYPE_BLACK},
530
+ {"PAGEREVEAL", TYPE_BLACK}, /* WebKit: EventNames.json */
531
+ {"PAGESHOW", TYPE_BLACK},
532
+ {"PAGESWAP", TYPE_BLACK}, /* WebKit: EventNames.json */
533
+ {"PASTE", TYPE_BLACK},
534
+ {"PAUSE", TYPE_BLACK},
535
+ {"PAYERDETAILCHANGE", TYPE_BLACK},
536
+ {"PAYMENTAUTHORIZED", TYPE_BLACK},
537
+ {"PAYMENTMETHODCHANGE", TYPE_BLACK},
538
+ {"PAYMENTMETHODSELECTED", TYPE_BLACK},
539
+ {"PLAY", TYPE_BLACK},
540
+ {"PLAYING", TYPE_BLACK},
541
+ {"POINTERAUXCLICK",
542
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
543
+ */
544
+ {"POINTERCANCEL", TYPE_BLACK},
545
+ {"POINTERCLICK",
546
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
547
+ */
548
+ {"POINTERDOWN", TYPE_BLACK},
549
+ {"POINTERENTER", TYPE_BLACK},
550
+ {"POINTERGOTCAPTURE",
551
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
552
+ */
553
+ {"POINTERLEAVE", TYPE_BLACK},
554
+ {"POINTERLOCKCHANGE", TYPE_BLACK},
555
+ {"POINTERLOCKERROR", TYPE_BLACK},
556
+ {"POINTERLOSTCAPTURE",
557
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
558
+ */
559
+ {"POINTERMOVE", TYPE_BLACK},
560
+ {"POINTEROUT", TYPE_BLACK},
561
+ {"POINTEROVER", TYPE_BLACK},
562
+ {"POINTERRAWUPDATE",
563
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, Firefox:
564
+ EventNameList.h */
565
+ {"POINTERUP", TYPE_BLACK},
566
+ {"POPSTATE", TYPE_BLACK},
567
+ {"PRESSTAPGESTURE",
568
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
569
+ */
570
+ {"PREVIOUSTRACK", TYPE_BLACK},
571
+ {"PROPERTYCHANGE", TYPE_BLACK},
572
+ {"PROCESSORERROR", TYPE_BLACK},
573
+ {"PROGRESS", TYPE_BLACK},
574
+ {"PUSH", TYPE_BLACK},
575
+ {"PUSHNOTIFICATION", TYPE_BLACK},
576
+ {"PUSHSUBSCRIPTIONCHANGE", TYPE_BLACK},
577
+ {"QUALITYCHANGE", TYPE_BLACK},
578
+ {"RATECHANGE", TYPE_BLACK},
579
+ {"READYSTATECHANGE", TYPE_BLACK},
580
+ {"REDRAW", TYPE_BLACK}, /* WebKit: EventNames.json */
581
+ {"REJECTIONHANDLED", TYPE_BLACK},
582
+ {"RELEASE", TYPE_BLACK},
583
+ {"REMOVE", TYPE_BLACK},
584
+ {"REMOVESOURCEBUFFER", TYPE_BLACK},
585
+ {"REMOVESTREAM", TYPE_BLACK},
586
+ {"REMOVETRACK", TYPE_BLACK},
587
+ {"REPEAT", TYPE_BLACK}, /* WebKit: EventNames.json - SVG animation */
588
+ {"REPEATEVENT", TYPE_BLACK}, /* WebKit: EventNames.json, Firefox:
589
+ EventNameList.h - SVG animation */
590
+ {"RESET", TYPE_BLACK},
591
+ {"RESIZE", TYPE_BLACK},
592
+ {"RESOURCETIMINGBUFFERFULL", TYPE_BLACK},
593
+ {"RESULT", TYPE_BLACK},
594
+ {"RESUME", TYPE_BLACK},
595
+ {"ROTATEGESTURE",
596
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
597
+ */
598
+ {"ROTATEGESTURESTART",
599
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
600
+ */
601
+ {"ROTATEGESTUREUPDATE",
602
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
603
+ */
604
+ {"RTCTRANSFORM", TYPE_BLACK},
605
+ {"SCROLL", TYPE_BLACK},
606
+ {"SCROLLEDAREACHANGED",
607
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
608
+ */
609
+ {"SCROLLEND",
610
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl, WebKit:
611
+ EventNames.json, Firefox: EventNameList.h */
612
+ {"SCROLLPORTOVERFLOW",
613
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
614
+ */
615
+ {"SCROLLPORTUNDERFLOW",
616
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
617
+ */
618
+ {"SCROLLSNAPCHANGE", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl
619
+ - CSS Scroll Snap */
620
+ {"SCROLLSNAPCHANGING",
621
+ TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl - CSS Scroll Snap
622
+ */
623
+ {"SEARCH", TYPE_BLACK},
624
+ {"SECURITYPOLICYVIOLATION", TYPE_BLACK},
625
+ {"SEEKED", TYPE_BLACK},
626
+ {"SEEKING", TYPE_BLACK},
627
+ {"SELECT", TYPE_BLACK},
628
+ {"SELECTEDCANDIDATEPAIRCHANGE", TYPE_BLACK},
629
+ {"SELECTEND", TYPE_BLACK},
630
+ {"SELECTIONCHANGE", TYPE_BLACK},
631
+ {"SELECTSTART", TYPE_BLACK},
632
+ {"SHIPPINGADDRESSCHANGE", TYPE_BLACK},
633
+ {"SHIPPINGCONTACTSELECTED", TYPE_BLACK},
634
+ {"SHIPPINGMETHODSELECTED", TYPE_BLACK},
635
+ {"SHIPPINGOPTIONCHANGE", TYPE_BLACK},
636
+ {"SHOW", TYPE_BLACK},
637
+ {"SIGNALINGSTATECHANGE", TYPE_BLACK},
638
+ {"SLOTCHANGE", TYPE_BLACK},
639
+ {"SMILBEGINEVENT",
640
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - SVG/SMIL */
641
+ {"SMILENDEVENT",
642
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - SVG/SMIL */
643
+ {"SMILREPEATEVENT",
644
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - SVG/SMIL */
645
+ {"SORT", TYPE_BLACK}, /* Chromium: Blink GlobalEventHandlers.idl */
646
+ {"SOUNDEND", TYPE_BLACK},
647
+ {"SOUNDSTART", TYPE_BLACK},
648
+ {"SOURCECLOSE", TYPE_BLACK},
649
+ {"SOURCEENDED", TYPE_BLACK},
650
+ {"SOURCEOPEN", TYPE_BLACK},
651
+ {"SPEECHEND", TYPE_BLACK},
652
+ {"SPEECHSTART", TYPE_BLACK},
653
+ {"SQUEEZE", TYPE_BLACK},
654
+ {"SQUEEZEEND", TYPE_BLACK},
655
+ {"SQUEEZESTART", TYPE_BLACK},
656
+ {"STALLED", TYPE_BLACK},
657
+ {"START", TYPE_BLACK},
658
+ {"STARTED", TYPE_BLACK},
659
+ {"STARTSTREAMING", TYPE_BLACK},
660
+ {"STATECHANGE", TYPE_BLACK},
661
+ {"STOP", TYPE_BLACK},
662
+ {"STORAGE", TYPE_BLACK},
663
+ {"SUBMIT", TYPE_BLACK},
664
+ {"SVGLOAD",
665
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
666
+ */
667
+ {"SVGSCROLL",
668
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
669
+ */
670
+ {"SWIPEGESTURE",
671
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
672
+ */
673
+ {"SWIPEGESTUREEND",
674
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
675
+ */
676
+ {"SWIPEGESTUREMAYSTART",
677
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
678
+ */
679
+ {"SWIPEGESTURESTART",
680
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
681
+ */
682
+ {"SWIPEGESTUREUPDATE",
683
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
684
+ */
685
+ {"SUCCESS", TYPE_BLACK},
686
+ {"SUSPEND", TYPE_BLACK},
687
+ {"TAPGESTURE",
688
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
689
+ */
690
+ {"TEXTINPUT", TYPE_BLACK},
691
+ {"TIMEOUT", TYPE_BLACK},
692
+ {"TIMEUPDATE", TYPE_BLACK},
693
+ {"TOGGLE", TYPE_BLACK},
694
+ {"TONECHANGE", TYPE_BLACK},
695
+ {"TOUCHCANCEL", TYPE_BLACK},
696
+ {"TOUCHEND", TYPE_BLACK},
697
+ {"TOUCHFORCECHANGE", TYPE_BLACK},
698
+ {"TOUCHMOVE", TYPE_BLACK},
699
+ {"TOUCHSTART", TYPE_BLACK},
700
+ {"TRACK", TYPE_BLACK},
701
+ {"TRANSITIONCANCEL", TYPE_BLACK},
702
+ {"TRANSITIONEND", TYPE_BLACK},
703
+ {"TRANSITIONRUN", TYPE_BLACK},
704
+ {"TRANSITIONSTART", TYPE_BLACK},
705
+ {"UNCAPTUREDERROR", TYPE_BLACK},
706
+ {"UNHANDLEDREJECTION", TYPE_BLACK},
707
+ {"UNIDENTIFIEDEVENT",
708
+ TYPE_BLACK}, /* Firefox: mozilla-firefox/firefox/dom/events/EventNameList.h
709
+ */
710
+ {"UNLOAD", TYPE_BLACK},
711
+ {"UNMUTE", TYPE_BLACK},
712
+ {"USERPROXIMITY",
713
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - deprecated
714
+ */
715
+ {"UPDATE", TYPE_BLACK},
716
+ {"UPDATEEND", TYPE_BLACK},
717
+ {"UPDATEFOUND", TYPE_BLACK},
718
+ {"UPDATEREADY", TYPE_BLACK},
719
+ {"UPDATESTART", TYPE_BLACK},
720
+ {"UPGRADENEEDED", TYPE_BLACK},
721
+ {"VALIDATEMERCHANT", TYPE_BLACK},
722
+ {"VERSIONCHANGE", TYPE_BLACK},
723
+ {"VISIBILITYCHANGE", TYPE_BLACK},
724
+ {"VOICESCHANGED", TYPE_BLACK},
725
+ {"VOLUMECHANGE", TYPE_BLACK},
726
+ {"VRDISPLAYACTIVATE",
727
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - WebVR
728
+ deprecated */
729
+ {"VRDISPLAYCONNECT",
730
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - WebVR
731
+ deprecated */
732
+ {"VRDISPLAYDEACTIVATE",
733
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - WebVR
734
+ deprecated */
735
+ {"VRDISPLAYDISCONNECT",
736
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - WebVR
737
+ deprecated */
738
+ {"VRDISPLAYPRESENTCHANGE",
739
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - WebVR
740
+ deprecated */
741
+ {"WAITING", TYPE_BLACK},
742
+ {"WAITINGFORKEY", TYPE_BLACK},
743
+ {"WEBGLCONTEXTCREATIONERROR", TYPE_BLACK},
744
+ {"WEBGLCONTEXTLOST", TYPE_BLACK},
745
+ {"WEBGLCONTEXTRESTORED", TYPE_BLACK},
746
+ {"WEBKITANIMATIONEND", TYPE_BLACK},
747
+ {"WEBKITANIMATIONITERATION", TYPE_BLACK},
748
+ {"WEBKITANIMATIONSTART", TYPE_BLACK},
749
+ {"WEBKITASSOCIATEFORMCONTROLS", TYPE_BLACK}, /* WebKit: EventNames.json */
750
+ {"WEBKITAUTOFILLREQUEST", TYPE_BLACK}, /* WebKit: EventNames.json */
751
+ {"WEBKITBEFORETEXTINSERTED", TYPE_BLACK},
752
+ {"WEBKITBEGINFULLSCREEN", TYPE_BLACK},
753
+ {"WEBKITCURRENTPLAYBACKTARGETISWIRELESSCHANGED", TYPE_BLACK},
754
+ {"WEBKITENDFULLSCREEN", TYPE_BLACK},
755
+ {"WEBKITFULLSCREENCHANGE", TYPE_BLACK},
756
+ {"WEBKITFULLSCREENERROR", TYPE_BLACK},
757
+ {"WEBKITKEYADDED", TYPE_BLACK},
758
+ {"WEBKITKEYERROR", TYPE_BLACK},
759
+ {"WEBKITKEYMESSAGE", TYPE_BLACK},
760
+ {"WEBKITMEDIASESSIONMETADATACHANGED",
761
+ TYPE_BLACK}, /* WebKit: EventNames.json */
762
+ {"WEBKITMOUSEFORCECHANGED", TYPE_BLACK},
763
+ {"WEBKITMOUSEFORCEDOWN", TYPE_BLACK},
764
+ {"WEBKITMOUSEFORCEUP", TYPE_BLACK},
765
+ {"WEBKITMOUSEFORCEWILLBEGIN", TYPE_BLACK},
766
+ {"WEBKITNEEDKEY", TYPE_BLACK},
767
+ {"WEBKITNETWORKINFOCHANGE", TYPE_BLACK},
768
+ {"WEBKITPLAYBACKTARGETAVAILABILITYCHANGED", TYPE_BLACK},
769
+ {"WEBKITPRESENTATIONMODECHANGED", TYPE_BLACK},
770
+ {"WEBKITREMOVESOURCEBUFFER", TYPE_BLACK},
771
+ {"WEBKITSHADOWROOTATTACHED", TYPE_BLACK}, /* WebKit: EventNames.json */
772
+ {"WEBKITSOURCECLOSE", TYPE_BLACK},
773
+ {"WEBKITSOURCEENDED", TYPE_BLACK},
774
+ {"WEBKITSOURCEOPEN", TYPE_BLACK},
775
+ {"WEBKITTRANSITIONEND", TYPE_BLACK},
776
+ {"WHEEL", TYPE_BLACK},
777
+ {"WRITE", TYPE_BLACK},
778
+ {"WRITEEND", TYPE_BLACK},
779
+ {"WRITESTART", TYPE_BLACK},
780
+ {"XULBROADCAST",
781
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
782
+ specific */
783
+ {"XULCOMMANDUPDATE",
784
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
785
+ specific */
786
+ {"XULPOPUPHIDDEN",
787
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
788
+ specific */
789
+ {"XULPOPUPHIDING",
790
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
791
+ specific */
792
+ {"XULPOPUPSHOWING",
793
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
794
+ specific */
795
+ {"XULPOPUPSHOWN",
796
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
797
+ specific */
798
+ {"XULSYSTEMSTATUSBARCLICK",
799
+ TYPE_BLACK}, /* Firefox: gecko-dev/dom/events/EventNameList.h - XUL
800
+ specific */
801
+ {"ZOOM", TYPE_BLACK},
802
+ {NULL, TYPE_NONE}};
803
+
804
+ /*
805
+ * view-source:
806
+ * data:
807
+ * javascript:
808
+ */
809
+ static stringtype_t BLACKATTR[] = {
810
+ {"ACTION", TYPE_ATTR_URL}, /* form */
811
+ {"ATTRIBUTENAME",
812
+ TYPE_ATTR_INDIRECT}, /* SVG allow indirection of attribute names */
813
+ {"BY", TYPE_ATTR_URL}, /* SVG */
814
+ {"BACKGROUND", TYPE_ATTR_URL}, /* IE6, O11 */
815
+ {"DATAFORMATAS", TYPE_BLACK}, /* IE */
816
+ {"DATASRC", TYPE_BLACK}, /* IE */
817
+ {"DYNSRC", TYPE_ATTR_URL}, /* Obsolete img attribute */
818
+ {"FILTER", TYPE_STYLE}, /* Opera, SVG inline style */
819
+ {"FORMACTION", TYPE_ATTR_URL}, /* HTML 5 */
820
+ {"FOLDER", TYPE_ATTR_URL}, /* Only on A tags, IE-only */
821
+ {"FROM", TYPE_ATTR_URL}, /* SVG */
822
+ {"HANDLER", TYPE_ATTR_URL}, /* SVG Tiny, Opera */
823
+ {"HREF", TYPE_ATTR_URL},
824
+ {"LOWSRC", TYPE_ATTR_URL}, /* Obsolete img attribute */
825
+ {"POSTER", TYPE_ATTR_URL}, /* Opera 10,11 */
826
+ {"SRC", TYPE_ATTR_URL},
827
+ {"STYLE", TYPE_STYLE},
828
+ {"TO", TYPE_ATTR_URL}, /* SVG */
829
+ {"VALUES", TYPE_ATTR_URL}, /* SVG */
830
+ {"XLINK:HREF", TYPE_ATTR_URL},
831
+ {NULL, TYPE_NONE}};
832
+
833
+ /* xmlns */
834
+ /* `xml-stylesheet` > <eval>, <if expr=> */
835
+
836
+ /*
837
+ static const char* BLACKATTR[] = {
838
+ "ATTRIBUTENAME",
839
+ "BACKGROUND",
840
+ "DATAFORMATAS",
841
+ "HREF",
842
+ "SCROLL",
843
+ "SRC",
844
+ "STYLE",
845
+ "SRCDOC",
846
+ NULL
847
+ };
848
+ */
849
+
850
+ static const char *BLACKTAG[] = {
851
+ "APPLET",
852
+ /* , "AUDIO" */
853
+ "BASE", "COMMENT", /* IE http://html5sec.org/#38 */
854
+ "EMBED",
855
+ /* , "FORM" */
856
+ "FRAME", "FRAMESET", "HANDLER", /* Opera SVG, effectively a script tag */
857
+ "IFRAME", "IMPORT", "ISINDEX", "LINK", "LISTENER",
858
+ /* , "MARQUEE" */
859
+ "META", "NOSCRIPT", "OBJECT", "SCRIPT", "STYLE",
860
+ /* , "VIDEO" */
861
+ "VMLFRAME", "XML", "XSS", NULL};
862
+
863
+ static int cstrcasecmp_with_null(const char *a, const char *b, size_t n) {
864
+ unsigned int ai = 0, bi = 0;
865
+ char ca;
866
+ char cb;
867
+ /* printf("Comparing to %s %.*s\n", a, (int)n, b); */
868
+ while (n-- > 0) {
869
+ cb = b[bi++];
870
+ if (cb == '\0') {
871
+ continue;
872
+ }
873
+
874
+ ca = a[ai++];
875
+
876
+ if (cb >= 'a' && cb <= 'z') {
877
+ cb -= 0x20;
878
+ }
879
+
880
+ /* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
881
+ if (ca != cb) {
882
+ return 1;
883
+ }
884
+ }
885
+ ca = a[ai++];
886
+
887
+ if (ca == '\0') {
888
+ /* printf(" MATCH \n"); */
889
+ return 0;
890
+ } else {
891
+ return 1;
892
+ }
893
+ }
894
+
895
+ /*
896
+ * Does an HTML encoded binary string (const char*, length) start with
897
+ * a all uppercase c-string (null terminated), case insensitive!
898
+ *
899
+ * also ignore any embedded nulls in the HTML string!
900
+ *
901
+ * @param a - the prefix to check for
902
+ * @param b - the string to check
903
+ * @param n - the length of the string to check
904
+ * @return 1 if the string starts with the prefix, 0 otherwise
905
+ */
906
+ static int htmlencode_startswith(const char *a, const char *b, size_t n) {
907
+ size_t consumed;
908
+ int cb;
909
+ int first = 1;
910
+ /* printf("Comparing %s with %.*s\n", a,(int)n,b); */
911
+ while (n > 0) {
912
+ if (*a == 0) {
913
+ /* printf("Match EOL!\n"); */
914
+ return 1;
915
+ }
916
+ cb = html_decode_char_at(b, n, &consumed);
917
+ b += consumed;
918
+ n -= consumed;
919
+
920
+ if (first && cb <= 32) {
921
+ /* ignore all leading whitespace and control characters */
922
+ continue;
923
+ }
924
+ first = 0;
925
+
926
+ if (cb == 0) {
927
+ /* always ignore null characters in user input */
928
+ continue;
929
+ }
930
+
931
+ if (cb == 10) {
932
+ /* always ignore vertical tab characters in user input */
933
+ /* who allows this?? */
934
+ continue;
935
+ }
936
+
937
+ if (cb >= 'a' && cb <= 'z') {
938
+ /* upcase */
939
+ cb -= 0x20;
940
+ }
941
+
942
+ if (*a != (char)cb) {
943
+ /* printf(" %c != %c\n", *a, cb); */
944
+ /* mismatch */
945
+ return 0;
946
+ }
947
+ a++;
948
+ }
949
+
950
+ return (*a == 0) ? 1 : 0;
951
+ }
952
+
953
+ static int is_black_tag(const char *s, size_t len) {
954
+ const char **black;
955
+
956
+ if (len < 3) {
957
+ return 0;
958
+ }
959
+
960
+ black = BLACKTAG;
961
+ while (*black != NULL) {
962
+ if (cstrcasecmp_with_null(*black, s, len) == 0) {
963
+ /* printf("Got black tag %s\n", *black); */
964
+ return 1;
965
+ }
966
+ black += 1;
967
+ }
968
+
969
+ /* anything SVG related */
970
+ if ((s[0] == 's' || s[0] == 'S') && (s[1] == 'v' || s[1] == 'V') &&
971
+ (s[2] == 'g' || s[2] == 'G')) {
972
+ /* printf("Got SVG tag \n"); */
973
+ return 1;
974
+ }
975
+
976
+ /* Anything XSL(t) related */
977
+ if ((s[0] == 'x' || s[0] == 'X') && (s[1] == 's' || s[1] == 'S') &&
978
+ (s[2] == 'l' || s[2] == 'L')) {
979
+ /* printf("Got XSL tag\n"); */
980
+ return 1;
981
+ }
982
+
983
+ return 0;
984
+ }
985
+
986
+ static attribute_t is_black_attr(const char *s, size_t len) {
987
+ stringtype_t *black;
988
+
989
+ if (len < 2) {
990
+ return TYPE_NONE;
991
+ }
992
+
993
+ if (len >= 5) {
994
+
995
+ /* JavaScript on.* event handlers */
996
+ if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
997
+ black = BLACKATTREVENT;
998
+ const char *s_without_on =
999
+ &s[2]; // start comparing from the third char
1000
+ size_t s_without_on_len = len - 2; // temporary length variable
1001
+ while (black->name != NULL) {
1002
+ size_t black_name_len = strlen(black->name);
1003
+ // determine the maximum length to compare
1004
+ size_t max_len = (s_without_on_len < black_name_len)
1005
+ ? s_without_on_len
1006
+ : black_name_len;
1007
+ if (cstrcasecmp_with_null(black->name, s_without_on, max_len) ==
1008
+ 0) {
1009
+ /* printf("Got banned attribute name %s\n", black->name); */
1010
+ return black->atype;
1011
+ }
1012
+ black += 1;
1013
+ }
1014
+ }
1015
+
1016
+ /* XMLNS can be used to create arbitrary tags */
1017
+ if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 ||
1018
+ cstrcasecmp_with_null("XLINK", s, 5) == 0) {
1019
+ /* printf("Got XMLNS and XLINK tags\n"); */
1020
+ return TYPE_BLACK;
1021
+ }
1022
+ }
1023
+
1024
+ black = BLACKATTR;
1025
+ while (black->name != NULL) {
1026
+ if (cstrcasecmp_with_null(black->name, s, len) == 0) {
1027
+ /* printf("Got banned attribute name %s\n", black->name); */
1028
+ return black->atype;
1029
+ }
1030
+ black += 1;
1031
+ }
1032
+
1033
+ return TYPE_NONE;
1034
+ }
1035
+
1036
+ static int is_black_url(const char *s, size_t len) {
1037
+
1038
+ static const char *data_url = "DATA";
1039
+ static const char *viewsource_url = "VIEW-SOURCE";
1040
+
1041
+ /* obsolete but interesting signal */
1042
+ static const char *vbscript_url = "VBSCRIPT";
1043
+
1044
+ /* covers JAVA, JAVASCRIPT, + colon */
1045
+ static const char *javascript_url = "JAVA";
1046
+
1047
+ /* skip whitespace */
1048
+ while (len > 0 && (*s <= 32 || *s >= 127)) {
1049
+ /*
1050
+ * HEY: this is a signed character.
1051
+ * We are intentionally skipping high-bit characters too
1052
+ * since they are not ASCII, and Opera sometimes uses UTF-8 whitespace.
1053
+ *
1054
+ * Also in EUC-JP some of the high bytes are just ignored.
1055
+ */
1056
+ ++s;
1057
+ --len;
1058
+ }
1059
+
1060
+ if (htmlencode_startswith(data_url, s, len)) {
1061
+ return 1;
1062
+ }
1063
+
1064
+ if (htmlencode_startswith(viewsource_url, s, len)) {
1065
+ return 1;
1066
+ }
1067
+
1068
+ if (htmlencode_startswith(javascript_url, s, len)) {
1069
+ return 1;
1070
+ }
1071
+
1072
+ if (htmlencode_startswith(vbscript_url, s, len)) {
1073
+ return 1;
1074
+ }
1075
+ return 0;
1076
+ }
1077
+
1078
+ injection_result_t libinjection_is_xss(const char *s, size_t len, int flags) {
1079
+ h5_state_t h5;
1080
+ attribute_t attr = TYPE_NONE;
1081
+ injection_result_t parser_result;
1082
+
1083
+ libinjection_h5_init(&h5, s, len, (enum html5_flags)flags);
1084
+ while ((parser_result = libinjection_h5_next(&h5)) ==
1085
+ LIBINJECTION_RESULT_TRUE) {
1086
+ if (h5.token_type != ATTR_VALUE) {
1087
+ attr = TYPE_NONE;
1088
+ }
1089
+
1090
+ if (h5.token_type == DOCTYPE) {
1091
+ return LIBINJECTION_RESULT_TRUE;
1092
+ } else if (h5.token_type == TAG_NAME_OPEN) {
1093
+ if (is_black_tag(h5.token_start, h5.token_len)) {
1094
+ return LIBINJECTION_RESULT_TRUE;
1095
+ }
1096
+ } else if (h5.token_type == ATTR_NAME) {
1097
+ attr = is_black_attr(h5.token_start, h5.token_len);
1098
+ } else if (h5.token_type == ATTR_VALUE) {
1099
+ /*
1100
+ * IE6,7,8 parsing works a bit differently so
1101
+ * a whole <script> or other black tag might be hiding
1102
+ * inside an attribute value under HTML 5 parsing
1103
+ * See http://html5sec.org/#102
1104
+ * to avoid doing a full reparse of the value, just
1105
+ * look for "<". This probably need adjusting to
1106
+ * handle escaped characters
1107
+ */
1108
+ /*
1109
+ if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
1110
+ return 1;
1111
+ }
1112
+ */
1113
+
1114
+ switch (attr) {
1115
+ case TYPE_NONE:
1116
+ break;
1117
+ case TYPE_BLACK:
1118
+ return LIBINJECTION_RESULT_TRUE;
1119
+ case TYPE_ATTR_URL:
1120
+ if (is_black_url(h5.token_start, h5.token_len)) {
1121
+ return LIBINJECTION_RESULT_TRUE;
1122
+ }
1123
+ break;
1124
+ case TYPE_STYLE:
1125
+ return LIBINJECTION_RESULT_TRUE;
1126
+ case TYPE_ATTR_INDIRECT:
1127
+ /* an attribute name is specified in a _value_ */
1128
+ if (is_black_attr(h5.token_start, h5.token_len)) {
1129
+ return LIBINJECTION_RESULT_TRUE;
1130
+ }
1131
+ break;
1132
+ }
1133
+ attr = TYPE_NONE;
1134
+ } else if (h5.token_type == TAG_COMMENT) {
1135
+ /* IE uses a "`" as a tag ending char */
1136
+ if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
1137
+ return LIBINJECTION_RESULT_TRUE;
1138
+ }
1139
+
1140
+ /* IE conditional comment */
1141
+ if (h5.token_len > 3) {
1142
+ if (h5.token_start[0] == '[' &&
1143
+ (h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
1144
+ (h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
1145
+ return LIBINJECTION_RESULT_TRUE;
1146
+ }
1147
+ if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') &&
1148
+ (h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
1149
+ (h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
1150
+ return LIBINJECTION_RESULT_TRUE;
1151
+ }
1152
+ }
1153
+
1154
+ if (h5.token_len > 5) {
1155
+ /* IE <?import pseudo-tag */
1156
+ if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
1157
+ return LIBINJECTION_RESULT_TRUE;
1158
+ }
1159
+
1160
+ /* XML Entity definition */
1161
+ if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
1162
+ return LIBINJECTION_RESULT_TRUE;
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ return parser_result;
1168
+ }
1169
+
1170
+ /*
1171
+ * wrapper
1172
+ *
1173
+ *
1174
+ * const char* s: input string, may contain nulls, does not need to be
1175
+ * null-terminated. size_t len: input string length.
1176
+ *
1177
+ *
1178
+ */
1179
+ injection_result_t libinjection_xss(const char *s, size_t slen) {
1180
+ injection_result_t result;
1181
+ if ((result = libinjection_is_xss(s, slen, DATA_STATE)) !=
1182
+ LIBINJECTION_RESULT_FALSE) {
1183
+ return result;
1184
+ }
1185
+ if ((result = libinjection_is_xss(s, slen, VALUE_NO_QUOTE)) !=
1186
+ LIBINJECTION_RESULT_FALSE) {
1187
+ return result;
1188
+ }
1189
+ if ((result = libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE)) !=
1190
+ LIBINJECTION_RESULT_FALSE) {
1191
+ return result;
1192
+ }
1193
+ if ((result = libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE)) !=
1194
+ LIBINJECTION_RESULT_FALSE) {
1195
+ return result;
1196
+ }
1197
+ if ((result = libinjection_is_xss(s, slen, VALUE_BACK_QUOTE)) !=
1198
+ LIBINJECTION_RESULT_FALSE) {
1199
+ return result;
1200
+ }
1201
+
1202
+ return LIBINJECTION_RESULT_FALSE;
1203
+ }