|
| 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