Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Promote exxperimental XXE sinks
  • Loading branch information
atorralba committed Apr 26, 2023
commit 1e66a544fd7afce688de975f03a1700d849322e0
90 changes: 90 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/** Provides XML definitions related to the `org.apache.commons` package. */

import java
private import semmle.code.java.dataflow.RangeUtils
private import semmle.code.java.security.XmlParsers

/**
* The classes `org.apache.commons.digester3.Digester`, `org.apache.commons.digester.Digester` or `org.apache.tomcat.util.digester.Digester`.
*/
private class Digester extends RefType {
Digester() {
this.hasQualifiedName([
"org.apache.commons.digester3", "org.apache.commons.digester",
"org.apache.tomcat.util.digester"
], "Digester")
}
}

/** A call to `Digester.parse`. */
private class DigesterParse extends XmlParserCall {
DigesterParse() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof Digester and
m.hasName("parse")
)
}

override Expr getSink() { result = this.getArgument(0) }

override predicate isSafe() { SafeDigesterFlow::flowToExpr(this.getQualifier()) }
}

/** A `ParserConfig` that is specific to `Digester`. */
private class DigesterConfig extends ParserConfig {
DigesterConfig() {
exists(Method m |
m = this.getMethod() and
m.getDeclaringType() instanceof Digester and
m.hasName("setFeature")
)
}
}

/**
* A safely configured `Digester`.
*/
private class SafeDigester extends VarAccess {
SafeDigester() {
exists(Variable v | v = this.getVariable() |
exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
config.enables(singleSafeConfig())
)
or
exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
config
.disables(any(ConstantStringExpr s |
s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
))
) and
exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
config
.disables(any(ConstantStringExpr s |
s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
))
) and
exists(DigesterConfig config | config.getQualifier() = v.getAnAccess() |
config
.disables(any(ConstantStringExpr s |
s.getStringValue() =
"http://apache.org/xml/features/nonvalidating/load-external-dtd"
))
)
)
}
}

private module SafeDigesterFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeDigester }

predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof Digester
)
}

int fieldFlowBranchLimit() { result = 0 }
}

private module SafeDigesterFlow = DataFlow::Global<SafeDigesterFlowConfig>;
64 changes: 64 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/javaee/Xml.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/** Provides definitions related to the `javax.xml` package. */

import java
private import semmle.code.java.security.XmlParsers

/** A call to `Validator.validate`. */
private class ValidatorValidate extends XmlParserCall {
ValidatorValidate() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof Validator and
m.hasName("validate")
)
}

override Expr getSink() { result = this.getArgument(0) }

override predicate isSafe() { SafeValidatorFlow::flowToExpr(this.getQualifier()) }
}

/** A `TransformerConfig` specific to `Validator`. */
private class ValidatorConfig extends TransformerConfig {
ValidatorConfig() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof Validator and
m.hasName("setProperty")
)
}
}

/** The class `javax.xml.validation.Validator`. */
private class Validator extends RefType {
Validator() { this.hasQualifiedName("javax.xml.validation", "Validator") }
}

/** A safely configured `Validator`. */
private class SafeValidator extends VarAccess {
SafeValidator() {
exists(Variable v | v = this.getVariable() |
exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
config.disables(configAccessExternalDtd())
) and
exists(ValidatorConfig config | config.getQualifier() = v.getAnAccess() |
config.disables(configAccessExternalSchema())
)
)
}
}

private module SafeValidatorFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeValidator }

predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getQualifier() and
ma.getMethod().getDeclaringType() instanceof Validator
)
}

int fieldFlowBranchLimit() { result = 0 }
}

private module SafeValidatorFlow = DataFlow::Global<SafeValidatorFlowConfig>;
24 changes: 24 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/javase/Beans.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** Provides definitions related to the `java.beans` package. */

import java
private import semmle.code.java.security.XmlParsers

/** The class `java.beans.XMLDecoder`. */
private class XmlDecoder extends RefType {
XmlDecoder() { this.hasQualifiedName("java.beans", "XMLDecoder") }
}

/** A call to `XMLDecoder.readObject`. */
private class XmlDecoderReadObject extends XmlParserCall {
XmlDecoderReadObject() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof XmlDecoder and
m.hasName("readObject")
)
}

override Expr getSink() { result = this.getQualifier() }

override predicate isSafe() { none() }
}
19 changes: 19 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/rundeck/RundeckXml.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** Provides definitions related to XML parsing in Rundeck. */

import java
private import semmle.code.java.security.XmlParsers

/** A call to `ParserHelper.loadDocument`. */
private class ParserHelperLoadDocument extends XmlParserCall {
ParserHelperLoadDocument() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType().hasQualifiedName("org.rundeck.api.parser", "ParserHelper") and
m.hasName("loadDocument")
)
}

override Expr getSink() { result = this.getArgument(0) }

override predicate isSafe() { none() }
}
12 changes: 7 additions & 5 deletions java/ql/lib/semmle/code/java/security/XmlParsers.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.DataFlow3
private import semmle.code.java.dataflow.RangeUtils

/*
* Various XML parsers in Java.
*/
private module Frameworks {
private import semmle.code.java.frameworks.apache.CommonsXml
private import semmle.code.java.frameworks.javaee.Xml
private import semmle.code.java.frameworks.javase.Beans
private import semmle.code.java.frameworks.rundeck.RundeckXml
}

/**
* An abstract type representing a call to parse XML files.
Expand Down Expand Up @@ -946,7 +948,7 @@ class TransformerFactorySource extends XmlParserCall {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof TransformerFactory and
m.hasName("newTransformer")
m.hasName(["newTransformer", "newTransformerHandler"])
)
}

Expand Down