From 4a37da35939b3c1bfc5b79b6ed92042af1371ebe Mon Sep 17 00:00:00 2001
From: amammad
Date: Sun, 25 Jun 2023 20:04:50 +1000
Subject: [PATCH 001/404] V1
---
.../CWE/CWE-409-DecompressionBomb/Bombs.ql | 69 +++++++++
.../DecompressionBomb.qhelp | 35 +++++
.../CWE-409-DecompressionBomb/example_bad.cpp | 30 ++++
.../example_good.cpp | 38 +++++
.../DecompressionBomb.ql | 121 +++++++++++++++
.../DecompressionBombs.qhelp | 26 ++++
.../RemoteFlowSource.qll | 62 ++++++++
.../DecompressionBombs.cs | 140 ++++++++++++++++++
.../DecompressionBombs.qlref | 1 +
9 files changed, 522 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
create mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
create mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
create mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
create mode 100644 csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
create mode 100644 csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
new file mode 100644
index 00000000000..2e595bb11b8
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
@@ -0,0 +1,69 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision medium
+ * @id cpp/user-controlled-file-decompression
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * The `gzopen` function, which can perform command substitution.
+ */
+private class GzopenFunction extends Function {
+ GzopenFunction() { hasGlobalName("gzopen") }
+}
+
+/**
+ * The `gzread` function, which can perform command substitution.
+ */
+private class GzreadFunction extends Function {
+ GzreadFunction() { hasGlobalName("gzread") }
+}
+
+module ZlibTaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
+ fc.getArgument(0) = source.asExpr()
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ not sanitizer(fc)
+ )
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
+}
+
+predicate sanitizer(FunctionCall fc) {
+ exists(Expr e | fc.getTarget() instanceof GzreadFunction |
+ // a RelationalOperation which isn't compared with a Literal that using for end of read
+ TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and
+ not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"]
+ )
+}
+
+module ZlibTaint = TaintTracking::Global;
+
+import ZlibTaint::PathGraph
+
+from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
+where ZlibTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
new file mode 100644
index 00000000000..44256d36ea9
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
@@ -0,0 +1,35 @@
+
+
+
+Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.
+Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.
+
+
+
+
+When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.
+
+
+
+
+
+Reading uncompressed Gzip file within a loop and check for a threshold size in each cycle.
+
+
+
+
+An Unsafe Approach can be this example which we don't check for uncompressed size.
+
+
+
+
+
+
+
+A great research to gain more impact by this kind of attacks
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
new file mode 100644
index 00000000000..843b200231a
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
@@ -0,0 +1,30 @@
+#include
+#include
+#include "zlib.h"
+int UnsafeRead() {
+ std::cout << "enter compressed file name!\n" << std::endl;
+ char fileName[100];
+ std::cin >> fileName;
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (inFileZ == nullptr) {
+ printf("Error: Failed to gzopen %s\n", fileName);
+ exit(0);
+ }
+ unsigned char unzipBuffer[8192];
+ unsigned int unzippedBytes;
+ std::vector unzippedData;
+ while (true) {
+ unzippedBytes = gzread(inFileZ, unzipBuffer, 8192);
+ if (unzippedBytes > 0) {
+ unzippedData.insert(unzippedData.end(), unzipBuffer, unzipBuffer + unzippedBytes);
+ } else {
+ break;
+ }
+ }
+
+ for ( auto &&i: unzippedData)
+ std::cout << i;
+ gzclose(inFileZ);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
new file mode 100644
index 00000000000..3d26569bdb4
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
@@ -0,0 +1,38 @@
+#include
+#include
+#include "zlib.h"
+int SafeRead() {
+ std::cout << "enter compressed file name!\n" << std::endl;
+ char fileName[100];
+ std::cin >> fileName;
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (inFileZ == nullptr) {
+ printf("Error: Failed to gzopen %s\n", fileName);
+ exit(0);
+ }
+ unsigned char unzipBuffer[8192];
+ unsigned int unzippedBytes;
+ uint totalRead = 0;
+ std::vector unzippedData;
+ while (true) {
+ unzippedBytes = gzread(inFileZ, unzipBuffer, 8192);
+ totalRead += unzippedBytes;
+ if (unzippedBytes > 0) {
+ unzippedData.insert(unzippedData.end(), unzipBuffer, unzipBuffer + unzippedBytes);
+ if (totalRead > 1024 * 1024 * 4) {
+ std::cout << "Bombs!" << totalRead;
+ exit(1);
+ } else {
+ std::cout << "not Bomb yet!!" << totalRead << std::endl;
+ }
+ } else {
+ break;
+ }
+ }
+
+ for (auto &&i: unzippedData)
+ std::cout << i;
+ gzclose(inFileZ);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
new file mode 100644
index 00000000000..72f81731800
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
@@ -0,0 +1,121 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision medium
+ * @id cs/user-controlled-file-decompression
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import csharp
+import semmle.code.csharp.security.dataflow.flowsources.Remote
+
+/**
+ * A data flow source for unsafe Decompression extraction.
+ */
+abstract class DecompressionSource extends DataFlow::Node { }
+
+class ZipOpenReadSource extends DecompressionSource {
+ ZipOpenReadSource() {
+ exists(MethodCall mc |
+ mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFile", ["OpenRead", "Open"]) and
+ this.asExpr() = mc.getArgument(0) and
+ not mc.getArgument(0).getType().isConst()
+ )
+ }
+}
+
+/** A path argument to a call to the `ZipArchive` constructor call. */
+class ZipArchiveArgSource extends DecompressionSource {
+ ZipArchiveArgSource() {
+ exists(ObjectCreation oc |
+ oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive")
+ |
+ this.asExpr() = oc.getArgument(0)
+ )
+ }
+}
+
+/**
+ * A data flow sink for unsafe zip extraction.
+ */
+abstract class DecompressionSink extends DataFlow::Node { }
+
+/** A Caller of the `ExtractToFile` method. */
+class ExtractToFileCallSink extends DecompressionSink {
+ ExtractToFileCallSink() {
+ exists(MethodCall mc |
+ mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
+ this.asExpr() = mc.getArgumentForName("source")
+ )
+ }
+}
+
+/** A Qualifier of the `Open()` method. */
+class OpenCallSink extends DecompressionSink {
+ OpenCallSink() {
+ exists(MethodCall mc |
+ mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipArchiveEntry", "Open") and
+ this.asExpr() = mc.getQualifier()
+ )
+ }
+}
+
+/** A Call to the `GZipStreamSink` first arugument of Constructor Call . */
+class GZipStreamSink extends DecompressionSink, DecompressionSource {
+ GZipStreamSink() {
+ exists(Constructor mc |
+ mc.getDeclaringType().hasQualifiedName("System.IO.Compression", "GZipStream") and
+ this.asExpr() = mc.getACall().getArgument(0)
+ )
+ }
+}
+
+/**
+ * A taint tracking configuration for Decompression Bomb.
+ */
+private module DecompressionBombConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ source instanceof DecompressionSource
+ or
+ source instanceof RemoteFlowSource
+ }
+
+ predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionSink }
+
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ // var node2 = new ZipArchive(node1, ZipArchiveMode.Read);
+ exists(ObjectCreation oc |
+ oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive") and
+ node2.asExpr() = oc and
+ node1.asExpr() = oc.getArgumentForName("stream")
+ )
+ or
+ // var node2 = node1.ExtractToFile("./output.txt", true)
+ exists(MethodCall mc |
+ mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
+ node2.asExpr() = mc and
+ node1.asExpr() = mc.getArgumentForName("source")
+ )
+ or
+ // var node2 = node1.OpenReadStream()
+ exists(MethodCall mc |
+ mc.getTarget().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormFile", "OpenReadStream") and
+ node2.asExpr() = mc and
+ node1.asExpr() = mc.getQualifier()
+ )
+ }
+}
+
+module DecompressionBomb = TaintTracking::Global;
+
+import DecompressionBomb::PathGraph
+
+from DecompressionBomb::PathNode source, DecompressionBomb::PathNode sink
+where DecompressionBomb::flowPath(source, sink)
+select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
new file mode 100644
index 00000000000..c3c874cddf7
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
@@ -0,0 +1,26 @@
+
+
+
+Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.
+Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.
+
+
+
+
+When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.
+
+
+
+
+A good Blog Post about decompression bombs and recommended method is already written by Gérald Barré in this blog post
+
+
+
+
+A great research to gain more impact by this kind of attack
+
+
+
+
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
new file mode 100644
index 00000000000..f849ab3f4a1
--- /dev/null
+++ b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
@@ -0,0 +1,62 @@
+import csharp
+import semmle.code.csharp.security.dataflow.flowsources.Remote
+
+/** A data flow source of remote user input by Form File (ASP.NET unvalidated request data). */
+class FormFile extends AspNetRemoteFlowSource {
+ FormFile() {
+ exists(MethodCall mc |
+ mc.getTarget()
+ .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
+ "IFormFile", ["OpenReadStream", "ContentType", "ContentDisposition", "Name", "FileName"]) and
+ this.asExpr() = mc
+ )
+ or
+ exists(MethodCall mc |
+ mc.getTarget()
+ .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
+ "IFormFile", "CopyTo") and
+ this.asParameter() = mc.getTarget().getParameter(0)
+ )
+ or
+ exists(Property fa |
+ fa.getDeclaringType()
+ .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
+ "IFormFile") and
+ fa.hasName(["ContentType", "ContentDisposition", "Name", "FileName"]) and
+ this.asExpr() = fa.getAnAccess()
+ )
+ }
+
+ override string getSourceType() {
+ result = "ASP.NET unvalidated request data from multipart request"
+ }
+}
+
+/** A data flow source of remote user input by Form (ASP.NET unvalidated request data). */
+class FormCollection extends AspNetRemoteFlowSource {
+ FormCollection() {
+ exists(Property fa |
+ fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormCollection") and
+ fa.hasName("Keys") and
+ this.asExpr() = fa.getAnAccess()
+ )
+ }
+
+ override string getSourceType() {
+ result = "ASP.NET unvalidated request data from multipart request Form Keys"
+ }
+}
+
+/** A data flow source of remote user input by Headers (ASP.NET unvalidated request data). */
+class HeaderDictionary extends AspNetRemoteFlowSource {
+ HeaderDictionary() {
+ exists(Property fa |
+ fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IHeaderDictionary") and
+ this.asExpr() = fa.getAnAccess()
+ )
+ }
+
+ override string getSourceType() {
+ result = "ASP.NET unvalidated request data from Headers of request"
+ }
+}
diff --git a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
new file mode 100644
index 00000000000..b73eac9bade
--- /dev/null
+++ b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
@@ -0,0 +1,140 @@
+using System.IO.Compression;
+using Microsoft.AspNetCore.Mvc;
+
+// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
+
+namespace MultipartFormWebAPITest.Controllers
+{
+ [Route("api/[controller]")]
+ [ApiController]
+ public class ZipFile1Controller : ControllerBase
+ {
+ // POST api/
+ [HttpPost]
+ public string Post(List files)
+ {
+ if (!Request.ContentType!.StartsWith("multipart/form-data"))
+ return "400";
+ if (files.Count == 0)
+ return "400";
+ foreach (var formFile in files)
+ {
+ using var readStream = formFile.OpenReadStream();
+ if (readStream.Length == 0) return "400";
+ ZipHelpers.Bomb3(readStream);
+ ZipHelpers.Bomb2(formFile.FileName);
+ ZipHelpers.Bomb1(formFile.FileName);
+ }
+ var tmp = Request.Form["aa"];
+ var tmp2 = Request.Form.Keys;
+ // when we don't have only one file as body
+ ZipHelpers.Bomb3(Request.Body);
+ ZipHelpers.Bomb2(Request.Query["param1"].ToString());
+ var headers = Request.Headers;
+ ZipHelpers.Bomb1(headers.ETag);
+ return "200";
+ }
+ }
+}
+
+internal static class ZipHelpers
+{
+ public static void Bomb3(Stream compressedFileStream)
+ {
+ // using FileStream compressedFileStream = File.Open(CompressedFileName, FileMode.Open);
+ // using FileStream outputFileStream = File.Create(DecompressedFileName);
+ using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
+ using var ms = new MemoryStream();
+ decompressor.CopyTo(ms);
+ }
+
+ public static void Bomb2(string filename)
+ {
+ using var zipToOpen = new FileStream(filename, FileMode.Open);
+ using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read);
+ foreach (var entry in archive.Entries) entry.ExtractToFile("./output.txt", true); // Sensitive
+ }
+
+ public static void Bomb1(string filename)
+ {
+ const long maxLength = 10 * 1024 * 1024; // 10MB
+ // var filename = "/home/am/0_WorkDir/Payloads/Bombs/bombs-bones-codes-BH-2016/archives/evil-headers/10GB.zip";
+ using var zipFile = ZipFile.OpenRead(filename);
+ // Quickly check the value from the zip header
+ var declaredSize = zipFile.Entries.Sum(entry => entry.Length);
+ if (declaredSize > maxLength)
+ throw new Exception("Archive is too big");
+ foreach (var entry in zipFile.Entries)
+ {
+ using var entryStream = entry.Open();
+ // Use MaxLengthStream to ensure we don't read more than the declared length
+ using var maxLengthStream = new MaxLengthStream(entryStream, entry.Length);
+ // Be sure to use the maxLengthSteam variable to read the content of the entry, not entryStream
+ using var ms = new MemoryStream();
+ maxLengthStream.CopyTo(ms);
+ }
+ }
+}
+
+internal sealed class MaxLengthStream : Stream
+{
+ private readonly Stream _stream;
+ private long _length;
+
+ public MaxLengthStream(Stream stream, long maxLength)
+ {
+ _stream = stream ?? throw new ArgumentNullException(nameof(stream));
+ MaxLength = maxLength;
+ }
+
+ private long MaxLength { get; }
+
+ public override bool CanRead => _stream.CanRead;
+ public override bool CanSeek => false;
+ public override bool CanWrite => false;
+ public override long Length => _stream.Length;
+
+ public override long Position
+ {
+ get => _stream.Position;
+ set => throw new NotSupportedException();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ var result = _stream.Read(buffer, offset, count);
+ _length += result;
+ if (_length > MaxLength)
+ throw new Exception("Stream is larger than the maximum allowed size");
+
+ return result;
+ }
+
+ // TODO ReadAsync
+
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _stream.Dispose();
+ base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
new file mode 100644
index 00000000000..19b7ebbb843
--- /dev/null
+++ b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
@@ -0,0 +1 @@
+experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
\ No newline at end of file
From 430375e2f01859e481de54860b3eadd11c614e00 Mon Sep 17 00:00:00 2001
From: amammad
Date: Sun, 25 Jun 2023 20:28:45 +1000
Subject: [PATCH 002/404] fix a commit mistake
---
.../DecompressionBomb.ql | 121 ---------------
.../DecompressionBombs.qhelp | 26 ----
.../RemoteFlowSource.qll | 62 --------
.../DecompressionBombs.cs | 140 ------------------
.../DecompressionBombs.qlref | 1 -
5 files changed, 350 deletions(-)
delete mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
delete mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
delete mode 100644 csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
delete mode 100644 csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
delete mode 100644 csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
deleted file mode 100644
index 72f81731800..00000000000
--- a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision medium
- * @id cs/user-controlled-file-decompression
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import csharp
-import semmle.code.csharp.security.dataflow.flowsources.Remote
-
-/**
- * A data flow source for unsafe Decompression extraction.
- */
-abstract class DecompressionSource extends DataFlow::Node { }
-
-class ZipOpenReadSource extends DecompressionSource {
- ZipOpenReadSource() {
- exists(MethodCall mc |
- mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFile", ["OpenRead", "Open"]) and
- this.asExpr() = mc.getArgument(0) and
- not mc.getArgument(0).getType().isConst()
- )
- }
-}
-
-/** A path argument to a call to the `ZipArchive` constructor call. */
-class ZipArchiveArgSource extends DecompressionSource {
- ZipArchiveArgSource() {
- exists(ObjectCreation oc |
- oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive")
- |
- this.asExpr() = oc.getArgument(0)
- )
- }
-}
-
-/**
- * A data flow sink for unsafe zip extraction.
- */
-abstract class DecompressionSink extends DataFlow::Node { }
-
-/** A Caller of the `ExtractToFile` method. */
-class ExtractToFileCallSink extends DecompressionSink {
- ExtractToFileCallSink() {
- exists(MethodCall mc |
- mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
- this.asExpr() = mc.getArgumentForName("source")
- )
- }
-}
-
-/** A Qualifier of the `Open()` method. */
-class OpenCallSink extends DecompressionSink {
- OpenCallSink() {
- exists(MethodCall mc |
- mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipArchiveEntry", "Open") and
- this.asExpr() = mc.getQualifier()
- )
- }
-}
-
-/** A Call to the `GZipStreamSink` first arugument of Constructor Call . */
-class GZipStreamSink extends DecompressionSink, DecompressionSource {
- GZipStreamSink() {
- exists(Constructor mc |
- mc.getDeclaringType().hasQualifiedName("System.IO.Compression", "GZipStream") and
- this.asExpr() = mc.getACall().getArgument(0)
- )
- }
-}
-
-/**
- * A taint tracking configuration for Decompression Bomb.
- */
-private module DecompressionBombConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) {
- source instanceof DecompressionSource
- or
- source instanceof RemoteFlowSource
- }
-
- predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionSink }
-
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- // var node2 = new ZipArchive(node1, ZipArchiveMode.Read);
- exists(ObjectCreation oc |
- oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive") and
- node2.asExpr() = oc and
- node1.asExpr() = oc.getArgumentForName("stream")
- )
- or
- // var node2 = node1.ExtractToFile("./output.txt", true)
- exists(MethodCall mc |
- mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
- node2.asExpr() = mc and
- node1.asExpr() = mc.getArgumentForName("source")
- )
- or
- // var node2 = node1.OpenReadStream()
- exists(MethodCall mc |
- mc.getTarget().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormFile", "OpenReadStream") and
- node2.asExpr() = mc and
- node1.asExpr() = mc.getQualifier()
- )
- }
-}
-
-module DecompressionBomb = TaintTracking::Global;
-
-import DecompressionBomb::PathGraph
-
-from DecompressionBomb::PathNode source, DecompressionBomb::PathNode sink
-where DecompressionBomb::flowPath(source, sink)
-select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
deleted file mode 100644
index c3c874cddf7..00000000000
--- a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qhelp
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.
-Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.
-
-
-
-
-When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.
-
-
-
-
-A good Blog Post about decompression bombs and recommended method is already written by Gérald Barré in this blog post
-
-
-
-
-A great research to gain more impact by this kind of attack
-
-
-
-
diff --git a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll b/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
deleted file mode 100644
index f849ab3f4a1..00000000000
--- a/csharp/ql/src/experimental/CWE-502-DecompressionBombs/RemoteFlowSource.qll
+++ /dev/null
@@ -1,62 +0,0 @@
-import csharp
-import semmle.code.csharp.security.dataflow.flowsources.Remote
-
-/** A data flow source of remote user input by Form File (ASP.NET unvalidated request data). */
-class FormFile extends AspNetRemoteFlowSource {
- FormFile() {
- exists(MethodCall mc |
- mc.getTarget()
- .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
- "IFormFile", ["OpenReadStream", "ContentType", "ContentDisposition", "Name", "FileName"]) and
- this.asExpr() = mc
- )
- or
- exists(MethodCall mc |
- mc.getTarget()
- .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
- "IFormFile", "CopyTo") and
- this.asParameter() = mc.getTarget().getParameter(0)
- )
- or
- exists(Property fa |
- fa.getDeclaringType()
- .hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
- "IFormFile") and
- fa.hasName(["ContentType", "ContentDisposition", "Name", "FileName"]) and
- this.asExpr() = fa.getAnAccess()
- )
- }
-
- override string getSourceType() {
- result = "ASP.NET unvalidated request data from multipart request"
- }
-}
-
-/** A data flow source of remote user input by Form (ASP.NET unvalidated request data). */
-class FormCollection extends AspNetRemoteFlowSource {
- FormCollection() {
- exists(Property fa |
- fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormCollection") and
- fa.hasName("Keys") and
- this.asExpr() = fa.getAnAccess()
- )
- }
-
- override string getSourceType() {
- result = "ASP.NET unvalidated request data from multipart request Form Keys"
- }
-}
-
-/** A data flow source of remote user input by Headers (ASP.NET unvalidated request data). */
-class HeaderDictionary extends AspNetRemoteFlowSource {
- HeaderDictionary() {
- exists(Property fa |
- fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IHeaderDictionary") and
- this.asExpr() = fa.getAnAccess()
- )
- }
-
- override string getSourceType() {
- result = "ASP.NET unvalidated request data from Headers of request"
- }
-}
diff --git a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
deleted file mode 100644
index b73eac9bade..00000000000
--- a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System.IO.Compression;
-using Microsoft.AspNetCore.Mvc;
-
-// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
-
-namespace MultipartFormWebAPITest.Controllers
-{
- [Route("api/[controller]")]
- [ApiController]
- public class ZipFile1Controller : ControllerBase
- {
- // POST api/
- [HttpPost]
- public string Post(List files)
- {
- if (!Request.ContentType!.StartsWith("multipart/form-data"))
- return "400";
- if (files.Count == 0)
- return "400";
- foreach (var formFile in files)
- {
- using var readStream = formFile.OpenReadStream();
- if (readStream.Length == 0) return "400";
- ZipHelpers.Bomb3(readStream);
- ZipHelpers.Bomb2(formFile.FileName);
- ZipHelpers.Bomb1(formFile.FileName);
- }
- var tmp = Request.Form["aa"];
- var tmp2 = Request.Form.Keys;
- // when we don't have only one file as body
- ZipHelpers.Bomb3(Request.Body);
- ZipHelpers.Bomb2(Request.Query["param1"].ToString());
- var headers = Request.Headers;
- ZipHelpers.Bomb1(headers.ETag);
- return "200";
- }
- }
-}
-
-internal static class ZipHelpers
-{
- public static void Bomb3(Stream compressedFileStream)
- {
- // using FileStream compressedFileStream = File.Open(CompressedFileName, FileMode.Open);
- // using FileStream outputFileStream = File.Create(DecompressedFileName);
- using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
- using var ms = new MemoryStream();
- decompressor.CopyTo(ms);
- }
-
- public static void Bomb2(string filename)
- {
- using var zipToOpen = new FileStream(filename, FileMode.Open);
- using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read);
- foreach (var entry in archive.Entries) entry.ExtractToFile("./output.txt", true); // Sensitive
- }
-
- public static void Bomb1(string filename)
- {
- const long maxLength = 10 * 1024 * 1024; // 10MB
- // var filename = "/home/am/0_WorkDir/Payloads/Bombs/bombs-bones-codes-BH-2016/archives/evil-headers/10GB.zip";
- using var zipFile = ZipFile.OpenRead(filename);
- // Quickly check the value from the zip header
- var declaredSize = zipFile.Entries.Sum(entry => entry.Length);
- if (declaredSize > maxLength)
- throw new Exception("Archive is too big");
- foreach (var entry in zipFile.Entries)
- {
- using var entryStream = entry.Open();
- // Use MaxLengthStream to ensure we don't read more than the declared length
- using var maxLengthStream = new MaxLengthStream(entryStream, entry.Length);
- // Be sure to use the maxLengthSteam variable to read the content of the entry, not entryStream
- using var ms = new MemoryStream();
- maxLengthStream.CopyTo(ms);
- }
- }
-}
-
-internal sealed class MaxLengthStream : Stream
-{
- private readonly Stream _stream;
- private long _length;
-
- public MaxLengthStream(Stream stream, long maxLength)
- {
- _stream = stream ?? throw new ArgumentNullException(nameof(stream));
- MaxLength = maxLength;
- }
-
- private long MaxLength { get; }
-
- public override bool CanRead => _stream.CanRead;
- public override bool CanSeek => false;
- public override bool CanWrite => false;
- public override long Length => _stream.Length;
-
- public override long Position
- {
- get => _stream.Position;
- set => throw new NotSupportedException();
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- var result = _stream.Read(buffer, offset, count);
- _length += result;
- if (_length > MaxLength)
- throw new Exception("Stream is larger than the maximum allowed size");
-
- return result;
- }
-
- // TODO ReadAsync
-
- public override void Flush()
- {
- throw new NotSupportedException();
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
-
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
-
- protected override void Dispose(bool disposing)
- {
- _stream.Dispose();
- base.Dispose(disposing);
- }
-}
\ No newline at end of file
diff --git a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref b/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
deleted file mode 100644
index 19b7ebbb843..00000000000
--- a/csharp/ql/test/experimental/CWE-502-DecompressionBombs/DecompressionBombs.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql
\ No newline at end of file
From ae98510f77671bb55e1a56f3a28b7acba4fb8205 Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 00:21:55 +1000
Subject: [PATCH 003/404] add more source and sinks and sanitizers
---
.../CWE/CWE-409-DecompressionBomb/Bombs.ql | 69 ---------
.../DecompressionBombsGzopen.ql | 139 ++++++++++++++++++
.../DecompressionBombsInflator.ql | 56 +++++++
.../DecompressionBombsUncompress.ql | 56 +++++++
4 files changed, 251 insertions(+), 69 deletions(-)
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
deleted file mode 100644
index 2e595bb11b8..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision medium
- * @id cpp/user-controlled-file-decompression
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * The `gzopen` function, which can perform command substitution.
- */
-private class GzopenFunction extends Function {
- GzopenFunction() { hasGlobalName("gzopen") }
-}
-
-/**
- * The `gzread` function, which can perform command substitution.
- */
-private class GzreadFunction extends Function {
- GzreadFunction() { hasGlobalName("gzread") }
-}
-
-module ZlibTaintConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
- fc.getArgument(0) = source.asExpr()
- )
- }
-
- predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
- fc.getArgument(0) = sink.asExpr() and
- not sanitizer(fc)
- )
- }
-
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- }
-}
-
-predicate sanitizer(FunctionCall fc) {
- exists(Expr e | fc.getTarget() instanceof GzreadFunction |
- // a RelationalOperation which isn't compared with a Literal that using for end of read
- TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and
- not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"]
- )
-}
-
-module ZlibTaint = TaintTracking::Global;
-
-import ZlibTaint::PathGraph
-
-from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
-where ZlibTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
new file mode 100644
index 00000000000..644841af23e
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
@@ -0,0 +1,139 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression1
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A gzFile Variable as a Flow source
+ */
+private class GzFileVar extends VariableAccess {
+ GzFileVar() { this.getType().hasName("gzFile") }
+}
+
+/**
+ * The `gzopen` function as a Flow source
+ */
+private class GzopenFunction extends Function {
+ GzopenFunction() { hasGlobalName("gzopen") }
+}
+
+/**
+ * The `gzdopen` function as a Flow source
+ */
+private class GzdopenFunction extends Function {
+ GzdopenFunction() { hasGlobalName("gzdopen") }
+}
+
+/**
+ * The `gzfread` function is used in Flow sink
+ */
+private class GzfreadFunction extends Function {
+ GzfreadFunction() { hasGlobalName("gzfread") }
+}
+
+/**
+ * The `gzread` function is used in Flow sink
+ */
+private class GzreadFunction extends Function {
+ GzreadFunction() { hasGlobalName("gzread") }
+}
+
+module ZlibTaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ // gzopen(const char *path, const char *mode);
+ exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
+ fc.getArgument(0) = source.asExpr() and
+ // arg 0 can be a path string whichwe must do following check
+ not fc.getArgument(0).isConstant()
+ )
+ or
+ //gzdopen(int fd, const char *mode);
+ // IDK whether it is good to use all file decriptors function returns as source or not
+ // because we can do more sanitization from fd function sources
+ exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
+ fc.getArgument(0) = source.asExpr()
+ )
+ or
+ source.asExpr() instanceof GzFileVar
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ // gzread(gzFile file, voidp buf, unsigned len));
+ exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ not sanitizer(fc)
+ // TODO: and not sanitizer2(fc.getArgument(2))
+ )
+ or
+ // gzfread((voidp buf, z_size_t size, z_size_t nitems, gzFile file));
+ exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
+ sink.asExpr() = fc.getArgument(3)
+ )
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ // gzopen(const char *path, const char *mode);
+ // gzdopen(int fd, const char *mode);
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
+ |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ or
+ // gzread(gzFile file, voidp buf, unsigned len);
+ exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1)
+ )
+ or
+ // gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file);
+ exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
+ node1.asExpr() = fc.getArgument(3) and
+ node2.asExpr() = fc.getArgument(0)
+ )
+ }
+}
+
+predicate sanitizer(FunctionCall fc) {
+ exists(Expr e |
+ // a RelationalOperation which isn't compared with a Literal that using for end of read
+ TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and
+ not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"]
+ )
+}
+
+// TODO:
+// predicate sanitizer2(Expr arg) {
+// exists(Expr e |
+// // a RelationalOperation which isn't compared with a Literal that using for end of read
+// TaintTracking::localExprTaint(arg, e.(RelationalOperation).getAChild*())
+// )
+// }
+// predicate test(FunctionCall fc, Expr sink) {
+// exists( | fc.getTarget() instanceof GzreadFunction |
+// // a RelationalOperation which isn't compared with a Literal that using for end of read
+// TaintTracking::localExprTaint(fc.getArgument(2).(VariableAccess), sink)
+// ) and
+// sink.getFile().getLocation().toString().matches("%main.cpp%")
+// }
+module ZlibTaint = TaintTracking::Global;
+
+import ZlibTaint::PathGraph
+
+from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
+where ZlibTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
new file mode 100644
index 00000000000..541c23c58ec
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
@@ -0,0 +1,56 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression2
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A z_stream Variable as a Flow source
+ */
+private class ZStreamVar extends VariableAccess {
+ ZStreamVar() { this.getType().hasName("z_stream") }
+}
+
+/**
+ * The `inflate`/`inflateSync` function is used in Flow sink
+ */
+private class DeflateFunction extends Function {
+ DeflateFunction() { hasGlobalName(["inflate", "inflateSync"]) }
+}
+
+module ZlibTaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ZStreamVar }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
+}
+
+module ZlibTaint = TaintTracking::Global;
+
+import ZlibTaint::PathGraph
+
+from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
+where ZlibTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql
new file mode 100644
index 00000000000..09327616fd9
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql
@@ -0,0 +1,56 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression3
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A Bytef Variable as a Flow source
+ */
+private class BytefVar extends VariableAccess {
+ BytefVar() { this.getType().hasName("Bytef") }
+}
+
+/**
+ * The `uncompress`/`uncompress2` function is used in Flow sink
+ */
+private class UncompressFunction extends Function {
+ UncompressFunction() { hasGlobalName(["uncompress", "uncompress2"]) }
+}
+
+module ZlibTaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) { source.asExpr() instanceof BytefVar }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
+}
+
+module ZlibTaint = TaintTracking::Global;
+
+import ZlibTaint::PathGraph
+
+from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
+where ZlibTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
+ "potentially untrusted source"
From 3ddc9a8b31612ad0fb264928ab20f73411cc81a7 Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 05:26:30 +1000
Subject: [PATCH 004/404] fix warnings, more sinks,sources,comments
---
.../DecompressionBombsGzopen.ql | 55 +++++++++++--------
.../DecompressionBombsInflator.ql | 16 ++++--
2 files changed, 43 insertions(+), 28 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
index 644841af23e..6c0804404cd 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
@@ -16,7 +16,7 @@ import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
/**
- * A gzFile Variable as a Flow source
+ * A `gzFile` Variable as a Flow source
*/
private class GzFileVar extends VariableAccess {
GzFileVar() { this.getType().hasName("gzFile") }
@@ -24,42 +24,57 @@ private class GzFileVar extends VariableAccess {
/**
* The `gzopen` function as a Flow source
+ *
+ * `gzopen(const char *path, const char *mode)`
*/
private class GzopenFunction extends Function {
- GzopenFunction() { hasGlobalName("gzopen") }
+ GzopenFunction() { this.hasGlobalName("gzopen") }
}
/**
* The `gzdopen` function as a Flow source
+ *
+ * `gzdopen(int fd, const char *mode)`
*/
private class GzdopenFunction extends Function {
- GzdopenFunction() { hasGlobalName("gzdopen") }
+ GzdopenFunction() { this.hasGlobalName("gzdopen") }
}
/**
* The `gzfread` function is used in Flow sink
+ *
+ * `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)`
*/
private class GzfreadFunction extends Function {
- GzfreadFunction() { hasGlobalName("gzfread") }
+ GzfreadFunction() { this.hasGlobalName("gzfread") }
+}
+
+/**
+ * The `gzgets` function is used in Flow sink.
+ *
+ * `gzgets(gzFile file, char *buf, int len)`
+ */
+private class GzgetsFunction extends Function {
+ GzgetsFunction() { this.hasGlobalName("gzgets") }
}
/**
* The `gzread` function is used in Flow sink
+ *
+ * `gzread(gzFile file, voidp buf, unsigned len)`
*/
private class GzreadFunction extends Function {
- GzreadFunction() { hasGlobalName("gzread") }
+ GzreadFunction() { this.hasGlobalName("gzread") }
}
module ZlibTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
- // gzopen(const char *path, const char *mode);
exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
fc.getArgument(0) = source.asExpr() and
// arg 0 can be a path string whichwe must do following check
not fc.getArgument(0).isConstant()
)
or
- //gzdopen(int fd, const char *mode);
// IDK whether it is good to use all file decriptors function returns as source or not
// because we can do more sanitization from fd function sources
exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
@@ -70,22 +85,22 @@ module ZlibTaintConfig implements DataFlow::ConfigSig {
}
predicate isSink(DataFlow::Node sink) {
- // gzread(gzFile file, voidp buf, unsigned len));
exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
fc.getArgument(0) = sink.asExpr() and
not sanitizer(fc)
// TODO: and not sanitizer2(fc.getArgument(2))
)
or
- // gzfread((voidp buf, z_size_t size, z_size_t nitems, gzFile file));
exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
sink.asExpr() = fc.getArgument(3)
)
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzgetsFunction |
+ sink.asExpr() = fc.getArgument(0)
+ )
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- // gzopen(const char *path, const char *mode);
- // gzdopen(int fd, const char *mode);
exists(FunctionCall fc |
fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
|
@@ -93,17 +108,20 @@ module ZlibTaintConfig implements DataFlow::ConfigSig {
node2.asExpr() = fc
)
or
- // gzread(gzFile file, voidp buf, unsigned len);
exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
node1.asExpr() = fc.getArgument(0) and
node2.asExpr() = fc.getArgument(1)
)
or
- // gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file);
exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
node1.asExpr() = fc.getArgument(3) and
node2.asExpr() = fc.getArgument(0)
)
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzgetsFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node1.asExpr() = fc.getArgument(1)
+ )
}
}
@@ -116,19 +134,12 @@ predicate sanitizer(FunctionCall fc) {
}
// TODO:
-// predicate sanitizer2(Expr arg) {
+// predicate sanitizer2(FunctionCall fc) {
// exists(Expr e |
// // a RelationalOperation which isn't compared with a Literal that using for end of read
-// TaintTracking::localExprTaint(arg, e.(RelationalOperation).getAChild*())
+// TaintTracking::localExprTaint(fc.getArgument(2), e)
// )
// }
-// predicate test(FunctionCall fc, Expr sink) {
-// exists( | fc.getTarget() instanceof GzreadFunction |
-// // a RelationalOperation which isn't compared with a Literal that using for end of read
-// TaintTracking::localExprTaint(fc.getArgument(2).(VariableAccess), sink)
-// ) and
-// sink.getFile().getLocation().toString().matches("%main.cpp%")
-// }
module ZlibTaint = TaintTracking::Global;
import ZlibTaint::PathGraph
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
index 541c23c58ec..fdd11e1fc16 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
@@ -16,30 +16,34 @@ import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
/**
- * A z_stream Variable as a Flow source
+ * A `z_stream` Variable as a Flow source
*/
private class ZStreamVar extends VariableAccess {
ZStreamVar() { this.getType().hasName("z_stream") }
}
/**
- * The `inflate`/`inflateSync` function is used in Flow sink
+ * The `inflate`/`inflateSync` functions are used in Flow sink
+ *
+ * `inflate(z_streamp strm, int flush)`
+ *
+ * `inflateSync(z_streamp strm)`
*/
-private class DeflateFunction extends Function {
- DeflateFunction() { hasGlobalName(["inflate", "inflateSync"]) }
+private class InflateFunction extends Function {
+ InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
}
module ZlibTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ZStreamVar }
predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction |
+ exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
fc.getArgument(0) = sink.asExpr()
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction |
+ exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
node1.asExpr() = fc.getArgument(0) and
node2.asExpr() = fc
)
From f715a3437b1793dfca1941111aae1af5b420544b Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 05:29:16 +1000
Subject: [PATCH 005/404] better examples
---
.../CWE-409-DecompressionBomb/example_bad.cpp | 112 ++++++++++++++++--
.../example_good.cpp | 45 ++++++-
2 files changed, 143 insertions(+), 14 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
index 843b200231a..94e3e969a51 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
@@ -1,7 +1,64 @@
-#include
-#include
-#include "zlib.h"
-int UnsafeRead() {
+
+int UnsafeInflate(int argc, char *argv[]) {
+ // original string len = 36
+ char a[50] = "Hello Hello Hello Hello Hello Hello!";
+ // placeholder for the compressed (deflated) version of "a"
+ char b[50];
+ // placeholder for the Uncompressed (inflated) version of "b"
+ char c[50];
+ printf("Uncompressed size is: %lu\n", strlen(a));
+ printf("Uncompressed string is: %s\n", a);
+ printf("\n----------\n\n");
+
+ // STEP 1.
+ // zlib struct
+ z_stream defstream;
+ defstream.zalloc = Z_NULL;
+ defstream.zfree = Z_NULL;
+ defstream.opaque = Z_NULL;
+ // setup "a" as the input and "b" as the compressed output
+ defstream.avail_in = (uInt) strlen(a) + 1; // size of input, string + terminator
+ defstream.next_in = (Bytef *) a; // input char array
+ defstream.avail_out = (uInt) sizeof(b); // size of output
+ defstream.next_out = (Bytef *) b; // output char array
+
+ // the actual compression work.
+ deflateInit(&defstream, Z_BEST_COMPRESSION);
+ deflate(&defstream, Z_FINISH);
+ deflateEnd(&defstream);
+
+ // This is one way of getting the size of the output
+ printf("Compressed size is: %lu\n", strlen(b));
+ printf("Compressed string is: %s\n", b);
+ printf("\n----------\n\n");
+ // STEP 2.
+ // inflate b into c
+ // zlib struct
+ z_stream infstream;
+ infstream.zalloc = Z_NULL;
+ infstream.zfree = Z_NULL;
+ infstream.opaque = Z_NULL;
+ // setup "b" as the input and "c" as the compressed output
+ // TOTHINK: Here we can add additional step from Right operand to z_stream variable access
+ infstream.avail_in = (uInt) ((char *) defstream.next_out - b); // size of input
+ infstream.next_in = (Bytef *) b; // input char array
+ infstream.avail_out = (uInt) sizeof(c); // size of output
+ infstream.next_out = (Bytef *) c; // output char array
+
+ // uLong total_out; /* total number of bytes output so far */
+ // the actual DE-compression work.
+ inflateInit(&infstream);
+ std::cout << infstream.total_out << std::endl;
+ inflate(&infstream, Z_NO_FLUSH);
+ std::cout << infstream.total_out << std::endl;
+ inflateEnd(&infstream);
+
+ printf("Uncompressed size is: %lu\n", strlen(c));
+ printf("Uncompressed string is: %s\n", c);
+ return 0;
+}
+
+int UnsafeGzread() {
std::cout << "enter compressed file name!\n" << std::endl;
char fileName[100];
std::cin >> fileName;
@@ -21,10 +78,47 @@ int UnsafeRead() {
break;
}
}
-
- for ( auto &&i: unzippedData)
- std::cout << i;
+ for (auto &&i: unzippedData)
+ std::cout << i;
gzclose(inFileZ);
-
return 0;
-}
\ No newline at end of file
+}
+
+int UnsafeGzfread() {
+ std::cout << "enter compressed file name!\n" << std::endl;
+ char fileName[100];
+ std::cin >> fileName;
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (inFileZ == nullptr) {
+ printf("Error: Failed to gzopen %s\n", fileName);
+ exit(0);
+ }
+ while (true) {
+ char buffer[1000];
+ if (!gzfread(buffer, 999, 1, inFileZ)) {
+ break;
+ }
+ }
+ gzclose(inFileZ);
+ return 0;
+}
+
+int UnsafeGzgets() {
+ std::cout << "enter compressed file name!\n" << std::endl;
+ char fileName[100];
+ std::cin >> fileName;
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (inFileZ == nullptr) {
+ printf("Error: Failed to gzopen %s\n", fileName);
+ exit(0);
+ }
+ char *buffer = new char[4000000000];
+ char *result = gzgets(inFileZ, buffer, 1000000000);
+ while (true) {
+ result = gzgets(inFileZ, buffer, 1000000000);
+ if (result == nullptr) {
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
index 3d26569bdb4..e3a2d4b2e50 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
@@ -1,7 +1,5 @@
-#include
-#include
-#include "zlib.h"
-int SafeRead() {
+
+int SafeGzread() {
std::cout << "enter compressed file name!\n" << std::endl;
char fileName[100];
std::cin >> fileName;
@@ -35,4 +33,41 @@ int SafeRead() {
gzclose(inFileZ);
return 0;
-}
\ No newline at end of file
+}
+
+int SafeGzread2() {
+ std::cout << "enter compressed file name!\n" << std::endl;
+ char fileName[100];
+ std::cin >> fileName;
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (inFileZ == nullptr) {
+ printf("Error: Failed to gzopen %s\n", fileName);
+ exit(0);
+ }
+ const int BUFFER_SIZE = 8192;
+ unsigned char unzipBuffer[BUFFER_SIZE];
+ unsigned int unzippedBytes;
+ uint totalRead = 0;
+ std::vector unzippedData;
+ while (true) {
+ unzippedBytes = gzread(inFileZ, unzipBuffer, BUFFER_SIZE);
+ totalRead += BUFFER_SIZE;
+ if (unzippedBytes > 0) {
+ unzippedData.insert(unzippedData.end(), unzipBuffer, unzipBuffer + unzippedBytes);
+ if (totalRead > 1024 * 1024 * 4) {
+ std::cout << "Bombs!" << totalRead;
+ exit(1);
+ } else {
+ std::cout << "not Bomb yet!!" << totalRead << std::endl;
+ }
+ } else {
+ break;
+ }
+ }
+
+ for (auto &&i: unzippedData)
+ std::cout << i;
+ gzclose(inFileZ);
+
+ return 0;
+}
From 042133a9912072040f8a67ad870d69d881a8a2e2 Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 3 Jul 2023 09:12:37 +1000
Subject: [PATCH 006/404] add queries for more popular libs
---
.../DecompressionBomb.qhelp | 6 +-
.../DecompressionBombsBrotli.ql | 107 +++++++++++++
.../DecompressionBombsBzip2.ql | 126 ++++++++++++++++
.../DecompressionBombsMiniZip.ql | 102 +++++++++++++
.../DecompressionBombsXZ.ql | 100 +++++++++++++
.../DecompressionBombsZSTD.ql | 140 ++++++++++++++++++
...pen.ql => DecompressionBombsZlibGzopen.ql} | 2 +-
...r.ql => DecompressionBombsZlibInflator.ql} | 9 +-
...ql => DecompressionBombsZlibUncompress.ql} | 9 +-
.../CWE-409-DecompressionBomb/example_bad.cpp | 5 +
.../example_good.cpp | 5 +
11 files changed, 593 insertions(+), 18 deletions(-)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
rename cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/{DecompressionBombsGzopen.ql => DecompressionBombsZlibGzopen.ql} (98%)
rename cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/{DecompressionBombsInflator.ql => DecompressionBombsZlibInflator.ql} (84%)
rename cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/{DecompressionBombsUncompress.ql => DecompressionBombsZlibUncompress.ql} (83%)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
index 44256d36ea9..cdadabbf207 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
@@ -5,7 +5,6 @@
Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.
Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.
-
@@ -25,8 +24,13 @@ An Unsafe Approach can be this example which we don't check for uncompressed siz
+
+
+Zlib Documentation
+
+
A great research to gain more impact by this kind of attacks
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
new file mode 100644
index 00000000000..b09858d6471
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
@@ -0,0 +1,107 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-brotli
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+// https://github.com/google/brotli
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+/**
+ * A Pointer Variable is used in Flow source
+ */
+private class PointerVar extends VariableAccess {
+ PointerVar() { this.getType() instanceof PointerType }
+}
+
+/**
+ * A Pointer Variable is used in Flow source
+ */
+private class Uint8Var extends VariableAccess {
+ Uint8Var() { this.getType() instanceof UInt8_t }
+}
+
+/**
+ * A ZSTD_inBuffer Variable is used in Flow source
+ */
+private class ZSTDinBufferVar extends VariableAccess {
+ ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
+}
+
+/**
+ * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ * Ref: https://www.brotli.org/decode.html#af68
+ */
+private class BrotliDecoderDecompressFunction extends Function {
+ BrotliDecoderDecompressFunction() { this.hasGlobalName(["BrotliDecoderDecompress"]) }
+}
+
+/**
+ * The `BrotliDecoderDecompressStream` function is used in Flow sink
+ * Ref: https://www.brotli.org/decode.html#a234
+ */
+private class BrotliDecoderDecompressStreamFunction extends Function {
+ BrotliDecoderDecompressStreamFunction() { this.hasGlobalName(["BrotliDecoderDecompressStream"]) }
+}
+
+module BrotliTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction |
+ fc = source.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fopenCall(fc) |
+ fc = source.asExpr() and
+ state = ""
+ )
+ or
+ source.asExpr() instanceof PointerVar and
+ state = ""
+ or
+ source.asExpr() instanceof Uint8Var and
+ state = ""
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
+ fc.getArgument(1) = sink.asExpr() and
+ state = ""
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ none()
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module BrotliTaint = TaintTracking::GlobalWithState;
+
+import BrotliTaint::PathGraph
+
+from BrotliTaint::PathNode source, BrotliTaint::PathNode sink
+where BrotliTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
new file mode 100644
index 00000000000..e9b7d60715d
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
@@ -0,0 +1,126 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-bzip2
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+/**
+ * A `bz_stream` Variable as a Flow source
+ */
+private class BzStreamVar extends VariableAccess {
+ BzStreamVar() { this.getType().hasName("bz_stream") }
+}
+
+/**
+ * A `BZFILE` Variable as a Flow source
+ */
+private class BZFILEVar extends VariableAccess {
+ BZFILEVar() { this.getType().hasName("BZFILE") }
+}
+
+/**
+ * The `BZ2_bzopen`,`BZ2_bzdopen` functions as a Flow source
+ */
+private class BZ2BzopenFunction extends Function {
+ BZ2BzopenFunction() { this.hasGlobalName(["BZ2_bzopen", "BZ2_bzdopen"]) }
+}
+
+/**
+ * The `BZ2_bzDecompress` function as a Flow source
+ */
+private class BZ2BzDecompressFunction extends Function {
+ BZ2BzDecompressFunction() { this.hasGlobalName(["BZ2_bzDecompress"]) }
+}
+
+/**
+ * The `BZ2_bzReadOpen` function
+ */
+private class BZ2BzReadOpenFunction extends Function {
+ BZ2BzReadOpenFunction() { this.hasGlobalName(["BZ2_bzReadOpen"]) }
+}
+
+/**
+ * The `BZ2_bzRead` function is used in Flow sink
+ */
+private class BZ2BzReadFunction extends Function {
+ BZ2BzReadFunction() { this.hasGlobalName("BZ2_bzRead") }
+}
+
+/**
+ * The `BZ2_bzRead` function is used in Flow sink
+ */
+private class BZ2BzBuffToBuffDecompressFunction extends Function {
+ BZ2BzBuffToBuffDecompressFunction() { this.hasGlobalName("BZ2_bzBuffToBuffDecompress") }
+}
+
+/**
+ * https://www.sourceware.org/bzip2/manual/manual.html
+ */
+module Bzip2TaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ source.asExpr() instanceof BzStreamVar and
+ state = ""
+ or
+ source.asExpr() instanceof BZFILEVar and
+ state = ""
+ or
+ // will flow into BZ2BzReadOpenFunction
+ exists(FunctionCall fc | fopenCall(fc) |
+ fc = source.asExpr() and
+ state = ""
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
+ fc.getArgument(1) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
+ fc.getArgument(2) = sink.asExpr() and
+ state = ""
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadOpenFunction |
+ node1.asExpr() = fc.getArgument(1) and
+ node2.asExpr() = fc and
+ state1 = "" and
+ state2 = ""
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module Bzip2Taint = TaintTracking::GlobalWithState;
+
+import Bzip2Taint::PathGraph
+
+from Bzip2Taint::PathNode source, Bzip2Taint::PathNode sink
+where Bzip2Taint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
new file mode 100644
index 00000000000..b8cd89fc542
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
@@ -0,0 +1,102 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-minizip
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A `unzFile` Variable as a Flow source
+ */
+private class UnzFileVar extends VariableAccess {
+ UnzFileVar() { this.getType().hasName("unzFile") }
+}
+
+/**
+ * The `UnzOpen` function as a Flow source
+ */
+private class UnzOpenFunction extends Function {
+ UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
+}
+
+/**
+ * The `mz_stream_open` function is used in Flow source
+ */
+private class MzStreamOpenFunction extends Function {
+ MzStreamOpenFunction() { this.hasGlobalName("mz_stream_open") }
+}
+
+/**
+ * The `unzReadCurrentFile` function is used in Flow sink
+ */
+private class UnzReadCurrentFileFunction extends Function {
+ UnzReadCurrentFileFunction() { this.hasGlobalName(["unzReadCurrentFile"]) }
+}
+
+module MiniZipTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
+ fc.getArgument(0) = source.asExpr() and
+ state = "unzFile"
+ )
+ or
+ source.asExpr() instanceof UnzFileVar and
+ state = "unzFile"
+ or
+ // TO Check
+ exists(FunctionCall fc | fc.getTarget() instanceof MzStreamOpenFunction |
+ fc.getArgument(0).getEnclosingVariable() = source.asVariable() and
+ state = "MzStream"
+ )
+ or
+ // TO Check
+ exists(FunctionCall fc | fc.getTarget() instanceof MzStreamOpenFunction |
+ fc.getArgument(0).getEnclosingVariable() = source.asVariable() and
+ state = "MzStream"
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ state = "unzFile"
+ // and not sanitizer(fc)
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc and
+ state1 = "" and
+ state2 = "unzFile"
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+
+module MiniZipTaint = TaintTracking::GlobalWithState;
+
+import MiniZipTaint::PathGraph
+
+from MiniZipTaint::PathNode source, MiniZipTaint::PathNode sink
+where MiniZipTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
new file mode 100644
index 00000000000..dadbf7a5a8a
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
@@ -0,0 +1,100 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-xz
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A Pointer Variable as a Flow source
+ */
+private class Uint8Var extends VariableAccess {
+ Uint8Var() { this.getType() instanceof UInt8_t }
+}
+
+/**
+ * A `lzma_stream` Variable as a Flow source
+ */
+private class LzmaStreamVar extends VariableAccess {
+ LzmaStreamVar() { this.getType().hasName("lzma_stream") }
+}
+
+/**
+ * The `lzma_*_decoder` function is used as a required condition for decompression
+ */
+private class LzmaDecoderFunction extends Function {
+ LzmaDecoderFunction() {
+ this.hasGlobalName(["lzma_stream_decoder", "lzma_auto_decoder", "lzma_alone_decoder"])
+ }
+}
+
+/**
+ * The `lzma_code` function is used in Flow sink
+ */
+private class LzmaCodeFunction extends Function {
+ LzmaCodeFunction() { this.hasGlobalName(["lzma_code"]) }
+}
+
+/**
+ * The `lzma_stream_buffer_decode` function is used in Flow sink
+ */
+private class LzmaStreamBufferDecodeFunction extends Function {
+ LzmaStreamBufferDecodeFunction() { this.hasGlobalName(["lzma_stream_buffer_decode"]) }
+}
+
+/**
+ * https://github.com/tukaani-project/xz
+ */
+module XzTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ source.asExpr() instanceof LzmaStreamVar and
+ state = ""
+ or
+ source.asExpr() instanceof Uint8Var and
+ state = ""
+ // and not exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamDecoderFunction)
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
+ fc.getArgument(1) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ state = ""
+ ) and
+ exists(FunctionCall fc2 | fc2.getTarget() instanceof LzmaDecoderFunction)
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ none()
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module XzTaint = TaintTracking::GlobalWithState;
+
+import XzTaint::PathGraph
+
+from XzTaint::PathNode source, XzTaint::PathNode sink
+where XzTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
new file mode 100644
index 00000000000..9e984bb89e2
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
@@ -0,0 +1,140 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-zstd
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+// https://github.com/facebook/zstd/blob/dev/examples/streaming_decompression.c
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+// /**
+// * A Pointer Variable as a Flow source
+// */
+// private class PointerVar extends VariableAccess {
+// PointerVar() { this.getType() instanceof PointerType }
+// }
+/**
+ * A ZSTD_inBuffer Variable as a Flow source
+ */
+private class ZSTDinBufferVar extends VariableAccess {
+ ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
+}
+
+/**
+ * A ZSTD_inBuffer_s Variable as a Flow source
+ */
+private class ZSTDinBufferSVar extends VariableAccess {
+ ZSTDinBufferSVar() { this.getType().hasName("ZSTD_inBuffer_s") }
+}
+
+/**
+ * The `ZSTD_decompress` function is used in Flow sink
+ */
+private class ZSTDDecompressFunction extends Function {
+ ZSTDDecompressFunction() { this.hasGlobalName(["ZSTD_decompress"]) }
+}
+
+/**
+ * The `ZSTD_decompressDCtx` function is used in Flow sink
+ */
+private class ZSTDDecompressDCtxFunction extends Function {
+ ZSTDDecompressDCtxFunction() { this.hasGlobalName(["ZSTD_decompressDCtx"]) }
+}
+
+/**
+ * The `ZSTD_decompressStream` function is used in Flow sink
+ */
+private class ZSTDDecompressStreamFunction extends Function {
+ ZSTDDecompressStreamFunction() { this.hasGlobalName(["ZSTD_decompressStream"]) }
+}
+
+/**
+ * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ */
+private class ZSTDDecompressUsingDictFunction extends Function {
+ ZSTDDecompressUsingDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+}
+
+/**
+ * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ */
+private class ZSTDDecompressUsingDDictFunction extends Function {
+ ZSTDDecompressUsingDDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+}
+
+module ZstdTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction |
+ fc = source.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fopenCall(fc) |
+ fc = source.asExpr() and
+ state = ""
+ )
+ or
+ source.asExpr() instanceof ZSTDinBufferSVar and
+ state = ""
+ or
+ source.asExpr() instanceof ZSTDinBufferVar and
+ state = ""
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
+ fc.getArgument(2) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
+ fc.getArgument(3) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
+ fc.getArgument(3) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
+ fc.getArgument(3) = sink.asExpr() and
+ state = ""
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ none()
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module ZstdTaint = TaintTracking::GlobalWithState;
+
+import ZstdTaint::PathGraph
+
+from ZstdTaint::PathNode source, ZstdTaint::PathNode sink
+where ZstdTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
similarity index 98%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
rename to cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
index 6c0804404cd..ce7324eded0 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
@@ -5,7 +5,7 @@
* @problem.severity error
* @security-severity 7.8
* @precision high
- * @id cpp/user-controlled-file-decompression1
+ * @id cpp/user-controlled-file-zlibgz
* @tags security
* experimental
* external/cwe/cwe-409
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
similarity index 84%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
rename to cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
index fdd11e1fc16..e526b4d9273 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
@@ -5,7 +5,7 @@
* @problem.severity error
* @security-severity 7.8
* @precision high
- * @id cpp/user-controlled-file-decompression2
+ * @id cpp/user-controlled-file-zlibinflator
* @tags security
* experimental
* external/cwe/cwe-409
@@ -41,13 +41,6 @@ module ZlibTaintConfig implements DataFlow::ConfigSig {
fc.getArgument(0) = sink.asExpr()
)
}
-
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- }
}
module ZlibTaint = TaintTracking::Global;
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
similarity index 83%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql
rename to cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
index 09327616fd9..b385523f185 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
@@ -5,7 +5,7 @@
* @problem.severity error
* @security-severity 7.8
* @precision high
- * @id cpp/user-controlled-file-decompression3
+ * @id cpp/user-controlled-file-zlibuncompress
* @tags security
* experimental
* external/cwe/cwe-409
@@ -37,13 +37,6 @@ module ZlibTaintConfig implements DataFlow::ConfigSig {
fc.getArgument(0) = sink.asExpr()
)
}
-
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- }
}
module ZlibTaint = TaintTracking::Global;
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
index 94e3e969a51..af513817386 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
@@ -1,3 +1,8 @@
+#include
+#include
+#include "zlib.h"
+#include
+#include
int UnsafeInflate(int argc, char *argv[]) {
// original string len = 36
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
index e3a2d4b2e50..7ad34658367 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
@@ -1,3 +1,8 @@
+#include
+#include
+#include "zlib.h"
+#include
+#include
int SafeGzread() {
std::cout << "enter compressed file name!\n" << std::endl;
From d4d505d7af7195a3163bcf962b652ef0148f68f2 Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 3 Jul 2023 20:39:08 +1000
Subject: [PATCH 007/404] complete the minizip query
---
.../DecompressionBombsMiniZip.ql | 84 +++++++++++++++----
1 file changed, 67 insertions(+), 17 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
index b8cd89fc542..67999bff3d6 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
@@ -15,6 +15,43 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+/**
+ * The `mz_zip_reader_create` function as a Flow source
+ * create a `mz_zip_reader` instance
+ */
+private class Mz_zip_reader_create extends Function {
+ Mz_zip_reader_create() { this.hasGlobalName("mz_zip_reader_create") }
+}
+
+/**
+ * The `mz_zip_create` function as a Flow source
+ * create a `mz_zip` instance
+ */
+private class Mz_zip_create extends Function {
+ Mz_zip_create() { this.hasGlobalName("mz_zip_create") }
+}
+
+/**
+ * The `mz_zip_entry` function is used in Flow source
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
+ */
+private class Mz_zip_entry extends Function {
+ Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") }
+}
+
+/**
+ * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in Flow source
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
+ */
+private class Mz_zip_reader_entry extends Function {
+ Mz_zip_reader_entry() {
+ this.hasGlobalName([
+ "mz_zip_reader_entry_save", "mz_zip_reader_entry_read", "mz_zip_reader_entry_save_process",
+ "mz_zip_reader_entry_save_file", "mz_zip_reader_entry_save_buffer", "mz_zip_reader_save_all"
+ ])
+ }
+}
+
/**
* A `unzFile` Variable as a Flow source
*/
@@ -29,13 +66,6 @@ private class UnzOpenFunction extends Function {
UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
}
-/**
- * The `mz_stream_open` function is used in Flow source
- */
-private class MzStreamOpenFunction extends Function {
- MzStreamOpenFunction() { this.hasGlobalName("mz_stream_open") }
-}
-
/**
* The `unzReadCurrentFile` function is used in Flow sink
*/
@@ -55,16 +85,14 @@ module MiniZipTaintConfig implements DataFlow::StateConfigSig {
source.asExpr() instanceof UnzFileVar and
state = "unzFile"
or
- // TO Check
- exists(FunctionCall fc | fc.getTarget() instanceof MzStreamOpenFunction |
- fc.getArgument(0).getEnclosingVariable() = source.asVariable() and
- state = "MzStream"
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_create |
+ fc = source.asExpr() and
+ state = "mz_zip_reader"
)
or
- // TO Check
- exists(FunctionCall fc | fc.getTarget() instanceof MzStreamOpenFunction |
- fc.getArgument(0).getEnclosingVariable() = source.asVariable() and
- state = "MzStream"
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_create |
+ fc = source.asExpr() and
+ state = "mz_zip"
)
}
@@ -72,7 +100,16 @@ module MiniZipTaintConfig implements DataFlow::StateConfigSig {
exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
fc.getArgument(0) = sink.asExpr() and
state = "unzFile"
- // and not sanitizer(fc)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
+ fc.getArgument(1) = sink.asExpr() and
+ state = "mz_zip_reader"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
+ fc.getArgument(1) = sink.asExpr() and
+ state = "mz_zip"
)
}
@@ -86,12 +123,25 @@ module MiniZipTaintConfig implements DataFlow::StateConfigSig {
state1 = "" and
state2 = "unzFile"
)
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1) and
+ state1 = "" and
+ state2 = "mz_zip_reader"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1) and
+ state1 = "" and
+ state2 = "mz_zip"
+ )
}
predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
}
-
module MiniZipTaint = TaintTracking::GlobalWithState;
import MiniZipTaint::PathGraph
From 56bc32ff916f4ea0e87748fac3712bccb1058371 Mon Sep 17 00:00:00 2001
From: amammad
Date: Tue, 4 Jul 2023 01:17:22 +1000
Subject: [PATCH 008/404] add libarchive
---
.../DecompressionBombsLibArchive.ql | 75 +++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
new file mode 100644
index 00000000000..57a9a4f72c9
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
@@ -0,0 +1,75 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-libarchive
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * The `archive_read_new` function as a Flow source
+ * create a `archive` instance
+ */
+private class Archive_read_new extends Function {
+ Archive_read_new() { this.hasGlobalName("archive_read_new") }
+}
+
+/**
+ * The `archive_read_data*` functions are used in Flow Sink
+ * [Examples](https://github.com/libarchive/libarchive/wiki/Examples)
+ */
+private class Archive_read_data_block extends Function {
+ Archive_read_data_block() {
+ this.hasGlobalName(["archive_read_data_block", "archive_read_data", "archive_read_data_into_fd"])
+ }
+}
+
+module LibArchiveTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_new |
+ fc.getArgument(0) = source.asExpr() and
+ state = "unzFile"
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
+ fc.getArgument(1) = sink.asExpr() and
+ state = "unzFile"
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1) and
+ state1 = "" and
+ state2 = "mz_zip_reader"
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module LibArchiveTaint = TaintTracking::GlobalWithState;
+
+import LibArchiveTaint::PathGraph
+
+from LibArchiveTaint::PathNode source, LibArchiveTaint::PathNode sink
+where LibArchiveTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
From 16be908cb38f35036e82ae13f112524e7b7d6d9b Mon Sep 17 00:00:00 2001
From: amammad
Date: Tue, 4 Jul 2023 06:56:30 +1000
Subject: [PATCH 009/404] add Miniz
---
.../DecompressionBombsLibMiniz.ql | 187 ++++++++++++++++++
1 file changed, 187 insertions(+)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
new file mode 100644
index 00000000000..b7e5ea913f3
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
@@ -0,0 +1,187 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/user-controlled-file-decompression-miniz
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A unsigned char Variable is used in Flow source
+ */
+private class Uint8Var extends VariableAccess {
+ Uint8Var() { this.getType().stripType().resolveTypedefs() instanceof UnsignedCharType }
+}
+
+/**
+ * The `mz_streamp`, `z_stream` Variables are used in Flow source
+ */
+private class MzStreampVar extends VariableAccess {
+ MzStreampVar() { this.getType().hasName(["mz_streamp", "z_stream"]) }
+}
+
+/**
+ * A Char Variable is used in Flow source
+ */
+private class CharVar extends VariableAccess {
+ CharVar() { this.getType().stripType() instanceof CharType }
+}
+
+/**
+ * A `mz_zip_archive` Variable is used in Flow source
+ */
+private class MzZipArchiveVar extends VariableAccess {
+ MzZipArchiveVar() { this.getType().hasName("mz_zip_archive") }
+}
+
+/**
+ * The `mz_uncompress` functions are used in Flow Sink
+ */
+private class MzUncompress extends Function {
+ MzUncompress() { this.hasGlobalName(["uncompress", "mz_uncompress", "mz_uncompress2"]) }
+}
+
+/**
+ * The `mz_inflate` functions are used in Flow Sink
+ */
+private class MzInflate extends Function {
+ MzInflate() { this.hasGlobalName(["mz_inflate", "inflate"]) }
+}
+
+/**
+ * The `mz_inflateInit` functions are used in Flow Sink
+ */
+private class MzInflateInit extends Function {
+ MzInflateInit() { this.hasGlobalName(["inflateInit", "mz_inflateInit"]) }
+}
+
+/**
+ * The `mz_zip_reader_extract_*` functions are used in Flow Sink
+ */
+private class MzZipReaderExtract extends Function {
+ MzZipReaderExtract() {
+ this.hasGlobalName([
+ "mz_zip_reader_extract_file_to_heap", "mz_zip_reader_extract_to_heap",
+ "mz_zip_reader_extract_to_callback"
+ ])
+ }
+}
+
+/**
+ * The `mz_zip_reader_locate_file_*` functions are used in Flow Sink
+ */
+private class MzZipReaderLocateFile extends Function {
+ MzZipReaderLocateFile() {
+ this.hasGlobalName(["mz_zip_reader_locate_file", "mz_zip_reader_locate_file_v2"])
+ }
+}
+
+/**
+ * The `tinfl_decompress_mem_*` functions are used in Flow Sink
+ */
+private class TinflDecompressMem extends Function {
+ TinflDecompressMem() {
+ this.hasGlobalName([
+ "tinfl_decompress_mem_to_callback", "tinfl_decompress_mem_to_mem",
+ "tinfl_decompress_mem_to_heap"
+ ])
+ }
+}
+
+/**
+ * The `tinfl_decompress_*` functions are used in Flow Sink
+ */
+private class TinflDecompress extends Function {
+ TinflDecompress() { this.hasGlobalName(["tinfl_decompress"]) }
+}
+
+module MinizTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ source.asExpr() instanceof Uint8Var and
+ state = ""
+ or
+ source.asExpr() instanceof CharVar and
+ state = ""
+ or
+ source.asExpr() instanceof MzZipArchiveVar and
+ state = ""
+ or
+ source.asExpr() instanceof MzStreampVar and
+ state = ""
+ or
+ // source of inflate(&arg0) is OK
+ // but the sink which is a call to MzInflate Function first arg can not be determined
+ // if I debug the query we'll reach to the first arg, it is weird I think.
+ source.asDefiningArgument() =
+ any(Call call | call.getTarget() instanceof MzInflateInit).getArgument(0) and
+ state = "inflate"
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
+ fc.getArgument(2) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
+ fc.getArgument(1) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
+ fc.getArgument(0) = sink.asExpr() and
+ state = "inflate"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
+ fc.getArgument(1) = sink.asExpr() and
+ state = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
+ fc.getArgument(0) = sink.asExpr() and
+ state = ""
+ )
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
+ node1.asExpr() = fc.getArgument(2) and
+ node2.asExpr() = fc.getArgument(0) and
+ state1 = "" and
+ state2 = ""
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderLocateFile |
+ node1.asExpr() = fc.getArgument(1) and
+ node2.asExpr() = fc.getArgument(3) and
+ state1 = "" and
+ state2 = ""
+ )
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module MinizTaint = TaintTracking::GlobalWithState;
+
+import MinizTaint::PathGraph
+
+from MinizTaint::PathNode source, MinizTaint::PathNode sink
+where MinizTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
+ "potentially untrusted source"
From 065c52761510535c976c53f3b1bf5d67006a4cfa Mon Sep 17 00:00:00 2001
From: amammad
Date: Tue, 4 Jul 2023 07:19:33 +1000
Subject: [PATCH 010/404] update Miniz
---
.../DecompressionBombsLibMiniz.ql | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
index b7e5ea913f3..9f694b07cdd 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
@@ -15,11 +15,17 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+/**
+ * A Pointer Variable is used in Flow source
+ */
+private class PointerVar extends VariableAccess {
+ PointerVar() { this.getType() instanceof PointerType }
+}
/**
* A unsigned char Variable is used in Flow source
*/
private class Uint8Var extends VariableAccess {
- Uint8Var() { this.getType().stripType().resolveTypedefs() instanceof UnsignedCharType }
+ Uint8Var() { this.getType().stripType().resolveTypedefs*() instanceof UnsignedCharType }
}
/**
@@ -33,7 +39,7 @@ private class MzStreampVar extends VariableAccess {
* A Char Variable is used in Flow source
*/
private class CharVar extends VariableAccess {
- CharVar() { this.getType().stripType() instanceof CharType }
+ CharVar() { this.getType().stripType().resolveTypedefs*() instanceof CharType }
}
/**
@@ -71,7 +77,10 @@ private class MzZipReaderExtract extends Function {
MzZipReaderExtract() {
this.hasGlobalName([
"mz_zip_reader_extract_file_to_heap", "mz_zip_reader_extract_to_heap",
- "mz_zip_reader_extract_to_callback"
+ "mz_zip_reader_extract_to_callback", "mz_zip_reader_extract_file_to_callback",
+ "mz_zip_reader_extract_to_mem", "mz_zip_reader_extract_file_to_mem",
+ "mz_zip_reader_extract_iter_read", "mz_zip_reader_extract_to_file",
+ "mz_zip_reader_extract_file_to_file"
])
}
}
@@ -111,6 +120,9 @@ module MinizTaintConfig implements DataFlow::StateConfigSig {
source.asExpr() instanceof Uint8Var and
state = ""
or
+ source.asExpr() instanceof PointerVar and
+ state = ""
+ or
source.asExpr() instanceof CharVar and
state = ""
or
From e0798b29da1e2c87d77e1e19169a9c5d01eebdb3 Mon Sep 17 00:00:00 2001
From: amammad
Date: Tue, 4 Jul 2023 18:28:00 +1000
Subject: [PATCH 011/404] stash: change sinks to zip handles and sources to the
zip handle initializers
---
.../DecompressionBombsLibMiniz.ql | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
index 9f694b07cdd..e0310704af2 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
@@ -21,6 +21,7 @@ import semmle.code.cpp.security.FlowSources
private class PointerVar extends VariableAccess {
PointerVar() { this.getType() instanceof PointerType }
}
+
/**
* A unsigned char Variable is used in Flow source
*/
@@ -56,6 +57,18 @@ private class MzUncompress extends Function {
MzUncompress() { this.hasGlobalName(["uncompress", "mz_uncompress", "mz_uncompress2"]) }
}
+/**
+ * A `zip handle` is used in Flow source
+ */
+private class MzZip extends Function {
+ MzZip() {
+ this.hasGlobalName([
+ "mz_zip_reader_open", "mz_zip_reader_open_file", "mz_zip_reader_open_file_in_memory",
+ "mz_zip_reader_open_buffer", "mz_zip_reader_entry_open"
+ ])
+ }
+}
+
/**
* The `mz_inflate` functions are used in Flow Sink
*/
@@ -138,6 +151,9 @@ module MinizTaintConfig implements DataFlow::StateConfigSig {
source.asDefiningArgument() =
any(Call call | call.getTarget() instanceof MzInflateInit).getArgument(0) and
state = "inflate"
+ or
+ source.asDefiningArgument() = any(Call call | call.getTarget() instanceof MzZip).getArgument(0) and
+ state = ""
}
predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
From e37ceac3b137e7a453a8be032692fbca061505c6 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Fri, 7 Jun 2024 05:26:51 +0200
Subject: [PATCH 012/404] merge all query files into one query file
---
.../CWE/CWE-409-DecompressionBomb/Brotli.qll | 38 ++
.../CWE/CWE-409-DecompressionBomb/Bzip2.qll | 50 +++
.../DecompressionBombs.ql | 330 ++++++++++++++++++
.../DecompressionBombsBrotli.ql | 107 ------
.../DecompressionBombsBzip2.ql | 126 -------
.../DecompressionBombsLibArchive.ql | 75 ----
.../DecompressionBombsLibMiniz.ql | 215 ------------
.../DecompressionBombsMiniZip.ql | 152 --------
.../DecompressionBombsXZ.ql | 100 ------
.../DecompressionBombsZSTD.ql | 140 --------
.../DecompressionBombsZlibGzopen.ql | 150 --------
.../DecompressionBombsZlibInflator.ql | 53 ---
.../DecompressionBombsZlibUncompress.ql | 49 ---
.../CWE-409-DecompressionBomb/LibArchive.qll | 25 ++
.../CWE-409-DecompressionBomb/LibMiniz.qll | 102 ++++++
.../CWE/CWE-409-DecompressionBomb/MiniZip.qll | 65 ++++
.../CWE/CWE-409-DecompressionBomb/XZ.qll | 37 ++
.../CWE/CWE-409-DecompressionBomb/ZSTD.qll | 57 +++
.../CWE-409-DecompressionBomb/ZlibGzopen.qll | 75 ++++
.../ZlibInflator.qll | 25 ++
.../ZlibUncompress.qll | 21 ++
21 files changed, 825 insertions(+), 1167 deletions(-)
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
delete mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll
create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll
new file mode 100644
index 00000000000..46314826a62
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll
@@ -0,0 +1,38 @@
+/**
+ * https://github.com/google/brotli
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+/**
+ * A Pointer Variable is used in Flow source
+ */
+class PointerVar extends VariableAccess {
+ PointerVar() { this.getType() instanceof PointerType }
+}
+
+/**
+ * A Pointer Variable is used in Flow source
+ */
+class Uint8Var extends VariableAccess {
+ Uint8Var() { this.getType() instanceof UInt8_t }
+}
+
+/**
+ * The `BrotliDecoderDecompress` function is used in Flow sink
+ * Ref: https://www.brotli.org/decode.html#af68
+ */
+class BrotliDecoderDecompressFunction extends Function {
+ BrotliDecoderDecompressFunction() { this.hasGlobalName(["BrotliDecoderDecompress"]) }
+}
+
+/**
+ * The `BrotliDecoderDecompressStream` function is used in Flow sink
+ * Ref: https://www.brotli.org/decode.html#a234
+ */
+class BrotliDecoderDecompressStreamFunction extends Function {
+ BrotliDecoderDecompressStreamFunction() { this.hasGlobalName(["BrotliDecoderDecompressStream"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll
new file mode 100644
index 00000000000..662424b022a
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll
@@ -0,0 +1,50 @@
+/**
+ * https://www.sourceware.org/bzip2/manual/manual.html
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+/**
+ * A `bz_stream` Variable as a Flow source
+ */
+class BzStreamVar extends VariableAccess {
+ BzStreamVar() { this.getType().hasName("bz_stream") }
+}
+
+/**
+ * A `BZFILE` Variable as a Flow source
+ */
+class BzFileVar extends VariableAccess {
+ BzFileVar() { this.getType().hasName("BZFILE") }
+}
+
+/**
+ * The `BZ2_bzDecompress` function as a Flow source
+ */
+class BZ2BzDecompressFunction extends Function {
+ BZ2BzDecompressFunction() { this.hasGlobalName(["BZ2_bzDecompress"]) }
+}
+
+/**
+ * The `BZ2_bzReadOpen` function
+ */
+class BZ2BzReadOpenFunction extends Function {
+ BZ2BzReadOpenFunction() { this.hasGlobalName(["BZ2_bzReadOpen"]) }
+}
+
+/**
+ * The `BZ2_bzRead` function is used in Flow sink
+ */
+class BZ2BzReadFunction extends Function {
+ BZ2BzReadFunction() { this.hasGlobalName("BZ2_bzRead") }
+}
+
+/**
+ * The `BZ2_bzBuffToBuffDecompress` function is used in Flow sink
+ */
+class BZ2BzBuffToBuffDecompressFunction extends Function {
+ BZ2BzBuffToBuffDecompressFunction() { this.hasGlobalName("BZ2_bzBuffToBuffDecompress") }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
new file mode 100644
index 00000000000..8506dccaea7
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
@@ -0,0 +1,330 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision high
+ * @id cpp/data-decompression
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+import Bzip2
+import Brotli
+import LibArchive
+import LibMiniz
+import ZSTD
+import MiniZip
+import XZ
+import ZlibGzopen
+import ZlibUncompress
+import ZlibInflator
+import Brotli
+
+module DecompressionTaintConfig implements DataFlow::StateConfigSig {
+ class FlowState = DataFlow::FlowState;
+
+ predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction | fc = source.asExpr())
+ or
+ exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
+ or
+ source.asExpr() instanceof PointerVar
+ or
+ source.asExpr() instanceof Uint8Var
+ ) and
+ state = "brotli"
+ or
+ (
+ source.asExpr() instanceof BzStreamVar
+ or
+ source.asExpr() instanceof BzFileVar
+ or
+ exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
+ ) and
+ state = "bzip2"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_new |
+ fc.getArgument(0) = source.asExpr()
+ ) and
+ state = "libarchive"
+ or
+ (
+ source.asExpr() instanceof UnsignedCharVar
+ or
+ source.asExpr() instanceof PointerVar
+ or
+ source.asExpr() instanceof CharVar
+ or
+ source.asExpr() instanceof MzZipArchiveVar
+ or
+ source.asExpr() instanceof MzStreampVar
+ or
+ source.asDefiningArgument() =
+ any(Call call | call.getTarget() instanceof MzInflateInit).getArgument(0)
+ or
+ source.asDefiningArgument() =
+ any(Call call | call.getTarget() instanceof MzZip).getArgument(0)
+ ) and
+ state = "libminiz"
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction | fc = source.asExpr())
+ or
+ exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
+ or
+ source.asExpr() instanceof ZSTDinBufferSVar
+ or
+ source.asExpr() instanceof ZSTDinBufferVar
+ ) and
+ state = "zstd"
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
+ fc.getArgument(0) = source.asExpr()
+ )
+ or
+ source.asExpr() instanceof UnzFileVar
+ ) and
+ state = "unzFile"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_create |
+ fc = source.asExpr() and
+ state = "mz_zip_reader"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_create |
+ fc = source.asExpr() and
+ state = "mz_zip"
+ )
+ or
+ (
+ source.asExpr() instanceof LzmaStreamVar
+ or
+ source.asExpr() instanceof Uint8Var
+ ) and
+ state = "xz"
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
+ fc.getArgument(0) = source.asExpr() and
+ // arg 0 can be a path string whichwe must do following check
+ not fc.getArgument(0).isConstant()
+ )
+ or
+ // IDK whether it is good to use all file decriptors function returns as source or not
+ // because we can do more sanitization from fd function sources
+ exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
+ fc.getArgument(0) = source.asExpr()
+ )
+ or
+ source.asExpr() instanceof GzFileVar
+ ) and
+ state = "zlibgzopen"
+ or
+ source.asExpr() instanceof ZStreamVar and state = "zlifinflator"
+ or
+ source.asExpr() instanceof BytefVar and state = "zlibuncompress"
+ }
+
+ predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ ) and
+ state = "brotli"
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ ) and
+ state = "bzip2"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
+ fc.getArgument(0) = sink.asExpr() and
+ state = "libarchive"
+ )
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ ) and
+ state = "libminiz"
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
+ ) and
+ state = "zstd"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
+ fc.getArgument(0) = sink.asExpr() and
+ state = "unzFile"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
+ fc.getArgument(1) = sink.asExpr() and
+ state = "mz_zip_reader"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
+ fc.getArgument(1) = sink.asExpr() and
+ state = "mz_zip"
+ )
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ ) and
+ state = "xz" and
+ exists(FunctionCall fc2 | fc2.getTarget() instanceof LzmaDecoderFunction)
+ or
+ (
+ exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
+ sink.asExpr() = fc.getArgument(3)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
+ sink.asExpr() = fc.getArgument(0)
+ )
+ ) and
+ state = "zlibgzopen"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
+ fc.getArgument(0) = sink.asExpr()
+ ) and
+ state = "zlifinflator"
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
+ fc.getArgument(0) = sink.asExpr()
+ ) and
+ state = "zlibuncompress"
+ }
+
+ predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc and
+ state1 = "" and
+ state2 = "unzFile"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1) and
+ state1 = "" and
+ state2 = "mz_zip_reader"
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1) and
+ state1 = "" and
+ state2 = "mz_zip"
+ )
+ or
+ (
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
+ |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
+ node1.asExpr() = fc.getArgument(3) and
+ node2.asExpr() = fc.getArgument(0)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node1.asExpr() = fc.getArgument(1)
+ )
+ ) and
+ state1 = "" and
+ state2 = "gzopen"
+ }
+
+ predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
+}
+
+module DecompressionTaint = TaintTracking::GlobalWithState;
+
+import DecompressionTaint::PathGraph
+
+from DecompressionTaint::PathNode source, DecompressionTaint::PathNode sink
+where DecompressionTaint::flowPath(source, sink)
+select sink.getNode(), source, sink, "This Decompression output $@.", source.getNode(),
+ "is not limited"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
deleted file mode 100644
index b09858d6471..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBrotli.ql
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-brotli
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-// https://github.com/google/brotli
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-import semmle.code.cpp.commons.File
-
-/**
- * A Pointer Variable is used in Flow source
- */
-private class PointerVar extends VariableAccess {
- PointerVar() { this.getType() instanceof PointerType }
-}
-
-/**
- * A Pointer Variable is used in Flow source
- */
-private class Uint8Var extends VariableAccess {
- Uint8Var() { this.getType() instanceof UInt8_t }
-}
-
-/**
- * A ZSTD_inBuffer Variable is used in Flow source
- */
-private class ZSTDinBufferVar extends VariableAccess {
- ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
-}
-
-/**
- * The `ZSTD_decompress_usingDDict` function is used in Flow sink
- * Ref: https://www.brotli.org/decode.html#af68
- */
-private class BrotliDecoderDecompressFunction extends Function {
- BrotliDecoderDecompressFunction() { this.hasGlobalName(["BrotliDecoderDecompress"]) }
-}
-
-/**
- * The `BrotliDecoderDecompressStream` function is used in Flow sink
- * Ref: https://www.brotli.org/decode.html#a234
- */
-private class BrotliDecoderDecompressStreamFunction extends Function {
- BrotliDecoderDecompressStreamFunction() { this.hasGlobalName(["BrotliDecoderDecompressStream"]) }
-}
-
-module BrotliTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction |
- fc = source.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fopenCall(fc) |
- fc = source.asExpr() and
- state = ""
- )
- or
- source.asExpr() instanceof PointerVar and
- state = ""
- or
- source.asExpr() instanceof Uint8Var and
- state = ""
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
- fc.getArgument(1) = sink.asExpr() and
- state = ""
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- none()
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module BrotliTaint = TaintTracking::GlobalWithState;
-
-import BrotliTaint::PathGraph
-
-from BrotliTaint::PathNode source, BrotliTaint::PathNode sink
-where BrotliTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
deleted file mode 100644
index e9b7d60715d..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsBzip2.ql
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-bzip2
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-import semmle.code.cpp.commons.File
-
-/**
- * A `bz_stream` Variable as a Flow source
- */
-private class BzStreamVar extends VariableAccess {
- BzStreamVar() { this.getType().hasName("bz_stream") }
-}
-
-/**
- * A `BZFILE` Variable as a Flow source
- */
-private class BZFILEVar extends VariableAccess {
- BZFILEVar() { this.getType().hasName("BZFILE") }
-}
-
-/**
- * The `BZ2_bzopen`,`BZ2_bzdopen` functions as a Flow source
- */
-private class BZ2BzopenFunction extends Function {
- BZ2BzopenFunction() { this.hasGlobalName(["BZ2_bzopen", "BZ2_bzdopen"]) }
-}
-
-/**
- * The `BZ2_bzDecompress` function as a Flow source
- */
-private class BZ2BzDecompressFunction extends Function {
- BZ2BzDecompressFunction() { this.hasGlobalName(["BZ2_bzDecompress"]) }
-}
-
-/**
- * The `BZ2_bzReadOpen` function
- */
-private class BZ2BzReadOpenFunction extends Function {
- BZ2BzReadOpenFunction() { this.hasGlobalName(["BZ2_bzReadOpen"]) }
-}
-
-/**
- * The `BZ2_bzRead` function is used in Flow sink
- */
-private class BZ2BzReadFunction extends Function {
- BZ2BzReadFunction() { this.hasGlobalName("BZ2_bzRead") }
-}
-
-/**
- * The `BZ2_bzRead` function is used in Flow sink
- */
-private class BZ2BzBuffToBuffDecompressFunction extends Function {
- BZ2BzBuffToBuffDecompressFunction() { this.hasGlobalName("BZ2_bzBuffToBuffDecompress") }
-}
-
-/**
- * https://www.sourceware.org/bzip2/manual/manual.html
- */
-module Bzip2TaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- source.asExpr() instanceof BzStreamVar and
- state = ""
- or
- source.asExpr() instanceof BZFILEVar and
- state = ""
- or
- // will flow into BZ2BzReadOpenFunction
- exists(FunctionCall fc | fopenCall(fc) |
- fc = source.asExpr() and
- state = ""
- )
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
- fc.getArgument(0) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
- fc.getArgument(1) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
- fc.getArgument(2) = sink.asExpr() and
- state = ""
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadOpenFunction |
- node1.asExpr() = fc.getArgument(1) and
- node2.asExpr() = fc and
- state1 = "" and
- state2 = ""
- )
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module Bzip2Taint = TaintTracking::GlobalWithState;
-
-import Bzip2Taint::PathGraph
-
-from Bzip2Taint::PathNode source, Bzip2Taint::PathNode sink
-where Bzip2Taint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
deleted file mode 100644
index 57a9a4f72c9..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibArchive.ql
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-libarchive
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * The `archive_read_new` function as a Flow source
- * create a `archive` instance
- */
-private class Archive_read_new extends Function {
- Archive_read_new() { this.hasGlobalName("archive_read_new") }
-}
-
-/**
- * The `archive_read_data*` functions are used in Flow Sink
- * [Examples](https://github.com/libarchive/libarchive/wiki/Examples)
- */
-private class Archive_read_data_block extends Function {
- Archive_read_data_block() {
- this.hasGlobalName(["archive_read_data_block", "archive_read_data", "archive_read_data_into_fd"])
- }
-}
-
-module LibArchiveTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_new |
- fc.getArgument(0) = source.asExpr() and
- state = "unzFile"
- )
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
- fc.getArgument(1) = sink.asExpr() and
- state = "unzFile"
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1) and
- state1 = "" and
- state2 = "mz_zip_reader"
- )
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module LibArchiveTaint = TaintTracking::GlobalWithState;
-
-import LibArchiveTaint::PathGraph
-
-from LibArchiveTaint::PathNode source, LibArchiveTaint::PathNode sink
-where LibArchiveTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
deleted file mode 100644
index e0310704af2..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsLibMiniz.ql
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-miniz
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * A Pointer Variable is used in Flow source
- */
-private class PointerVar extends VariableAccess {
- PointerVar() { this.getType() instanceof PointerType }
-}
-
-/**
- * A unsigned char Variable is used in Flow source
- */
-private class Uint8Var extends VariableAccess {
- Uint8Var() { this.getType().stripType().resolveTypedefs*() instanceof UnsignedCharType }
-}
-
-/**
- * The `mz_streamp`, `z_stream` Variables are used in Flow source
- */
-private class MzStreampVar extends VariableAccess {
- MzStreampVar() { this.getType().hasName(["mz_streamp", "z_stream"]) }
-}
-
-/**
- * A Char Variable is used in Flow source
- */
-private class CharVar extends VariableAccess {
- CharVar() { this.getType().stripType().resolveTypedefs*() instanceof CharType }
-}
-
-/**
- * A `mz_zip_archive` Variable is used in Flow source
- */
-private class MzZipArchiveVar extends VariableAccess {
- MzZipArchiveVar() { this.getType().hasName("mz_zip_archive") }
-}
-
-/**
- * The `mz_uncompress` functions are used in Flow Sink
- */
-private class MzUncompress extends Function {
- MzUncompress() { this.hasGlobalName(["uncompress", "mz_uncompress", "mz_uncompress2"]) }
-}
-
-/**
- * A `zip handle` is used in Flow source
- */
-private class MzZip extends Function {
- MzZip() {
- this.hasGlobalName([
- "mz_zip_reader_open", "mz_zip_reader_open_file", "mz_zip_reader_open_file_in_memory",
- "mz_zip_reader_open_buffer", "mz_zip_reader_entry_open"
- ])
- }
-}
-
-/**
- * The `mz_inflate` functions are used in Flow Sink
- */
-private class MzInflate extends Function {
- MzInflate() { this.hasGlobalName(["mz_inflate", "inflate"]) }
-}
-
-/**
- * The `mz_inflateInit` functions are used in Flow Sink
- */
-private class MzInflateInit extends Function {
- MzInflateInit() { this.hasGlobalName(["inflateInit", "mz_inflateInit"]) }
-}
-
-/**
- * The `mz_zip_reader_extract_*` functions are used in Flow Sink
- */
-private class MzZipReaderExtract extends Function {
- MzZipReaderExtract() {
- this.hasGlobalName([
- "mz_zip_reader_extract_file_to_heap", "mz_zip_reader_extract_to_heap",
- "mz_zip_reader_extract_to_callback", "mz_zip_reader_extract_file_to_callback",
- "mz_zip_reader_extract_to_mem", "mz_zip_reader_extract_file_to_mem",
- "mz_zip_reader_extract_iter_read", "mz_zip_reader_extract_to_file",
- "mz_zip_reader_extract_file_to_file"
- ])
- }
-}
-
-/**
- * The `mz_zip_reader_locate_file_*` functions are used in Flow Sink
- */
-private class MzZipReaderLocateFile extends Function {
- MzZipReaderLocateFile() {
- this.hasGlobalName(["mz_zip_reader_locate_file", "mz_zip_reader_locate_file_v2"])
- }
-}
-
-/**
- * The `tinfl_decompress_mem_*` functions are used in Flow Sink
- */
-private class TinflDecompressMem extends Function {
- TinflDecompressMem() {
- this.hasGlobalName([
- "tinfl_decompress_mem_to_callback", "tinfl_decompress_mem_to_mem",
- "tinfl_decompress_mem_to_heap"
- ])
- }
-}
-
-/**
- * The `tinfl_decompress_*` functions are used in Flow Sink
- */
-private class TinflDecompress extends Function {
- TinflDecompress() { this.hasGlobalName(["tinfl_decompress"]) }
-}
-
-module MinizTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- source.asExpr() instanceof Uint8Var and
- state = ""
- or
- source.asExpr() instanceof PointerVar and
- state = ""
- or
- source.asExpr() instanceof CharVar and
- state = ""
- or
- source.asExpr() instanceof MzZipArchiveVar and
- state = ""
- or
- source.asExpr() instanceof MzStreampVar and
- state = ""
- or
- // source of inflate(&arg0) is OK
- // but the sink which is a call to MzInflate Function first arg can not be determined
- // if I debug the query we'll reach to the first arg, it is weird I think.
- source.asDefiningArgument() =
- any(Call call | call.getTarget() instanceof MzInflateInit).getArgument(0) and
- state = "inflate"
- or
- source.asDefiningArgument() = any(Call call | call.getTarget() instanceof MzZip).getArgument(0) and
- state = ""
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
- fc.getArgument(2) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
- fc.getArgument(1) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
- fc.getArgument(0) = sink.asExpr() and
- state = "inflate"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
- fc.getArgument(1) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
- fc.getArgument(0) = sink.asExpr() and
- state = ""
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
- node1.asExpr() = fc.getArgument(2) and
- node2.asExpr() = fc.getArgument(0) and
- state1 = "" and
- state2 = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderLocateFile |
- node1.asExpr() = fc.getArgument(1) and
- node2.asExpr() = fc.getArgument(3) and
- state1 = "" and
- state2 = ""
- )
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module MinizTaint = TaintTracking::GlobalWithState;
-
-import MinizTaint::PathGraph
-
-from MinizTaint::PathNode source, MinizTaint::PathNode sink
-where MinizTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
deleted file mode 100644
index 67999bff3d6..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsMiniZip.ql
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-minizip
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * The `mz_zip_reader_create` function as a Flow source
- * create a `mz_zip_reader` instance
- */
-private class Mz_zip_reader_create extends Function {
- Mz_zip_reader_create() { this.hasGlobalName("mz_zip_reader_create") }
-}
-
-/**
- * The `mz_zip_create` function as a Flow source
- * create a `mz_zip` instance
- */
-private class Mz_zip_create extends Function {
- Mz_zip_create() { this.hasGlobalName("mz_zip_create") }
-}
-
-/**
- * The `mz_zip_entry` function is used in Flow source
- * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
- */
-private class Mz_zip_entry extends Function {
- Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") }
-}
-
-/**
- * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in Flow source
- * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
- */
-private class Mz_zip_reader_entry extends Function {
- Mz_zip_reader_entry() {
- this.hasGlobalName([
- "mz_zip_reader_entry_save", "mz_zip_reader_entry_read", "mz_zip_reader_entry_save_process",
- "mz_zip_reader_entry_save_file", "mz_zip_reader_entry_save_buffer", "mz_zip_reader_save_all"
- ])
- }
-}
-
-/**
- * A `unzFile` Variable as a Flow source
- */
-private class UnzFileVar extends VariableAccess {
- UnzFileVar() { this.getType().hasName("unzFile") }
-}
-
-/**
- * The `UnzOpen` function as a Flow source
- */
-private class UnzOpenFunction extends Function {
- UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
-}
-
-/**
- * The `unzReadCurrentFile` function is used in Flow sink
- */
-private class UnzReadCurrentFileFunction extends Function {
- UnzReadCurrentFileFunction() { this.hasGlobalName(["unzReadCurrentFile"]) }
-}
-
-module MiniZipTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
- fc.getArgument(0) = source.asExpr() and
- state = "unzFile"
- )
- or
- source.asExpr() instanceof UnzFileVar and
- state = "unzFile"
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_create |
- fc = source.asExpr() and
- state = "mz_zip_reader"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_create |
- fc = source.asExpr() and
- state = "mz_zip"
- )
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
- fc.getArgument(0) = sink.asExpr() and
- state = "unzFile"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
- fc.getArgument(1) = sink.asExpr() and
- state = "mz_zip_reader"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
- fc.getArgument(1) = sink.asExpr() and
- state = "mz_zip"
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc and
- state1 = "" and
- state2 = "unzFile"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1) and
- state1 = "" and
- state2 = "mz_zip_reader"
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1) and
- state1 = "" and
- state2 = "mz_zip"
- )
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module MiniZipTaint = TaintTracking::GlobalWithState;
-
-import MiniZipTaint::PathGraph
-
-from MiniZipTaint::PathNode source, MiniZipTaint::PathNode sink
-where MiniZipTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
deleted file mode 100644
index dadbf7a5a8a..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsXZ.ql
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-xz
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * A Pointer Variable as a Flow source
- */
-private class Uint8Var extends VariableAccess {
- Uint8Var() { this.getType() instanceof UInt8_t }
-}
-
-/**
- * A `lzma_stream` Variable as a Flow source
- */
-private class LzmaStreamVar extends VariableAccess {
- LzmaStreamVar() { this.getType().hasName("lzma_stream") }
-}
-
-/**
- * The `lzma_*_decoder` function is used as a required condition for decompression
- */
-private class LzmaDecoderFunction extends Function {
- LzmaDecoderFunction() {
- this.hasGlobalName(["lzma_stream_decoder", "lzma_auto_decoder", "lzma_alone_decoder"])
- }
-}
-
-/**
- * The `lzma_code` function is used in Flow sink
- */
-private class LzmaCodeFunction extends Function {
- LzmaCodeFunction() { this.hasGlobalName(["lzma_code"]) }
-}
-
-/**
- * The `lzma_stream_buffer_decode` function is used in Flow sink
- */
-private class LzmaStreamBufferDecodeFunction extends Function {
- LzmaStreamBufferDecodeFunction() { this.hasGlobalName(["lzma_stream_buffer_decode"]) }
-}
-
-/**
- * https://github.com/tukaani-project/xz
- */
-module XzTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- source.asExpr() instanceof LzmaStreamVar and
- state = ""
- or
- source.asExpr() instanceof Uint8Var and
- state = ""
- // and not exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamDecoderFunction)
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
- fc.getArgument(1) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
- fc.getArgument(0) = sink.asExpr() and
- state = ""
- ) and
- exists(FunctionCall fc2 | fc2.getTarget() instanceof LzmaDecoderFunction)
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- none()
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module XzTaint = TaintTracking::GlobalWithState;
-
-import XzTaint::PathGraph
-
-from XzTaint::PathNode source, XzTaint::PathNode sink
-where XzTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
deleted file mode 100644
index 9e984bb89e2..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZSTD.ql
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-decompression-zstd
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-// https://github.com/facebook/zstd/blob/dev/examples/streaming_decompression.c
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-import semmle.code.cpp.commons.File
-
-// /**
-// * A Pointer Variable as a Flow source
-// */
-// private class PointerVar extends VariableAccess {
-// PointerVar() { this.getType() instanceof PointerType }
-// }
-/**
- * A ZSTD_inBuffer Variable as a Flow source
- */
-private class ZSTDinBufferVar extends VariableAccess {
- ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
-}
-
-/**
- * A ZSTD_inBuffer_s Variable as a Flow source
- */
-private class ZSTDinBufferSVar extends VariableAccess {
- ZSTDinBufferSVar() { this.getType().hasName("ZSTD_inBuffer_s") }
-}
-
-/**
- * The `ZSTD_decompress` function is used in Flow sink
- */
-private class ZSTDDecompressFunction extends Function {
- ZSTDDecompressFunction() { this.hasGlobalName(["ZSTD_decompress"]) }
-}
-
-/**
- * The `ZSTD_decompressDCtx` function is used in Flow sink
- */
-private class ZSTDDecompressDCtxFunction extends Function {
- ZSTDDecompressDCtxFunction() { this.hasGlobalName(["ZSTD_decompressDCtx"]) }
-}
-
-/**
- * The `ZSTD_decompressStream` function is used in Flow sink
- */
-private class ZSTDDecompressStreamFunction extends Function {
- ZSTDDecompressStreamFunction() { this.hasGlobalName(["ZSTD_decompressStream"]) }
-}
-
-/**
- * The `ZSTD_decompress_usingDDict` function is used in Flow sink
- */
-private class ZSTDDecompressUsingDictFunction extends Function {
- ZSTDDecompressUsingDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
-}
-
-/**
- * The `ZSTD_decompress_usingDDict` function is used in Flow sink
- */
-private class ZSTDDecompressUsingDDictFunction extends Function {
- ZSTDDecompressUsingDDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
-}
-
-module ZstdTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
-
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction |
- fc = source.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fopenCall(fc) |
- fc = source.asExpr() and
- state = ""
- )
- or
- source.asExpr() instanceof ZSTDinBufferSVar and
- state = ""
- or
- source.asExpr() instanceof ZSTDinBufferVar and
- state = ""
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
- fc.getArgument(2) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
- fc.getArgument(3) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
- fc.getArgument(3) = sink.asExpr() and
- state = ""
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
- fc.getArgument(3) = sink.asExpr() and
- state = ""
- )
- }
-
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- none()
- }
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
-}
-
-module ZstdTaint = TaintTracking::GlobalWithState;
-
-import ZstdTaint::PathGraph
-
-from ZstdTaint::PathNode source, ZstdTaint::PathNode sink
-where ZstdTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
deleted file mode 100644
index ce7324eded0..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibGzopen.ql
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-zlibgz
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * A `gzFile` Variable as a Flow source
- */
-private class GzFileVar extends VariableAccess {
- GzFileVar() { this.getType().hasName("gzFile") }
-}
-
-/**
- * The `gzopen` function as a Flow source
- *
- * `gzopen(const char *path, const char *mode)`
- */
-private class GzopenFunction extends Function {
- GzopenFunction() { this.hasGlobalName("gzopen") }
-}
-
-/**
- * The `gzdopen` function as a Flow source
- *
- * `gzdopen(int fd, const char *mode)`
- */
-private class GzdopenFunction extends Function {
- GzdopenFunction() { this.hasGlobalName("gzdopen") }
-}
-
-/**
- * The `gzfread` function is used in Flow sink
- *
- * `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)`
- */
-private class GzfreadFunction extends Function {
- GzfreadFunction() { this.hasGlobalName("gzfread") }
-}
-
-/**
- * The `gzgets` function is used in Flow sink.
- *
- * `gzgets(gzFile file, char *buf, int len)`
- */
-private class GzgetsFunction extends Function {
- GzgetsFunction() { this.hasGlobalName("gzgets") }
-}
-
-/**
- * The `gzread` function is used in Flow sink
- *
- * `gzread(gzFile file, voidp buf, unsigned len)`
- */
-private class GzreadFunction extends Function {
- GzreadFunction() { this.hasGlobalName("gzread") }
-}
-
-module ZlibTaintConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
- fc.getArgument(0) = source.asExpr() and
- // arg 0 can be a path string whichwe must do following check
- not fc.getArgument(0).isConstant()
- )
- or
- // IDK whether it is good to use all file decriptors function returns as source or not
- // because we can do more sanitization from fd function sources
- exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
- fc.getArgument(0) = source.asExpr()
- )
- or
- source.asExpr() instanceof GzFileVar
- }
-
- predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
- fc.getArgument(0) = sink.asExpr() and
- not sanitizer(fc)
- // TODO: and not sanitizer2(fc.getArgument(2))
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
- sink.asExpr() = fc.getArgument(3)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzgetsFunction |
- sink.asExpr() = fc.getArgument(0)
- )
- }
-
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc |
- fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
- |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction |
- node1.asExpr() = fc.getArgument(3) and
- node2.asExpr() = fc.getArgument(0)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzgetsFunction |
- node1.asExpr() = fc.getArgument(0) and
- node1.asExpr() = fc.getArgument(1)
- )
- }
-}
-
-predicate sanitizer(FunctionCall fc) {
- exists(Expr e |
- // a RelationalOperation which isn't compared with a Literal that using for end of read
- TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and
- not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"]
- )
-}
-
-// TODO:
-// predicate sanitizer2(FunctionCall fc) {
-// exists(Expr e |
-// // a RelationalOperation which isn't compared with a Literal that using for end of read
-// TaintTracking::localExprTaint(fc.getArgument(2), e)
-// )
-// }
-module ZlibTaint = TaintTracking::Global;
-
-import ZlibTaint::PathGraph
-
-from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
-where ZlibTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
deleted file mode 100644
index e526b4d9273..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibInflator.ql
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-zlibinflator
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * A `z_stream` Variable as a Flow source
- */
-private class ZStreamVar extends VariableAccess {
- ZStreamVar() { this.getType().hasName("z_stream") }
-}
-
-/**
- * The `inflate`/`inflateSync` functions are used in Flow sink
- *
- * `inflate(z_streamp strm, int flush)`
- *
- * `inflateSync(z_streamp strm)`
- */
-private class InflateFunction extends Function {
- InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
-}
-
-module ZlibTaintConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ZStreamVar }
-
- predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- }
-}
-
-module ZlibTaint = TaintTracking::Global;
-
-import ZlibTaint::PathGraph
-
-from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
-where ZlibTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
deleted file mode 100644
index b385523f185..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsZlibUncompress.ql
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.8
- * @precision high
- * @id cpp/user-controlled-file-zlibuncompress
- * @tags security
- * experimental
- * external/cwe/cwe-409
- */
-
-import cpp
-import semmle.code.cpp.ir.dataflow.TaintTracking
-import semmle.code.cpp.security.FlowSources
-
-/**
- * A Bytef Variable as a Flow source
- */
-private class BytefVar extends VariableAccess {
- BytefVar() { this.getType().hasName("Bytef") }
-}
-
-/**
- * The `uncompress`/`uncompress2` function is used in Flow sink
- */
-private class UncompressFunction extends Function {
- UncompressFunction() { hasGlobalName(["uncompress", "uncompress2"]) }
-}
-
-module ZlibTaintConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) { source.asExpr() instanceof BytefVar }
-
- predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- }
-}
-
-module ZlibTaint = TaintTracking::Global;
-
-import ZlibTaint::PathGraph
-
-from ZlibTaint::PathNode source, ZlibTaint::PathNode sink
-where ZlibTaint::flowPath(source, sink)
-select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll
new file mode 100644
index 00000000000..4d96e83c195
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll
@@ -0,0 +1,25 @@
+/**
+ * https://github.com/libarchive/libarchive/wiki
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * The `archive_read_new` function as a Flow source
+ * create a `archive` instance
+ */
+class Archive_read_new extends Function {
+ Archive_read_new() { this.hasGlobalName("archive_read_new") }
+}
+
+/**
+ * The `archive_read_data*` functions are used in Flow Sink
+ * [Examples](https://github.com/libarchive/libarchive/wiki/Examples)
+ */
+class Archive_read_data_block extends Function {
+ Archive_read_data_block() {
+ this.hasGlobalName(["archive_read_data_block", "archive_read_data", "archive_read_data_into_fd"])
+ }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll
new file mode 100644
index 00000000000..64eba1e7b28
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll
@@ -0,0 +1,102 @@
+/**
+ * https://github.com/richgel999/miniz
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A unsigned char Variable is used in Flow source
+ */
+class UnsignedCharVar extends VariableAccess {
+ UnsignedCharVar() { this.getType().stripType().resolveTypedefs*() instanceof UnsignedCharType }
+}
+
+/**
+ * The `mz_streamp`, `z_stream` Variables are used in Flow source
+ */
+class MzStreampVar extends VariableAccess {
+ MzStreampVar() { this.getType().hasName(["mz_streamp", "z_stream"]) }
+}
+
+/**
+ * A Char Variable is used in Flow source
+ */
+class CharVar extends VariableAccess {
+ CharVar() { this.getType().stripType().resolveTypedefs*() instanceof CharType }
+}
+
+/**
+ * A `mz_zip_archive` Variable is used in Flow source
+ */
+class MzZipArchiveVar extends VariableAccess {
+ MzZipArchiveVar() { this.getType().hasName("mz_zip_archive") }
+}
+
+/**
+ * The `mz_uncompress` functions are used in Flow Sink
+ */
+class MzUncompress extends Function {
+ MzUncompress() { this.hasGlobalName(["uncompress", "mz_uncompress", "mz_uncompress2"]) }
+}
+
+/**
+ * A `zip handle` is used in Flow source
+ */
+class MzZip extends Function {
+ MzZip() {
+ this.hasGlobalName([
+ "mz_zip_reader_open", "mz_zip_reader_open_file", "mz_zip_reader_open_file_in_memory",
+ "mz_zip_reader_open_buffer", "mz_zip_reader_entry_open"
+ ])
+ }
+}
+
+/**
+ * The `mz_inflate` functions are used in Flow Sink
+ */
+class MzInflate extends Function {
+ MzInflate() { this.hasGlobalName(["mz_inflate", "inflate"]) }
+}
+
+/**
+ * The `mz_inflateInit` functions are used in Flow Sink
+ */
+class MzInflateInit extends Function {
+ MzInflateInit() { this.hasGlobalName(["inflateInit", "mz_inflateInit"]) }
+}
+
+/**
+ * The `mz_zip_reader_extract_*` functions are used in Flow Sink
+ */
+class MzZipReaderExtract extends Function {
+ MzZipReaderExtract() {
+ this.hasGlobalName([
+ "mz_zip_reader_extract_file_to_heap", "mz_zip_reader_extract_to_heap",
+ "mz_zip_reader_extract_to_callback", "mz_zip_reader_extract_file_to_callback",
+ "mz_zip_reader_extract_to_mem", "mz_zip_reader_extract_file_to_mem",
+ "mz_zip_reader_extract_iter_read", "mz_zip_reader_extract_to_file",
+ "mz_zip_reader_extract_file_to_file"
+ ])
+ }
+}
+
+/**
+ * The `tinfl_decompress_mem_*` functions are used in Flow Sink
+ */
+class TinflDecompressMem extends Function {
+ TinflDecompressMem() {
+ this.hasGlobalName([
+ "tinfl_decompress_mem_to_callback", "tinfl_decompress_mem_to_mem",
+ "tinfl_decompress_mem_to_heap"
+ ])
+ }
+}
+
+/**
+ * The `tinfl_decompress_*` functions are used in Flow Sink
+ */
+class TinflDecompress extends Function {
+ TinflDecompress() { this.hasGlobalName(["tinfl_decompress"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll
new file mode 100644
index 00000000000..7d2280166e0
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll
@@ -0,0 +1,65 @@
+/**
+ * https://github.com/zlib-ng/minizip-ng
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * The `mz_zip_reader_create` function as a Flow source
+ * create a `mz_zip_reader` instance
+ */
+class Mz_zip_reader_create extends Function {
+ Mz_zip_reader_create() { this.hasGlobalName("mz_zip_reader_create") }
+}
+
+/**
+ * The `mz_zip_create` function as a Flow source
+ * create a `mz_zip` instance
+ */
+class Mz_zip_create extends Function {
+ Mz_zip_create() { this.hasGlobalName("mz_zip_create") }
+}
+
+/**
+ * The `mz_zip_entry` function is used in Flow source
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
+ */
+class Mz_zip_entry extends Function {
+ Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") }
+}
+
+/**
+ * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in Flow source
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
+ */
+class Mz_zip_reader_entry extends Function {
+ Mz_zip_reader_entry() {
+ this.hasGlobalName([
+ "mz_zip_reader_entry_save", "mz_zip_reader_entry_read", "mz_zip_reader_entry_save_process",
+ "mz_zip_reader_entry_save_file", "mz_zip_reader_entry_save_buffer", "mz_zip_reader_save_all"
+ ])
+ }
+}
+
+/**
+ * A `unzFile` Variable as a Flow source
+ */
+class UnzFileVar extends VariableAccess {
+ UnzFileVar() { this.getType().hasName("unzFile") }
+}
+
+/**
+ * The `UnzOpen` function as a Flow source
+ */
+class UnzOpenFunction extends Function {
+ UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
+}
+
+/**
+ * The `unzReadCurrentFile` function is used in Flow sink
+ */
+class UnzReadCurrentFileFunction extends Function {
+ UnzReadCurrentFileFunction() { this.hasGlobalName(["unzReadCurrentFile"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
new file mode 100644
index 00000000000..84b6a311f86
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
@@ -0,0 +1,37 @@
+/**
+ * https://github.com/tukaani-project/xz
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A `lzma_stream` Variable as a Flow source
+ */
+class LzmaStreamVar extends VariableAccess {
+ LzmaStreamVar() { this.getType().hasName("lzma_stream") }
+}
+
+/**
+ * The `lzma_*_decoder` function is used as a required condition for decompression
+ */
+class LzmaDecoderFunction extends Function {
+ LzmaDecoderFunction() {
+ this.hasGlobalName(["lzma_stream_decoder", "lzma_auto_decoder", "lzma_alone_decoder"])
+ }
+}
+
+/**
+ * The `lzma_code` function is used in Flow sink
+ */
+class LzmaCodeFunction extends Function {
+ LzmaCodeFunction() { this.hasGlobalName(["lzma_code"]) }
+}
+
+/**
+ * The `lzma_stream_buffer_decode` function is used in Flow sink
+ */
+class LzmaStreamBufferDecodeFunction extends Function {
+ LzmaStreamBufferDecodeFunction() { this.hasGlobalName(["lzma_stream_buffer_decode"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll
new file mode 100644
index 00000000000..902903efb78
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll
@@ -0,0 +1,57 @@
+/**
+ * https://github.com/facebook/zstd/blob/dev/examples/streaming_decompression.c
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.commons.File
+
+/**
+ * A ZSTD_inBuffer Variable as a Flow source
+ */
+class ZSTDinBufferVar extends VariableAccess {
+ ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
+}
+
+/**
+ * A ZSTD_inBuffer_s Variable as a Flow source
+ */
+class ZSTDinBufferSVar extends VariableAccess {
+ ZSTDinBufferSVar() { this.getType().hasName("ZSTD_inBuffer_s") }
+}
+
+/**
+ * The `ZSTD_decompress` function is used in Flow sink
+ */
+class ZSTDDecompressFunction extends Function {
+ ZSTDDecompressFunction() { this.hasGlobalName(["ZSTD_decompress"]) }
+}
+
+/**
+ * The `ZSTD_decompressDCtx` function is used in Flow sink
+ */
+class ZSTDDecompressDCtxFunction extends Function {
+ ZSTDDecompressDCtxFunction() { this.hasGlobalName(["ZSTD_decompressDCtx"]) }
+}
+
+/**
+ * The `ZSTD_decompressStream` function is used in Flow sink
+ */
+class ZSTDDecompressStreamFunction extends Function {
+ ZSTDDecompressStreamFunction() { this.hasGlobalName(["ZSTD_decompressStream"]) }
+}
+
+/**
+ * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ */
+class ZSTDDecompressUsingDictFunction extends Function {
+ ZSTDDecompressUsingDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+}
+
+/**
+ * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ */
+class ZSTDDecompressUsingDDictFunction extends Function {
+ ZSTDDecompressUsingDDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll
new file mode 100644
index 00000000000..306130ac8f2
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll
@@ -0,0 +1,75 @@
+/**
+ * https://www.zlib.net/
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A `gzFile` Variable as a Flow source
+ */
+class GzFileVar extends VariableAccess {
+ GzFileVar() { this.getType().hasName("gzFile") }
+}
+
+/**
+ * The `gzopen` function as a Flow source
+ *
+ * `gzopen(const char *path, const char *mode)`
+ */
+class GzopenFunction extends Function {
+ GzopenFunction() { this.hasGlobalName("gzopen") }
+}
+
+/**
+ * The `gzdopen` function as a Flow source
+ *
+ * `gzdopen(int fd, const char *mode)`
+ */
+class GzdopenFunction extends Function {
+ GzdopenFunction() { this.hasGlobalName("gzdopen") }
+}
+
+/**
+ * The `gzfread` function is used in Flow sink
+ *
+ * `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)`
+ */
+class GzFreadFunction extends Function {
+ GzFreadFunction() { this.hasGlobalName("gzfread") }
+}
+
+/**
+ * The `gzgets` function is used in Flow sink.
+ *
+ * `gzgets(gzFile file, char *buf, int len)`
+ */
+class GzGetsFunction extends Function {
+ GzGetsFunction() { this.hasGlobalName("gzgets") }
+}
+
+/**
+ * The `gzread` function is used in Flow sink
+ *
+ * `gzread(gzFile file, voidp buf, unsigned len)`
+ */
+class GzReadFunction extends Function {
+ GzReadFunction() { this.hasGlobalName("gzread") }
+}
+
+predicate isSource(DataFlow::Node source) {
+ exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
+ fc.getArgument(0) = source.asExpr() and
+ // arg 0 can be a path string whichwe must do following check
+ not fc.getArgument(0).isConstant()
+ )
+ or
+ // IDK whether it is good to use all file decriptors function returns as source or not
+ // because we can do more sanitization from fd function sources
+ exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
+ fc.getArgument(0) = source.asExpr()
+ )
+ or
+ source.asExpr() instanceof GzFileVar
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll
new file mode 100644
index 00000000000..b67caecfcbd
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll
@@ -0,0 +1,25 @@
+/**
+ * https://www.zlib.net/
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A `z_stream` Variable as a Flow source
+ */
+class ZStreamVar extends VariableAccess {
+ ZStreamVar() { this.getType().hasName("z_stream") }
+}
+
+/**
+ * The `inflate`/`inflateSync` functions are used in Flow sink
+ *
+ * `inflate(z_streamp strm, int flush)`
+ *
+ * `inflateSync(z_streamp strm)`
+ */
+class InflateFunction extends Function {
+ InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
new file mode 100644
index 00000000000..3518719bc87
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
@@ -0,0 +1,21 @@
+/**
+ * https://www.zlib.net/
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.security.FlowSources
+
+/**
+ * A Bytef Variable as a Flow source
+ */
+class BytefVar extends VariableAccess {
+ BytefVar() { this.getType().hasName("Bytef") }
+}
+
+/**
+ * The `uncompress`/`uncompress2` function is used in Flow sink
+ */
+class UncompressFunction extends Function {
+ UncompressFunction() { hasGlobalName(["uncompress", "uncompress2"]) }
+}
From a5363286f1a184914aaa43cdee9f937ec6def845 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Fri, 7 Jun 2024 05:37:58 +0200
Subject: [PATCH 013/404] add implicit this
---
.../Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
index 3518719bc87..1f33b065409 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
@@ -17,5 +17,5 @@ class BytefVar extends VariableAccess {
* The `uncompress`/`uncompress2` function is used in Flow sink
*/
class UncompressFunction extends Function {
- UncompressFunction() { hasGlobalName(["uncompress", "uncompress2"]) }
+ UncompressFunction() { this.hasGlobalName(["uncompress", "uncompress2"]) }
}
From 273848c8796f02c4b583c99144b6bc4ca9d7af31 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Fri, 7 Jun 2024 05:40:17 +0200
Subject: [PATCH 014/404] remove old comments
---
.../CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
index 8506dccaea7..d75eae39056 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
@@ -115,12 +115,10 @@ module DecompressionTaintConfig implements DataFlow::StateConfigSig {
(
exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
fc.getArgument(0) = source.asExpr() and
- // arg 0 can be a path string whichwe must do following check
+ // arg 0 can be a path string which we must do following check
not fc.getArgument(0).isConstant()
)
or
- // IDK whether it is good to use all file decriptors function returns as source or not
- // because we can do more sanitization from fd function sources
exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
fc.getArgument(0) = source.asExpr()
)
From 11a416ea7c80493d44cbd6e02c218cb2f0164ead Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Thu, 13 Jun 2024 03:30:07 +0200
Subject: [PATCH 015/404] add FlowSources as a common source for all sinks, so
we don't need States anymore
---
.../DecompressionBombs.ql | 350 ++++++------------
.../CWE/CWE-409-DecompressionBomb/XZ.qll | 8 -
2 files changed, 104 insertions(+), 254 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
index d75eae39056..03eab33a6ce 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
@@ -27,298 +27,156 @@ import ZlibUncompress
import ZlibInflator
import Brotli
-module DecompressionTaintConfig implements DataFlow::StateConfigSig {
- class FlowState = DataFlow::FlowState;
+module DecompressionTaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
- predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- (
- exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction | fc = source.asExpr())
- or
- exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
- or
- source.asExpr() instanceof PointerVar
- or
- source.asExpr() instanceof Uint8Var
- ) and
- state = "brotli"
- or
- (
- source.asExpr() instanceof BzStreamVar
- or
- source.asExpr() instanceof BzFileVar
- or
- exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
- ) and
- state = "bzip2"
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_new |
- fc.getArgument(0) = source.asExpr()
- ) and
- state = "libarchive"
- or
- (
- source.asExpr() instanceof UnsignedCharVar
- or
- source.asExpr() instanceof PointerVar
- or
- source.asExpr() instanceof CharVar
- or
- source.asExpr() instanceof MzZipArchiveVar
- or
- source.asExpr() instanceof MzStreampVar
- or
- source.asDefiningArgument() =
- any(Call call | call.getTarget() instanceof MzInflateInit).getArgument(0)
- or
- source.asDefiningArgument() =
- any(Call call | call.getTarget() instanceof MzZip).getArgument(0)
- ) and
- state = "libminiz"
- or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof AllocationFunction | fc = source.asExpr())
- or
- exists(FunctionCall fc | fopenCall(fc) | fc = source.asExpr())
- or
- source.asExpr() instanceof ZSTDinBufferSVar
- or
- source.asExpr() instanceof ZSTDinBufferVar
- ) and
- state = "zstd"
- or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
- fc.getArgument(0) = source.asExpr()
- )
- or
- source.asExpr() instanceof UnzFileVar
- ) and
- state = "unzFile"
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_create |
- fc = source.asExpr() and
- state = "mz_zip_reader"
+ predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr()
)
or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_create |
- fc = source.asExpr() and
- state = "mz_zip"
+ exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
+ fc.getArgument(1) = sink.asExpr()
)
or
- (
- source.asExpr() instanceof LzmaStreamVar
- or
- source.asExpr() instanceof Uint8Var
- ) and
- state = "xz"
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
- fc.getArgument(0) = source.asExpr() and
- // arg 0 can be a path string which we must do following check
- not fc.getArgument(0).isConstant()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
- fc.getArgument(0) = source.asExpr()
- )
- or
- source.asExpr() instanceof GzFileVar
- ) and
- state = "zlibgzopen"
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
+ fc.getArgument(1) = sink.asExpr()
+ )
or
- source.asExpr() instanceof ZStreamVar and state = "zlifinflator"
- or
- source.asExpr() instanceof BytefVar and state = "zlibuncompress"
- }
-
- predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- (
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- ) and
- state = "brotli"
- or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- ) and
- state = "bzip2"
+ exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
or
exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
- fc.getArgument(0) = sink.asExpr() and
- state = "libarchive"
+ fc.getArgument(0) = sink.asExpr()
)
or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
- fc.getArgument(0) = sink.asExpr()
- )
- ) and
- state = "libminiz"
+ exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
+ fc.getArgument(0) = sink.asExpr()
+ )
or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- ) and
- state = "zstd"
+ exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
+ fc.getArgument(1) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
+ fc.getArgument(2) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
+ fc.getArgument(3) = sink.asExpr()
+ )
or
exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
- fc.getArgument(0) = sink.asExpr() and
- state = "unzFile"
+ fc.getArgument(0) = sink.asExpr()
)
or
exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
- fc.getArgument(1) = sink.asExpr() and
- state = "mz_zip_reader"
+ fc.getArgument(1) = sink.asExpr()
)
or
exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
- fc.getArgument(1) = sink.asExpr() and
- state = "mz_zip"
+ fc.getArgument(1) = sink.asExpr()
)
or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- ) and
- state = "xz" and
- exists(FunctionCall fc2 | fc2.getTarget() instanceof LzmaDecoderFunction)
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
+ fc.getArgument(1) = sink.asExpr()
+ )
or
- (
- exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
- sink.asExpr() = fc.getArgument(3)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
- sink.asExpr() = fc.getArgument(0)
- )
- ) and
- state = "zlibgzopen"
+ exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
+ fc.getArgument(0) = sink.asExpr()
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
+ sink.asExpr() = fc.getArgument(3)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
+ sink.asExpr() = fc.getArgument(0)
+ )
or
exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
fc.getArgument(0) = sink.asExpr()
- ) and
- state = "zlifinflator"
+ )
or
exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
fc.getArgument(0) = sink.asExpr()
- ) and
- state = "zlibuncompress"
+ )
}
- predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
+ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc and
- state1 = "" and
- state2 = "unzFile"
+ node2.asExpr() = fc
)
or
exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1) and
- state1 = "" and
- state2 = "mz_zip_reader"
+ node2.asExpr() = fc.getArgument(1)
)
or
exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1) and
- state1 = "" and
- state2 = "mz_zip"
+ node2.asExpr() = fc.getArgument(1)
)
or
- (
- exists(FunctionCall fc |
- fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
- |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
- node1.asExpr() = fc.getArgument(3) and
- node2.asExpr() = fc.getArgument(0)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
- node1.asExpr() = fc.getArgument(0) and
- node1.asExpr() = fc.getArgument(1)
- )
- ) and
- state1 = "" and
- state2 = "gzopen"
+ exists(FunctionCall fc |
+ fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
+ |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
+ node1.asExpr() = fc.getArgument(3) and
+ node2.asExpr() = fc.getArgument(0)
+ )
+ or
+ exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
+ node1.asExpr() = fc.getArgument(0) and
+ node1.asExpr() = fc.getArgument(1)
+ )
}
-
- predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { none() }
}
-module DecompressionTaint = TaintTracking::GlobalWithState;
+module DecompressionTaint = TaintTracking::Global;
import DecompressionTaint::PathGraph
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
index 84b6a311f86..621558e07fd 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
@@ -13,14 +13,6 @@ class LzmaStreamVar extends VariableAccess {
LzmaStreamVar() { this.getType().hasName("lzma_stream") }
}
-/**
- * The `lzma_*_decoder` function is used as a required condition for decompression
- */
-class LzmaDecoderFunction extends Function {
- LzmaDecoderFunction() {
- this.hasGlobalName(["lzma_stream_decoder", "lzma_auto_decoder", "lzma_alone_decoder"])
- }
-}
/**
* The `lzma_code` function is used in Flow sink
From 13f697c0569c2266ec10f0d47979ee0a80c82a0c Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Tue, 25 Jun 2024 17:31:40 +0200
Subject: [PATCH 016/404] relocate the query
---
.../Security/CWE/CWE-409}/Brotli.qll | 0
.../Security/CWE/CWE-409}/Bzip2.qll | 0
.../Security/CWE/CWE-409}/DecompressionBomb.qhelp | 0
.../Security/CWE/CWE-409}/DecompressionBombs.ql | 0
.../Security/CWE/CWE-409}/LibArchive.qll | 0
.../Security/CWE/CWE-409}/LibMiniz.qll | 0
.../Security/CWE/CWE-409}/MiniZip.qll | 0
.../Security/CWE/CWE-409}/XZ.qll | 0
.../Security/CWE/CWE-409}/ZSTD.qll | 0
.../Security/CWE/CWE-409}/ZlibGzopen.qll | 0
.../Security/CWE/CWE-409}/ZlibInflator.qll | 0
.../Security/CWE/CWE-409}/ZlibUncompress.qll | 0
.../Security/CWE/CWE-409}/example_bad.cpp | 0
.../Security/CWE/CWE-409}/example_good.cpp | 0
14 files changed, 0 insertions(+), 0 deletions(-)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/Brotli.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/Bzip2.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/DecompressionBomb.qhelp (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/DecompressionBombs.ql (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/LibArchive.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/LibMiniz.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/MiniZip.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/XZ.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/ZSTD.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/ZlibGzopen.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/ZlibInflator.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/ZlibUncompress.qll (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/example_bad.cpp (100%)
rename cpp/ql/src/experimental/{Security/CWE/CWE-409-DecompressionBomb => query-tests/Security/CWE/CWE-409}/example_good.cpp (100%)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Brotli.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bzip2.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qhelp
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBomb.qhelp
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qhelp
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombs.ql
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibArchive.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/LibMiniz.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/MiniZip.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/XZ.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZSTD.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibGzopen.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibInflator.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/ZlibUncompress.qll
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/example_bad.cpp
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_bad.cpp
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/example_bad.cpp
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/example_good.cpp
similarity index 100%
rename from cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/example_good.cpp
rename to cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/example_good.cpp
From 656dc4e2760a36c3eab87475d5e450f4fa606096 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Tue, 25 Jun 2024 18:09:27 +0200
Subject: [PATCH 017/404] use abstract class for decompression sinks
---
.../Security/CWE/CWE-409/Brotli.qll | 29 ++--
.../Security/CWE/CWE-409/Bzip2.qll | 35 ++---
.../CWE/CWE-409/DecompressionBomb.qll | 8 ++
.../CWE/CWE-409/DecompressionBombs.ql | 129 +-----------------
.../Security/CWE/CWE-409/LibArchive.qll | 15 +-
.../Security/CWE/CWE-409/LibMiniz.qll | 70 ++++------
.../Security/CWE/CWE-409/MiniZip.qll | 45 ++----
.../query-tests/Security/CWE/CWE-409/XZ.qll | 21 ++-
.../query-tests/Security/CWE/CWE-409/ZSTD.qll | 45 +++---
.../Security/CWE/CWE-409/ZlibGzopen.qll | 84 +++++-------
.../Security/CWE/CWE-409/ZlibInflator.qll | 14 +-
.../Security/CWE/CWE-409/ZlibUncompress.qll | 14 +-
12 files changed, 148 insertions(+), 361 deletions(-)
create mode 100644 cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll
index 46314826a62..5c19e53df07 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll
@@ -6,33 +6,22 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.commons.File
+import DecompressionBomb
/**
- * A Pointer Variable is used in Flow source
+ * The `BrotliDecoderDecompress` function is used in flow sink. * Ref: https://www.brotli.org/decode.html#af68
*/
-class PointerVar extends VariableAccess {
- PointerVar() { this.getType() instanceof PointerType }
-}
-
-/**
- * A Pointer Variable is used in Flow source
- */
-class Uint8Var extends VariableAccess {
- Uint8Var() { this.getType() instanceof UInt8_t }
-}
-
-/**
- * The `BrotliDecoderDecompress` function is used in Flow sink
- * Ref: https://www.brotli.org/decode.html#af68
- */
-class BrotliDecoderDecompressFunction extends Function {
+class BrotliDecoderDecompressFunction extends DecompressionFunction {
BrotliDecoderDecompressFunction() { this.hasGlobalName(["BrotliDecoderDecompress"]) }
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * The `BrotliDecoderDecompressStream` function is used in Flow sink
- * Ref: https://www.brotli.org/decode.html#a234
+ * The `BrotliDecoderDecompressStream` function is used in flow sink. * Ref: https://www.brotli.org/decode.html#a234
*/
-class BrotliDecoderDecompressStreamFunction extends Function {
+class BrotliDecoderDecompressStreamFunction extends DecompressionFunction {
BrotliDecoderDecompressStreamFunction() { this.hasGlobalName(["BrotliDecoderDecompressStream"]) }
+
+ override int getArchiveParameterIndex() { result = 2 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll
index 662424b022a..7f69aa494c6 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Bzip2.qll
@@ -6,45 +6,40 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.commons.File
-
-/**
- * A `bz_stream` Variable as a Flow source
- */
-class BzStreamVar extends VariableAccess {
- BzStreamVar() { this.getType().hasName("bz_stream") }
-}
-
-/**
- * A `BZFILE` Variable as a Flow source
- */
-class BzFileVar extends VariableAccess {
- BzFileVar() { this.getType().hasName("BZFILE") }
-}
+import DecompressionBomb
/**
* The `BZ2_bzDecompress` function as a Flow source
*/
-class BZ2BzDecompressFunction extends Function {
+class BZ2BzDecompressFunction extends DecompressionFunction {
BZ2BzDecompressFunction() { this.hasGlobalName(["BZ2_bzDecompress"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
* The `BZ2_bzReadOpen` function
*/
-class BZ2BzReadOpenFunction extends Function {
+class BZ2BzReadOpenFunction extends DecompressionFunction {
BZ2BzReadOpenFunction() { this.hasGlobalName(["BZ2_bzReadOpen"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
- * The `BZ2_bzRead` function is used in Flow sink
+ * The `BZ2_bzRead` function is used in flow sink.
*/
-class BZ2BzReadFunction extends Function {
+class BZ2BzReadFunction extends DecompressionFunction {
BZ2BzReadFunction() { this.hasGlobalName("BZ2_bzRead") }
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * The `BZ2_bzBuffToBuffDecompress` function is used in Flow sink
+ * The `BZ2_bzBuffToBuffDecompress` function is used in flow sink.
*/
-class BZ2BzBuffToBuffDecompressFunction extends Function {
+class BZ2BzBuffToBuffDecompressFunction extends DecompressionFunction {
BZ2BzBuffToBuffDecompressFunction() { this.hasGlobalName("BZ2_bzBuffToBuffDecompress") }
+
+ override int getArchiveParameterIndex() { result = 2 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
new file mode 100644
index 00000000000..2cca1453d2f
--- /dev/null
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
@@ -0,0 +1,8 @@
+import cpp
+
+/**
+ * The Decompression Sink instances, extend this class to defind new decompression sinks.
+ */
+abstract class DecompressionFunction extends Function {
+ abstract int getArchiveParameterIndex();
+}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
index 03eab33a6ce..b7eda35f2c7 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
@@ -15,124 +15,16 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.commons.File
-import Bzip2
-import Brotli
-import LibArchive
-import LibMiniz
-import ZSTD
import MiniZip
-import XZ
import ZlibGzopen
-import ZlibUncompress
-import ZlibInflator
-import Brotli
+import DecompressionBomb
module DecompressionTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
predicate isSink(DataFlow::Node sink) {
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BrotliDecoderDecompressFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzDecompressFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzReadFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof BZ2BzBuffToBuffDecompressFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Archive_read_data_block |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzUncompress |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzZipReaderExtract |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof MzInflate |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompress |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof TinflDecompressMem |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressDCtxFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressStreamFunction |
- fc.getArgument(2) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDictFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof ZSTDDecompressUsingDDictFunction |
- fc.getArgument(3) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof UnzReadCurrentFileFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaStreamBufferDecodeFunction |
- fc.getArgument(1) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof LzmaCodeFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
- sink.asExpr() = fc.getArgument(3)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
- sink.asExpr() = fc.getArgument(0)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof InflateFunction |
- fc.getArgument(0) = sink.asExpr()
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction |
- fc.getArgument(0) = sink.asExpr()
+ exists(FunctionCall fc, DecompressionFunction f | fc.getTarget() = f |
+ fc.getArgument(f.getArchiveParameterIndex()) = sink.asExpr()
)
}
@@ -158,21 +50,6 @@ module DecompressionTaintConfig implements DataFlow::ConfigSig {
node1.asExpr() = fc.getArgument(0) and
node2.asExpr() = fc
)
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzReadFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzFreadFunction |
- node1.asExpr() = fc.getArgument(3) and
- node2.asExpr() = fc.getArgument(0)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof GzGetsFunction |
- node1.asExpr() = fc.getArgument(0) and
- node1.asExpr() = fc.getArgument(1)
- )
}
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll
index 4d96e83c195..e5124006504 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll
@@ -5,21 +5,16 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * The `archive_read_new` function as a Flow source
- * create a `archive` instance
- */
-class Archive_read_new extends Function {
- Archive_read_new() { this.hasGlobalName("archive_read_new") }
-}
-
-/**
- * The `archive_read_data*` functions are used in Flow Sink
+ * The `archive_read_data*` functions are used in flow sink.
* [Examples](https://github.com/libarchive/libarchive/wiki/Examples)
*/
-class Archive_read_data_block extends Function {
+class Archive_read_data_block extends DecompressionFunction {
Archive_read_data_block() {
this.hasGlobalName(["archive_read_data_block", "archive_read_data", "archive_read_data_into_fd"])
}
+
+ override int getArchiveParameterIndex() { result = 0 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll
index 64eba1e7b28..0025ecca97d 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibMiniz.qll
@@ -5,72 +5,44 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * A unsigned char Variable is used in Flow source
+ * The `mz_uncompress` functions are used in flow sink.
*/
-class UnsignedCharVar extends VariableAccess {
- UnsignedCharVar() { this.getType().stripType().resolveTypedefs*() instanceof UnsignedCharType }
-}
-
-/**
- * The `mz_streamp`, `z_stream` Variables are used in Flow source
- */
-class MzStreampVar extends VariableAccess {
- MzStreampVar() { this.getType().hasName(["mz_streamp", "z_stream"]) }
-}
-
-/**
- * A Char Variable is used in Flow source
- */
-class CharVar extends VariableAccess {
- CharVar() { this.getType().stripType().resolveTypedefs*() instanceof CharType }
-}
-
-/**
- * A `mz_zip_archive` Variable is used in Flow source
- */
-class MzZipArchiveVar extends VariableAccess {
- MzZipArchiveVar() { this.getType().hasName("mz_zip_archive") }
-}
-
-/**
- * The `mz_uncompress` functions are used in Flow Sink
- */
-class MzUncompress extends Function {
+class MzUncompress extends DecompressionFunction {
MzUncompress() { this.hasGlobalName(["uncompress", "mz_uncompress", "mz_uncompress2"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
* A `zip handle` is used in Flow source
*/
-class MzZip extends Function {
+class MzZip extends DecompressionFunction {
MzZip() {
this.hasGlobalName([
"mz_zip_reader_open", "mz_zip_reader_open_file", "mz_zip_reader_open_file_in_memory",
"mz_zip_reader_open_buffer", "mz_zip_reader_entry_open"
])
}
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * The `mz_inflate` functions are used in Flow Sink
+ * The `mz_inflate` functions are used in flow sink.
*/
-class MzInflate extends Function {
+class MzInflate extends DecompressionFunction {
MzInflate() { this.hasGlobalName(["mz_inflate", "inflate"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
- * The `mz_inflateInit` functions are used in Flow Sink
+ * The `mz_zip_reader_extract_*` functions are used in flow sink.
*/
-class MzInflateInit extends Function {
- MzInflateInit() { this.hasGlobalName(["inflateInit", "mz_inflateInit"]) }
-}
-
-/**
- * The `mz_zip_reader_extract_*` functions are used in Flow Sink
- */
-class MzZipReaderExtract extends Function {
+class MzZipReaderExtract extends DecompressionFunction {
MzZipReaderExtract() {
this.hasGlobalName([
"mz_zip_reader_extract_file_to_heap", "mz_zip_reader_extract_to_heap",
@@ -80,23 +52,29 @@ class MzZipReaderExtract extends Function {
"mz_zip_reader_extract_file_to_file"
])
}
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * The `tinfl_decompress_mem_*` functions are used in Flow Sink
+ * The `tinfl_decompress_mem_*` functions are used in flow sink.
*/
-class TinflDecompressMem extends Function {
+class TinflDecompressMem extends DecompressionFunction {
TinflDecompressMem() {
this.hasGlobalName([
"tinfl_decompress_mem_to_callback", "tinfl_decompress_mem_to_mem",
"tinfl_decompress_mem_to_heap"
])
}
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
- * The `tinfl_decompress_*` functions are used in Flow Sink
+ * The `tinfl_decompress_*` functions are used in flow sink.
*/
-class TinflDecompress extends Function {
+class TinflDecompress extends DecompressionFunction {
TinflDecompress() { this.hasGlobalName(["tinfl_decompress"]) }
+
+ override int getArchiveParameterIndex() { result = 1 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
index 7d2280166e0..4bfa9a13b2c 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
@@ -5,61 +5,36 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * The `mz_zip_reader_create` function as a Flow source
- * create a `mz_zip_reader` instance
- */
-class Mz_zip_reader_create extends Function {
- Mz_zip_reader_create() { this.hasGlobalName("mz_zip_reader_create") }
-}
-
-/**
- * The `mz_zip_create` function as a Flow source
- * create a `mz_zip` instance
- */
-class Mz_zip_create extends Function {
- Mz_zip_create() { this.hasGlobalName("mz_zip_create") }
-}
-
-/**
- * The `mz_zip_entry` function is used in Flow source
+ * The `mz_zip_entry` function is used in flow source.
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
*/
-class Mz_zip_entry extends Function {
+class Mz_zip_entry extends DecompressionFunction {
Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") }
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in Flow source
+ * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in flow source.
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
*/
-class Mz_zip_reader_entry extends Function {
+class Mz_zip_reader_entry extends DecompressionFunction {
Mz_zip_reader_entry() {
this.hasGlobalName([
"mz_zip_reader_entry_save", "mz_zip_reader_entry_read", "mz_zip_reader_entry_save_process",
"mz_zip_reader_entry_save_file", "mz_zip_reader_entry_save_buffer", "mz_zip_reader_save_all"
])
}
+
+ override int getArchiveParameterIndex() { result = 1 }
}
/**
- * A `unzFile` Variable as a Flow source
- */
-class UnzFileVar extends VariableAccess {
- UnzFileVar() { this.getType().hasName("unzFile") }
-}
-
-/**
- * The `UnzOpen` function as a Flow source
+ * The `UnzOpen` function as a flow source.
*/
class UnzOpenFunction extends Function {
UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
}
-
-/**
- * The `unzReadCurrentFile` function is used in Flow sink
- */
-class UnzReadCurrentFileFunction extends Function {
- UnzReadCurrentFileFunction() { this.hasGlobalName(["unzReadCurrentFile"]) }
-}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll
index 621558e07fd..a0b17388b93 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/XZ.qll
@@ -5,25 +5,22 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * A `lzma_stream` Variable as a Flow source
+ * The `lzma_code` function is used in flow sink.
*/
-class LzmaStreamVar extends VariableAccess {
- LzmaStreamVar() { this.getType().hasName("lzma_stream") }
-}
-
-
-/**
- * The `lzma_code` function is used in Flow sink
- */
-class LzmaCodeFunction extends Function {
+class LzmaCodeFunction extends DecompressionFunction {
LzmaCodeFunction() { this.hasGlobalName(["lzma_code"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
- * The `lzma_stream_buffer_decode` function is used in Flow sink
+ * The `lzma_stream_buffer_decode` function is used in flow sink.
*/
-class LzmaStreamBufferDecodeFunction extends Function {
+class LzmaStreamBufferDecodeFunction extends DecompressionFunction {
LzmaStreamBufferDecodeFunction() { this.hasGlobalName(["lzma_stream_buffer_decode"]) }
+
+ override int getArchiveParameterIndex() { result = 1 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll
index 902903efb78..c34170df625 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll
@@ -6,52 +6,49 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.commons.File
+import DecompressionBomb
/**
- * A ZSTD_inBuffer Variable as a Flow source
+ * The `ZSTD_decompress` function is used in flow sink.
*/
-class ZSTDinBufferVar extends VariableAccess {
- ZSTDinBufferVar() { this.getType().hasName("ZSTD_inBuffer") }
-}
-
-/**
- * A ZSTD_inBuffer_s Variable as a Flow source
- */
-class ZSTDinBufferSVar extends VariableAccess {
- ZSTDinBufferSVar() { this.getType().hasName("ZSTD_inBuffer_s") }
-}
-
-/**
- * The `ZSTD_decompress` function is used in Flow sink
- */
-class ZSTDDecompressFunction extends Function {
+class ZSTDDecompressFunction extends DecompressionFunction {
ZSTDDecompressFunction() { this.hasGlobalName(["ZSTD_decompress"]) }
+
+ override int getArchiveParameterIndex() { result = 2 }
}
/**
- * The `ZSTD_decompressDCtx` function is used in Flow sink
+ * The `ZSTD_decompressDCtx` function is used in flow sink.
*/
-class ZSTDDecompressDCtxFunction extends Function {
+class ZSTDDecompressDCtxFunction extends DecompressionFunction {
ZSTDDecompressDCtxFunction() { this.hasGlobalName(["ZSTD_decompressDCtx"]) }
+
+ override int getArchiveParameterIndex() { result = 3 }
}
/**
- * The `ZSTD_decompressStream` function is used in Flow sink
+ * The `ZSTD_decompressStream` function is used in flow sink.
*/
-class ZSTDDecompressStreamFunction extends Function {
+class ZSTDDecompressStreamFunction extends DecompressionFunction {
ZSTDDecompressStreamFunction() { this.hasGlobalName(["ZSTD_decompressStream"]) }
+
+ override int getArchiveParameterIndex() { result = 2 }
}
/**
- * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ * The `ZSTD_decompress_usingDDict` function is used in flow sink.
*/
-class ZSTDDecompressUsingDictFunction extends Function {
+class ZSTDDecompressUsingDictFunction extends DecompressionFunction {
ZSTDDecompressUsingDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+
+ override int getArchiveParameterIndex() { result = 3 }
}
/**
- * The `ZSTD_decompress_usingDDict` function is used in Flow sink
+ * The `ZSTD_decompress_usingDDict` function is used in flow sink.
*/
-class ZSTDDecompressUsingDDictFunction extends Function {
+class ZSTDDecompressUsingDDictFunction extends DecompressionFunction {
ZSTDDecompressUsingDDictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
+
+ override int getArchiveParameterIndex() { result = 3 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
index 306130ac8f2..df2381220fd 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
@@ -5,25 +5,43 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * A `gzFile` Variable as a Flow source
- */
-class GzFileVar extends VariableAccess {
- GzFileVar() { this.getType().hasName("gzFile") }
-}
-
-/**
- * The `gzopen` function as a Flow source
+ * The `gzfread` function is used in flow sink.
*
- * `gzopen(const char *path, const char *mode)`
+ * `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)`
*/
-class GzopenFunction extends Function {
- GzopenFunction() { this.hasGlobalName("gzopen") }
+class GzFreadFunction extends DecompressionFunction {
+ GzFreadFunction() { this.hasGlobalName("gzfread") }
+
+ override int getArchiveParameterIndex() { result = 3 }
}
/**
- * The `gzdopen` function as a Flow source
+ * The `gzgets` function is used in flow sink.
+ *
+ * `gzgets(gzFile file, char *buf, int len)`
+ */
+class GzGetsFunction extends DecompressionFunction {
+ GzGetsFunction() { this.hasGlobalName("gzgets") }
+
+ override int getArchiveParameterIndex() { result = 0 }
+}
+
+/**
+ * The `gzread` function is used in flow sink.
+ *
+ * `gzread(gzFile file, voidp buf, unsigned len)`
+ */
+class GzReadFunction extends DecompressionFunction {
+ GzReadFunction() { this.hasGlobalName("gzread") }
+
+ override int getArchiveParameterIndex() { result = 1 }
+}
+
+/**
+ * The `gzdopen` function.
*
* `gzdopen(int fd, const char *mode)`
*/
@@ -32,44 +50,10 @@ class GzdopenFunction extends Function {
}
/**
- * The `gzfread` function is used in Flow sink
+ * The `gzopen` function.
*
- * `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)`
+ * `gzopen(const char *path, const char *mode)`
*/
-class GzFreadFunction extends Function {
- GzFreadFunction() { this.hasGlobalName("gzfread") }
-}
-
-/**
- * The `gzgets` function is used in Flow sink.
- *
- * `gzgets(gzFile file, char *buf, int len)`
- */
-class GzGetsFunction extends Function {
- GzGetsFunction() { this.hasGlobalName("gzgets") }
-}
-
-/**
- * The `gzread` function is used in Flow sink
- *
- * `gzread(gzFile file, voidp buf, unsigned len)`
- */
-class GzReadFunction extends Function {
- GzReadFunction() { this.hasGlobalName("gzread") }
-}
-
-predicate isSource(DataFlow::Node source) {
- exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction |
- fc.getArgument(0) = source.asExpr() and
- // arg 0 can be a path string whichwe must do following check
- not fc.getArgument(0).isConstant()
- )
- or
- // IDK whether it is good to use all file decriptors function returns as source or not
- // because we can do more sanitization from fd function sources
- exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction |
- fc.getArgument(0) = source.asExpr()
- )
- or
- source.asExpr() instanceof GzFileVar
+class GzopenFunction extends Function {
+ GzopenFunction() { this.hasGlobalName("gzopen") }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
index b67caecfcbd..1897b8e09d3 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
@@ -5,21 +5,17 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * A `z_stream` Variable as a Flow source
- */
-class ZStreamVar extends VariableAccess {
- ZStreamVar() { this.getType().hasName("z_stream") }
-}
-
-/**
- * The `inflate`/`inflateSync` functions are used in Flow sink
+ * The `inflate` and `inflateSync` functions are used in flow sink.
*
* `inflate(z_streamp strm, int flush)`
*
* `inflateSync(z_streamp strm)`
*/
-class InflateFunction extends Function {
+class InflateFunction extends DecompressionFunction {
InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll
index 1f33b065409..f69c71ec01c 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll
@@ -5,17 +5,13 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
+import DecompressionBomb
/**
- * A Bytef Variable as a Flow source
+ * The `uncompress`/`uncompress2` function is used in flow sink.
*/
-class BytefVar extends VariableAccess {
- BytefVar() { this.getType().hasName("Bytef") }
-}
-
-/**
- * The `uncompress`/`uncompress2` function is used in Flow sink
- */
-class UncompressFunction extends Function {
+class UncompressFunction extends DecompressionFunction {
UncompressFunction() { this.hasGlobalName(["uncompress", "uncompress2"]) }
+
+ override int getArchiveParameterIndex() { result = 0 }
}
From 361ad6be6a663c04c4d6c6b5777129fc194c5be2 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Wed, 26 Jun 2024 12:45:31 +0200
Subject: [PATCH 018/404] use abstract class for decompression flow steps
---
.../CWE/CWE-409/DecompressionBomb.qll | 10 ++++-
.../CWE/CWE-409/DecompressionBombs.ql | 22 +---------
.../Security/CWE/CWE-409/MiniZip.qll | 41 ++++++++++++++++++-
.../Security/CWE/CWE-409/ZlibGzopen.qll | 22 ++++++++--
4 files changed, 67 insertions(+), 28 deletions(-)
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
index 2cca1453d2f..ecc32a9ba0d 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
@@ -1,8 +1,16 @@
import cpp
+import semmle.code.cpp.ir.dataflow.TaintTracking
/**
- * The Decompression Sink instances, extend this class to defind new decompression sinks.
+ * The Decompression Sink instances, extend this class to define new decompression sinks.
*/
abstract class DecompressionFunction extends Function {
abstract int getArchiveParameterIndex();
}
+
+/**
+ * The Decompression Flow Steps, extend this class to define new decompression sinks.
+ */
+abstract class DecompressionFlowStep extends Function {
+ abstract predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2);
+}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
index b7eda35f2c7..6f68390fdd2 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
@@ -29,27 +29,7 @@ module DecompressionTaintConfig implements DataFlow::ConfigSig {
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(FunctionCall fc | fc.getTarget() instanceof UnzOpenFunction |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_reader_entry |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1)
- )
- or
- exists(FunctionCall fc | fc.getTarget() instanceof Mz_zip_entry |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc.getArgument(1)
- )
- or
- exists(FunctionCall fc |
- fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction
- |
- node1.asExpr() = fc.getArgument(0) and
- node2.asExpr() = fc
- )
+ any(DecompressionFlowStep f).isAdditionalFlowStep(node1, node2)
}
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
index 4bfa9a13b2c..a76abff8f6a 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll
@@ -8,7 +8,7 @@ import semmle.code.cpp.security.FlowSources
import DecompressionBomb
/**
- * The `mz_zip_entry` function is used in flow source.
+ * The `mz_zip_entry` function is used in flow sink.
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
*/
class Mz_zip_entry extends DecompressionFunction {
@@ -17,6 +17,21 @@ class Mz_zip_entry extends DecompressionFunction {
override int getArchiveParameterIndex() { result = 1 }
}
+/**
+ * The `mz_zip_entry` function is used in flow steps.
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
+ */
+class Mz_zip_entry_flow_steps extends DecompressionFlowStep {
+ Mz_zip_entry_flow_steps() { this.hasGlobalName("mz_zip_entry_read") }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() = this |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1)
+ )
+ }
+}
+
/**
* The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in flow source.
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
@@ -32,9 +47,31 @@ class Mz_zip_reader_entry extends DecompressionFunction {
override int getArchiveParameterIndex() { result = 1 }
}
+/**
+ * The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in flow steps.
+ * [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
+ */
+class Mz_zip_reader_entry_flow_steps extends DecompressionFlowStep {
+ Mz_zip_reader_entry_flow_steps() { this instanceof Mz_zip_reader_entry }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() = this |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc.getArgument(1)
+ )
+ }
+}
+
/**
* The `UnzOpen` function as a flow source.
*/
-class UnzOpenFunction extends Function {
+class UnzOpenFunction extends DecompressionFlowStep {
UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() = this |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
index df2381220fd..5c4f367b1b4 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
@@ -41,19 +41,33 @@ class GzReadFunction extends DecompressionFunction {
}
/**
- * The `gzdopen` function.
+ * The `gzdopen` function is used in flow steps.
*
* `gzdopen(int fd, const char *mode)`
*/
-class GzdopenFunction extends Function {
+class GzdopenFunction extends DecompressionFlowStep {
GzdopenFunction() { this.hasGlobalName("gzdopen") }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() = this |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
}
/**
- * The `gzopen` function.
+ * The `gzopen` function is used in flow steps.
*
* `gzopen(const char *path, const char *mode)`
*/
-class GzopenFunction extends Function {
+class GzopenFunction extends DecompressionFlowStep {
GzopenFunction() { this.hasGlobalName("gzopen") }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall fc | fc.getTarget() = this |
+ node1.asExpr() = fc.getArgument(0) and
+ node2.asExpr() = fc
+ )
+ }
}
From 87b6495c917efe78751cc80cf9bb7a353c4a82bf Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Sun, 14 Jul 2024 21:10:56 +0200
Subject: [PATCH 019/404] add zlib tests with stubs :)
---
.../CWE/CWE-409/DecompressionBombs.qlref | 1 +
.../Security/CWE/CWE-409/zlibTest.cpp | 182 ++++++++++++++++++
2 files changed, 183 insertions(+)
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.qlref
create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.qlref
new file mode 100644
index 00000000000..b3f71c4891a
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.qlref
@@ -0,0 +1 @@
+experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
\ No newline at end of file
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
new file mode 100644
index 00000000000..c55e18475fe
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
@@ -0,0 +1,182 @@
+
+#define Z_NULL 0
+# define FAR
+typedef unsigned char Byte;
+typedef Byte FAR Bytef;
+typedef unsigned int uInt;
+#define Z_BEST_COMPRESSION 9
+#define Z_FINISH 4
+#define Z_NO_FLUSH 0
+
+
+typedef struct {
+ int *zalloc;
+ int *zfree;
+ Bytef *next_in;
+ Bytef *next_out;
+ int *opaque;
+ uInt avail_out;
+ uInt avail_in;
+} z_stream;
+
+
+void deflateInit(z_stream *defstream, int i);
+
+void deflate(z_stream *defstream, int i);
+
+void deflateEnd(z_stream *defstream);
+
+void inflateInit(z_stream *infstream);
+
+void inflate(z_stream *infstream, int i);
+
+void inflateEnd(z_stream *infstream);
+
+namespace std {
+ template
+ struct char_traits;
+
+ template >
+ class basic_ostream {
+ public:
+ typedef charT char_type;
+ };
+
+ template
+ basic_ostream &operator<<(basic_ostream &, const charT *);
+
+ typedef basic_ostream ostream;
+
+ extern ostream cout;
+}
+
+int UnsafeInflate(int argc, char *argv[]) {
+ // original string len = 36
+ char a[50] = "Hello Hello Hello Hello Hello Hello!";
+ // placeholder for the compressed (deflated) version of "a"
+ char b[50];
+ // placeholder for the Uncompressed (inflated) version of "b"
+ char c[50];
+
+
+ // STEP 1.
+ // zlib struct
+ z_stream defstream;
+ defstream.zalloc = Z_NULL;
+ defstream.zfree = Z_NULL;
+ defstream.opaque = Z_NULL;
+ // setup "a" as the input and "b" as the compressed output
+ defstream.avail_in = (uInt) 50 + 1; // size of input, string + terminator
+ defstream.next_in = (Bytef *) a; // input char array
+ defstream.avail_out = (uInt) sizeof(b); // size of output
+ defstream.next_out = (Bytef *) b; // output char array
+
+ // the actual compression work.
+ deflateInit(&defstream, Z_BEST_COMPRESSION);
+ deflate(&defstream, Z_FINISH);
+ deflateEnd(&defstream);
+
+ // This is one way of getting the size of the output
+ // STEP 2.
+ // inflate b into c
+ // zlib struct
+ z_stream infstream;
+ infstream.zalloc = Z_NULL;
+ infstream.zfree = Z_NULL;
+ infstream.opaque = Z_NULL;
+ // setup "b" as the input and "c" as the compressed output
+ // TOTHINK: Here we can add additional step from Right operand to z_stream variable access
+ infstream.avail_in = (uInt) ((char *) defstream.next_out - b); // size of input
+ infstream.next_in = (Bytef *) b; // input char array
+ infstream.avail_out = (uInt) sizeof(c); // size of output
+ infstream.next_out = (Bytef *) c; // output char array
+
+ // uLong total_out; /* total number of bytes output so far */
+ // the actual DE-compression work.
+ inflateInit(&infstream);
+ inflate(&infstream, Z_NO_FLUSH);
+ inflateEnd(&infstream);
+
+
+ return 0;
+}
+
+
+typedef struct {
+} gzFile;
+
+gzFile gzopen(char *str, const char *rb);
+
+
+void exit(int i);
+
+unsigned int gzread(gzFile gz_file, unsigned char *str, int i);
+
+void gzclose(gzFile gz_file);
+
+std::ostream operator<<(const std::ostream &lhs, unsigned char rhs);
+
+
+int send(int, const void *, int, int);
+
+
+int UnsafeGzread(char **argv) {
+ char *fileName;
+ send(0, fileName, 0, 0);
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (&inFileZ == nullptr) {
+ exit(0);
+ }
+ unsigned char unzipBuffer[8192];
+ unsigned int unzippedBytes;
+ while (true) {
+ unzippedBytes = gzread(inFileZ, unzipBuffer, 8192);
+ if (unzippedBytes > 0) {
+ std::cout << unzippedBytes;
+ } else {
+ break;
+ }
+ }
+ gzclose(inFileZ);
+ return 0;
+}
+
+bool gzfread(char *str, int i, int i1, gzFile gz_file);
+
+int UnsafeGzfread(char **argv) {
+ char *fileName;
+ send(0, fileName, 0, 0);
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (&inFileZ == nullptr) {
+ exit(0);
+ }
+ while (true) {
+ char buffer[1000];
+ if (!gzfread(buffer, 999, 1, inFileZ)) {
+ break;
+ }
+ }
+ gzclose(inFileZ);
+ return 0;
+}
+
+char *gzgets(gzFile gz_file, char *buffer, int i);
+
+int UnsafeGzgets(char **argv) {
+ char *fileName;
+ send(0, fileName, 0, 0);
+ gzFile inFileZ = gzopen(fileName, "rb");
+ if (&inFileZ == nullptr) {
+ exit(0);
+ }
+ char *buffer = new char[4000000000];
+ char *result;
+ result = gzgets(inFileZ, buffer, 1000000000);
+ while (true) {
+ result = gzgets(inFileZ, buffer, 1000000000);
+ if (result == nullptr) {
+ break;
+ }
+ }
+ return 0;
+}
From a10b5021b4b70cbc8e86817539568cd757b4132b Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Mon, 15 Jul 2024 10:13:57 +0200
Subject: [PATCH 020/404] fix tests, it is not fixed 100%
---
.../CWE/CWE-409/DecompressionBomb.qll | 10 +++++++
.../CWE/CWE-409/DecompressionBombs.ql | 2 --
.../Security/CWE/CWE-409/zlibTest.cpp | 27 ++++++++-----------
3 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
index ecc32a9ba0d..c17616d020d 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll
@@ -1,5 +1,15 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
+import MiniZip
+import ZlibGzopen
+import ZlibInflator
+import ZlibUncompress
+import LibArchive
+import LibMiniz
+import XZ
+import ZSTD
+import Bzip2
+import Brotli
/**
* The Decompression Sink instances, extend this class to define new decompression sinks.
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
index 6f68390fdd2..896c15f7a80 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
@@ -15,8 +15,6 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.commons.File
-import MiniZip
-import ZlibGzopen
import DecompressionBomb
module DecompressionTaintConfig implements DataFlow::ConfigSig {
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
index c55e18475fe..b829cfaa61d 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
@@ -50,15 +50,12 @@ namespace std {
extern ostream cout;
}
-int UnsafeInflate(int argc, char *argv[]) {
- // original string len = 36
- char a[50] = "Hello Hello Hello Hello Hello Hello!";
+int UnsafeInflate(char *a) {
// placeholder for the compressed (deflated) version of "a"
char b[50];
// placeholder for the Uncompressed (inflated) version of "b"
char c[50];
-
// STEP 1.
// zlib struct
z_stream defstream;
@@ -117,12 +114,7 @@ void gzclose(gzFile gz_file);
std::ostream operator<<(const std::ostream &lhs, unsigned char rhs);
-int send(int, const void *, int, int);
-
-
-int UnsafeGzread(char **argv) {
- char *fileName;
- send(0, fileName, 0, 0);
+int UnsafeGzread(char *fileName) {
gzFile inFileZ = gzopen(fileName, "rb");
if (&inFileZ == nullptr) {
exit(0);
@@ -143,9 +135,7 @@ int UnsafeGzread(char **argv) {
bool gzfread(char *str, int i, int i1, gzFile gz_file);
-int UnsafeGzfread(char **argv) {
- char *fileName;
- send(0, fileName, 0, 0);
+int UnsafeGzfread(char *fileName) {
gzFile inFileZ = gzopen(fileName, "rb");
if (&inFileZ == nullptr) {
exit(0);
@@ -162,9 +152,7 @@ int UnsafeGzfread(char **argv) {
char *gzgets(gzFile gz_file, char *buffer, int i);
-int UnsafeGzgets(char **argv) {
- char *fileName;
- send(0, fileName, 0, 0);
+int UnsafeGzgets(char *fileName) {
gzFile inFileZ = gzopen(fileName, "rb");
if (&inFileZ == nullptr) {
exit(0);
@@ -180,3 +168,10 @@ int UnsafeGzgets(char **argv) {
}
return 0;
}
+
+int main(int argc, char **argv) {
+ UnsafeGzfread(argv[2]);
+ UnsafeGzgets(argv[2]);
+ UnsafeInflate(argv[2]);
+ UnsafeGzread(argv[2]);
+}
From f97b1039cd3a1f19d18ef1a775dbd66eef08bb19 Mon Sep 17 00:00:00 2001
From: am0o0 <77095239+am0o0@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:49:34 +0200
Subject: [PATCH 021/404] update test files, add one more additional flow step
for inflate function, fix gzopen additional flow step thanks to @jketema
---
.../CWE/CWE-409/DecompressionBombs.ql | 5 +--
.../Security/CWE/CWE-409/ZlibGzopen.qll | 4 +--
.../Security/CWE/CWE-409/ZlibInflator.qll | 19 +++++++++--
.../Security/CWE/CWE-409/zlibTest.cpp | 32 +++----------------
4 files changed, 26 insertions(+), 34 deletions(-)
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
index 896c15f7a80..05f39c47e13 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql
@@ -22,12 +22,13 @@ module DecompressionTaintConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
exists(FunctionCall fc, DecompressionFunction f | fc.getTarget() = f |
- fc.getArgument(f.getArchiveParameterIndex()) = sink.asExpr()
+ fc.getArgument(f.getArchiveParameterIndex()) = sink.asExpr()
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- any(DecompressionFlowStep f).isAdditionalFlowStep(node1, node2)
+ any(DecompressionFlowStep f).isAdditionalFlowStep(node1, node2) or
+ nextInAdditionalFlowStep(node1, node2)
}
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
index 5c4f367b1b4..edb8ef7ff68 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll
@@ -37,7 +37,7 @@ class GzGetsFunction extends DecompressionFunction {
class GzReadFunction extends DecompressionFunction {
GzReadFunction() { this.hasGlobalName("gzread") }
- override int getArchiveParameterIndex() { result = 1 }
+ override int getArchiveParameterIndex() { result = 0 }
}
/**
@@ -66,7 +66,7 @@ class GzopenFunction extends DecompressionFlowStep {
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall fc | fc.getTarget() = this |
- node1.asExpr() = fc.getArgument(0) and
+ node1.asIndirectExpr() = fc.getArgument(0) and
node2.asExpr() = fc
)
}
diff --git a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
index 1897b8e09d3..464dce3ac45 100644
--- a/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
+++ b/cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibInflator.qll
@@ -10,12 +10,27 @@ import DecompressionBomb
/**
* The `inflate` and `inflateSync` functions are used in flow sink.
*
- * `inflate(z_streamp strm, int flush)`
+ * `inflate(z_stream strm, int flush)`
*
- * `inflateSync(z_streamp strm)`
+ * `inflateSync(z_stream strm)`
*/
class InflateFunction extends DecompressionFunction {
InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
override int getArchiveParameterIndex() { result = 0 }
}
+
+/**
+ * The `next_in` member of a `z_stream` variable is used in flow steps.
+ */
+predicate nextInAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(Variable nextInVar, VariableAccess zStreamAccess |
+ nextInVar.getDeclaringType().hasName("z_stream") and
+ nextInVar.hasName("next_in") and
+ zStreamAccess.getType().hasName("z_stream")
+ |
+ nextInVar.getAnAccess().getQualifier().(VariableAccess).getTarget() = zStreamAccess.getTarget() and
+ node1.asIndirectExpr() = nextInVar.getAnAssignedValue() and
+ node2.asExpr() = zStreamAccess
+ )
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
index b829cfaa61d..07805f6c83f 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-409/zlibTest.cpp
@@ -51,40 +51,17 @@ namespace std {
}
int UnsafeInflate(char *a) {
- // placeholder for the compressed (deflated) version of "a"
- char b[50];
- // placeholder for the Uncompressed (inflated) version of "b"
- char c[50];
+ // placeholder for the Uncompressed (inflated) version of "a"
+ char c[1024000];
- // STEP 1.
- // zlib struct
- z_stream defstream;
- defstream.zalloc = Z_NULL;
- defstream.zfree = Z_NULL;
- defstream.opaque = Z_NULL;
- // setup "a" as the input and "b" as the compressed output
- defstream.avail_in = (uInt) 50 + 1; // size of input, string + terminator
- defstream.next_in = (Bytef *) a; // input char array
- defstream.avail_out = (uInt) sizeof(b); // size of output
- defstream.next_out = (Bytef *) b; // output char array
-
- // the actual compression work.
- deflateInit(&defstream, Z_BEST_COMPRESSION);
- deflate(&defstream, Z_FINISH);
- deflateEnd(&defstream);
-
- // This is one way of getting the size of the output
- // STEP 2.
- // inflate b into c
- // zlib struct
z_stream infstream;
infstream.zalloc = Z_NULL;
infstream.zfree = Z_NULL;
infstream.opaque = Z_NULL;
// setup "b" as the input and "c" as the compressed output
// TOTHINK: Here we can add additional step from Right operand to z_stream variable access
- infstream.avail_in = (uInt) ((char *) defstream.next_out - b); // size of input
- infstream.next_in = (Bytef *) b; // input char array
+ infstream.avail_in = (uInt) (1000); // size of input
+ infstream.next_in = (Bytef *) a; // input char array
infstream.avail_out = (uInt) sizeof(c); // size of output
infstream.next_out = (Bytef *) c; // output char array
@@ -159,7 +136,6 @@ int UnsafeGzgets(char *fileName) {
}
char *buffer = new char[4000000000];
char *result;
- result = gzgets(inFileZ, buffer, 1000000000);
while (true) {
result = gzgets(inFileZ, buffer, 1000000000);
if (result == nullptr) {
From be0a60a7f65f7de42062a617b1566768bb9ed398 Mon Sep 17 00:00:00 2001
From: Mauro Baluda
Date: Tue, 13 Aug 2024 14:45:03 +0200
Subject: [PATCH 022/404] Add support for importing NPM modules in XSJS sources
---
javascript/ql/lib/semmle/javascript/NodeJS.qll | 9 +++++++++
.../Security/CWE-326/InsufficientKeySize.expected | 1 +
javascript/ql/test/query-tests/Security/CWE-326/tst.xsjs | 5 +++++
3 files changed, 15 insertions(+)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-326/tst.xsjs
diff --git a/javascript/ql/lib/semmle/javascript/NodeJS.qll b/javascript/ql/lib/semmle/javascript/NodeJS.qll
index 221cee084b6..823ddae93c4 100644
--- a/javascript/ql/lib/semmle/javascript/NodeJS.qll
+++ b/javascript/ql/lib/semmle/javascript/NodeJS.qll
@@ -295,6 +295,15 @@ private predicate isRequire(DataFlow::Node nd) {
isCreateRequire(call.getCallee().flow()) and
nd = call.flow()
)
+ or
+ // `$.require('underscore');`.
+ // NPM as supported in [XSJS files](https://www.npmjs.com/package/@sap/async-xsjs#npm-packages-support).
+ exists(MethodCallExpr require |
+ nd.getFile().getExtension() = ["xsjs", "xsjslib"] and
+ require.getCalleeName() = "require" and
+ require.getReceiver().(GlobalVarAccess).getName() = "$" and
+ nd = require.getCallee().flow()
+ )
}
/**
diff --git a/javascript/ql/test/query-tests/Security/CWE-326/InsufficientKeySize.expected b/javascript/ql/test/query-tests/Security/CWE-326/InsufficientKeySize.expected
index 95bb8d45832..323e5b24875 100644
--- a/javascript/ql/test/query-tests/Security/CWE-326/InsufficientKeySize.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-326/InsufficientKeySize.expected
@@ -9,3 +9,4 @@
| tst.js:35:13:35:43 | crypto. ... an(512) | Creation of an asymmetric key uses 512 bits, which is below 2048 and considered breakable. |
| tst.js:39:13:39:33 | new Nod ... : 512}) | Creation of an asymmetric RSA key uses 512 bits, which is below 2048 and considered breakable. |
| tst.js:43:1:43:31 | key.gen ... 65537) | Creation of an asymmetric RSA key uses 512 bits, which is below 2048 and considered breakable. |
+| tst.xsjs:3:14:3:71 | crypto. ... 1024 }) | Creation of an asymmetric RSA key uses 1024 bits, which is below 2048 and considered breakable. |
diff --git a/javascript/ql/test/query-tests/Security/CWE-326/tst.xsjs b/javascript/ql/test/query-tests/Security/CWE-326/tst.xsjs
new file mode 100644
index 00000000000..d5e5051af66
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-326/tst.xsjs
@@ -0,0 +1,5 @@
+const crypto = $.require("crypto");
+
+const bad1 = crypto.generateKeyPairSync("rsa", { modulusLength: 1024 }); // NOT OK
+
+const good1 = crypto.generateKeyPairSync("rsa", { modulusLength: 4096 }); // OK
From f6ec56a977ede420d4be1bc041ad72b8abf4fc31 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 13 Aug 2024 09:16:36 +0200
Subject: [PATCH 023/404] C#: Implement `ContentSet`
---
.../dataflow/internal/DataFlowPrivate.qll | 186 ++++++++++--------
.../dataflow/internal/DataFlowPublic.qll | 72 ++++++-
.../dataflow/internal/FlowSummaryImpl.qll | 49 +++--
.../internal/TaintTrackingPrivate.qll | 12 +-
.../csharp/frameworks/EntityFramework.qll | 42 ++--
.../modelgenerator/internal/CaptureModels.qll | 18 +-
.../internal/CaptureModelsSpecific.qll | 25 ++-
.../dataflow/external-models/steps.ql | 4 +-
8 files changed, 267 insertions(+), 141 deletions(-)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index f1c51f222d5..fd31d2a3a38 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -908,19 +908,20 @@ private class Argument extends Expr {
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
-private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, boolean postUpdate) {
+private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
exists(FieldOrProperty f |
- c = f.getContent() and
+ c = f.getContentSet() and
(
f.isFieldLike() and
f instanceof InstanceFieldOrProperty
or
exists(
FlowSummaryImpl::Private::SummarizedCallableImpl sc,
- FlowSummaryImpl::Private::SummaryComponentStack input
+ FlowSummaryImpl::Private::SummaryComponentStack input, ContentSet readSet
|
sc.propagatesFlow(input, _, _, _) and
- input.contains(FlowSummaryImpl::Private::SummaryComponent::content(f.getContent()))
+ input.contains(FlowSummaryImpl::Private::SummaryComponent::content(readSet)) and
+ c.getAStoreContent() = readSet.getAReadContent()
)
)
|
@@ -970,28 +971,13 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
)
}
-/** Holds if property `p1` overrides or implements source declaration property `p2`. */
-private predicate overridesOrImplementsSourceDecl(Property p1, Property p2) {
- p1.getOverridee*().getUnboundDeclaration() = p2
- or
- p1.getAnUltimateImplementee().getUnboundDeclaration() = p2
-}
-
/**
* Holds if `e2` is an expression that reads field or property `c` from
- * expression `e1`. This takes overriding into account for properties written
- * from library code.
+ * expression `e1`.
*/
-private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2) {
+private predicate fieldOrPropertyRead(Expr e1, ContentSet c, FieldOrPropertyRead e2) {
e1 = e2.getQualifier() and
- exists(FieldOrProperty ret | c = ret.getContent() |
- ret = e2.getTarget()
- or
- exists(Property target |
- target.getGetter() = e2.(PropertyCall).getARuntimeTarget() and
- overridesOrImplementsSourceDecl(target, ret)
- )
- )
+ c = e2.getTarget().(FieldOrProperty).getContentSet()
}
/**
@@ -1208,6 +1194,11 @@ private module Cached {
} or
TCapturedVariableContent(VariableCapture::CapturedVariable v)
+ cached
+ newtype TContentSet =
+ TSingletonContent(Content c) { not c instanceof PropertyContent } or
+ TPropertyContentSet(Property p) { p.isUnboundDeclaration() }
+
cached
newtype TContentApprox =
TFieldApproxContent(string firstChar) { firstChar = approximateFieldContent(_) } or
@@ -2076,10 +2067,10 @@ class FieldOrProperty extends Assignable, Modifiable {
}
/** Gets the content that matches this field or property. */
- Content getContent() {
- result.(FieldContent).getField() = this.getUnboundDeclaration()
+ ContentSet getContentSet() {
+ result.isField(this.getUnboundDeclaration())
or
- result.(PropertyContent).getProperty() = this.getUnboundDeclaration()
+ result.isProperty(this.getUnboundDeclaration())
}
}
@@ -2209,8 +2200,8 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio
}
pragma[nomagic]
-private PropertyContent getResultContent() {
- result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty()
+private ContentSet getResultContent() {
+ result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
}
private predicate primaryConstructorParameterStore(
@@ -2224,17 +2215,16 @@ private predicate primaryConstructorParameterStore(
)
}
-/**
- * Holds if data can flow from `node1` to `node2` via an assignment to
- * content `c`.
- */
-predicate storeStep(Node node1, ContentSet c, Node node2) {
+pragma[nomagic]
+private predicate recordParameter(RecordType t, Parameter p, string name) {
+ p.getName() = name and p.getCallable().getDeclaringType() = t
+}
+
+private predicate storeContentStep(Node node1, Content c, Node node2) {
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
- fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
- or
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
)
or
@@ -2255,26 +2245,59 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c instanceof ElementContent
)
or
+ primaryConstructorParameterStore(node1, c, node2)
+ or
+ exists(Parameter p, DataFlowCallable callable |
+ node1 = TExplicitParameterNode(p, callable) and
+ node2 = TPrimaryConstructorThisAccessNode(p, true, callable) and
+ not recordParameter(_, p, _) and
+ c.(PrimaryConstructorParameterContent).getParameter() = p
+ )
+ or
+ VariableCapture::storeStep(node1, c, node2)
+}
+
+pragma[nomagic]
+private predicate recordProperty(RecordType t, ContentSet c, string name) {
+ exists(Property p |
+ c.isProperty(p) and
+ p.getName() = name and
+ p.getDeclaringType() = t
+ )
+}
+
+/**
+ * Holds if data can flow from `node1` to `node2` via an assignment to
+ * content `c`.
+ */
+predicate storeStep(Node node1, ContentSet c, Node node2) {
+ exists(Content cont |
+ storeContentStep(node1, cont, node2) and
+ c.isSingleton(cont)
+ )
+ or
+ exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
+ hasNodePath(x, node1, node) and
+ if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
+ |
+ fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
+ )
+ or
exists(Expr e |
e = node1.asExpr() and
node2.(AsyncReturnNode).getExpr() = e and
c = getResultContent()
)
or
- primaryConstructorParameterStore(node1, c, node2)
- or
- exists(Parameter p, DataFlowCallable callable |
+ exists(Parameter p, DataFlowCallable callable, RecordType t, string name |
node1 = TExplicitParameterNode(p, callable) and
node2 = TPrimaryConstructorThisAccessNode(p, true, callable) and
- if p.getCallable().getDeclaringType() instanceof RecordType
- then c.(PropertyContent).getProperty().getName() = p.getName()
- else c.(PrimaryConstructorParameterContent).getParameter() = p
+ recordParameter(t, p, name) and
+ recordProperty(t, c, name)
)
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
- or
- VariableCapture::storeStep(node1, c, node2)
}
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -2342,14 +2365,8 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
}
}
-/**
- * Holds if data can flow from `node1` to `node2` via a read of content `c`.
- */
-predicate readStep(Node node1, ContentSet c, Node node2) {
+private predicate readContentStep(Node node1, Content c, Node node2) {
exists(ReadStepConfiguration x |
- hasNodePath(x, node1, node2) and
- fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
- or
hasNodePath(x, node1, node2) and
arrayRead(node1.asExpr(), node2.asExpr()) and
c instanceof ElementContent
@@ -2361,10 +2378,6 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
c instanceof ElementContent
)
or
- hasNodePath(x, node1, node2) and
- node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
- c = getResultContent()
- or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
@@ -2402,31 +2415,30 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
)
)
or
- FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
- node2.(FlowSummaryNode).getSummaryNode())
- or
VariableCapture::readStep(node1, c, node2)
}
/**
- * Holds if values stored inside content `c` are cleared at node `n`. For example,
- * any value stored inside `f` is cleared at the pre-update node associated with `x`
- * in `x.f = newValue`.
+ * Holds if data can flow from `node1` to `node2` via a read of content `c`.
*/
-predicate clearsContent(Node n, ContentSet c) {
- fieldOrPropertyStore(_, c, _, n.asExpr(), true)
- or
- fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
- or
- FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
- or
- exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f |
- oi = we.getInitializer() and
- n.asExpr() = oi and
- f = oi.getAMemberInitializer().getInitializedMember() and
- c = f.getContent()
+predicate readStep(Node node1, ContentSet c, Node node2) {
+ exists(Content cont |
+ readContentStep(node1, cont, node2) and
+ c.isSingleton(cont)
)
or
+ exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
+ fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
+ or
+ node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
+ c = getResultContent()
+ )
+ or
+ FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
+ node2.(FlowSummaryNode).getSummaryNode())
+}
+
+private predicate clearsCont(Node n, Content c) {
exists(Argument a, Struct s, Field f |
a = n.(PostUpdateNode).getPreUpdateNode().asExpr() and
a.getType() = s and
@@ -2440,6 +2452,31 @@ predicate clearsContent(Node n, ContentSet c) {
VariableCapture::clearsContent(n, c)
}
+/**
+ * Holds if values stored inside content `c` are cleared at node `n`. For example,
+ * any value stored inside `f` is cleared at the pre-update node associated with `x`
+ * in `x.f = newValue`.
+ */
+predicate clearsContent(Node n, ContentSet c) {
+ exists(Content cont |
+ clearsCont(n, cont) and
+ c.isSingleton(cont)
+ )
+ or
+ fieldOrPropertyStore(_, c, _, n.asExpr(), true)
+ or
+ fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
+ or
+ FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
+ or
+ exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f |
+ oi = we.getInitializer() and
+ n.asExpr() = oi and
+ f = oi.getAMemberInitializer().getInitializedMember() and
+ c = f.getContentSet()
+ )
+}
+
/**
* Holds if the value that is being tracked is expected to be stored inside content `c`
* at node `n`.
@@ -2447,7 +2484,7 @@ predicate clearsContent(Node n, ContentSet c) {
predicate expectsContent(Node n, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or
- n.asExpr() instanceof SpreadElementExpr and c instanceof ElementContent
+ n.asExpr() instanceof SpreadElementExpr and c.isElement()
}
class NodeRegion instanceof ControlFlow::BasicBlock {
@@ -3048,8 +3085,3 @@ abstract class SyntheticField extends string {
/** Gets the type of this synthetic field. */
Type getType() { result instanceof ObjectType }
}
-
-/**
- * Holds if the the content `c` is a container.
- */
-predicate containerContent(DataFlow::Content c) { c instanceof DataFlow::ElementContent }
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
index 4d1dc25f6e9..84e486c7a55 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
@@ -267,22 +267,84 @@ class CapturedVariableContent extends Content, TCapturedVariableContent {
override Location getLocation() { result = v.getLocation() }
}
+/** Holds if property `p1` overrides or implements source declaration property `p2`. */
+private predicate overridesOrImplementsSourceDecl(Property p1, Property p2) {
+ p1.getOverridee*().getUnboundDeclaration() = p2
+ or
+ p1.getAnUltimateImplementee().getUnboundDeclaration() = p2
+}
+
/**
* An entity that represents a set of `Content`s.
*
* The set may be interpreted differently depending on whether it is
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
*/
-class ContentSet instanceof Content {
+class ContentSet extends TContentSet {
+ /** Holds if this content set is the singleton `{c}`. */
+ predicate isSingleton(Content c) { this = TSingletonContent(c) }
+
+ /**
+ * Holds if this content set represents the property `p`.
+ *
+ *
+ * For `getAReadContent`, this set represents all properties that may
+ * (reflexively and transitively) override/implement `p` (or vice versa).
+ */
+ predicate isProperty(Property p) { this = TPropertyContentSet(p) }
+
+ /** Holds if this content set represent the field `f`. */
+ predicate isField(Field f) { this.isSingleton(TFieldContent(f)) }
+
+ /** Holds if this content set represents an element in a collection. */
+ predicate isElement() { this.isSingleton(TElementContent()) }
+
/** Gets a content that may be stored into when storing into this set. */
- Content getAStoreContent() { result = this }
+ Content getAStoreContent() {
+ this.isSingleton(result)
+ or
+ this.isProperty(result.(PropertyContent).getProperty())
+ }
/** Gets a content that may be read from when reading from this set. */
- Content getAReadContent() { result = this }
+ Content getAReadContent() {
+ this.isSingleton(result)
+ or
+ exists(Property p1, Property p2 |
+ this.isProperty(p1) and
+ p2 = result.(PropertyContent).getProperty()
+ |
+ p1 = p2
+ or
+ overridesOrImplementsSourceDecl(p2, p1)
+ or
+ overridesOrImplementsSourceDecl(p1, p2)
+ )
+ }
/** Gets a textual representation of this content set. */
- string toString() { result = super.toString() }
+ string toString() {
+ exists(Content c |
+ this.isSingleton(c) and
+ result = c.toString()
+ )
+ or
+ exists(Property p |
+ this.isProperty(p) and
+ result = "property " + p.getName()
+ )
+ }
/** Gets the location of this content set. */
- Location getLocation() { result = super.getLocation() }
+ Location getLocation() {
+ exists(Content c |
+ this.isSingleton(c) and
+ result = c.getLocation()
+ )
+ or
+ exists(Property p |
+ this.isProperty(p) and
+ result = p.getLocation()
+ )
+ }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index d70f66f2cbe..d777566a336 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
@@ -46,7 +46,7 @@ module Input implements InputSig
result = "delegate-self"
}
- string encodeContent(ContentSet c, string arg) {
+ private string encodeCont(Content c, string arg) {
c = TElementContent() and result = "Element" and arg = ""
or
exists(Field f, string qualifier, string name |
@@ -56,27 +56,34 @@ module Input implements InputSig
result = "Field"
)
or
- exists(Property p, string qualifier, string name |
- c = TPropertyContent(p) and
- p.hasFullyQualifiedName(qualifier, name) and
- arg = getQualifiedName(qualifier, name) and
- result = "Property"
- )
- or
exists(SyntheticField f |
c = TSyntheticFieldContent(f) and result = "SyntheticField" and arg = f
)
}
+ string encodeContent(ContentSet c, string arg) {
+ exists(Content cont |
+ c.isSingleton(cont) and
+ result = encodeCont(cont, arg)
+ )
+ or
+ exists(Property p, string qualifier, string name |
+ c.isProperty(p) and
+ p.hasFullyQualifiedName(qualifier, name) and
+ arg = getQualifiedName(qualifier, name) and
+ result = "Property"
+ )
+ }
+
string encodeWithoutContent(ContentSet c, string arg) {
result = "WithoutElement" and
- c = TElementContent() and
+ c.isElement() and
arg = ""
}
string encodeWithContent(ContentSet c, string arg) {
result = "WithElement" and
- c = TElementContent() and
+ c.isElement() and
arg = ""
}
@@ -103,12 +110,10 @@ private module TypesInput implements Impl::Private::TypesInputSig {
result.asGvnType() = Gvn::getGlobalValueNumber(any(ObjectType t))
}
- DataFlowType getContentType(ContentSet c) {
+ private DataFlowType getContType(Content c) {
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
t = c.(FieldContent).getField().getType()
or
- t = c.(PropertyContent).getProperty().getType()
- or
t = c.(SyntheticFieldContent).getField().getType()
or
c instanceof ElementContent and
@@ -116,6 +121,17 @@ private module TypesInput implements Impl::Private::TypesInputSig {
)
}
+ DataFlowType getContentType(ContentSet c) {
+ exists(Content cont |
+ c.isSingleton(cont) and
+ result = getContType(cont)
+ )
+ or
+ exists(Property p |
+ c.isProperty(p) and result.asGvnType() = Gvn::getGlobalValueNumber(p.getType())
+ )
+ }
+
DataFlowType getParameterType(Impl::Public::SummarizedCallable c, ParameterPosition pos) {
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
exists(int i |
@@ -311,17 +327,16 @@ module Private {
}
/** Gets a summary component that represents an element in a collection. */
- SummaryComponent element() { result = content(any(DataFlow::ElementContent c)) }
+ SummaryComponent element() { result = content(any(ContentSet cs | cs.isElement())) }
/** Gets a summary component for property `p`. */
SummaryComponent property(Property p) {
- result =
- content(any(DataFlow::PropertyContent c | c.getProperty() = p.getUnboundDeclaration()))
+ result = content(any(DataFlow::ContentSet c | c.isProperty(p.getUnboundDeclaration())))
}
/** Gets a summary component for field `f`. */
SummaryComponent field(Field f) {
- result = content(any(DataFlow::FieldContent c | c.getField() = f.getUnboundDeclaration()))
+ result = content(any(DataFlow::ContentSet c | c.isField(f.getUnboundDeclaration())))
}
/** Gets a summary component that represents the return value of a call. */
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
index 11c47c1d37e..25de2681862 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -122,22 +122,22 @@ private module Cached {
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
or
// Taint collection by adding a tainted element
- exists(DataFlow::ElementContent c |
+ exists(DataFlow::ContentSet c | c.isElement() |
storeStep(nodeFrom, c, nodeTo)
or
FlowSummaryImpl::Private::Steps::summarySetterStep(nodeFrom, c, nodeTo, _)
)
or
- exists(DataFlow::Content c |
+ exists(DataFlow::ContentSet c |
readStep(nodeFrom, c, nodeTo)
or
FlowSummaryImpl::Private::Steps::summaryGetterStep(nodeFrom, c, nodeTo, _)
|
// Taint members
- c = any(TaintedMember m).(FieldOrProperty).getContent()
+ c = any(TaintedMember m).(FieldOrProperty).getContentSet()
or
// Read from a tainted collection
- c = TElementContent()
+ c.isElement()
)
)
}
@@ -152,12 +152,12 @@ private module Cached {
localTaintStepCommon(nodeFrom, nodeTo)
or
// Taint members
- readStep(nodeFrom, any(TaintedMember m).(FieldOrProperty).getContent(), nodeTo)
+ readStep(nodeFrom, any(TaintedMember m).(FieldOrProperty).getContentSet(), nodeTo)
or
// Although flow through collections is modeled precisely using stores/reads, we still
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
// tracking configurations where the source is a collection
- readStep(nodeFrom, TElementContent(), nodeTo)
+ readStep(nodeFrom, any(DataFlow::ContentSet c | c.isElement()), nodeTo)
or
nodeTo = nodeFrom.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
) and
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
index deeccdbf020..9bf516bc876 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
@@ -282,12 +282,12 @@ module EntityFramework {
* If `t2` is a column type, `c2` will be included in the model (see
* https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations).
*/
- private predicate step(Content c1, Type t1, Content c2, Type t2, int dist) {
+ private predicate step(ContentSet c1, Type t1, ContentSet c2, Type t2, int dist) {
exists(Property p1 |
p1 = this.getADbSetProperty(t2) and
- c1.(PropertyContent).getProperty() = p1 and
+ c1.isProperty(p1) and
t1 = p1.getType() and
- c2 instanceof ElementContent and
+ c2.isElement() and
dist = 0
)
or
@@ -299,17 +299,17 @@ module EntityFramework {
exists(Property p2 |
p2.getDeclaringType().(Class) = t1 and
not isColumnType(t1) and
- c2.(PropertyContent).getProperty() = p2 and
+ c2.isProperty(p2) and
t2 = p2.getType() and
not isNotMapped(p2)
)
or
exists(ConstructedInterface ci |
- c1 instanceof PropertyContent and
+ c1.isProperty(_) and
t1.(ValueOrRefType).getABaseType*() = ci and
not t1 instanceof StringType and
ci.getUnboundDeclaration() instanceof SystemCollectionsGenericIEnumerableTInterface and
- c2 instanceof ElementContent and
+ c2.isElement() and
t2 = ci.getTypeArgument(0)
)
)
@@ -340,16 +340,16 @@ module EntityFramework {
* ```
*/
Property getAColumnProperty(int dist) {
- exists(PropertyContent c, Type t |
+ exists(ContentSet c, Type t |
this.step(_, _, c, t, dist) and
- c.getProperty() = result and
+ c.isProperty(result) and
isColumnType(t)
)
}
- private predicate stepRev(Content c1, Type t1, Content c2, Type t2, int dist) {
+ private predicate stepRev(ContentSet c1, Type t1, ContentSet c2, Type t2, int dist) {
this.step(c1, t1, c2, t2, dist) and
- c2.(PropertyContent).getProperty() = this.getAColumnProperty(dist)
+ c2.isProperty(this.getAColumnProperty(dist))
or
this.stepRev(c2, t2, _, _, dist + 1) and
this.step(c1, t1, c2, t2, dist)
@@ -364,13 +364,13 @@ module EntityFramework {
/** Holds if component stack `head :: tail` is required for the input specification. */
predicate requiresComponentStackIn(
- Content head, Type headType, SummaryComponentStack tail, int dist
+ ContentSet head, Type headType, SummaryComponentStack tail, int dist
) {
tail = SummaryComponentStack::qualifier() and
this.stepRev(head, headType, _, _, 0) and
dist = -1
or
- exists(Content tailHead, Type tailType, SummaryComponentStack tailTail |
+ exists(ContentSet tailHead, Type tailType, SummaryComponentStack tailTail |
this.requiresComponentStackIn(tailHead, tailType, tailTail, dist - 1) and
tail = SummaryComponentStack::push(SummaryComponent::content(tailHead), tailTail) and
this.stepRev(tailHead, tailType, head, headType, dist)
@@ -379,18 +379,18 @@ module EntityFramework {
/** Holds if component stack `head :: tail` is required for the output specification. */
predicate requiresComponentStackOut(
- Content head, Type headType, SummaryComponentStack tail, int dist,
+ ContentSet head, Type headType, SummaryComponentStack tail, int dist,
DbContextClassSetProperty dbSetProp
) {
- exists(PropertyContent c1 |
+ exists(ContentSet c1 |
dbSetProp = this.getADbSetProperty(headType) and
this.stepRev(c1, _, head, headType, 0) and
- c1.getProperty() = dbSetProp and
+ c1.isProperty(dbSetProp) and
tail = SummaryComponentStack::return() and
dist = 0
)
or
- exists(Content tailHead, SummaryComponentStack tailTail, Type tailType |
+ exists(ContentSet tailHead, SummaryComponentStack tailTail, Type tailType |
this.requiresComponentStackOut(tailHead, tailType, tailTail, dist - 1, dbSetProp) and
tail = SummaryComponentStack::push(SummaryComponent::content(tailHead), tailTail) and
this.stepRev(tailHead, tailType, head, headType, dist)
@@ -402,9 +402,9 @@ module EntityFramework {
*/
pragma[noinline]
predicate input(SummaryComponentStack input, Property mapped) {
- exists(PropertyContent head, SummaryComponentStack tail |
+ exists(ContentSet head, SummaryComponentStack tail |
this.requiresComponentStackIn(head, _, tail, _) and
- head.getProperty() = mapped and
+ head.isProperty(mapped) and
mapped = this.getAColumnProperty(_) and
input = SummaryComponentStack::push(SummaryComponent::content(head), tail)
)
@@ -418,9 +418,9 @@ module EntityFramework {
private predicate output(
SummaryComponentStack output, Property mapped, DbContextClassSetProperty dbSet
) {
- exists(PropertyContent head, SummaryComponentStack tail |
+ exists(ContentSet head, SummaryComponentStack tail |
this.requiresComponentStackOut(head, _, tail, _, dbSet) and
- head.getProperty() = mapped and
+ head.isProperty(mapped) and
mapped = this.getAColumnProperty(_) and
output = SummaryComponentStack::push(SummaryComponent::content(head), tail)
)
@@ -505,7 +505,7 @@ module EntityFramework {
private class DbContextSaveChangesRequiredSummaryComponentStack extends RequiredSummaryComponentStack
{
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
- exists(Content c | head = SummaryComponent::content(c) |
+ exists(ContentSet c | head = SummaryComponent::content(c) |
any(DbContextClass cls).requiresComponentStackIn(c, _, tail, _)
or
any(DbContextClass cls).requiresComponentStackOut(c, _, tail, _, _)
diff --git a/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll
index b8bc01f0800..b5eff2664d2 100644
--- a/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll
+++ b/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll
@@ -50,7 +50,7 @@ module Printing = ModelPrinting;
/**
* Holds if `c` is a relevant content kind, where the underlying type is relevant.
*/
-private predicate isRelevantTypeInContent(DataFlow::Content c) {
+private predicate isRelevantTypeInContent(DataFlow::ContentSet c) {
isRelevantType(getUnderlyingContentType(c))
}
@@ -58,24 +58,22 @@ private predicate isRelevantTypeInContent(DataFlow::Content c) {
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
*/
private predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(DataFlow::Content f |
+ exists(DataFlow::ContentSet f |
DataFlowPrivate::readStep(node1, f, node2) and
// Partially restrict the content types used for intermediate steps.
(not exists(getUnderlyingContentType(f)) or isRelevantTypeInContent(f))
)
or
- exists(DataFlow::Content f | DataFlowPrivate::storeStep(node1, f, node2) |
- DataFlowPrivate::containerContent(f)
- )
+ exists(DataFlow::ContentSet f | DataFlowPrivate::storeStep(node1, f, node2) | containerContent(f))
}
/**
* Holds if content `c` is either a field, a synthetic field or language specific
* content of a relevant type or a container like content.
*/
-private predicate isRelevantContent(DataFlow::Content c) {
+private predicate isRelevantContent(DataFlow::ContentSet c) {
isRelevantTypeInContent(c) or
- DataFlowPrivate::containerContent(c)
+ containerContent(c)
}
/**
@@ -170,8 +168,8 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
- exists(DataFlow::Content c |
- DataFlowImplCommon::store(node1, c, node2, _, _) and
+ exists(DataFlow::ContentSet c |
+ DataFlowImplCommon::store(node1, c.getAStoreContent(), node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
@@ -180,7 +178,7 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {
)
)
or
- exists(DataFlow::Content c |
+ exists(DataFlow::ContentSet c |
DataFlowPrivate::readStep(node1, c, node2) and
isRelevantContent(c) and
state1.(TaintRead).getStep() + 1 = state2.(TaintRead).getStep()
diff --git a/csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
index c442261d780..89d721b300d 100644
--- a/csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
+++ b/csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
@@ -210,10 +210,24 @@ predicate isRelevantType(CS::Type t) {
/**
* Gets the underlying type of the content `c`.
*/
-CS::Type getUnderlyingContentType(DataFlow::Content c) {
+private CS::Type getUnderlyingContType(DataFlow::Content c) {
result = c.(DataFlow::FieldContent).getField().getType() or
- result = c.(DataFlow::SyntheticFieldContent).getField().getType() or
- result = c.(DataFlow::PropertyContent).getProperty().getType()
+ result = c.(DataFlow::SyntheticFieldContent).getField().getType()
+}
+
+/**
+ * Gets the underlying type of the content `c`.
+ */
+CS::Type getUnderlyingContentType(DataFlow::ContentSet c) {
+ exists(DataFlow::Content cont |
+ c.isSingleton(cont) and
+ result = getUnderlyingContType(cont)
+ )
+ or
+ exists(CS::Property p |
+ c.isProperty(p) and
+ result = p.getType()
+ )
}
/**
@@ -325,3 +339,8 @@ predicate isRelevantSinkKind(string kind) { any() }
*/
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
+
+/**
+ * Holds if the the content `c` is a container.
+ */
+predicate containerContent(DataFlow::ContentSet c) { c.isElement() }
diff --git a/csharp/ql/test/library-tests/dataflow/external-models/steps.ql b/csharp/ql/test/library-tests/dataflow/external-models/steps.ql
index 05d96df8f32..aaeda41c163 100644
--- a/csharp/ql/test/library-tests/dataflow/external-models/steps.ql
+++ b/csharp/ql/test/library-tests/dataflow/external-models/steps.ql
@@ -16,10 +16,10 @@ query predicate summaryThroughStep(
preservesValue = false
}
-query predicate summaryGetterStep(DataFlow::Node arg, DataFlow::Node out, Content c) {
+query predicate summaryGetterStep(DataFlow::Node arg, DataFlow::Node out, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryGetterStep(arg, c, out, _)
}
-query predicate summarySetterStep(DataFlow::Node arg, DataFlow::Node out, Content c) {
+query predicate summarySetterStep(DataFlow::Node arg, DataFlow::Node out, ContentSet c) {
FlowSummaryImpl::Private::Steps::summarySetterStep(arg, c, out, _)
}
From d638b5c7d4efbabc7ca4101de05cc98fc3c21219 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 13 Aug 2024 13:26:48 +0200
Subject: [PATCH 024/404] Sync shared file
---
.../modelgenerator/internal/CaptureModels.qll | 18 ++++++++----------
.../internal/CaptureModelsSpecific.qll | 2 ++
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll
index b8bc01f0800..b5eff2664d2 100644
--- a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll
+++ b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll
@@ -50,7 +50,7 @@ module Printing = ModelPrinting;
/**
* Holds if `c` is a relevant content kind, where the underlying type is relevant.
*/
-private predicate isRelevantTypeInContent(DataFlow::Content c) {
+private predicate isRelevantTypeInContent(DataFlow::ContentSet c) {
isRelevantType(getUnderlyingContentType(c))
}
@@ -58,24 +58,22 @@ private predicate isRelevantTypeInContent(DataFlow::Content c) {
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
*/
private predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- exists(DataFlow::Content f |
+ exists(DataFlow::ContentSet f |
DataFlowPrivate::readStep(node1, f, node2) and
// Partially restrict the content types used for intermediate steps.
(not exists(getUnderlyingContentType(f)) or isRelevantTypeInContent(f))
)
or
- exists(DataFlow::Content f | DataFlowPrivate::storeStep(node1, f, node2) |
- DataFlowPrivate::containerContent(f)
- )
+ exists(DataFlow::ContentSet f | DataFlowPrivate::storeStep(node1, f, node2) | containerContent(f))
}
/**
* Holds if content `c` is either a field, a synthetic field or language specific
* content of a relevant type or a container like content.
*/
-private predicate isRelevantContent(DataFlow::Content c) {
+private predicate isRelevantContent(DataFlow::ContentSet c) {
isRelevantTypeInContent(c) or
- DataFlowPrivate::containerContent(c)
+ containerContent(c)
}
/**
@@ -170,8 +168,8 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
- exists(DataFlow::Content c |
- DataFlowImplCommon::store(node1, c, node2, _, _) and
+ exists(DataFlow::ContentSet c |
+ DataFlowImplCommon::store(node1, c.getAStoreContent(), node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
@@ -180,7 +178,7 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {
)
)
or
- exists(DataFlow::Content c |
+ exists(DataFlow::ContentSet c |
DataFlowPrivate::readStep(node1, c, node2) and
isRelevantContent(c) and
state1.(TaintRead).getStep() + 1 = state2.(TaintRead).getStep()
diff --git a/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
index eb37aa93dbe..b881deb6e6a 100644
--- a/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
+++ b/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
@@ -303,3 +303,5 @@ predicate isRelevantSinkKind(string kind) {
*/
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
+
+predicate containerContent = DataFlowPrivate::containerContent/1;
From e84dda4fa66a3b0388dd3f92b271ca3be955d470 Mon Sep 17 00:00:00 2001
From: Kristen Newbury
Date: Thu, 15 Aug 2024 16:08:48 -0400
Subject: [PATCH 025/404] Update JS helmet model structure
---
.../helmet/Helmet.Required.Setting.model.yml | 2 +-
.../javascript/frameworks/helmet/Helmet.qll | 27 +++++++++++++++++++
.../ql/src/Security/CWE-693/CUSTOMIZING.md | 2 +-
.../ql/src/Security/CWE-693/InsecureHelmet.ql | 24 +----------------
4 files changed, 30 insertions(+), 25 deletions(-)
create mode 100644 javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.Required.Setting.model.yml b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.Required.Setting.model.yml
index ab01ec5206d..a8c14532152 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.Required.Setting.model.yml
+++ b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.Required.Setting.model.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/javascript-queries
+ pack: codeql/javascript-all
extensible: requiredHelmetSecuritySetting
data:
- ["frameguard"]
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
new file mode 100644
index 00000000000..9e241ee59c7
--- /dev/null
+++ b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
@@ -0,0 +1,27 @@
+/**
+ * Provides classes for working with Helmet
+ */
+
+import javascript
+
+class HelmetProperty extends DataFlow::Node instanceof DataFlow::PropWrite {
+ ExpressLibraries::HelmetRouteHandler helmet;
+
+ HelmetProperty() {
+ this = helmet.(DataFlow::CallNode).getAnArgument().getALocalSource().getAPropertyWrite()
+ }
+
+ ExpressLibraries::HelmetRouteHandler getHelmet() { result = helmet }
+
+ predicate isFalse() { DataFlow::PropWrite.super.getRhs().mayHaveBooleanValue(false) }
+
+ string getName() { result = DataFlow::PropWrite.super.getPropertyName() }
+
+ predicate isImportantSecuritySetting() {
+ // read from data extensions to allow enforcing custom settings
+ // defaults are located in javascript/ql/lib/semmle/frameworks/helmet/Helmet.Required.Setting.model.yml
+ requiredHelmetSecuritySetting(this.getName())
+ }
+}
+
+extensible predicate requiredHelmetSecuritySetting(string name);
diff --git a/javascript/ql/src/Security/CWE-693/CUSTOMIZING.md b/javascript/ql/src/Security/CWE-693/CUSTOMIZING.md
index 34ae2851a85..7eeb9e9b19a 100644
--- a/javascript/ql/src/Security/CWE-693/CUSTOMIZING.md
+++ b/javascript/ql/src/Security/CWE-693/CUSTOMIZING.md
@@ -24,7 +24,7 @@ A suitable [model pack](https://docs.github.com/en/code-security/codeql-cli/usin
name: my-org/javascript-helmet-insecure-config-model-pack
version: 1.0.0
extensionTargets:
- codeql/java-all: '*'
+ codeql/javascript-all: '*'
dataExtensions:
- models/**/*.yml
```
diff --git a/javascript/ql/src/Security/CWE-693/InsecureHelmet.ql b/javascript/ql/src/Security/CWE-693/InsecureHelmet.ql
index 8f837669ffc..66652e9e58a 100644
--- a/javascript/ql/src/Security/CWE-693/InsecureHelmet.ql
+++ b/javascript/ql/src/Security/CWE-693/InsecureHelmet.ql
@@ -12,30 +12,8 @@
*/
import javascript
-import DataFlow
import semmle.javascript.frameworks.ExpressModules
-
-class HelmetProperty extends DataFlow::Node instanceof DataFlow::PropWrite {
- ExpressLibraries::HelmetRouteHandler helmet;
-
- HelmetProperty() {
- this = helmet.(DataFlow::CallNode).getAnArgument().getALocalSource().getAPropertyWrite()
- }
-
- ExpressLibraries::HelmetRouteHandler getHelmet() { result = helmet }
-
- predicate isFalse() { DataFlow::PropWrite.super.getRhs().mayHaveBooleanValue(false) }
-
- string getName() { result = DataFlow::PropWrite.super.getPropertyName() }
-
- predicate isImportantSecuritySetting() {
- // read from data extensions to allow enforcing custom settings
- // defaults are located in javascript/ql/lib/semmle/frameworks/helmet/Helmet.Required.Setting.model.yml
- requiredHelmetSecuritySetting(this.getName())
- }
-}
-
-extensible predicate requiredHelmetSecuritySetting(string name);
+import semmle.javascript.frameworks.helmet.Helmet
from HelmetProperty helmetProperty, ExpressLibraries::HelmetRouteHandler helmet
where
From 81787a159e00136292bf9e1d0d63ddd737d77261 Mon Sep 17 00:00:00 2001
From: Kristen Newbury
Date: Thu, 15 Aug 2024 16:32:37 -0400
Subject: [PATCH 026/404] Add QL docs to helmet model
---
.../javascript/frameworks/helmet/Helmet.qll | 24 +++++++++++++++----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
index 9e241ee59c7..765b5a36fc4 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
@@ -4,6 +4,9 @@
import javascript
+/**
+ * A write to a property of a route handler from the "helmet" module.
+ */
class HelmetProperty extends DataFlow::Node instanceof DataFlow::PropWrite {
ExpressLibraries::HelmetRouteHandler helmet;
@@ -11,17 +14,28 @@ class HelmetProperty extends DataFlow::Node instanceof DataFlow::PropWrite {
this = helmet.(DataFlow::CallNode).getAnArgument().getALocalSource().getAPropertyWrite()
}
+ /**
+ * Gets the route handler associated to this property.
+ */
ExpressLibraries::HelmetRouteHandler getHelmet() { result = helmet }
+ /**
+ * Gets the boolean value of this property, if it may evaluate to a `Boolean`.
+ */
predicate isFalse() { DataFlow::PropWrite.super.getRhs().mayHaveBooleanValue(false) }
+ /**
+ * Gets the name of the `HelmetProperty`.
+ */
string getName() { result = DataFlow::PropWrite.super.getPropertyName() }
- predicate isImportantSecuritySetting() {
- // read from data extensions to allow enforcing custom settings
- // defaults are located in javascript/ql/lib/semmle/frameworks/helmet/Helmet.Required.Setting.model.yml
- requiredHelmetSecuritySetting(this.getName())
- }
+ /**
+ * read from data extensions to allow enforcing custom settings
+ */
+ predicate isImportantSecuritySetting() { requiredHelmetSecuritySetting(this.getName()) }
}
+/**
+ * defaults are located in `javascript/ql/lib/semmle/frameworks/helmet/Helmet.Required.Setting.model.yml`
+ */
extensible predicate requiredHelmetSecuritySetting(string name);
From 7dcdad066f474637ca14351c5b5817bc98aa8f24 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 16 Aug 2024 09:44:53 +0200
Subject: [PATCH 027/404] Update
javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
---
.../ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
index 765b5a36fc4..375e87e9646 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/helmet/Helmet.qll
@@ -2,7 +2,7 @@
* Provides classes for working with Helmet
*/
-import javascript
+private import javascript
/**
* A write to a property of a route handler from the "helmet" module.
From 0e3c867cb9920197cbc48d1fc5e977d4cd318d39 Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Fri, 16 Aug 2024 10:37:49 +0200
Subject: [PATCH 028/404] Toy rust program listing definitions of a cargo
project
---
rust/.gitignore | 2 +
rust/Cargo.lock | 1561 ++++++++++++++++++++++++++++++++++++++++++++++
rust/Cargo.toml | 9 +
rust/src/main.rs | 66 ++
4 files changed, 1638 insertions(+)
create mode 100644 rust/.gitignore
create mode 100644 rust/Cargo.lock
create mode 100644 rust/Cargo.toml
create mode 100644 rust/src/main.rs
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 00000000000..d81f12ed1b1
--- /dev/null
+++ b/rust/.gitignore
@@ -0,0 +1,2 @@
+/target
+/.idea
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
new file mode 100644
index 00000000000..3c02ab3803a
--- /dev/null
+++ b/rust/Cargo.lock
@@ -0,0 +1,1561 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "always-assert"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1078fa1ce1e34b1872d8611ad921196d76bdd7027e949fbe31231abde201892"
+dependencies = [
+ "tracing",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "camino"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chalk-derive"
+version = "0.98.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9426c8fd0fe61c3da880b801d3b510524df17843a8f9ec1f5b9cec24fb7412df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "chalk-ir"
+version = "0.98.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
+dependencies = [
+ "bitflags 2.6.0",
+ "chalk-derive",
+]
+
+[[package]]
+name = "chalk-recursive"
+version = "0.98.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "129dc03458f71cfb9c3cd621c9c68166a94e87b85b16ccd29af015d7ff9a1c61"
+dependencies = [
+ "chalk-derive",
+ "chalk-ir",
+ "chalk-solve",
+ "rustc-hash",
+ "tracing",
+]
+
+[[package]]
+name = "chalk-solve"
+version = "0.98.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7e8a8c1e928f98cdf227b868416ef21dcd8cc3c61b347576d783713444d41c8"
+dependencies = [
+ "chalk-derive",
+ "chalk-ir",
+ "ena",
+ "indexmap",
+ "itertools",
+ "petgraph",
+ "rustc-hash",
+ "tracing",
+]
+
+[[package]]
+name = "codeql-rust"
+version = "0.1.0"
+dependencies = [
+ "ra_ap_hir",
+ "ra_ap_load-cargo",
+ "ra_ap_project_model",
+]
+
+[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
+name = "cov-mark"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4"
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "drop_bomb"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "ena"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "filetime"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "fsevent-sys"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "fst"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "inotify"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
+dependencies = [
+ "bitflags 1.3.2",
+ "inotify-sys",
+ "libc",
+]
+
+[[package]]
+name = "inotify-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "jod-thread"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
+
+[[package]]
+name = "kqueue"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
+dependencies = [
+ "kqueue-sys",
+ "libc",
+]
+
+[[package]]
+name = "kqueue-sys"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
+[[package]]
+name = "la-arena"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "line-index"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67d61795376ae2683928c218fda7d7d7db136fd38c06b7552904667f0d55580a"
+dependencies = [
+ "nohash-hasher",
+ "text-size",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "lz4_flex"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "miow"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
+name = "notify"
+version = "6.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
+dependencies = [
+ "bitflags 2.6.0",
+ "crossbeam-channel",
+ "filetime",
+ "fsevent-sys",
+ "inotify",
+ "kqueue",
+ "libc",
+ "log",
+ "mio",
+ "walkdir",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "oorandom"
+version = "11.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "perf-event"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5396562cd2eaa828445d6d34258ae21ee1eb9d40fe626ca7f51c8dccb4af9d66"
+dependencies = [
+ "libc",
+ "perf-event-open-sys",
+]
+
+[[package]]
+name = "perf-event-open-sys"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ra-ap-rustc_abi"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
+dependencies = [
+ "bitflags 2.6.0",
+ "ra-ap-rustc_index",
+ "tracing",
+]
+
+[[package]]
+name = "ra-ap-rustc_index"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
+dependencies = [
+ "arrayvec",
+ "ra-ap-rustc_index_macros",
+ "smallvec",
+]
+
+[[package]]
+name = "ra-ap-rustc_index_macros"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "ra-ap-rustc_lexer"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
+dependencies = [
+ "unicode-properties",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ra-ap-rustc_parse_format"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
+dependencies = [
+ "ra-ap-rustc_index",
+ "ra-ap-rustc_lexer",
+]
+
+[[package]]
+name = "ra-ap-rustc_pattern_analysis"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
+dependencies = [
+ "ra-ap-rustc_index",
+ "rustc-hash",
+ "rustc_apfloat",
+ "smallvec",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_base_db"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aae8f3b0315876c1aa11dc0aac7795b27cb9e21a2a870667842e97e6b7dc3b5"
+dependencies = [
+ "la-arena",
+ "lz4_flex",
+ "ra_ap_cfg",
+ "ra_ap_intern",
+ "ra_ap_salsa",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_vfs",
+ "rustc-hash",
+ "semver",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_cfg"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac59191045154789b3b3f9f8c67f35439c3cbd9e36107dd62d04d4a4b2c95322"
+dependencies = [
+ "ra_ap_intern",
+ "ra_ap_tt",
+ "rustc-hash",
+]
+
+[[package]]
+name = "ra_ap_hir"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b321e9c63fd434ac7b1617d6ddaed7329b8e21f6cae06ca05fec91c6361bb40d"
+dependencies = [
+ "arrayvec",
+ "either",
+ "itertools",
+ "once_cell",
+ "ra_ap_base_db",
+ "ra_ap_cfg",
+ "ra_ap_hir_def",
+ "ra_ap_hir_expand",
+ "ra_ap_hir_ty",
+ "ra_ap_intern",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_tt",
+ "rustc-hash",
+ "smallvec",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_hir_def"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc0e2dd39d1162650dd6668f1e179d24aec336e3ff12aae9f0e50c10928d1158"
+dependencies = [
+ "arrayvec",
+ "bitflags 2.6.0",
+ "cov-mark",
+ "dashmap",
+ "drop_bomb",
+ "either",
+ "fst",
+ "hashbrown",
+ "indexmap",
+ "itertools",
+ "la-arena",
+ "once_cell",
+ "ra-ap-rustc_abi",
+ "ra-ap-rustc_parse_format",
+ "ra_ap_base_db",
+ "ra_ap_cfg",
+ "ra_ap_hir_expand",
+ "ra_ap_intern",
+ "ra_ap_limit",
+ "ra_ap_mbe",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_tt",
+ "rustc-hash",
+ "rustc_apfloat",
+ "smallvec",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_hir_expand"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e3c27bb239be9c6f835790f32d1a6e82345713c2ea63605d3270f06daf5caf"
+dependencies = [
+ "cov-mark",
+ "either",
+ "hashbrown",
+ "itertools",
+ "la-arena",
+ "ra_ap_base_db",
+ "ra_ap_cfg",
+ "ra_ap_intern",
+ "ra_ap_limit",
+ "ra_ap_mbe",
+ "ra_ap_parser",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_syntax-bridge",
+ "ra_ap_tt",
+ "rustc-hash",
+ "smallvec",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_hir_ty"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c437b9971d421dea75d72f137ccd49612cff401b9118da0d5b31c033ea0bcf3d"
+dependencies = [
+ "arrayvec",
+ "bitflags 2.6.0",
+ "chalk-derive",
+ "chalk-ir",
+ "chalk-recursive",
+ "chalk-solve",
+ "cov-mark",
+ "either",
+ "ena",
+ "indexmap",
+ "itertools",
+ "la-arena",
+ "nohash-hasher",
+ "once_cell",
+ "oorandom",
+ "ra-ap-rustc_abi",
+ "ra-ap-rustc_index",
+ "ra-ap-rustc_pattern_analysis",
+ "ra_ap_base_db",
+ "ra_ap_hir_def",
+ "ra_ap_hir_expand",
+ "ra_ap_intern",
+ "ra_ap_limit",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "rustc-hash",
+ "rustc_apfloat",
+ "scoped-tls",
+ "smallvec",
+ "tracing",
+ "triomphe",
+ "typed-arena",
+]
+
+[[package]]
+name = "ra_ap_ide_db"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa72d28b51a29efd6416979b43976663708894cee98fb44cabaa222cb049417"
+dependencies = [
+ "arrayvec",
+ "bitflags 2.6.0",
+ "cov-mark",
+ "crossbeam-channel",
+ "either",
+ "fst",
+ "indexmap",
+ "itertools",
+ "line-index",
+ "memchr",
+ "nohash-hasher",
+ "once_cell",
+ "ra_ap_base_db",
+ "ra_ap_hir",
+ "ra_ap_limit",
+ "ra_ap_parser",
+ "ra_ap_profile",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_text_edit",
+ "rayon",
+ "rustc-hash",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_intern"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeec56dfe875fd865cc9afb7d0a0a6af832bf323b6db461844ba1de2fa7006b"
+dependencies = [
+ "dashmap",
+ "hashbrown",
+ "rustc-hash",
+ "sptr",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_limit"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6d33f2d8eda04460315de38992e3681dd799c27e712788a7aeee9909bc1c0f5"
+
+[[package]]
+name = "ra_ap_load-cargo"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b7a4626cb8477733b02d836a7215db629249dd0076b2c9edcfe3f3a4e639f33"
+dependencies = [
+ "anyhow",
+ "crossbeam-channel",
+ "itertools",
+ "ra_ap_hir_expand",
+ "ra_ap_ide_db",
+ "ra_ap_intern",
+ "ra_ap_paths",
+ "ra_ap_proc_macro_api",
+ "ra_ap_project_model",
+ "ra_ap_span",
+ "ra_ap_tt",
+ "ra_ap_vfs",
+ "ra_ap_vfs-notify",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_mbe"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e45337e002d5c0efcba96769da0bd2d94c4e66e2d315f627fea7c877380141"
+dependencies = [
+ "arrayvec",
+ "cov-mark",
+ "ra_ap_intern",
+ "ra_ap_parser",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_syntax-bridge",
+ "ra_ap_tt",
+ "rustc-hash",
+ "smallvec",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_parser"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "094cb41571e0c97501e1171f638bfdf4edb5f376d283b1ab04f3411aa3a44f51"
+dependencies = [
+ "drop_bomb",
+ "ra-ap-rustc_lexer",
+ "ra_ap_limit",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_paths"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6602581f62922de09c93fdf3d6ddffa3f4fdcafba6c9dd08827951fb5ad511cc"
+dependencies = [
+ "camino",
+ "serde",
+]
+
+[[package]]
+name = "ra_ap_proc_macro_api"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a2480058c6228d6ccdd6cfe1fa01c081f081ec6a6ea1a241b88618576ae6eff"
+dependencies = [
+ "indexmap",
+ "ra_ap_base_db",
+ "ra_ap_intern",
+ "ra_ap_paths",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_tt",
+ "rustc-hash",
+ "serde",
+ "serde_json",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_profile"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60263554aef637b9143a2d480a7fa93661297d795f7c46108386454a86885d49"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "perf-event",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "ra_ap_project_model"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ff3383dcd32a63b914c59f42f38e81ebfa5d5d921067ca0d1b01585aed1a4bd"
+dependencies = [
+ "anyhow",
+ "cargo_metadata",
+ "itertools",
+ "la-arena",
+ "ra_ap_base_db",
+ "ra_ap_cfg",
+ "ra_ap_intern",
+ "ra_ap_paths",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_toolchain",
+ "rustc-hash",
+ "semver",
+ "serde",
+ "serde_json",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_salsa"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f6e29648a086801b2bd047a88e9b73782c354fca9117d1208bebbfd6a849a61"
+dependencies = [
+ "indexmap",
+ "itertools",
+ "lock_api",
+ "oorandom",
+ "parking_lot",
+ "ra_ap_salsa-macros",
+ "rustc-hash",
+ "smallvec",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_salsa-macros"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d104209e089a63a1aa8f9a5f06c6350b1508d856fca4c25002f1d8e7c04a685b"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ra_ap_span"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d30beb9ac55357e4c56f8f8e5c84571ce204f10ba43b55640c51053e806e6b8"
+dependencies = [
+ "hashbrown",
+ "la-arena",
+ "ra_ap_salsa",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_vfs",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "ra_ap_stdx"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51c29e7dfc7282ed14e91419d09b59e8dca27e7cc10c3cfcbf1964d7db0bef34"
+dependencies = [
+ "always-assert",
+ "crossbeam-channel",
+ "itertools",
+ "jod-thread",
+ "libc",
+ "miow",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "ra_ap_syntax"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "575c1e3deb1cb39e0939a95a4cb4339ee95b1283157cf03bc1bdaf76e6ebca8d"
+dependencies = [
+ "cov-mark",
+ "either",
+ "indexmap",
+ "itertools",
+ "once_cell",
+ "ra-ap-rustc_lexer",
+ "ra_ap_parser",
+ "ra_ap_stdx",
+ "ra_ap_text_edit",
+ "rowan",
+ "rustc-hash",
+ "smol_str",
+ "tracing",
+ "triomphe",
+]
+
+[[package]]
+name = "ra_ap_syntax-bridge"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f19c53a1f9f50d76f50b7c0c83cda0fa5de09a182d8f4ae3575ea986a24fa234"
+dependencies = [
+ "ra_ap_intern",
+ "ra_ap_parser",
+ "ra_ap_span",
+ "ra_ap_stdx",
+ "ra_ap_syntax",
+ "ra_ap_tt",
+ "rustc-hash",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_text_edit"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b38bb92b8924b2267d84524f09cc31851f7f93741811e5374701ceb2be47d0b"
+dependencies = [
+ "itertools",
+ "text-size",
+]
+
+[[package]]
+name = "ra_ap_toolchain"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18076d6d6b2ef93a9d0396b06d58369231e50916456d2fa4488bc78c97ea6714"
+dependencies = [
+ "camino",
+ "home",
+]
+
+[[package]]
+name = "ra_ap_tt"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "851bc23c870c67bd729e33056e001a2e07b16a31da13affe73d78e77049b12b8"
+dependencies = [
+ "arrayvec",
+ "ra-ap-rustc_lexer",
+ "ra_ap_intern",
+ "ra_ap_stdx",
+ "text-size",
+]
+
+[[package]]
+name = "ra_ap_vfs"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3b152d631514508b7322cda383a454e2a56b0b5ecd8562a8f1e40aabe9fb236"
+dependencies = [
+ "fst",
+ "indexmap",
+ "nohash-hasher",
+ "ra_ap_paths",
+ "ra_ap_stdx",
+ "rustc-hash",
+ "tracing",
+]
+
+[[package]]
+name = "ra_ap_vfs-notify"
+version = "0.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e516915fb4822a3d480404d35c20f240ae16a297ef52a68dc8cff748093f3abd"
+dependencies = [
+ "crossbeam-channel",
+ "notify",
+ "ra_ap_paths",
+ "ra_ap_stdx",
+ "ra_ap_vfs",
+ "rayon",
+ "rustc-hash",
+ "tracing",
+ "walkdir",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
+[[package]]
+name = "rowan"
+version = "0.15.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
+dependencies = [
+ "countme",
+ "hashbrown",
+ "memoffset",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_apfloat"
+version = "0.2.1+llvm-462a31f5a5ab"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "886d94c63c812a8037c4faca2607453a0fa4cf82f734665266876b022244543f"
+dependencies = [
+ "bitflags 1.3.2",
+ "smallvec",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.207"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.207"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "sptr"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "syn"
+version = "2.0.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "text-size"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
+
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "triomphe"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "typed-arena"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-properties"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 00000000000..e36cdfced04
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "codeql-rust"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+ra_ap_hir = "0.0.229"
+ra_ap_load-cargo = "0.0.229"
+ra_ap_project_model = "0.0.229"
diff --git a/rust/src/main.rs b/rust/src/main.rs
new file mode 100644
index 00000000000..f5f966119d7
--- /dev/null
+++ b/rust/src/main.rs
@@ -0,0 +1,66 @@
+use ra_ap_hir::{Crate, ModuleDef, Name, HirDisplay, DefWithBody};
+use ra_ap_project_model::CargoConfig;
+use std::env;
+use std::path::Path;
+use ra_ap_load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
+
+fn extract_name(n: Option) -> String {
+ match n {
+ Some(v) => v.as_str().to_owned(),
+ None => String::from(""),
+ }
+}
+
+fn main() {
+ let args: Vec = env::args().collect();
+ let config = CargoConfig { ..Default::default() };
+ let no_progress = &|_| ();
+ let load_config = LoadCargoConfig {
+ load_out_dirs_from_check: true,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+ prefill_caches: false,
+ };
+ let (db, vfs, macro_server) = load_workspace_at(&Path::new(&args[1]), &config, &load_config, no_progress).unwrap();
+ let mut worklist: Vec<_> =
+ Crate::all(&db).into_iter().map(|krate| krate.root_module()).collect();
+
+ while let Some(module) = worklist.pop() {
+ println!("Module: {}", extract_name(module.name(&db)));
+ for d in module.declarations(&db) {
+ match d {
+ ModuleDef::Module(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Function(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Adt(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Variant(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Const(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Static(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::Trait(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::TraitAlias(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::TypeAlias(e) => {
+ println!(" {}", e.display(&db));
+ }
+ ModuleDef::BuiltinType(_e) => {}
+ ModuleDef::Macro(e) => {
+ println!(" {}", e.display(&db));
+ }
+ }
+ }
+ worklist.extend(module.children(&db));
+ }
+}
From 0126fbcb8f2cb9d7f38f8adce31716981f7a8422 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 16 Aug 2024 10:56:46 +0100
Subject: [PATCH 029/404] Swift: Clear the language for Swift code snippets
that are rendering incorrectly.
---
.../queries/Security/CWE-020/IncompleteHostnameRegex.qhelp | 4 ++--
swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp | 2 +-
swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelp | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
index ef374fc9752..347a0ee0e29 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
@@ -46,7 +46,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
index b406faf8e17..c312fb1a6f5 100644
--- a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
+++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
@@ -28,7 +28,7 @@ likely to handle corner cases correctly than a custom implementation.
The following example attempts to filters out all <script> tags.
-
+
The above sanitizer does not filter out all <script> tags.
diff --git a/swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelp b/swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelp
index ddbb2835bc2..e641d9b4e61 100644
--- a/swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelp
+++ b/swift/ql/src/queries/Security/CWE-1333/ReDoS.qhelp
@@ -3,7 +3,7 @@
Consider the following regular expression:
-
+
/^_(__|.)+_$/
Its sub-expression "(__|.)+" can match the string
@@ -19,7 +19,7 @@
the ambiguity between the two branches of the alternative inside the
repetition:
-
+
/^_(__|[^_])+_$/
From 2d19d6f61ecc12f8a888f5a5cdd76888e0c01763 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 16 Aug 2024 11:40:57 +0100
Subject: [PATCH 030/404] Swift: Fix two of the qhelps by slightly modifying
the sample code instead.
---
.../queries/Security/CWE-020/IncompleteHostnameRegex.qhelp | 4 ++--
.../queries/Security/CWE-020/IncompleteHostnameRegexBad.swift | 4 ++--
.../Security/CWE-020/IncompleteHostnameRegexGood.swift | 4 ++--
swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp | 2 +-
swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift | 2 +-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
index 347a0ee0e29..ef374fc9752 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
@@ -46,7 +46,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
index 3e28022ab98..6f553b2fbee 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
@@ -1,11 +1,11 @@
-func handleUrl(_ urlString: String) {
+func handleUrl(_ urlString: String) throws {
// get the 'url=' parameter from the URL
let components = URLComponents(string: urlString)
let redirectParam = components?.queryItems?.first(where: { $0.name == "url" })
// check we trust the host
- let regex = #/^(www|beta).example.com//# // BAD
+ let regex = try Regex("^(www|beta).example.com/") // BAD
if let match = redirectParam?.value?.firstMatch(of: regex) {
// ... trust the URL ...
}
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
index fad4135a263..1413a7ffa73 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
@@ -1,11 +1,11 @@
-func handleUrl(_ urlString: String) {
+func handleUrl(_ urlString: String) throws {
// get the 'url=' parameter from the URL
let components = URLComponents(string: urlString)
let redirectParam = components?.queryItems?.first(where: { $0.name == "url" })
// check we trust the host
- let regex = #/^(www|beta)\.example\.com//# // GOOD
+ let regex = try Regex("^(www|beta)\\.example\\.com/") // GOOD
if let match = redirectParam?.value?.firstMatch(of: regex) {
// ... trust the URL ...
}
diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
index c312fb1a6f5..b406faf8e17 100644
--- a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
+++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
@@ -28,7 +28,7 @@ likely to handle corner cases correctly than a custom implementation.
The following example attempts to filters out all <script> tags.
-
+
The above sanitizer does not filter out all <script> tags.
diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
index d399bf5a166..f2a8273d31a 100644
--- a/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
+++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
@@ -1,4 +1,4 @@
-let script_tag_regex = /")
var old_html = ""
while (html != old_html) {
From 0088ece3ea7772811f7c2c75a2a75e16abb49d66 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:24:03 +0100
Subject: [PATCH 031/404] Revert "Swift: Fix two of the qhelps by slightly
modifying the sample code instead."
This reverts commit 2d19d6f61ecc12f8a888f5a5cdd76888e0c01763.
---
.../queries/Security/CWE-020/IncompleteHostnameRegex.qhelp | 4 ++--
.../queries/Security/CWE-020/IncompleteHostnameRegexBad.swift | 4 ++--
.../Security/CWE-020/IncompleteHostnameRegexGood.swift | 4 ++--
swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp | 2 +-
swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift | 2 +-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
index ef374fc9752..347a0ee0e29 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegex.qhelp
@@ -46,7 +46,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
index 6f553b2fbee..3e28022ab98 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexBad.swift
@@ -1,11 +1,11 @@
-func handleUrl(_ urlString: String) throws {
+func handleUrl(_ urlString: String) {
// get the 'url=' parameter from the URL
let components = URLComponents(string: urlString)
let redirectParam = components?.queryItems?.first(where: { $0.name == "url" })
// check we trust the host
- let regex = try Regex("^(www|beta).example.com/") // BAD
+ let regex = #/^(www|beta).example.com//# // BAD
if let match = redirectParam?.value?.firstMatch(of: regex) {
// ... trust the URL ...
}
diff --git a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
index 1413a7ffa73..fad4135a263 100644
--- a/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
+++ b/swift/ql/src/queries/Security/CWE-020/IncompleteHostnameRegexGood.swift
@@ -1,11 +1,11 @@
-func handleUrl(_ urlString: String) throws {
+func handleUrl(_ urlString: String) {
// get the 'url=' parameter from the URL
let components = URLComponents(string: urlString)
let redirectParam = components?.queryItems?.first(where: { $0.name == "url" })
// check we trust the host
- let regex = try Regex("^(www|beta)\\.example\\.com/") // GOOD
+ let regex = #/^(www|beta)\.example\.com//# // GOOD
if let match = redirectParam?.value?.firstMatch(of: regex) {
// ... trust the URL ...
}
diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
index b406faf8e17..c312fb1a6f5 100644
--- a/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
+++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilter.qhelp
@@ -28,7 +28,7 @@ likely to handle corner cases correctly than a custom implementation.
The following example attempts to filters out all <script> tags.
-
+
The above sanitizer does not filter out all <script> tags.
diff --git a/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
index f2a8273d31a..d399bf5a166 100644
--- a/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
+++ b/swift/ql/src/queries/Security/CWE-116/BadTagFilterBad.swift
@@ -1,4 +1,4 @@
-let script_tag_regex = try Regex("")
+let script_tag_regex = /