mirror of
https://github.com/github/codeql.git
synced 2026-04-19 14:04:09 +02:00
Merge pull request #13555 from am0o0/amammad-java-bombs
Java: Decompression Bombs
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package org.example;
|
||||
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Enumeration;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
|
||||
class BadExample {
|
||||
public static void ZipInputStreamUnSafe(String filename) throws IOException {
|
||||
File f = new File(filename);
|
||||
try (ZipFile zipFile = new ZipFile(f)) {
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry ze = entries.nextElement();
|
||||
File out = new File("./tmp/tmp.txt");
|
||||
Files.copy(zipFile.getInputStream(ze), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Extracting Compressed files with any compression algorithm like gzip can cause a denial of service attack.</p>
|
||||
<p>Attackers can create a huge file by just repeating a single byte and compress it to a small file.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>When decompressing a user-provided compressed file, verify the decompression ratio or decompress the files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following example, the decompressed file size is not checked before decompression, exposing the application to a denial of service.
|
||||
</p>
|
||||
<sample src="BadExample.java" />
|
||||
|
||||
<p>
|
||||
A better approach is shown in the following example, where a ZIP file is read within a loop and a size threshold is checked every cycle.
|
||||
</p>
|
||||
<sample src="GoodExample.java"/>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="https://github.com/advisories/GHSA-47vx-fqr5-j2gw">CVE-2022-4565</a>
|
||||
</li>
|
||||
<li>
|
||||
David Fifield: <a href="https://www.bamsoftware.com/hacks/zipbomb/">A better zip bomb</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Uncontrolled file decompression
|
||||
* @description Decompressing user-controlled files without checking the compression ratio may allow attackers to perform denial-of-service attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id java/uncontrolled-file-decompression
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-409
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.semmle.code.java.security.DecompressionBombQuery
|
||||
import DecompressionBombsFlow::PathGraph
|
||||
|
||||
from DecompressionBombsFlow::PathNode source, DecompressionBombsFlow::PathNode sink
|
||||
where DecompressionBombsFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
|
||||
"potentially untrusted source"
|
||||
@@ -0,0 +1,33 @@
|
||||
import java.util.zip.*;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class GoodExample {
|
||||
public static void ZipInputStreamSafe(String filename) throws IOException {
|
||||
int UncompressedSizeThreshold = 10 * 1024 * 1024; // 10MB
|
||||
int BUFFERSIZE = 256;
|
||||
FileInputStream fis = new FileInputStream(filename);
|
||||
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
int count;
|
||||
byte[] data = new byte[BUFFERSIZE];
|
||||
FileOutputStream fos = new FileOutputStream(entry.getName());
|
||||
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFERSIZE);
|
||||
int totalRead = 0;
|
||||
while ((count = zis.read(data, 0, BUFFERSIZE)) != -1) {
|
||||
totalRead = totalRead + count;
|
||||
if (totalRead > UncompressedSizeThreshold) {
|
||||
System.out.println("This Compressed file can be a bomb!");
|
||||
break;
|
||||
}
|
||||
dest.write(data, 0, count);
|
||||
}
|
||||
dest.flush();
|
||||
dest.close();
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
module DecompressionBomb {
|
||||
/**
|
||||
* The Decompression bomb Sink
|
||||
*
|
||||
* Extend this class for creating new decompression bomb sinks
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* The Additional flow steps that help to create a dataflow or taint tracking query
|
||||
*
|
||||
* Extend this class for creating new additional taint steps
|
||||
*/
|
||||
class AdditionalStep extends Unit {
|
||||
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
|
||||
}
|
||||
|
||||
abstract class BombReadInputStreamCall extends MethodCall { }
|
||||
|
||||
private class ReadInputStreamQualifierSink extends DecompressionBomb::Sink {
|
||||
ReadInputStreamQualifierSink() { this.asExpr() = any(BombReadInputStreamCall r).getQualifier() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `org.xerial.snappy` package
|
||||
*/
|
||||
module XerialSnappy {
|
||||
/**
|
||||
* A type that is responsible for `SnappyInputStream` Class
|
||||
*/
|
||||
class TypeInputStream extends RefType {
|
||||
TypeInputStream() {
|
||||
this.getASupertype*().hasQualifiedName("org.xerial.snappy", "SnappyInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `SnappyInputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
this.getReceiverType() instanceof TypeInputStream and
|
||||
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, DataFlow::Node n2) {
|
||||
exists(ConstructorCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeInputStream and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `org.apache.commons.compress` package
|
||||
*/
|
||||
module ApacheCommons {
|
||||
/**
|
||||
* A type that is responsible for `ArchiveInputStream` Class
|
||||
*/
|
||||
class TypeArchiveInputStream extends RefType {
|
||||
TypeArchiveInputStream() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers", "ArchiveInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is responsible for `CompressorInputStream` Class
|
||||
*/
|
||||
class TypeCompressorInputStream extends RefType {
|
||||
TypeCompressorInputStream() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors", "CompressorInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `org.apache.commons.compress.compressors.*` Types
|
||||
*/
|
||||
module Compressors {
|
||||
/**
|
||||
* The types that are responsible for specific compression format of `CompressorInputStream` Class
|
||||
*/
|
||||
class TypeCompressors extends RefType {
|
||||
TypeCompressors() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.gzip",
|
||||
"GzipCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.brotli",
|
||||
"BrotliCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.bzip2",
|
||||
"BZip2CompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.deflate",
|
||||
"DeflateCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.deflate64",
|
||||
"Deflate64CompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.lz4",
|
||||
"BlockLZ4CompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.lzma",
|
||||
"LZMACompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.pack200",
|
||||
"Pack200CompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.snappy",
|
||||
"SnappyCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.xz",
|
||||
"XZCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.z", "ZCompressorInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors.zstandard",
|
||||
"ZstdCompressorInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `*CompressorInputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
this.getReceiverType() instanceof TypeCompressors and
|
||||
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `GzipCompressorInputStream n2 = new GzipCompressorInputStream(n1)`
|
||||
*/
|
||||
private class CompressorsAndArchiversAdditionalTaintStep extends DecompressionBomb::AdditionalStep
|
||||
{
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(ConstructorCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeCompressors and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for Types from `org.apache.commons.compress.archivers.*` packages
|
||||
*/
|
||||
module Archivers {
|
||||
/**
|
||||
* The types that are responsible for specific compression format of `ArchiveInputStream` Class
|
||||
*/
|
||||
class TypeArchivers extends RefType {
|
||||
TypeArchivers() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.ar", "ArArchiveInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.arj", "ArjArchiveInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.cpio", "CpioArchiveInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.ar", "ArArchiveInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.jar", "JarArchiveInputStream") or
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers.zip", "ZipArchiveInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `*ArchiveInputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
this.getReceiverType() instanceof TypeArchivers and
|
||||
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, DataFlow::Node n2) {
|
||||
exists(ConstructorCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeArchivers and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `CompressorStreamFactory` and `ArchiveStreamFactory` Types
|
||||
*/
|
||||
module Factory {
|
||||
/**
|
||||
* A type that is responsible for `ArchiveInputStream` Class
|
||||
*/
|
||||
class TypeArchivers extends RefType {
|
||||
TypeArchivers() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.archivers", "ArchiveStreamFactory")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is responsible for `CompressorStreamFactory` Class
|
||||
*/
|
||||
class TypeCompressors extends RefType {
|
||||
TypeCompressors() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("org.apache.commons.compress.compressors", "CompressorStreamFactory")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `n1` and `n2` which `ZipInputStream n2 = new ZipInputStream(n1)`
|
||||
*/
|
||||
private class CompressorsAndArchiversAdditionalTaintStep extends DecompressionBomb::AdditionalStep
|
||||
{
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(MethodCall call |
|
||||
(
|
||||
call.getCallee().getDeclaringType() instanceof TypeCompressors
|
||||
or
|
||||
call.getCallee().getDeclaringType() instanceof TypeArchivers
|
||||
) and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `CompressorInputStream` or `ArchiveInputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
(
|
||||
this.getReceiverType() instanceof TypeArchiveInputStream
|
||||
or
|
||||
this.getReceiverType() instanceof TypeCompressorInputStream
|
||||
) and
|
||||
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `net.lingala.zip4j.io` package
|
||||
*/
|
||||
module Zip4j {
|
||||
/**
|
||||
* A type that is responsible for `ZipInputStream` Class
|
||||
*/
|
||||
class TypeZipInputStream extends RefType {
|
||||
TypeZipInputStream() {
|
||||
this.hasQualifiedName("net.lingala.zip4j.io.inputstream", "ZipInputStream")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `CompressorInputStream` or `ArchiveInputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
this.getReceiverType() instanceof TypeZipInputStream and
|
||||
this.getMethod().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, DataFlow::Node n2) {
|
||||
exists(ConstructorCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeZipInputStream and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing Decompression sinks and additional taint steps for `java.util.zip` package
|
||||
*/
|
||||
module Zip {
|
||||
/**
|
||||
* The Types that are responsible for `ZipInputStream`, `GZIPInputStream`, `InflaterInputStream` Classes
|
||||
*/
|
||||
class TypeInputStream extends RefType {
|
||||
TypeInputStream() {
|
||||
this.getASupertype*()
|
||||
.hasQualifiedName("java.util.zip",
|
||||
["ZipInputStream", "GZIPInputStream", "InflaterInputStream"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods that read bytes and belong to `*InputStream` Types
|
||||
*/
|
||||
class ReadInputStreamCall extends DecompressionBomb::BombReadInputStreamCall {
|
||||
ReadInputStreamCall() {
|
||||
this.getReceiverType() instanceof TypeInputStream and
|
||||
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is responsible for `Inflater` Class
|
||||
*/
|
||||
class TypeInflator extends RefType {
|
||||
TypeInflator() { this.hasQualifiedName("java.util.zip", "Inflater") }
|
||||
}
|
||||
|
||||
class InflateSink extends DecompressionBomb::Sink {
|
||||
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 {
|
||||
ZipFileSink() {
|
||||
exists(MethodCall call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeZipFile and
|
||||
call.getCallee().hasName("getInputStream") and
|
||||
call.getQualifier() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is responsible for `ZipFile` Class
|
||||
*/
|
||||
class TypeZipFile extends RefType {
|
||||
TypeZipFile() { this.hasQualifiedName("java.util.zip", "ZipFile") }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import experimental.semmle.code.java.security.FileAndFormRemoteSource
|
||||
import experimental.semmle.code.java.security.DecompressionBomb::DecompressionBomb
|
||||
|
||||
module DecompressionBombsConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
any(AdditionalStep ads).step(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
module DecompressionBombsFlow = TaintTracking::Global<DecompressionBombsConfig>;
|
||||
@@ -0,0 +1,118 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
class CommonsFileUploadAdditionalTaintStep extends Unit {
|
||||
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
|
||||
}
|
||||
|
||||
module ApacheCommonsFileUpload {
|
||||
module RemoteFlowSource {
|
||||
class TypeServletFileUpload extends RefType {
|
||||
TypeServletFileUpload() {
|
||||
this.hasQualifiedName("org.apache.commons.fileupload.servlet", "ServletFileUpload")
|
||||
}
|
||||
}
|
||||
|
||||
class TypeFileUpload extends RefType {
|
||||
TypeFileUpload() {
|
||||
this.getAStrictAncestor*().hasQualifiedName("org.apache.commons.fileupload", "FileItem")
|
||||
}
|
||||
}
|
||||
|
||||
class TypeFileItemStream extends RefType {
|
||||
TypeFileItemStream() {
|
||||
this.getAStrictAncestor*()
|
||||
.hasQualifiedName("org.apache.commons.fileupload", "FileItemStream")
|
||||
}
|
||||
}
|
||||
|
||||
class ServletFileUpload extends RemoteFlowSource {
|
||||
ServletFileUpload() {
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeServletFileUpload and
|
||||
ma.getCallee().hasName("parseRequest") and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Apache Commons Fileupload" }
|
||||
}
|
||||
|
||||
private class FileItemRemoteSource extends RemoteFlowSource {
|
||||
FileItemRemoteSource() {
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeFileUpload and
|
||||
ma.getCallee()
|
||||
.hasName([
|
||||
"getInputStream", "getFieldName", "getContentType", "get", "getName", "getString"
|
||||
]) and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Apache Commons Fileupload" }
|
||||
}
|
||||
|
||||
private class FileItemStreamRemoteSource extends RemoteFlowSource {
|
||||
FileItemStreamRemoteSource() {
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypeFileItemStream and
|
||||
ma.getCallee().hasName(["getContentType", "getFieldName", "getName", "openStream"]) and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Apache Commons Fileupload" }
|
||||
}
|
||||
}
|
||||
|
||||
module Util {
|
||||
class TypeStreams extends RefType {
|
||||
TypeStreams() { this.hasQualifiedName("org.apache.commons.fileupload.util", "Streams") }
|
||||
}
|
||||
|
||||
private class AsStringAdditionalTaintStep extends CommonsFileUploadAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(Call call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeStreams and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call = n2.asExpr() and
|
||||
call.getCallee().hasName("asString")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CopyAdditionalTaintStep extends CommonsFileUploadAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(Call call |
|
||||
call.getCallee().getDeclaringType() instanceof TypeStreams and
|
||||
call.getArgument(0) = n1.asExpr() and
|
||||
call.getArgument(1) = n2.asExpr() and
|
||||
call.getCallee().hasName("copy")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module ServletRemoteMultiPartSources {
|
||||
class TypePart extends RefType {
|
||||
TypePart() { this.hasQualifiedName(["javax.servlet.http", "jakarta.servlet.http"], "Part") }
|
||||
}
|
||||
|
||||
private class ServletPartCalls extends RemoteFlowSource {
|
||||
ServletPartCalls() {
|
||||
exists(MethodCall ma |
|
||||
ma.getReceiverType() instanceof TypePart and
|
||||
ma.getCallee()
|
||||
.hasName([
|
||||
"getInputStream", "getName", "getContentType", "getHeader", "getHeaders",
|
||||
"getHeaderNames", "getSubmittedFileName", "write"
|
||||
]) and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Javax Servlet Http" }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user