Skip to content

Commit 8d58b7b

Browse files
committed
Ruby: add XML library
1 parent 7e8d441 commit 8d58b7b

File tree

2 files changed

+357
-2
lines changed

2 files changed

+357
-2
lines changed

config/identical-files.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,8 @@
419419
"csharp/ql/lib/semmle/code/csharp/XML.qll",
420420
"java/ql/lib/semmle/code/xml/XML.qll",
421421
"javascript/ql/lib/semmle/javascript/XML.qll",
422-
"python/ql/lib/semmle/python/xml/XML.qll"
422+
"python/ql/lib/semmle/python/xml/XML.qll",
423+
"ruby/ql/lib/codeql/xml/XML.qll"
423424
],
424425
"DuplicationProblems.inc.qhelp": [
425426
"cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
@@ -604,4 +605,4 @@
604605
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
605606
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
606607
]
607-
}
608+
}

ruby/ql/lib/codeql/xml/XML.qll

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
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+
/** DEPRECATED: Alias for XmlLocatable */
36+
deprecated class XMLLocatable = XmlLocatable;
37+
38+
/**
39+
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
40+
* both of which can contain other elements.
41+
*/
42+
class XmlParent extends @xmlparent {
43+
XmlParent() {
44+
// explicitly restrict `this` to be either an `XmlElement` or an `XmlFile`;
45+
// the type `@xmlparent` currently also includes non-XML files
46+
this instanceof @xmlelement or xmlEncoding(this, _)
47+
}
48+
49+
/**
50+
* Gets a printable representation of this XML parent.
51+
* (Intended to be overridden in subclasses.)
52+
*/
53+
string getName() { none() } // overridden in subclasses
54+
55+
/** Gets the file to which this XML parent belongs. */
56+
XmlFile getFile() { result = this or xmlElements(this, _, _, _, result) }
57+
58+
/** Gets the child element at a specified index of this XML parent. */
59+
XmlElement getChild(int index) { xmlElements(result, _, this, index, _) }
60+
61+
/** Gets a child element of this XML parent. */
62+
XmlElement getAChild() { xmlElements(result, _, this, _, _) }
63+
64+
/** Gets a child element of this XML parent with the given `name`. */
65+
XmlElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
66+
67+
/** Gets a comment that is a child of this XML parent. */
68+
XmlComment getAComment() { xmlComments(result, _, this, _) }
69+
70+
/** Gets a character sequence that is a child of this XML parent. */
71+
XmlCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
72+
73+
/** Gets the depth in the tree. (Overridden in XmlElement.) */
74+
int getDepth() { result = 0 }
75+
76+
/** Gets the number of child XML elements of this XML parent. */
77+
int getNumberOfChildren() { result = count(XmlElement e | xmlElements(e, _, this, _, _)) }
78+
79+
/** Gets the number of places in the body of this XML parent where text occurs. */
80+
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
81+
82+
/**
83+
* Gets the result of appending all the character sequences of this XML parent from
84+
* left to right, separated by a space.
85+
*/
86+
string allCharactersString() {
87+
result =
88+
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
89+
}
90+
91+
/** Gets the text value contained in this XML parent. */
92+
string getTextValue() { result = this.allCharactersString() }
93+
94+
/** Gets a printable representation of this XML parent. */
95+
string toString() { result = this.getName() }
96+
}
97+
98+
/** DEPRECATED: Alias for XmlParent */
99+
deprecated class XMLParent = XmlParent;
100+
101+
/** An XML file. */
102+
class XmlFile extends XmlParent, File {
103+
XmlFile() { xmlEncoding(this, _) }
104+
105+
/** Gets a printable representation of this XML file. */
106+
override string toString() { result = this.getName() }
107+
108+
/** Gets the name of this XML file. */
109+
override string getName() { result = File.super.getAbsolutePath() }
110+
111+
/**
112+
* DEPRECATED: Use `getAbsolutePath()` instead.
113+
*
114+
* Gets the path of this XML file.
115+
*/
116+
deprecated string getPath() { result = this.getAbsolutePath() }
117+
118+
/**
119+
* DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead.
120+
*
121+
* Gets the path of the folder that contains this XML file.
122+
*/
123+
deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() }
124+
125+
/** Gets the encoding of this XML file. */
126+
string getEncoding() { xmlEncoding(this, result) }
127+
128+
/** Gets the XML file itself. */
129+
override XmlFile getFile() { result = this }
130+
131+
/** Gets a top-most element in an XML file. */
132+
XmlElement getARootElement() { result = this.getAChild() }
133+
134+
/** Gets a DTD associated with this XML file. */
135+
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
136+
137+
/** DEPRECATED: Alias for getADtd */
138+
deprecated XmlDtd getADTD() { result = this.getADtd() }
139+
}
140+
141+
/** DEPRECATED: Alias for XmlFile */
142+
deprecated class XMLFile = XmlFile;
143+
144+
/**
145+
* An XML document type definition (DTD).
146+
*
147+
* Example:
148+
*
149+
* ```
150+
* <!ELEMENT person (firstName, lastName?)>
151+
* <!ELEMENT firstName (#PCDATA)>
152+
* <!ELEMENT lastName (#PCDATA)>
153+
* ```
154+
*/
155+
class XmlDtd extends XmlLocatable, @xmldtd {
156+
/** Gets the name of the root element of this DTD. */
157+
string getRoot() { xmlDTDs(this, result, _, _, _) }
158+
159+
/** Gets the public ID of this DTD. */
160+
string getPublicId() { xmlDTDs(this, _, result, _, _) }
161+
162+
/** Gets the system ID of this DTD. */
163+
string getSystemId() { xmlDTDs(this, _, _, result, _) }
164+
165+
/** Holds if this DTD is public. */
166+
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
167+
168+
/** Gets the parent of this DTD. */
169+
XmlParent getParent() { xmlDTDs(this, _, _, _, result) }
170+
171+
override string toString() {
172+
this.isPublic() and
173+
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
174+
or
175+
not this.isPublic() and
176+
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
177+
}
178+
}
179+
180+
/** DEPRECATED: Alias for XmlDtd */
181+
deprecated class XMLDTD = XmlDtd;
182+
183+
/**
184+
* An XML element in an XML file.
185+
*
186+
* Example:
187+
*
188+
* ```
189+
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
190+
* package="com.example.exampleapp" android:versionCode="1">
191+
* </manifest>
192+
* ```
193+
*/
194+
class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
195+
/** Holds if this XML element has the given `name`. */
196+
predicate hasName(string name) { name = this.getName() }
197+
198+
/** Gets the name of this XML element. */
199+
override string getName() { xmlElements(this, result, _, _, _) }
200+
201+
/** Gets the XML file in which this XML element occurs. */
202+
override XmlFile getFile() { xmlElements(this, _, _, _, result) }
203+
204+
/** Gets the parent of this XML element. */
205+
XmlParent getParent() { xmlElements(this, _, result, _, _) }
206+
207+
/** Gets the index of this XML element among its parent's children. */
208+
int getIndex() { xmlElements(this, _, _, result, _) }
209+
210+
/** Holds if this XML element has a namespace. */
211+
predicate hasNamespace() { xmlHasNs(this, _, _) }
212+
213+
/** Gets the namespace of this XML element, if any. */
214+
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
215+
216+
/** Gets the index of this XML element among its parent's children. */
217+
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
218+
219+
/** Gets the depth of this element within the XML file tree structure. */
220+
override int getDepth() { result = this.getParent().getDepth() + 1 }
221+
222+
/** Gets an XML attribute of this XML element. */
223+
XmlAttribute getAnAttribute() { result.getElement() = this }
224+
225+
/** Gets the attribute with the specified `name`, if any. */
226+
XmlAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
227+
228+
/** Holds if this XML element has an attribute with the specified `name`. */
229+
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
230+
231+
/** Gets the value of the attribute with the specified `name`, if any. */
232+
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
233+
234+
/** Gets a printable representation of this XML element. */
235+
override string toString() { result = this.getName() }
236+
}
237+
238+
/** DEPRECATED: Alias for XmlElement */
239+
deprecated class XMLElement = XmlElement;
240+
241+
/**
242+
* An attribute that occurs inside an XML element.
243+
*
244+
* Examples:
245+
*
246+
* ```
247+
* package="com.example.exampleapp"
248+
* android:versionCode="1"
249+
* ```
250+
*/
251+
class XmlAttribute extends @xmlattribute, XmlLocatable {
252+
/** Gets the name of this attribute. */
253+
string getName() { xmlAttrs(this, _, result, _, _, _) }
254+
255+
/** Gets the XML element to which this attribute belongs. */
256+
XmlElement getElement() { xmlAttrs(this, result, _, _, _, _) }
257+
258+
/** Holds if this attribute has a namespace. */
259+
predicate hasNamespace() { xmlHasNs(this, _, _) }
260+
261+
/** Gets the namespace of this attribute, if any. */
262+
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }
263+
264+
/** Gets the value of this attribute. */
265+
string getValue() { xmlAttrs(this, _, _, result, _, _) }
266+
267+
/** Gets a printable representation of this XML attribute. */
268+
override string toString() { result = this.getName() + "=" + this.getValue() }
269+
}
270+
271+
/** DEPRECATED: Alias for XmlAttribute */
272+
deprecated class XMLAttribute = XmlAttribute;
273+
274+
/**
275+
* A namespace used in an XML file.
276+
*
277+
* Example:
278+
*
279+
* ```
280+
* xmlns:android="http://schemas.android.com/apk/res/android"
281+
* ```
282+
*/
283+
class XmlNamespace extends XmlLocatable, @xmlnamespace {
284+
/** Gets the prefix of this namespace. */
285+
string getPrefix() { xmlNs(this, result, _, _) }
286+
287+
/** Gets the URI of this namespace. */
288+
string getUri() { xmlNs(this, _, result, _) }
289+
290+
/** DEPRECATED: Alias for getUri */
291+
deprecated string getURI() { result = this.getUri() }
292+
293+
/** Holds if this namespace has no prefix. */
294+
predicate isDefault() { this.getPrefix() = "" }
295+
296+
override string toString() {
297+
this.isDefault() and result = this.getUri()
298+
or
299+
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
300+
}
301+
}
302+
303+
/** DEPRECATED: Alias for XmlNamespace */
304+
deprecated class XMLNamespace = XmlNamespace;
305+
306+
/**
307+
* A comment in an XML file.
308+
*
309+
* Example:
310+
*
311+
* ```
312+
* <!-- This is a comment. -->
313+
* ```
314+
*/
315+
class XmlComment extends @xmlcomment, XmlLocatable {
316+
/** Gets the text content of this XML comment. */
317+
string getText() { xmlComments(this, result, _, _) }
318+
319+
/** Gets the parent of this XML comment. */
320+
XmlParent getParent() { xmlComments(this, _, result, _) }
321+
322+
/** Gets a printable representation of this XML comment. */
323+
override string toString() { result = this.getText() }
324+
}
325+
326+
/** DEPRECATED: Alias for XmlComment */
327+
deprecated class XMLComment = XmlComment;
328+
329+
/**
330+
* A sequence of characters that occurs between opening and
331+
* closing tags of an XML element, excluding other elements.
332+
*
333+
* Example:
334+
*
335+
* ```
336+
* <content>This is a sequence of characters.</content>
337+
* ```
338+
*/
339+
class XmlCharacters extends @xmlcharacters, XmlLocatable {
340+
/** Gets the content of this character sequence. */
341+
string getCharacters() { xmlChars(this, result, _, _, _, _) }
342+
343+
/** Gets the parent of this character sequence. */
344+
XmlParent getParent() { xmlChars(this, _, result, _, _, _) }
345+
346+
/** Holds if this character sequence is CDATA. */
347+
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
348+
349+
/** Gets a printable representation of this XML character sequence. */
350+
override string toString() { result = this.getCharacters() }
351+
}
352+
353+
/** DEPRECATED: Alias for XmlCharacters */
354+
deprecated class XMLCharacters = XmlCharacters;

0 commit comments

Comments
 (0)