mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
remove additional taint steps and flow states
This commit is contained in:
@@ -15,37 +15,19 @@ import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import experimental.semmle.code.java.security.FileAndFormRemoteSource
|
||||
import experimental.semmle.code.java.security.DecompressionBomb::DecompressionBomb
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
module DecompressionBombsConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = DecompressionState;
|
||||
module DecompressionBombsConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
source instanceof RemoteFlowSource and
|
||||
(
|
||||
state instanceof ZipFile
|
||||
or
|
||||
state instanceof Zip4j
|
||||
or
|
||||
state instanceof Inflator
|
||||
or
|
||||
state instanceof ApacheCommons
|
||||
or
|
||||
state instanceof XerialSnappy
|
||||
or
|
||||
state instanceof UtilZip
|
||||
)
|
||||
}
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) { any(Sink s).sink(sink, state) }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo
|
||||
) {
|
||||
any(AdditionalStep ads).step(nodeFrom, stateFrom, nodeTo, stateTo)
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
any(AdditionalStep ads).step(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
module DecompressionBombsFlow = TaintTracking::GlobalWithState<DecompressionBombsConfig>;
|
||||
module DecompressionBombsFlow = TaintTracking::Global<DecompressionBombsConfig>;
|
||||
|
||||
import DecompressionBombsFlow::PathGraph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
module DecompressionBomb {
|
||||
newtype DecompressionState =
|
||||
@@ -15,9 +15,7 @@ module DecompressionBomb {
|
||||
*
|
||||
* Extend this class for creating new decompression bomb sinks
|
||||
*/
|
||||
class Sink extends Unit {
|
||||
abstract predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state);
|
||||
}
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* The Additional flow steps that help to create a dataflow or taint tracking query
|
||||
@@ -25,10 +23,7 @@ module DecompressionBomb {
|
||||
* Extend this class for creating new additional taint steps
|
||||
*/
|
||||
class AdditionalStep extends Unit {
|
||||
abstract predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
);
|
||||
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,34 +40,6 @@ module XerialSnappy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `SnappyInputStream n2 = new SnappyInputStream(n1)` or
|
||||
* `n1.read(n2)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class InputStreamAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
call.getCallee().getDeclaringType() = any(TypeInputStream t) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
call.(MethodCall).getReceiverType() = any(TypeInputStream t) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::XerialSnappy and
|
||||
stateTo instanceof DecompressionBomb::XerialSnappy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `SnappyInputStream` Types
|
||||
*/
|
||||
@@ -84,9 +51,12 @@ module XerialSnappy {
|
||||
}
|
||||
|
||||
class Sink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::XerialSnappy
|
||||
Sink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call | call.getConstructedType() instanceof TypeInputStream |
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,34 +132,6 @@ module ApacheCommons {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `*CompressorInputStream n2 = new *CompressorInputStream(n2)` or
|
||||
* `n2 = inputStream.read(n1)` or `n1.read(n2)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class CompressorsAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
call.getCallee().getDeclaringType() = any(TypeCompressors t) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
call.(MethodCall).getReceiverType() = any(TypeCompressors t) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::ApacheCommons and
|
||||
stateTo instanceof DecompressionBomb::ApacheCommons
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `*CompressorInputStream` Types
|
||||
*/
|
||||
@@ -201,9 +143,12 @@ module ApacheCommons {
|
||||
}
|
||||
|
||||
class Sink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::ApacheCommons
|
||||
Sink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call | call.getConstructedType() instanceof TypeCompressors |
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,34 +177,6 @@ module ApacheCommons {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `*ArchiveInputStream n2 = new *ArchiveInputStream(n2)` or
|
||||
* `n2 = inputStream.read(n2)` or `n1.read(n2)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class ArchiversAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
call.getCallee().getDeclaringType() = any(TypeArchivers t) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
call.(MethodCall).getReceiverType() = any(TypeArchivers t) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::ApacheCommons and
|
||||
stateTo instanceof DecompressionBomb::ApacheCommons
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `*ArchiveInputStream` Types
|
||||
*/
|
||||
@@ -271,9 +188,12 @@ module ApacheCommons {
|
||||
}
|
||||
|
||||
class Sink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::ApacheCommons
|
||||
Sink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call | call.getConstructedType() instanceof TypeArchivers |
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,44 +222,6 @@ module ApacheCommons {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `CompressorInputStream n2 = new CompressorStreamFactory().createCompressorInputStream(n1)`
|
||||
* or `ArchiveInputStream n2 = new ArchiveStreamFactory().createArchiveInputStream(n1)` or
|
||||
* `n1.read(n2)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class CompressorsAndArchiversAdditionalTaintStep extends DecompressionBomb::AdditionalStep
|
||||
{
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
(
|
||||
call.getCallee().getDeclaringType() = any(TypeCompressors t)
|
||||
or
|
||||
call.getCallee().getDeclaringType() = any(TypeArchivers t)
|
||||
) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
(
|
||||
call.(MethodCall).getReceiverType() = any(TypeArchiveInputStream t)
|
||||
or
|
||||
call.(MethodCall).getReceiverType() = any(TypeCompressorInputStream t)
|
||||
) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::ApacheCommons and
|
||||
stateTo instanceof DecompressionBomb::ApacheCommons
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `CompressorInputStream` or `ArchiveInputStream` Types
|
||||
*/
|
||||
@@ -355,9 +237,15 @@ module ApacheCommons {
|
||||
}
|
||||
|
||||
class Sink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::ApacheCommons
|
||||
Sink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call |
|
||||
call.getConstructedType() instanceof TypeCompressors or
|
||||
call.getConstructedType() instanceof TypeArchivers
|
||||
|
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,36 +275,12 @@ module Zip4j {
|
||||
}
|
||||
|
||||
class Sink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::Zip4j
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `ZipInputStream n2 = new ZipInputStream(n1)` or `n2 = zipInputStream.read(n1)` or `n1.read(n2)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class InputStreamAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
call.getCallee().getDeclaringType() = any(TypeZipInputStream t) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
call.(MethodCall).getReceiverType() = any(TypeZipInputStream t) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::Zip4j and
|
||||
stateTo instanceof DecompressionBomb::Zip4j
|
||||
Sink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call | call.getConstructedType() instanceof TypeZipInputStream |
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,37 +311,12 @@ module Zip {
|
||||
}
|
||||
|
||||
class ReadInputStreamSink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(ReadInputStreamCall r) and
|
||||
state instanceof DecompressionBomb::UtilZip
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `*InputStream n2 = new *InputStream(n1)` or
|
||||
* `n2 = data.read(n1, 0, BUFFER)` or `n1.read(n2, 0, BUFFER)`,
|
||||
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class InputStreamAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
exists(Call call |
|
||||
// Constructors
|
||||
call.getCallee().getDeclaringType() = any(TypeInputStream t) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
or
|
||||
// Method calls
|
||||
call.(MethodCall).getReceiverType() = any(TypeInputStream t) and
|
||||
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
|
||||
call.getQualifier() = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::UtilZip and
|
||||
stateTo instanceof DecompressionBomb::UtilZip
|
||||
ReadInputStreamSink() {
|
||||
this.asExpr() = any(ReadInputStreamCall r).getQualifier()
|
||||
or
|
||||
exists(ConstructorCall call | call.getConstructedType() instanceof TypeInputStream |
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,100 +327,32 @@ module Zip {
|
||||
TypeInflator() { this.hasQualifiedName("java.util.zip", "Inflater") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `Inflater inflater_As_n2 = new Inflater(); inflater_As_n2 = inflater.setInput(n1)` or `n1.inflate(n2)` or
|
||||
* `n2 = inflater.inflate(n1)`,
|
||||
* third one is added because of sanitizer, we want to compare return value of each `read` or similar method
|
||||
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
|
||||
*/
|
||||
private class InflatorAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
// n1.inflate(n2)
|
||||
(
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeInflator and
|
||||
ma.getArgument(0) = n2.asExpr() and
|
||||
ma.getQualifier() = n1.asExpr() and
|
||||
ma.getCallee().hasName("inflate")
|
||||
)
|
||||
or
|
||||
// n2 = inflater.inflate(n1)
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeInflator and
|
||||
ma = n2.asExpr() and
|
||||
ma.getArgument(0) = n1.asExpr() and
|
||||
ma.getCallee().hasName("inflate")
|
||||
)
|
||||
or
|
||||
// Inflater inflater = new Inflater();
|
||||
// inflater_As_n2 = inflater.setInput(n1)
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeInflator and
|
||||
n1.asExpr() = ma.getArgument(0) and
|
||||
n2.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = ma.getQualifier() and
|
||||
ma.getCallee().hasName("setInput")
|
||||
)
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::Inflator and
|
||||
stateTo instanceof DecompressionBomb::Inflator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `Inflater` Type
|
||||
*/
|
||||
class InflateCall extends MethodCall {
|
||||
InflateCall() {
|
||||
this.getReceiverType() instanceof TypeInflator and
|
||||
this.getCallee().hasName("inflate")
|
||||
}
|
||||
}
|
||||
|
||||
class InflateSink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
sink.asExpr() = any(InflateCall r) and
|
||||
state instanceof DecompressionBomb::Inflator
|
||||
InflateSink() {
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeInflator and
|
||||
ma.getCallee().hasName("inflate") and
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
or
|
||||
ma.getReceiverType() instanceof TypeInflator and
|
||||
ma.getMethod().hasName("setInput") and
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ZipFileSink extends DecompressionBomb::Sink {
|
||||
override predicate sink(DataFlow::Node sink, DecompressionBomb::DecompressionState state) {
|
||||
ZipFileSink() {
|
||||
exists(MethodCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeZipFile and
|
||||
call.getCallee().hasName("getInputStream") and
|
||||
sink.asExpr() = call
|
||||
) and
|
||||
state instanceof DecompressionBomb::ZipFile
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `InputStream n2 = zipFile.getInputStream(n1)`
|
||||
*/
|
||||
private class ZipFileAdditionalTaintStep extends DecompressionBomb::AdditionalStep {
|
||||
override predicate step(
|
||||
DataFlow::Node n1, DecompressionBomb::DecompressionState stateFrom, DataFlow::Node n2,
|
||||
DecompressionBomb::DecompressionState stateTo
|
||||
) {
|
||||
(
|
||||
exists(ConstructorCall call |
|
||||
call.getConstructedType() instanceof TypeZipFile and
|
||||
n1.asExpr() = call.getAnArgument() and
|
||||
n2.asExpr() = call
|
||||
)
|
||||
or
|
||||
exists(MethodCall call |
|
||||
call.getCallee().getDeclaringType().hasQualifiedName("java.util.zip", "ZipFile") and
|
||||
call.getCallee().hasName("getInputStream") and
|
||||
n1.asExpr() = call.getQualifier() and
|
||||
n2.asExpr() = call
|
||||
)
|
||||
) and
|
||||
stateFrom instanceof DecompressionBomb::ZipFile and
|
||||
stateTo instanceof DecompressionBomb::ZipFile
|
||||
call.getQualifier() = this.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall call |
|
||||
call.getConstructedType() instanceof TypeZipFile and
|
||||
call.getAnArgument() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user