Skip to content

Commit ccfbd29

Browse files
committed
Copy existing XML.qll into new a new codeql/xml pack
1 parent f025430 commit ccfbd29

File tree

2 files changed

+314
-0
lines changed

2 files changed

+314
-0
lines changed

shared/xml/codeql/xml/Xml.qll

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/**
2+
* Provides classes and predicates for working with XML files and their content.
3+
*/
4+
5+
import semmle.files.FileSystem
6+
7+
private class TXmlLocatable =
8+
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
9+
10+
/** An XML element that has a location. */
11+
class XmlLocatable extends @xmllocatable, TXmlLocatable {
12+
/** Gets the source location for this element. */
13+
Location getLocation() { xmllocations(this, result) }
14+
15+
/**
16+
* Holds if this element is at the specified location.
17+
* The location spans column `startcolumn` of line `startline` to
18+
* column `endcolumn` of line `endline` in file `filepath`.
19+
* For more information, see
20+
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
21+
*/
22+
predicate hasLocationInfo(
23+
string filepath, int startline, int startcolumn, int endline, int endcolumn
24+
) {
25+
exists(File f, Location l | l = this.getLocation() |
26+
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
27+
filepath = f.getAbsolutePath()
28+
)
29+
}
30+
31+
/** Gets a textual representation of this element. */
32+
string toString() { none() } // overridden in subclasses
33+
}
34+
35+
/**
36+
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
37+
* both of which can contain other elements.
38+
*/
39+
class XmlParent extends @xmlparent {
40+
XmlParent() {
41+
// explicitly restrict `this` to be either an `XmlElement` or an `XmlFile`;
42+
// the type `@xmlparent` currently also includes non-XML files
43+
this instanceof @xmlelement or xmlEncoding(this, _)
44+
}
45+
46+
/**
47+
* Gets a printable representation of this XML parent.
48+
* (Intended to be overridden in subclasses.)
49+
*/
50+
string getName() { none() } // overridden in subclasses
51+
52+
/** Gets the file to which this XML parent belongs. */
53+
XmlFile getFile() { result = this or xmlElements(this, _, _, _, result) }
54+
55+
/** Gets the child element at a specified index of this XML parent. */
56+
XmlElement getChild(int index) { xmlElements(result, _, this, index, _) }
57+
58+
/** Gets a child element of this XML parent. */
59+
XmlElement getAChild() { xmlElements(result, _, this, _, _) }
60+
61+
/** Gets a child element of this XML parent with the given `name`. */
62+
XmlElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
63+
64+
/** Gets a comment that is a child of this XML parent. */
65+
XmlComment getAComment() { xmlComments(result, _, this, _) }
66+
67+
/** Gets a character sequence that is a child of this XML parent. */
68+
XmlCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
69+
70+
/** Gets the depth in the tree. (Overridden in XmlElement.) */
71+
int getDepth() { result = 0 }
72+
73+
/** Gets the number of child XML elements of this XML parent. */
74+
int getNumberOfChildren() { result = count(XmlElement e | xmlElements(e, _, this, _, _)) }
75+
76+
/** Gets the number of places in the body of this XML parent where text occurs. */
77+
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
78+
79+
/**
80+
* Gets the result of appending all the character sequences of this XML parent from
81+
* left to right, separated by a space.
82+
*/
83+
string allCharactersString() {
84+
result =
85+
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
86+
}
87+
88+
/** Gets the text value contained in this XML parent. */
89+
string getTextValue() { result = this.allCharactersString() }
90+
91+
/** Gets a printable representation of this XML parent. */
92+
string toString() { result = this.getName() }
93+
}
94+
95+
/** An XML file. */
96+
class XmlFile extends XmlParent, File {
97+
XmlFile() { xmlEncoding(this, _) }
98+
99+
/** Gets a printable representation of this XML file. */
100+
override string toString() { result = this.getName() }
101+
102+
/** Gets the name of this XML file. */
103+
override string getName() { result = File.super.getAbsolutePath() }
104+
105+
/** Gets the encoding of this XML file. */
106+
string getEncoding() { xmlEncoding(this, result) }
107+
108+
/** Gets the XML file itself. */
109+
override XmlFile getFile() { result = this }
110+
111+
/** Gets a top-most element in an XML file. */
112+
XmlElement getARootElement() { result = this.getAChild() }
113+
114+
/** Gets a DTD associated with this XML file. */
115+
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
116+
}
117+
118+
/**
119+
* An XML document type definition (DTD).
120+
*
121+
* Example:
122+
*
123+
* ```
124+
* <!ELEMENT person (firstName, lastName?)>
125+
* <!ELEMENT firstName (#PCDATA)>
126+
* <!ELEMENT lastName (#PCDATA)>
127+
* ```
128+
*/
129+
class XmlDtd extends XmlLocatable, @xmldtd {
130+
/** Gets the name of the root element of this DTD. */
131+
string getRoot() { xmlDTDs(this, result, _, _, _) }
132+
133+
/** Gets the public ID of this DTD. */
134+
string getPublicId() { xmlDTDs(this, _, result, _, _) }
135+
136+
/** Gets the system ID of this DTD. */
137+
string getSystemId() { xmlDTDs(this, _, _, result, _) }
138+
139+
/** Holds if this DTD is public. */
140+
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
141+
142+
/** Gets the parent of this DTD. */
143+
XmlParent getParent() { xmlDTDs(this, _, _, _, result) }
144+
145+
override string toString() {
146+
this.isPublic() and
147+
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
148+
or
149+
not this.isPublic() and
150+
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
151+
}
152+
}
153+
154+
/**
155+
* An XML element in an XML file.
156+
*
157+
* Example:
158+
*
159+
* ```
160+
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
161+
* package="com.example.exampleapp" android:versionCode="1">
162+
* </manifest>
163+
* ```
164+
*/
165+
class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
166+
/** Holds if this XML element has the given `name`. */
167+
predicate hasName(string name) { name = this.getName() }
168+
169+
/** Gets the name of this XML element. */
170+
override string getName() { xmlElements(this, result, _, _, _) }
171+
172+
/** Gets the XML file in which this XML element occurs. */
173+
override XmlFile getFile() { xmlElements(this, _, _, _, result) }
174+
175+
/** Gets the parent of this XML element. */
176+
XmlParent getParent() { xmlElements(this, _, result, _, _) }
177+
178+
/** Gets the index of this XML element among its parent's children. */
179+
int getIndex() { xmlElements(this, _, _, result, _) }
180+
181+
/** Holds if this XML element has a namespace. */
182+
predicate hasNamespace() { xmlHasNs(this, _, _) }
183+
184+
/** Gets the namespace of this XML element, if any. */
185+
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
186+
187+
/** Gets the index of this XML element among its parent's children. */
188+
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
189+
190+
/** Gets the depth of this element within the XML file tree structure. */
191+
override int getDepth() { result = this.getParent().getDepth() + 1 }
192+
193+
/** Gets an XML attribute of this XML element. */
194+
XmlAttribute getAnAttribute() { result.getElement() = this }
195+
196+
/** Gets the attribute with the specified `name`, if any. */
197+
XmlAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
198+
199+
/** Holds if this XML element has an attribute with the specified `name`. */
200+
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
201+
202+
/** Gets the value of the attribute with the specified `name`, if any. */
203+
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
204+
205+
/** Gets a printable representation of this XML element. */
206+
override string toString() { result = this.getName() }
207+
}
208+
209+
/**
210+
* An attribute that occurs inside an XML element.
211+
*
212+
* Examples:
213+
*
214+
* ```
215+
* package="com.example.exampleapp"
216+
* android:versionCode="1"
217+
* ```
218+
*/
219+
class XmlAttribute extends @xmlattribute, XmlLocatable {
220+
/** Gets the name of this attribute. */
221+
string getName() { xmlAttrs(this, _, result, _, _, _) }
222+
223+
/** Gets the XML element to which this attribute belongs. */
224+
XmlElement getElement() { xmlAttrs(this, result, _, _, _, _) }
225+
226+
/** Holds if this attribute has a namespace. */
227+
predicate hasNamespace() { xmlHasNs(this, _, _) }
228+
229+
/** Gets the namespace of this attribute, if any. */
230+
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
231+
232+
/** Gets the value of this attribute. */
233+
string getValue() { xmlAttrs(this, _, _, result, _, _) }
234+
235+
/** Gets a printable representation of this XML attribute. */
236+
override string toString() { result = this.getName() + "=" + this.getValue() }
237+
}
238+
239+
/**
240+
* A namespace used in an XML file.
241+
*
242+
* Example:
243+
*
244+
* ```
245+
* xmlns:android="http://schemas.android.com/apk/res/android"
246+
* ```
247+
*/
248+
class XmlNamespace extends XmlLocatable, @xmlnamespace {
249+
/** Gets the prefix of this namespace. */
250+
string getPrefix() { xmlNs(this, result, _, _) }
251+
252+
/** Gets the URI of this namespace. */
253+
string getUri() { xmlNs(this, _, result, _) }
254+
255+
/** Holds if this namespace has no prefix. */
256+
predicate isDefault() { this.getPrefix() = "" }
257+
258+
override string toString() {
259+
this.isDefault() and result = this.getUri()
260+
or
261+
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
262+
}
263+
}
264+
265+
/**
266+
* A comment in an XML file.
267+
*
268+
* Example:
269+
*
270+
* ```
271+
* <!-- This is a comment. -->
272+
* ```
273+
*/
274+
class XmlComment extends @xmlcomment, XmlLocatable {
275+
/** Gets the text content of this XML comment. */
276+
string getText() { xmlComments(this, result, _, _) }
277+
278+
/** Gets the parent of this XML comment. */
279+
XmlParent getParent() { xmlComments(this, _, result, _) }
280+
281+
/** Gets a printable representation of this XML comment. */
282+
override string toString() { result = this.getText() }
283+
}
284+
285+
/**
286+
* A sequence of characters that occurs between opening and
287+
* closing tags of an XML element, excluding other elements.
288+
*
289+
* Example:
290+
*
291+
* ```
292+
* <content>This is a sequence of characters.</content>
293+
* ```
294+
*/
295+
class XmlCharacters extends @xmlcharacters, XmlLocatable {
296+
/** Gets the content of this character sequence. */
297+
string getCharacters() { xmlChars(this, result, _, _, _, _) }
298+
299+
/** Gets the parent of this character sequence. */
300+
XmlParent getParent() { xmlChars(this, _, result, _, _, _) }
301+
302+
/** Holds if this character sequence is CDATA. */
303+
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
304+
305+
/** Gets a printable representation of this XML character sequence. */
306+
override string toString() { result = this.getCharacters() }
307+
}

shared/xml/qlpack.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: codeql/xml
2+
version: 0.0.1-dev
3+
groups: shared
4+
library: true
5+
dependencies:
6+
codeql/util: ${workspace}
7+
warnOnImplicitThis: true

0 commit comments

Comments
 (0)