forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHTML.qll
More file actions
355 lines (301 loc) · 9.28 KB
/
Copy pathHTML.qll
File metadata and controls
355 lines (301 loc) · 9.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/** Provides classes for working with HTML documents. */
import javascript
module HTML {
/**
* An HTML file.
*/
class HtmlFile extends File {
HtmlFile() { this.getFileType().isHtml() }
}
/**
* A file that may contain HTML elements.
*
* This is either an `.html` file or a source code file containing
* embedded HTML snippets.
*/
private class FileContainingHtml extends File {
FileContainingHtml() {
this.getFileType().isHtml()
or
// The file contains an expression containing an HTML element
exists(Expr e |
e.getFile() = this and
xml_element_parent_expression(_, e, _)
)
}
}
/** Gets `i`th root node of the HTML fragment embedded in the given expression, if any. */
Element getHtmlElementFromExpr(Expr e, int i) { xml_element_parent_expression(result, e, i) }
/**
* An HTML element.
*
* Example:
*
* ```
* <a href="semmle.com">Semmle</a>
* ```
*/
class Element extends Locatable, @xmlelement {
Element() { exists(FileContainingHtml f | xmlElements(this, _, _, _, f)) }
override Location getLocation() { xmllocations(this, result) }
/**
* Gets the name of this HTML element.
*
* For example, the name of `<br>` is `br`.
*/
string getName() { xmlElements(this, result, _, _, _) }
/**
* Gets the parent element of this element, if any.
*/
Element getParent() { xmlElements(this, _, result, _, _) }
/**
* Holds if this is a toplevel element, that is, if it does not have a parent element.
*/
predicate isTopLevel() { not exists(this.getParent()) }
/**
* Gets the root HTML document element in which this element is contained.
*/
DocumentElement getDocument() { result = this.getRoot() }
/**
* Gets the root element in which this element is contained.
*/
Element getRoot() {
if this.isTopLevel() then result = this else result = this.getParent().getRoot()
}
/**
* Gets the `i`th child element (0-based) of this element.
*/
Element getChild(int i) { xmlElements(result, _, this, i, _) }
/**
* Gets a child element of this element.
*/
Element getChild() { result = this.getChild(_) }
/**
* Gets the `i`th attribute (0-based) of this element.
*/
Attribute getAttribute(int i) { xmlAttrs(result, this, _, _, i, _) }
/**
* Gets an attribute of this element.
*/
Attribute getAnAttribute() { result = this.getAttribute(_) }
/**
* Gets an attribute of this element that has the given name.
*/
Attribute getAttributeByName(string name) {
result = this.getAnAttribute() and
result.getName() = name
}
override string toString() { result = "<" + this.getName() + ">...</>" }
override string getAPrimaryQlClass() { result = "HTML::Element" }
}
/**
* An attribute of an HTML element.
*
* Examples:
*
* ```
* <a
* href ="semmle.com" <!-- an attribute -->
* target=_blank <!-- also an attribute -->
* >Semmle</a>
* ```
*/
class Attribute extends Locatable, @xmlattribute {
Attribute() { exists(FileContainingHtml f | xmlAttrs(this, _, _, _, _, f)) }
override Location getLocation() { xmllocations(this, result) }
/**
* Gets the inline script of this attribute, if any.
*/
CodeInAttribute getCodeInAttribute() { toplevel_parent_xml_node(result, this) }
/**
* Gets the element to which this attribute belongs.
*/
Element getElement() { xmlAttrs(this, result, _, _, _, _) }
/**
* Gets the root element in which the element to which this attribute
* belongs is contained.
*/
Element getRoot() { result = this.getElement().getRoot() }
/**
* Gets the name of this attribute.
*/
string getName() { xmlAttrs(this, _, result, _, _, _) }
/**
* Gets the value of this attribute.
*
* For attributes without an explicitly specified value, the
* result is the empty string.
*/
string getValue() { xmlAttrs(this, _, _, result, _, _) }
override string toString() { result = this.getName() + "=" + this.getValue() }
override string getAPrimaryQlClass() { result = "HTML::Attribute" }
}
/**
* An HTML `<html>` element.
*
* Example:
*
* ```
* <html>
* <body>
* This is a test.
* </body>
* </html>
* ```
*/
class DocumentElement extends Element {
DocumentElement() { this.getName() = "html" }
}
/**
* An HTML `<iframe>` element.
*
* Example:
*
* ```
* <iframe src="https://test.local/somepage.html"></iframe>
* ```
*/
class IframeElement extends Element {
IframeElement() { this.getName() = "iframe" }
/**
* Gets the value of the `src` attribute.
*/
string getSourcePath() { result = this.getAttributeByName("src").getValue() }
}
/**
* An HTML `<script>` element.
*
* Example:
*
* ```
* <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
* ```
*/
class ScriptElement extends Element {
ScriptElement() { this.getName() = "script" }
/**
* Gets the absolute file system path the value of the `src` attribute
* of this script tag resolves to, if any.
*
* Path resolution is currently limited to absolute `file://` URLs,
* absolute file system paths starting with `/`, and paths relative
* to the enclosing HTML file. Base URLs are not taken into account.
*/
string resolveSourcePath() {
exists(string path | path = this.getSourcePath() |
result = path.regexpCapture("file://(/.*)", 1)
or
not path.regexpMatch("(\\w+:)?//.*") and
result = this.getSourcePath().(ScriptSrcPath).resolve(this.getSearchRoot()).toString()
)
}
/**
* Gets the value of the `src` attribute.
*/
string getSourcePath() { result = this.getAttributeByName("src").getValue() }
/**
* Gets the value of the `integrity` attribute.
*/
string getIntegrityDigest() { result = this.getAttributeByName("integrity").getValue() }
/**
* Gets the folder relative to which the `src` attribute is resolved.
*/
Folder getSearchRoot() {
if this.getSourcePath().matches("/%")
then result.getBaseName() = ""
else result = this.getFile().getParentContainer()
}
/**
* Gets the script referred to by the `src` attribute,
* if it can be determined.
*/
Script resolveSource() { result.getFile().getAbsolutePath() = this.resolveSourcePath() }
/**
* Gets the inline script of this script element, if any.
*/
private InlineScript getInlineScript() {
toplevel_parent_xml_node(result, this) and
// the src attribute has precedence
not exists(this.getSourcePath())
}
/**
* Gets the script of this element, if it can be determined.
*/
Script getScript() {
result = this.getInlineScript() or
result = this.resolveSource()
}
override string getAPrimaryQlClass() { result = "HTML::ScriptElement" }
}
/**
* Holds if there is an HTML `<script>` tag with the given `src`
* such that the script is resolved relative to `root`.
*/
private predicate scriptSrc(string src, Folder root) {
exists(ScriptElement script |
src = script.getSourcePath() and
root = script.getSearchRoot()
)
}
/**
* A path string arising from the `src` attribute of a `script` tag.
*/
private class ScriptSrcPath extends PathString {
ScriptSrcPath() { scriptSrc(this, _) }
override Folder getARootFolder() { scriptSrc(this, result) }
}
/**
* An HTML text node.
*
* Example:
*
* ```
* <div>
* This text is represented as a text node.
* </div>
* ```
*
* Note that instances of this class are only available if extraction is done with `--html all` or `--experimental`.
*/
class TextNode extends Locatable, @xmlcharacters {
TextNode() { exists(FileContainingHtml f | xmlChars(this, _, _, _, _, f)) }
override string toString() { result = this.getText() }
/**
* Gets the content of this text node.
*
* Note that entity expansion has been performed already.
*/
string getText() { xmlChars(this, result, _, _, _, _) }
/**
* Gets the parent this text.
*/
Element getParent() { xmlChars(this, _, result, _, _, _) }
/**
* Gets the child index number of this text node.
*/
int getIndex() { xmlChars(this, _, _, result, _, _) }
/**
* Holds if this text node is inside a `CDATA` tag.
*/
predicate isCData() { xmlChars(this, _, _, _, 1, _) }
override Location getLocation() { xmllocations(this, result) }
}
/**
* An HTML comment.
*
* Example:
*
* ```
* <!-- this is a comment -->
* ```
*/
class CommentNode extends Locatable, @xmlcomment {
CommentNode() { exists(FileContainingHtml f | xmlComments(this, _, _, f)) }
/** Gets the element in which this comment occurs. */
Element getParent() { xmlComments(this, _, result, _) }
/** Gets the text of this comment, not including delimiters. */
string getText() { result = this.toString().regexpCapture("(?s)<!--(.*)-->", 1) }
override string toString() { xmlComments(this, result, _, _) }
override Location getLocation() { xmllocations(this, result) }
}
}