Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/fuzz/analyzers/xss/analyzer_test.go
4538 views
1
package xss
2
3
import "testing"
4
5
func TestAnalyzeReflectionContext(t *testing.T) {
6
const marker = "FUZZ1337MARKER"
7
8
tests := []struct {
9
name string
10
body string
11
marker string
12
expected XSSContext
13
}{
14
// === HTML Body Context ===
15
{
16
name: "reflection in plain HTML body text",
17
body: `<html><body><p>Hello FUZZ1337MARKER world</p></body></html>`,
18
marker: marker,
19
expected: ContextHTMLBody,
20
},
21
{
22
name: "reflection in nested div body text",
23
body: `<div><span>text FUZZ1337MARKER more</span></div>`,
24
marker: marker,
25
expected: ContextHTMLBody,
26
},
27
28
// === Generic Attribute Context ===
29
{
30
name: "reflection in regular attribute value",
31
body: `<input type="text" value="FUZZ1337MARKER">`,
32
marker: marker,
33
expected: ContextHTMLAttribute,
34
},
35
{
36
name: "reflection in class attribute",
37
body: `<div class="foo FUZZ1337MARKER bar">text</div>`,
38
marker: marker,
39
expected: ContextHTMLAttribute,
40
},
41
{
42
name: "reflection in data-custom attribute",
43
body: `<div data-info="FUZZ1337MARKER">text</div>`,
44
marker: marker,
45
expected: ContextHTMLAttribute,
46
},
47
{
48
name: "reflection in title attribute",
49
body: `<img title="FUZZ1337MARKER">`,
50
marker: marker,
51
expected: ContextHTMLAttribute,
52
},
53
54
// === URL Attribute Context ===
55
{
56
name: "reflection in href with regular URL",
57
body: `<a href="https://example.com/FUZZ1337MARKER">link</a>`,
58
marker: marker,
59
expected: ContextHTMLAttributeURL,
60
},
61
{
62
name: "reflection in src attribute",
63
body: `<img src="/images/FUZZ1337MARKER.png">`,
64
marker: marker,
65
expected: ContextHTMLAttributeURL,
66
},
67
{
68
name: "reflection in action attribute",
69
body: `<form action="/submit/FUZZ1337MARKER"></form>`,
70
marker: marker,
71
expected: ContextHTMLAttributeURL,
72
},
73
{
74
name: "reflection in formaction attribute",
75
body: `<button formaction="/do/FUZZ1337MARKER">Go</button>`,
76
marker: marker,
77
expected: ContextHTMLAttributeURL,
78
},
79
80
{
81
name: "reflection in longdesc attribute",
82
body: `<img longdesc="https://example.com/desc/FUZZ1337MARKER">`,
83
marker: marker,
84
expected: ContextHTMLAttributeURL,
85
},
86
87
// === Event Handler Attribute Context ===
88
{
89
name: "reflection in onclick handler",
90
body: `<button onclick="doSomething('FUZZ1337MARKER')">Click</button>`,
91
marker: marker,
92
expected: ContextHTMLAttributeEvent,
93
},
94
{
95
name: "reflection in onmouseover handler",
96
body: `<div onmouseover="alert('FUZZ1337MARKER')">hover</div>`,
97
marker: marker,
98
expected: ContextHTMLAttributeEvent,
99
},
100
{
101
name: "reflection in onerror handler",
102
body: `<img src="x" onerror="log('FUZZ1337MARKER')">`,
103
marker: marker,
104
expected: ContextHTMLAttributeEvent,
105
},
106
{
107
name: "reflection in onload handler",
108
body: `<body onload="init('FUZZ1337MARKER')">`,
109
marker: marker,
110
expected: ContextHTMLAttributeEvent,
111
},
112
{
113
name: "reflection in onauxclick handler",
114
body: `<div onauxclick="handle('FUZZ1337MARKER')">right-click</div>`,
115
marker: marker,
116
expected: ContextHTMLAttributeEvent,
117
},
118
{
119
name: "reflection in onbeforeinput handler",
120
body: `<input onbeforeinput="check('FUZZ1337MARKER')">`,
121
marker: marker,
122
expected: ContextHTMLAttributeEvent,
123
},
124
125
// === Script Context (executable) ===
126
{
127
name: "reflection in script block with no type",
128
body: `<script>var x = "FUZZ1337MARKER";</script>`,
129
marker: marker,
130
expected: ContextScript,
131
},
132
{
133
name: "reflection in script type=text/javascript",
134
body: `<script type="text/javascript">var x = "FUZZ1337MARKER";</script>`,
135
marker: marker,
136
expected: ContextScript,
137
},
138
{
139
name: "reflection in script type=module",
140
body: `<script type="module">import "FUZZ1337MARKER";</script>`,
141
marker: marker,
142
expected: ContextScript,
143
},
144
{
145
name: "reflection in script type=application/javascript",
146
body: `<script type="application/javascript">var y = "FUZZ1337MARKER";</script>`,
147
marker: marker,
148
expected: ContextScript,
149
},
150
{
151
name: "script type with MIME parameters still executable",
152
body: `<script type="text/javascript; charset=utf-8">var x = "FUZZ1337MARKER";</script>`,
153
marker: marker,
154
expected: ContextScript,
155
},
156
157
// === javascript: URI -> ContextScript ===
158
{
159
name: "javascript URI in href must be ContextScript",
160
body: `<a href="javascript:alert('FUZZ1337MARKER')">xss</a>`,
161
marker: marker,
162
expected: ContextScript,
163
},
164
{
165
name: "javascript URI with whitespace prefix",
166
body: `<a href=" javascript:void(FUZZ1337MARKER)">xss</a>`,
167
marker: marker,
168
expected: ContextScript,
169
},
170
{
171
name: "javascript URI case-insensitive",
172
body: `<a href="JavaScript:alert('FUZZ1337MARKER')">xss</a>`,
173
marker: marker,
174
expected: ContextScript,
175
},
176
{
177
name: "data:text/html URI in src",
178
body: `<iframe src="data:text/html,<h1>FUZZ1337MARKER</h1>">`,
179
marker: marker,
180
expected: ContextScript,
181
},
182
{
183
name: "data:application/xhtml+xml URI in src",
184
body: `<iframe src="data:application/xhtml+xml,<html xmlns='http://www.w3.org/1999/xhtml'><body>FUZZ1337MARKER</body></html>">`,
185
marker: marker,
186
expected: ContextScript,
187
},
188
{
189
name: "data:image/svg+xml URI in iframe src",
190
body: `<iframe src="data:image/svg+xml,<svg onload=alert(FUZZ1337MARKER)>">`,
191
marker: marker,
192
expected: ContextScript,
193
},
194
{
195
name: "data:image/svg+xml URI in img src does not execute",
196
body: `<img src="data:image/svg+xml,<svg>FUZZ1337MARKER</svg>">`,
197
marker: marker,
198
expected: ContextHTMLAttributeURL,
199
},
200
{
201
name: "vbscript URI in href",
202
body: `<a href="vbscript:msgbox(FUZZ1337MARKER)">click</a>`,
203
marker: marker,
204
expected: ContextScript,
205
},
206
{
207
name: "javascript URI in img src does not execute",
208
body: `<img src="javascript:alert('FUZZ1337MARKER')">`,
209
marker: marker,
210
expected: ContextHTMLAttributeURL,
211
},
212
{
213
name: "javascript URI in ping does not execute",
214
body: `<a href="/" ping="javascript:FUZZ1337MARKER">click</a>`,
215
marker: marker,
216
expected: ContextHTMLAttributeURL,
217
},
218
{
219
name: "reflection in ping attribute",
220
body: `<a href="/" ping="https://tracker.example.com/FUZZ1337MARKER">click</a>`,
221
marker: marker,
222
expected: ContextHTMLAttributeURL,
223
},
224
225
// === ScriptData Context (non-executable script) ===
226
{
227
name: "reflection in script type=application/json",
228
body: `<script type="application/json">{"key": "FUZZ1337MARKER"}</script>`,
229
marker: marker,
230
expected: ContextScriptData,
231
},
232
{
233
name: "reflection in script type=text/template",
234
body: `<script type="text/template"><div>FUZZ1337MARKER</div></script>`,
235
marker: marker,
236
expected: ContextScriptData,
237
},
238
{
239
name: "reflection in script type=text/x-handlebars-template",
240
body: `<script type="text/x-handlebars-template">{{FUZZ1337MARKER}}</script>`,
241
marker: marker,
242
expected: ContextScriptData,
243
},
244
{
245
name: "reflection in script type=application/ld+json",
246
body: `<script type="application/ld+json">{"name":"FUZZ1337MARKER"}</script>`,
247
marker: marker,
248
expected: ContextScriptData,
249
},
250
251
{
252
name: "duplicate type attributes uses first per HTML5 spec",
253
body: `<script type="application/json" type="text/javascript">{"key": "FUZZ1337MARKER"}</script>`,
254
marker: marker,
255
expected: ContextScriptData,
256
},
257
258
// === Style Context ===
259
{
260
name: "reflection in style block",
261
body: `<style>.foo { color: FUZZ1337MARKER; }</style>`,
262
marker: marker,
263
expected: ContextStyle,
264
},
265
{
266
name: "reflection in style attribute",
267
body: `<div style="color: FUZZ1337MARKER">text</div>`,
268
marker: marker,
269
expected: ContextStyle,
270
},
271
272
// === Comment Context ===
273
{
274
name: "reflection in HTML comment",
275
body: `<html><!-- FUZZ1337MARKER --><body></body></html>`,
276
marker: marker,
277
expected: ContextComment,
278
},
279
{
280
name: "reflection in comment between tags",
281
body: `<div>text</div><!-- secret: FUZZ1337MARKER --><p>more</p>`,
282
marker: marker,
283
expected: ContextComment,
284
},
285
286
// === srcdoc attribute -> HTMLBody ===
287
{
288
name: "reflection in srcdoc attribute",
289
body: `<iframe srcdoc="<b>FUZZ1337MARKER</b>"></iframe>`,
290
marker: marker,
291
expected: ContextHTMLBody,
292
},
293
294
// === Case-insensitive matching ===
295
{
296
name: "case-insensitive marker matching (lowercase body)",
297
body: `<p>fuzz1337marker appears here</p>`,
298
marker: marker,
299
expected: ContextHTMLBody,
300
},
301
{
302
name: "case-insensitive marker matching (mixed case body)",
303
body: `<p>Fuzz1337Marker appears here</p>`,
304
marker: marker,
305
expected: ContextHTMLBody,
306
},
307
{
308
name: "case-insensitive in attribute",
309
body: `<input value="fuzz1337marker">`,
310
marker: marker,
311
expected: ContextHTMLAttribute,
312
},
313
314
// === Edge cases: marker not found ===
315
{
316
name: "marker not found in response",
317
body: `<html><body><p>Hello world</p></body></html>`,
318
marker: marker,
319
expected: ContextUnknown,
320
},
321
322
// === Edge cases: empty inputs ===
323
{
324
name: "empty response body",
325
body: ``,
326
marker: marker,
327
expected: ContextUnknown,
328
},
329
{
330
name: "empty marker",
331
body: `<p>Hello</p>`,
332
marker: "",
333
expected: ContextUnknown,
334
},
335
336
// === Malformed HTML ===
337
{
338
name: "malformed HTML with unclosed tags",
339
body: `<div><p>FUZZ1337MARKER<span>`,
340
marker: marker,
341
expected: ContextHTMLBody,
342
},
343
{
344
name: "malformed HTML with no tags at all",
345
body: `just plain text FUZZ1337MARKER`,
346
marker: marker,
347
expected: ContextHTMLBody,
348
},
349
{
350
name: "malformed script tag not closed",
351
body: `<script>var x = "FUZZ1337MARKER";`,
352
marker: marker,
353
expected: ContextScript,
354
},
355
{
356
name: "broken HTML with unclosed attribute quote",
357
body: `<a href = "FUZZ1337MARKER >broken`,
358
marker: marker,
359
expected: ContextUnknown, // tokenizer cannot parse unclosed quote reliably
360
},
361
{
362
name: "broken HTML with missing closing quote but valid parse",
363
body: `<a href=FUZZ1337MARKER>broken</a>`,
364
marker: marker,
365
expected: ContextHTMLAttributeURL,
366
},
367
368
// === Multiple reflections: first context wins ===
369
{
370
name: "multiple reflections returns first context",
371
body: `<!-- FUZZ1337MARKER --><p>FUZZ1337MARKER</p>`,
372
marker: marker,
373
expected: ContextComment,
374
},
375
376
// === Self-closing tags ===
377
{
378
name: "reflection in self-closing tag attribute",
379
body: `<img src="FUZZ1337MARKER"/>`,
380
marker: marker,
381
expected: ContextHTMLAttributeURL,
382
},
383
384
// === Script tag attribute reflections ===
385
{
386
name: "reflection in script src attribute",
387
body: `<script src="FUZZ1337MARKER"></script>`,
388
marker: marker,
389
expected: ContextHTMLAttributeURL,
390
},
391
{
392
name: "reflection in script src with type attribute",
393
body: `<script type="text/javascript" src="FUZZ1337MARKER"></script>`,
394
marker: marker,
395
expected: ContextHTMLAttributeURL,
396
},
397
{
398
name: "script tag with src and type but reflection in text",
399
body: `<script type="text/javascript" src="app.js">var z = "FUZZ1337MARKER";</script>`,
400
marker: marker,
401
expected: ContextScript,
402
},
403
{
404
name: "non-executable script with marker in src attribute",
405
body: `<script type="application/json" src="FUZZ1337MARKER"></script>`,
406
marker: marker,
407
expected: ContextHTMLAttributeURL,
408
},
409
410
// === Noscript tag ===
411
{
412
name: "reflection inside noscript",
413
body: `<noscript><p>FUZZ1337MARKER</p></noscript>`,
414
marker: marker,
415
expected: ContextHTMLBody,
416
},
417
418
// === Textarea ===
419
{
420
name: "reflection inside textarea",
421
body: `<textarea>FUZZ1337MARKER</textarea>`,
422
marker: marker,
423
expected: ContextHTMLBody,
424
},
425
}
426
427
for _, tc := range tests {
428
t.Run(tc.name, func(t *testing.T) {
429
result, err := AnalyzeReflectionContext(tc.body, tc.marker)
430
if err != nil {
431
t.Fatalf("unexpected error: %v", err)
432
}
433
if result != tc.expected {
434
t.Errorf("got %s, want %s", result, tc.expected)
435
}
436
})
437
}
438
}
439
440
func TestAnalyzeReflectionContext_NoPanic(t *testing.T) {
441
// Ensure no panics on various malformed inputs.
442
inputs := []string{
443
`<`,
444
`<>`,
445
`</>`,
446
`<<<>>>`,
447
`<script`,
448
`<script>`,
449
`</script>`,
450
`<!-- `,
451
`<!-- -->`,
452
`<div attr=">">`,
453
`<div attr='`,
454
string([]byte{0, 1, 2, 3, 4, 5}),
455
`<script type="">FUZZ1337MARKER</script>`,
456
`<` + string(make([]byte, 0)) + `>`,
457
}
458
459
for i, input := range inputs {
460
func() {
461
defer func() {
462
if r := recover(); r != nil {
463
t.Errorf("panic on input %d: %v", i, r)
464
}
465
}()
466
_, _ = AnalyzeReflectionContext(input, "FUZZ1337MARKER")
467
}()
468
}
469
}
470
471
func TestXSSContextString(t *testing.T) {
472
tests := []struct {
473
ctx XSSContext
474
expected string
475
}{
476
{ContextUnknown, "Unknown"},
477
{ContextHTMLBody, "HTMLBody"},
478
{ContextHTMLAttribute, "HTMLAttribute"},
479
{ContextHTMLAttributeURL, "HTMLAttributeURL"},
480
{ContextHTMLAttributeEvent, "HTMLAttributeEvent"},
481
{ContextScript, "Script"},
482
{ContextScriptData, "ScriptData"},
483
{ContextStyle, "Style"},
484
{ContextComment, "Comment"},
485
{XSSContext(99), "XSSContext(99)"},
486
}
487
488
for _, tc := range tests {
489
t.Run(tc.expected, func(t *testing.T) {
490
if got := tc.ctx.String(); got != tc.expected {
491
t.Errorf("got %q, want %q", got, tc.expected)
492
}
493
})
494
}
495
}
496
497