Skip to content

Commit

Permalink
Merge pull request #15923 from hvitved/shared-xml-impl
Browse files Browse the repository at this point in the history
Properly shared `XML.qll` implementation
  • Loading branch information
hvitved authored Apr 3, 2024
2 parents cccb11f + 2e370e2 commit 1dc13cc
Show file tree
Hide file tree
Showing 18 changed files with 620 additions and 1,398 deletions.
6 changes: 0 additions & 6 deletions config/identical-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,6 @@
"cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
],
"XML": [
"cpp/ql/lib/semmle/code/cpp/XML.qll",
"csharp/ql/lib/semmle/code/csharp/XML.qll",
"java/ql/lib/semmle/code/xml/XML.qll",
"python/ql/lib/semmle/python/xml/XML.qll"
],
"DuplicationProblems.inc.qhelp": [
"cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
"javascript/ql/src/Metrics/DuplicationProblems.inc.qhelp",
Expand Down
1 change: 1 addition & 0 deletions cpp/ql/lib/qlpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ dependencies:
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
codeql/xml: ${workspace}
warnOnImplicitThis: true
318 changes: 40 additions & 278 deletions cpp/ql/lib/semmle/code/cpp/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,305 +3,67 @@
*/

import semmle.files.FileSystem
private import codeql.xml.Xml

private class TXmlLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
private module Input implements InputSig<File, Location> {
class XmlLocatableBase = @xmllocatable or @xmlnamespaceable;

/** An XML element that has a location. */
class XmlLocatable extends @xmllocatable, TXmlLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
predicate xmllocations_(XmlLocatableBase e, Location loc) { xmllocations(e, loc) }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(File f, Location l | l = this.getLocation() |
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
filepath = f.getAbsolutePath()
)
}

/** Gets a textual representation of this element. */
string toString() { none() } // overridden in subclasses
}

/**
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
* both of which can contain other elements.
*/
class XmlParent extends @xmlparent {
XmlParent() {
// explicitly restrict `this` to be either an `XmlElement` or an `XmlFile`;
// the type `@xmlparent` currently also includes non-XML files
this instanceof @xmlelement or xmlEncoding(this, _)
}

/**
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)
*/
string getName() { none() } // overridden in subclasses

/** Gets the file to which this XML parent belongs. */
XmlFile getFile() { result = this or xmlElements(this, _, _, _, result) }
class XmlParentBase = @xmlparent;

/** Gets the child element at a specified index of this XML parent. */
XmlElement getChild(int index) { xmlElements(result, _, this, index, _) }
class XmlNamespaceableBase = @xmlnamespaceable;

/** Gets a child element of this XML parent. */
XmlElement getAChild() { xmlElements(result, _, this, _, _) }
class XmlElementBase = @xmlelement;

/** Gets a child element of this XML parent with the given `name`. */
XmlElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
class XmlFileBase = File;

/** Gets a comment that is a child of this XML parent. */
XmlComment getAComment() { xmlComments(result, _, this, _) }
predicate xmlEncoding_(XmlFileBase f, string enc) { xmlEncoding(f, enc) }

/** Gets a character sequence that is a child of this XML parent. */
XmlCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
class XmlDtdBase = @xmldtd;

/** Gets the depth in the tree. (Overridden in XmlElement.) */
int getDepth() { result = 0 }

/** Gets the number of child XML elements of this XML parent. */
int getNumberOfChildren() { result = count(XmlElement e | xmlElements(e, _, this, _, _)) }

/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }

/**
* Gets the result of appending all the character sequences of this XML parent from
* left to right, separated by a space.
*/
string allCharactersString() {
result =
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
predicate xmlDTDs_(XmlDtdBase e, string root, string publicId, string systemId, XmlFileBase file) {
xmlDTDs(e, root, publicId, systemId, file)
}

/** Gets the text value contained in this XML parent. */
string getTextValue() { result = this.allCharactersString() }

/** Gets a printable representation of this XML parent. */
string toString() { result = this.getName() }
}

/** An XML file. */
class XmlFile extends XmlParent, File {
XmlFile() { xmlEncoding(this, _) }

/** Gets a printable representation of this XML file. */
override string toString() { result = this.getName() }

/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }

/** Gets the encoding of this XML file. */
string getEncoding() { xmlEncoding(this, result) }

/** Gets the XML file itself. */
override XmlFile getFile() { result = this }

/** Gets a top-most element in an XML file. */
XmlElement getARootElement() { result = this.getAChild() }

/** Gets a DTD associated with this XML file. */
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
}

/**
* An XML document type definition (DTD).
*
* Example:
*
* ```
* <!ELEMENT person (firstName, lastName?)>
* <!ELEMENT firstName (#PCDATA)>
* <!ELEMENT lastName (#PCDATA)>
* ```
*/
class XmlDtd extends XmlLocatable, @xmldtd {
/** Gets the name of the root element of this DTD. */
string getRoot() { xmlDTDs(this, result, _, _, _) }

/** Gets the public ID of this DTD. */
string getPublicId() { xmlDTDs(this, _, result, _, _) }

/** Gets the system ID of this DTD. */
string getSystemId() { xmlDTDs(this, _, _, result, _) }

/** Holds if this DTD is public. */
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }

/** Gets the parent of this DTD. */
XmlParent getParent() { xmlDTDs(this, _, _, _, result) }

override string toString() {
this.isPublic() and
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
or
not this.isPublic() and
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
predicate xmlElements_(
XmlElementBase e, string name, XmlParentBase parent, int idx, XmlFileBase file
) {
xmlElements(e, name, parent, idx, file)
}
}

/**
* An XML element in an XML file.
*
* Example:
*
* ```
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
* package="com.example.exampleapp" android:versionCode="1">
* </manifest>
* ```
*/
class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = this.getName() }

/** Gets the name of this XML element. */
override string getName() { xmlElements(this, result, _, _, _) }

/** Gets the XML file in which this XML element occurs. */
override XmlFile getFile() { xmlElements(this, _, _, _, result) }
class XmlAttributeBase = @xmlattribute;

/** Gets the parent of this XML element. */
XmlParent getParent() { xmlElements(this, _, result, _, _) }

/** Gets the index of this XML element among its parent's children. */
int getIndex() { xmlElements(this, _, _, result, _) }

/** Holds if this XML element has a namespace. */
predicate hasNamespace() { xmlHasNs(this, _, _) }

/** Gets the namespace of this XML element, if any. */
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }

/** Gets the index of this XML element among its parent's children. */
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }

/** Gets the depth of this element within the XML file tree structure. */
override int getDepth() { result = this.getParent().getDepth() + 1 }

/** Gets an XML attribute of this XML element. */
XmlAttribute getAnAttribute() { result.getElement() = this }

/** Gets the attribute with the specified `name`, if any. */
XmlAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }

/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }

/** Gets the value of the attribute with the specified `name`, if any. */
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }

/** Gets a printable representation of this XML element. */
override string toString() { result = this.getName() }
}

/**
* An attribute that occurs inside an XML element.
*
* Examples:
*
* ```
* package="com.example.exampleapp"
* android:versionCode="1"
* ```
*/
class XmlAttribute extends @xmlattribute, XmlLocatable {
/** Gets the name of this attribute. */
string getName() { xmlAttrs(this, _, result, _, _, _) }

/** Gets the XML element to which this attribute belongs. */
XmlElement getElement() { xmlAttrs(this, result, _, _, _, _) }

/** Holds if this attribute has a namespace. */
predicate hasNamespace() { xmlHasNs(this, _, _) }

/** Gets the namespace of this attribute, if any. */
XmlNamespace getNamespace() { xmlHasNs(this, result, _) }

/** Gets the value of this attribute. */
string getValue() { xmlAttrs(this, _, _, result, _, _) }
predicate xmlAttrs_(
XmlAttributeBase e, XmlElementBase elementid, string name, string value, int idx,
XmlFileBase file
) {
xmlAttrs(e, elementid, name, value, idx, file)
}

/** Gets a printable representation of this XML attribute. */
override string toString() { result = this.getName() + "=" + this.getValue() }
}
class XmlNamespaceBase = @xmlnamespace;

/**
* A namespace used in an XML file.
*
* Example:
*
* ```
* xmlns:android="http://schemas.android.com/apk/res/android"
* ```
*/
class XmlNamespace extends XmlLocatable, @xmlnamespace {
/** Gets the prefix of this namespace. */
string getPrefix() { xmlNs(this, result, _, _) }
predicate xmlNs_(XmlNamespaceBase e, string prefixName, string uri, XmlFileBase file) {
xmlNs(e, prefixName, uri, file)
}

/** Gets the URI of this namespace. */
string getUri() { xmlNs(this, _, result, _) }
predicate xmlHasNs_(XmlNamespaceableBase e, XmlNamespaceBase ns, XmlFileBase file) {
xmlHasNs(e, ns, file)
}

/** Holds if this namespace has no prefix. */
predicate isDefault() { this.getPrefix() = "" }
class XmlCommentBase = @xmlcomment;

override string toString() {
this.isDefault() and result = this.getUri()
or
not this.isDefault() and result = this.getPrefix() + ":" + this.getUri()
predicate xmlComments_(XmlCommentBase e, string text, XmlParentBase parent, XmlFileBase file) {
xmlComments(e, text, parent, file)
}
}

/**
* A comment in an XML file.
*
* Example:
*
* ```
* <!-- This is a comment. -->
* ```
*/
class XmlComment extends @xmlcomment, XmlLocatable {
/** Gets the text content of this XML comment. */
string getText() { xmlComments(this, result, _, _) }

/** Gets the parent of this XML comment. */
XmlParent getParent() { xmlComments(this, _, result, _) }
class XmlCharactersBase = @xmlcharacters;

/** Gets a printable representation of this XML comment. */
override string toString() { result = this.getText() }
predicate xmlChars_(
XmlCharactersBase e, string text, XmlParentBase parent, int idx, int isCDATA, XmlFileBase file
) {
xmlChars(e, text, parent, idx, isCDATA, file)
}
}

/**
* A sequence of characters that occurs between opening and
* closing tags of an XML element, excluding other elements.
*
* Example:
*
* ```
* <content>This is a sequence of characters.</content>
* ```
*/
class XmlCharacters extends @xmlcharacters, XmlLocatable {
/** Gets the content of this character sequence. */
string getCharacters() { xmlChars(this, result, _, _, _, _) }

/** Gets the parent of this character sequence. */
XmlParent getParent() { xmlChars(this, _, result, _, _, _) }

/** Holds if this character sequence is CDATA. */
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }

/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}
import Make<File, Location, Input>
1 change: 1 addition & 0 deletions csharp/ql/lib/qlpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies:
codeql/threat-models: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
codeql/xml: ${workspace}
dataExtensions:
- ext/*.model.yml
- ext/generated/*.model.yml
Expand Down
Loading

0 comments on commit 1dc13cc

Please sign in to comment.