From 796075f9dc98b9bf4f7c52bc4e1a1e41f7c52560 Mon Sep 17 00:00:00 2001 From: amammad Date: Sun, 18 Jun 2023 20:22:10 +1000 Subject: [PATCH 01/11] V1 Bombs --- .../CWE-522-DecompressionBombs/BombsV1.ql | 479 ++++++++++++++++++ .../CWE-522-DecompressionBombs/BombsV2.ql | 179 +++++++ .../DecompressionBombs.qhelp | 31 ++ .../CWE-522-DecompressionBombs/example_bad.rb | 5 + .../example_good.rb | 11 + .../DecompressionBombsV1.expected | 130 +++++ .../DecompressionBombsV1.qlref | 1 + .../DecompressionBombsV2.expected | 87 ++++ .../DecompressionBombsV2.qlref | 1 + .../CWE-522-DecompressionBombs/gzipBombs.rb | 24 + .../CWE-522-DecompressionBombs/zipBombs.rb | 55 ++ 11 files changed, 1003 insertions(+) create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_bad.rb create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_good.rb create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql new file mode 100644 index 00000000000..74c08c6e541 --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql @@ -0,0 +1,479 @@ +/** + * @name User-controlled file decompression + * @description User-controlled data that flows into decompression library APIs could be dangerous + * @kind path-problem + * @problem.severity error + * @security-severity 7.8 + * @precision high + * @id rb/user-controlled-file-decompression + * @tags security + * experimental + * external/cwe/cwe-409 + */ + +import codeql.ruby.AST +import codeql.ruby.ApiGraphs +import codeql.ruby.DataFlow +import codeql.ruby.dataflow.RemoteFlowSources +import codeql.ruby.TaintTracking +import DataFlow::PathGraph + +module Zip { + module InputStream { + /** + * `Zip::InputStream` + */ + private API::Node zipInputStream() { + result = [API::getTopLevelMember("Zip").getMember("InputStream")] + } + + /** + * input in following + * ```ruby + * input = ip::InputStream.open(path) + * Zip::InputStream.open(path) do |input| + * ... + * end + * ``` + */ + private API::Node instance() { + result = + [zipInputStream().getMethod("open").(GetReturnOrGetBlock).getReturnOrGetBlockParameter()] + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipInputStreamOpen | + zipInputStreamOpen = zipInputStream().getMethod("open") + | + nodeFrom = zipInputStreamOpen.getParameter(0).asSink() and + nodeTo = + [ + zipInputStreamOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .getMethod(_) + .getReturn() + .asSource() + ] + ) + } + + DataFlow::Node isSink() { + exists(string ioMethods | not ioMethods = ["get_next_entry"] | + result = instance().getMethod(ioMethods).getReturn().asSource() + ) + } + } + + class AdditionalGlobStep extends API::Node { + API::Node additionalGlobStepHelper() { + result = this + or + result = this.getMethod("glob").getReturn() + } + } + + class GetReturnOrGetBlock extends API::Node { + API::Node getReturnOrGetBlockParameter() { + result = this.getBlock().getParameter(0) or + result = this.getReturn() + } + } + + module File { + /** + * ```ruby + * Zip::File.open(path) + * # or + * Zip::File.new(path) + * ``` + */ + module OpenAndNew { + /** + * myZip in following + * + * ```ruby + * myZip = Zip::File.open(path) + * # or + * myZip = Zip::File.new(path) + * + * Zip::File.open(path) do |myZip| + * + * end + * ``` + */ + API::Node instance() { + result = + [ + zipFile() + .getMethod(["open", "new"]) + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + ] + } + + /** + * `extract` and `read` can be sink + * ```ruby + * ``` + */ + DataFlow::Node isSink() { + result = + instance() + .getMethod(["each", "each_entry", "first"]) + .getBlock() + .getParameter(0) + .getMethod(["extract", "read", "first"]) + .getReturn() + .asSource() + or + result = + instance() + .getMethod(["each", "each_entry", "first"]) + .getBlock() + .getParameter(0) + .getMethod("get_input_stream") + .getReturn() + .asSource() + or + exists(string ioMethods | not ioMethods = ["each", "each_entry", "glob", "first"] | + result = instance().getAMethodCall(ioMethods) + ) + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipFileOpen, API::Node zipFileOpenGlob | + zipFileOpen = zipFile().getMethod(["open", "new"]) + | + nodeFrom = zipFileOpen.getParameter(0).asSink() and + zipFileOpenGlob = zipFileOpen.(GetReturnOrGetBlock).getReturnOrGetBlockParameter() and + nodeTo = + [ + zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .(AdditionalGlobStep) + .additionalGlobStepHelper() + .asSource(), + zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .(AdditionalGlobStep) + .additionalGlobStepHelper() + .getMethod(["read", "extract"]) + .getReturn() + .asSource(), + zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .(AdditionalGlobStep) + .additionalGlobStepHelper() + .getMethod(["each", "each_entry", "first"]) + .getBlock() + .getParameter(0) + .asSource(), + isAdditionalTaintStepHelper(zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .(AdditionalGlobStep) + .additionalGlobStepHelper() + .getMethod(["each", "each_entry", "first"]) + .getBlock() + .getParameter(0)) + ] + ) + } + } + + /** + * # Find specific entry with Zip::File.open(zipfile_path).glob(pattern) + */ + module Glob { + API::Node instance() { + result = + [ + zipFile().getMethod(["open", "new"]).getReturn().getMethod("glob"), + zipFile().getMethod(["open", "new"]).getBlock().getParameter(0).getMethod("glob") + ] + } + + /** + * `extract` and `read` can be sink + * ```ruby + * ``` + */ + DataFlow::Node isSink() { + result = + zipFile() + .getMethod(["open", "new"]) + .getReturn() + .getMethod("glob") + .getBlock() + .getParameter(0) + .getMethod("get_input_stream") + .getReturn() + .asSource() + or + result = + zipFile() + .getMethod(["open", "new"]) + .getReturn() + .getMethod("glob") + .getReturn() + .getMethod("get_input_stream") + .getReturn() + .asSource() + or + result = + zipFile() + .getMethod(["open", "new"]) + .getBlock() + .getParameter(0) + .getMethod("glob") + .getReturn() + .getMethod("first") + .getReturn() + .getMethod("get_input_stream") + .getReturn() + .asSource() + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipFileOpen | zipFileOpen = zipFile().getMethod(["open", "new"]) | + nodeFrom = zipFileOpen.getParameter(0).asSink() and + nodeTo = + [ + isAdditionalTaintStepHelper(zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .getMethod("glob") + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter()) + ] + ) + } + } + + /** + * `Zip::File` + */ + private API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } + + /** + * ```ruby + * returun = inputNode do + * returun.each do |entry| + * outputnode = entry + * end + * ``` + */ + API::Node oneBlockParameter(API::Node nodeMiddle) { + result = + nodeMiddle.getReturn().getMethod(["each", "each_entry", "first"]).getBlock().getParameter(0) + } + + /** + * ```ruby + * inputNode do |param| + * param.each do |entry| + * outputnode = entry + * end + * end + * ``` + */ + API::Node twoBlockParameter(API::Node nodeMiddle) { + result = + nodeMiddle + .getBlock() + .getParameter(0) + .getMethod(["each", "each_entry", "first"]) + .getBlock() + .getParameter(0) + } + + DataFlow::Node isAdditionalTaintStepHelper(API::Node nodeMiddle) { + result = nodeMiddle.getMethod(_).getReturn().asSource() or + result = nodeMiddle.getMethod(_).getReturn().getMethod(_).getReturn().asSource() + } + } + + DataFlow::Node isSink() { + result = [File::Glob::isSink(), File::OpenAndNew::isSink(), InputStream::isSink()] + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + File::Glob::isAdditionalTaintStep(nodeFrom, nodeTo) + or + File::OpenAndNew::isAdditionalTaintStep(nodeFrom, nodeTo) + or + InputStream::isAdditionalTaintStep(nodeFrom, nodeTo) + } +} + +module Zlib { + /** + * `zlib::GzipReader` + */ + API::Node gzipReaderInstance() { result = API::getTopLevelMember("Zlib").getMember("GzipReader") } + + API::Node gzipReaderOpen() { + result = + [ + gzipReaderInstance().getMethod("open").getReturn(), + gzipReaderInstance().getMethod("open").getBlock().getParameter(0) + ] + } + + API::Node gzipReaderNew() { result = [gzipReaderInstance().getMethod("new").getReturn()] } + + /** + * `entry` and `read` can be sink + * ```ruby + *Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + * uncompressedfile.each do |entry| + * entry + * end + *end + * + *uncompressedfile = Zlib::GzipReader.open(gzip_path) + *uncompressedfile.each do |entry| + * entry + *end + * + *Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read + *Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| + * entry + *end + * ``` + */ + DataFlow::Node isSink() { + result = + gzipReaderOpen() + .getMethod(["glob", "each", "each_entry"]) + .getBlock() + .getParameter(0) + .asSource() + or + result = + gzipReaderNew() + .getMethod(["glob", "each", "each_entry"]) + .getBlock() + .getParameter(0) + .asSource() + or + // _ is one of ["read", "readlines", "readpartial", "readline", "gets"] and more because gzipReader return an IO instance, there are alot of methods and gzipReader is for reading gzip files, so there is low FP rate here if we use _ instead of exact IO method names + exists(string ioMethods | not ioMethods = ["glob", "each", "each_entry"] | + result = gzipReaderNew().getMethod(ioMethods).getReturn().asSource() or + result = gzipReaderOpen().getMethod(ioMethods).getReturn().asSource() + ) + or + // no need for any other methods after opening gzip with zcat method + result = gzipReaderInstance().getMethod("zcat").getParameter(0).asSink() + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + isAdditionalTaintStepCustomizeMiddleNode(nodeFrom, nodeTo, gzipReaderOpen()) or + isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "open", gzipReaderInstance()) or + isAdditionalTaintStepCustomizeMiddleNode(nodeFrom, nodeTo, gzipReaderNew()) or + isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "new", gzipReaderInstance()) or + isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "zcat", gzipReaderInstance()) + } + + /** + * `uncompressedfile` is nodeFrom/nodeMiddle + * + * `entry` is nodeTo + * + * ```ruby + * Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + * uncompressedfile.each do |entry| + * entry.bytes + * end + *end + *uncompressedfile = Zlib::GzipReader.open(gzip_path) + *uncompressedfile.each do |entry| + * entry.read + *end + * ``` + */ + predicate isAdditionalTaintStepCustomizeMiddleNode( + DataFlow::Node nodeFrom, DataFlow::Node nodeTo, API::Node nodeMiddle + ) { + nodeFrom = nodeMiddle.asSource() and + nodeTo = + nodeMiddle.getMethod(["glob", "each", "each_entry"]).getBlock().getParameter(0).asSource() + } + + /** + * `gzip_path` is nodeFrom + * + * `compressed_file`/`Zlib::GzipReader.open(gzip_path)` are nodeTo + * + * `Zlib::GzipReader` is nodeInstance + * + * `open` is beginMethodName + * ```ruby + *Zlib::GzipReader.open(gzip_path) do |compressed_file| + * compressed_file.each do |entry| + * entry.bytes + * end + *end + *``` + */ + predicate isAdditionalTaintStepCustomizeBeginningNode( + DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string beginMethodName, API::Node nodeInstance + ) { + nodeFrom = nodeInstance.getMethod(beginMethodName).getParameter(0).asSink() and + nodeTo = + [ + nodeInstance.getMethod(beginMethodName).getBlock().getParameter(0).asSource(), + nodeInstance + .getMethod(beginMethodName) + .getBlock() + .getParameter(0) + .getMethod(_) + .getReturn() + .asSource(), nodeInstance.getMethod(beginMethodName).getReturn().asSource(), + nodeInstance.getMethod(beginMethodName).getReturn().getMethod(_).getReturn().asSource() + ] + } +} + +class Bombs extends TaintTracking::Configuration { + Bombs() { this = "Decompression Bombs" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource or + source instanceof DataFlow::LocalSourceNode + } + + override predicate isSink(DataFlow::Node sink) { sink = [Zip::isSink(), Zlib::isSink()] } + + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + Zip::isAdditionalTaintStep(nodeFrom, nodeTo) + or + Zlib::isAdditionalTaintStep(nodeFrom, nodeTo) + or + exists(API::Node n | n = API::root().getMember("File").getMethod("open") | + nodeFrom = n.getParameter(0).asSink() and + nodeTo = n.getReturn().asSource() + ) + or + exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") | + nodeFrom = n.getParameter(0).asSink() and + nodeTo = n.getReturn().asSource() + ) + or + exists(DataFlow::CallNode cn | + cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" + | + nodeFrom = cn.getArgument(0) and + nodeTo = cn + ) + } +} + +from Bombs cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(), + "potentially untrusted source" diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql new file mode 100644 index 00000000000..e4c92e6266d --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql @@ -0,0 +1,179 @@ +/** + * @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 rb/user-controlled-file-decompression + * @tags security + * experimental + * external/cwe/cwe-409 + */ + +import codeql.ruby.AST +import codeql.ruby.ApiGraphs +import codeql.ruby.DataFlow +import codeql.ruby.dataflow.RemoteFlowSources +import codeql.ruby.TaintTracking +import DataFlow::PathGraph + +module DecompressionBombs { + abstract class DecompressionBombSink extends DataFlow::Node { } + + module Zlib { + /** + * `Zlib::GzipReader` + * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. + * + * according to above warning from Doc we don't need to go forward after open() + * or new() methods, we just need the argument node of them + */ + private API::Node gzipReaderInstance() { + result = API::getTopLevelMember("Zlib").getMember("GzipReader") + } + + /** + * return values of following methods + * `Zlib::GzipReader.open` + * `Zlib::GzipReader.zcat` + * `Zlib::GzipReader.new` + */ + class ZipSink extends DecompressionBombSink { + ZipSink() { + this = gzipReaderInstance().getMethod(["open", "new", "zcat"]).getReturn().asSource() + } + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipnode | zipnode = gzipReaderInstance().getMethod(["open", "new", "zcat"]) | + nodeFrom = zipnode.getParameter(0).asSink() and + nodeTo = zipnode.getReturn().asSource() + ) + } + } + + module ZipInputStream { + /** + * `Zip::InputStream` + * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. + * + * according to above warning from Doc we don't need to go forward after open() + * or new() methods, we just need the argument node of them + */ + private API::Node zipInputStream() { + result = API::getTopLevelMember("Zip").getMember("InputStream") + } + + /** + * return values of following methods + * `ZipIO.read` + * `ZipEntry.extract` + */ + class ZipSink extends DecompressionBombSink { + ZipSink() { this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource() } + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipnode | zipnode = zipInputStream().getMethod(["open", "new"]) | + nodeFrom = zipnode.getParameter(0).asSink() and + nodeTo = zipnode.getReturn().asSource() + ) + } + } + + module ZipFile { + // // Because of additional step and ZipSink predicates, I couldn't use unary predicate + // // I put the explanation because I think there should be a soloution to not use other rubyZipNode predicate + // API::Node rubyZipNode() { + // result = zipFile() or + // result = rubyZipNode().getMethod(_).getReturn() or + // result = rubyZipNode().getMethod(_).getBlock().getParameter(_) or + // result = rubyZipNode().getMethod(_).getParameter(0) or + // result = rubyZipNode().getAnElement() + // } + API::Node rubyZipNode(API::Node n) { + result = n + or + result = rubyZipNode(n).getMethod(_).getReturn() + or + result = rubyZipNode(n).getMethod(_).getBlock().getParameter(_) + or + result = rubyZipNode(n).getMethod(_).getParameter(0) + or + result = rubyZipNode(n).getAnElement() + } + + /** + * return values of following methods + * `ZipIO.read` + * `ZipEntry.extract` + * sanitize the nodes which have `entry.size > someOBJ` + */ + class ZipSink extends DecompressionBombSink { + ZipSink() { + exists(API::Node zipnodes | zipnodes = zipFile() | + this = rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource() and + not exists( + rubyZipNode(zipnodes).getMethod("size").getReturn().getMethod(">").getParameter(0) + ) + ) + } + } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipnodes | zipnodes = zipFile() | + nodeTo = [rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource()] and + nodeFrom = zipnodes.getMethod(["new", "open"]).getParameter(0).asSink() + ) + } + + /** + * `Zip::File` + */ + private API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } + } +} + +class Bombs extends TaintTracking::Configuration { + Bombs() { this = "Decompression Bombs" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource or + source instanceof DataFlow::LocalSourceNode + } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof DecompressionBombs::DecompressionBombSink + } + + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + DecompressionBombs::ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) + or + DecompressionBombs::ZipInputStream::isAdditionalTaintStep(nodeFrom, nodeTo) + or + DecompressionBombs::Zlib::isAdditionalTaintStep(nodeFrom, nodeTo) + or + exists(API::Node n | n = API::root().getMember("File").getMethod("open") | + nodeFrom = n.getParameter(0).asSink() and + nodeTo = n.getReturn().asSource() + ) + or + exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") | + nodeFrom = n.getParameter(0).asSink() and + nodeTo = n.getReturn().asSource() + ) + or + exists(DataFlow::CallNode cn | + cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" + | + nodeFrom = cn.getArgument(0) and + nodeTo = cn + ) + } +} + +from Bombs cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(), + "potentially untrusted source" diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp new file mode 100644 index 00000000000..71ceda79caa --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp @@ -0,0 +1,31 @@ + + + +

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.

+ +
+

Please read official RubyZip Documentation here + +

Rubyzip: According to official Documentation

+ + + + + +
  • +CVE-2023-22898 +
  • +
  • +A great research to gain more impact by this kind of attack +
  • + +
    +
    diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_bad.rb b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_bad.rb new file mode 100644 index 00000000000..2195921ac28 --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_bad.rb @@ -0,0 +1,5 @@ +# "Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes" +zip_stream = Zip::InputStream.new(File.open('file.zip')) +while entry = zip_stream.get_next_entry + # All required operations on `entry` go here. +end \ No newline at end of file diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_good.rb b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_good.rb new file mode 100644 index 00000000000..0785901ef6c --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/example_good.rb @@ -0,0 +1,11 @@ +MAX_FILE_SIZE = 10 * 1024**2 # 10MiB +MAX_FILES = 100 +Zip::File.open('foo.zip') do |zip_file| + num_files = 0 + zip_file.each do |entry| + num_files += 1 if entry.file? + raise 'Too many extracted files' if num_files > MAX_FILES + raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE + entry.extract + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected new file mode 100644 index 00000000000..72a6d137f58 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected @@ -0,0 +1,130 @@ +WARNING: Unused predicate instance (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:192,17-25) +WARNING: Unused predicate oneBlockParameter (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:270,15-32) +WARNING: Unused predicate twoBlockParameter (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:284,15-32) +edges +| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:8:29:8:33 | entry | +| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:13:27:13:31 | entry | +| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:18:59:18:63 | entry | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:3:1:3:37 | call to read | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:4:23:4:31 | gzip_path | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:7:23:7:31 | gzip_path | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:1:12:16 | uncompressedfile | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:20:12:51 | call to open | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:17:1:17:53 | call to read | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:18:1:18:48 | call to new | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:22:23:22:37 | call to open | +| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:2:1:2:9 | gzip_path | +| gzipBombs.rb:4:23:4:31 | gzip_path | gzipBombs.rb:5:8:5:28 | call to read | +| gzipBombs.rb:7:23:7:31 | gzip_path | gzipBombs.rb:7:38:7:53 | uncompressedfile | +| gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:3:8:18 | uncompressedfile | +| gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | +| gzipBombs.rb:8:3:8:18 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | +| gzipBombs.rb:12:1:12:16 | uncompressedfile | gzipBombs.rb:13:1:13:16 | uncompressedfile | +| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:1:12:16 | uncompressedfile | +| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:13:27:13:31 | entry | +| gzipBombs.rb:13:1:13:16 | uncompressedfile | gzipBombs.rb:13:27:13:31 | entry | +| gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:53 | call to read | +| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:59:18:63 | entry | +| gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:9:23:9:34 | zipfile_path | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:14:1:14:40 | call to read | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:15:1:15:49 | call to extract | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:17:16:17:27 | zipfile_path | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:29:27:29:38 | zipfile_path | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:36:16:36:27 | zipfile_path | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:48:6:48:27 | call to get_input_stream | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:50:27:50:38 | zipfile_path | +| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:2:1:2:12 | zipfile_path | +| zipBombs.rb:9:23:9:34 | zipfile_path | zipBombs.rb:10:3:10:12 | call to read | +| zipBombs.rb:17:16:17:27 | zipfile_path | zipBombs.rb:23:5:23:17 | call to extract | +| zipBombs.rb:17:16:17:27 | zipfile_path | zipBombs.rb:25:5:25:26 | call to get_input_stream | +| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:31:3:31:15 | call to extract | +| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:32:3:32:24 | call to get_input_stream | +| zipBombs.rb:36:16:36:27 | zipfile_path | zipBombs.rb:38:5:38:29 | call to read | +| zipBombs.rb:36:16:36:27 | zipfile_path | zipBombs.rb:43:8:43:29 | call to get_input_stream | +| zipBombs.rb:50:27:50:38 | zipfile_path | zipBombs.rb:52:3:52:24 | call to get_input_stream | +nodes +| file://:0:0:0:0 | [summary param] self in each(0) | semmle.label | [summary param] self in each(0) | +| gzipBombs.rb:2:1:2:9 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | semmle.label | "Path/To/bomb.gz" | +| gzipBombs.rb:3:1:3:37 | call to read | semmle.label | call to read | +| gzipBombs.rb:4:23:4:31 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:5:8:5:28 | call to read | semmle.label | call to read | +| gzipBombs.rb:7:23:7:31 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:7:38:7:53 | uncompressedfile | semmle.label | uncompressedfile | +| gzipBombs.rb:8:3:8:18 | uncompressedfile | semmle.label | uncompressedfile | +| gzipBombs.rb:8:29:8:33 | entry | semmle.label | entry | +| gzipBombs.rb:12:1:12:16 | uncompressedfile | semmle.label | uncompressedfile | +| gzipBombs.rb:12:20:12:51 | call to open | semmle.label | call to open | +| gzipBombs.rb:13:1:13:16 | uncompressedfile | semmle.label | uncompressedfile | +| gzipBombs.rb:13:27:13:31 | entry | semmle.label | entry | +| gzipBombs.rb:17:1:17:53 | call to read | semmle.label | call to read | +| gzipBombs.rb:17:22:17:47 | call to open | semmle.label | call to open | +| gzipBombs.rb:18:1:18:48 | call to new | semmle.label | call to new | +| gzipBombs.rb:18:22:18:47 | call to open | semmle.label | call to open | +| gzipBombs.rb:18:59:18:63 | entry | semmle.label | entry | +| gzipBombs.rb:22:23:22:37 | call to open | semmle.label | call to open | +| zipBombs.rb:2:1:2:12 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | semmle.label | "Path/To/bomb.zip" | +| zipBombs.rb:9:23:9:34 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:10:3:10:12 | call to read | semmle.label | call to read | +| zipBombs.rb:14:1:14:40 | call to read | semmle.label | call to read | +| zipBombs.rb:15:1:15:49 | call to extract | semmle.label | call to extract | +| zipBombs.rb:17:16:17:27 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:23:5:23:17 | call to extract | semmle.label | call to extract | +| zipBombs.rb:25:5:25:26 | call to get_input_stream | semmle.label | call to get_input_stream | +| zipBombs.rb:29:27:29:38 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:31:3:31:15 | call to extract | semmle.label | call to extract | +| zipBombs.rb:32:3:32:24 | call to get_input_stream | semmle.label | call to get_input_stream | +| zipBombs.rb:36:16:36:27 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:38:5:38:29 | call to read | semmle.label | call to read | +| zipBombs.rb:43:8:43:29 | call to get_input_stream | semmle.label | call to get_input_stream | +| zipBombs.rb:48:6:48:27 | call to get_input_stream | semmle.label | call to get_input_stream | +| zipBombs.rb:50:27:50:38 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:52:3:52:24 | call to get_input_stream | semmle.label | call to get_input_stream | +subpaths +#select +| gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:3:1:3:37 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:3:1:3:37 | call to read | This file extraction depends on a $@. | gzipBombs.rb:3:1:3:37 | call to read | potentially untrusted source | +| gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:5:8:5:28 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:5:8:5:28 | call to read | This file extraction depends on a $@. | gzipBombs.rb:5:8:5:28 | call to read | potentially untrusted source | +| gzipBombs.rb:8:29:8:33 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | +| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:7:38:7:53 | uncompressedfile | potentially untrusted source | +| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:8:29:8:33 | entry | potentially untrusted source | +| gzipBombs.rb:13:27:13:31 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | +| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:12:20:12:51 | call to open | potentially untrusted source | +| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:13:27:13:31 | entry | potentially untrusted source | +| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:17:1:17:53 | call to read | potentially untrusted source | +| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:17:22:17:47 | call to open | potentially untrusted source | +| gzipBombs.rb:18:59:18:63 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | +| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:1:18:48 | call to new | potentially untrusted source | +| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:22:18:47 | call to open | potentially untrusted source | +| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:59:18:63 | entry | potentially untrusted source | +| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:22:23:22:37 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:23:22:37 | call to open | This file extraction depends on a $@. | gzipBombs.rb:22:23:22:37 | call to open | potentially untrusted source | +| zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:10:3:10:12 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:10:3:10:12 | call to read | This file extraction depends on a $@. | zipBombs.rb:10:3:10:12 | call to read | potentially untrusted source | +| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:14:1:14:40 | call to read | potentially untrusted source | +| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:15:1:15:49 | call to extract | potentially untrusted source | +| zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:23:5:23:17 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:23:5:23:17 | call to extract | This file extraction depends on a $@. | zipBombs.rb:23:5:23:17 | call to extract | potentially untrusted source | +| zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:25:5:25:26 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:25:5:25:26 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:25:5:25:26 | call to get_input_stream | potentially untrusted source | +| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:31:3:31:15 | call to extract | potentially untrusted source | +| zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:32:3:32:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:32:3:32:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:32:3:32:24 | call to get_input_stream | potentially untrusted source | +| zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:38:5:38:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:38:5:38:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:38:5:38:29 | call to read | potentially untrusted source | +| zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:43:8:43:29 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:43:8:43:29 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:43:8:43:29 | call to get_input_stream | potentially untrusted source | +| zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:48:6:48:27 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:48:6:48:27 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:48:6:48:27 | call to get_input_stream | potentially untrusted source | +| zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:52:3:52:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:52:3:52:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:52:3:52:24 | call to get_input_stream | potentially untrusted source | diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref new file mode 100644 index 00000000000..975660828c5 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref @@ -0,0 +1 @@ +experimental/CWE-522-DecompressionBombs/BombsV1.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected new file mode 100644 index 00000000000..c9f13eb5272 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected @@ -0,0 +1,87 @@ +edges +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:3:1:3:32 | call to open | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:4:1:6:3 | call to open | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:7:1:11:3 | call to open | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:20:12:51 | call to open | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:17:1:17:48 | call to new | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:18:1:18:48 | call to new | +| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:22:1:22:38 | call to zcat | +| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:2:1:2:9 | gzip_path | +| gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:48 | call to new | +| gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | +| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:1:22:38 | call to zcat | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:3:1:8:3 | call to open | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:9:1:11:3 | call to open | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:12:9:12:43 | call to open | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:14:1:14:40 | call to read | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:15:1:15:49 | call to extract | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:29:27:29:38 | zipfile_path | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:48:6:48:32 | call to read | +| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:50:27:50:38 | zipfile_path | +| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:2:1:2:12 | zipfile_path | +| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:31:3:31:15 | call to extract | +| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:32:3:32:29 | call to read | +| zipBombs.rb:50:27:50:38 | zipfile_path | zipBombs.rb:52:3:52:29 | call to read | +nodes +| gzipBombs.rb:2:1:2:9 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | semmle.label | "Path/To/bomb.gz" | +| gzipBombs.rb:3:1:3:32 | call to open | semmle.label | call to open | +| gzipBombs.rb:4:1:6:3 | call to open | semmle.label | call to open | +| gzipBombs.rb:7:1:11:3 | call to open | semmle.label | call to open | +| gzipBombs.rb:12:20:12:51 | call to open | semmle.label | call to open | +| gzipBombs.rb:17:1:17:48 | call to new | semmle.label | call to new | +| gzipBombs.rb:17:22:17:47 | call to open | semmle.label | call to open | +| gzipBombs.rb:18:1:18:48 | call to new | semmle.label | call to new | +| gzipBombs.rb:18:22:18:47 | call to open | semmle.label | call to open | +| gzipBombs.rb:22:1:22:38 | call to zcat | semmle.label | call to zcat | +| gzipBombs.rb:22:23:22:37 | call to open | semmle.label | call to open | +| zipBombs.rb:2:1:2:12 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | semmle.label | "Path/To/bomb.zip" | +| zipBombs.rb:3:1:8:3 | call to open | semmle.label | call to open | +| zipBombs.rb:9:1:11:3 | call to open | semmle.label | call to open | +| zipBombs.rb:12:9:12:43 | call to open | semmle.label | call to open | +| zipBombs.rb:14:1:14:40 | call to read | semmle.label | call to read | +| zipBombs.rb:15:1:15:49 | call to extract | semmle.label | call to extract | +| zipBombs.rb:29:27:29:38 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:31:3:31:15 | call to extract | semmle.label | call to extract | +| zipBombs.rb:32:3:32:29 | call to read | semmle.label | call to read | +| zipBombs.rb:48:6:48:32 | call to read | semmle.label | call to read | +| zipBombs.rb:50:27:50:38 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:52:3:52:29 | call to read | semmle.label | call to read | +subpaths +#select +| gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:3:1:3:32 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:3:1:3:32 | call to open | This file extraction depends on a $@. | gzipBombs.rb:3:1:3:32 | call to open | potentially untrusted source | +| gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:4:1:6:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:4:1:6:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:4:1:6:3 | call to open | potentially untrusted source | +| gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:7:1:11:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:7:1:11:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:7:1:11:3 | call to open | potentially untrusted source | +| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:12:20:12:51 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:20:12:51 | call to open | This file extraction depends on a $@. | gzipBombs.rb:12:20:12:51 | call to open | potentially untrusted source | +| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:17:1:17:48 | call to new | potentially untrusted source | +| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:17:22:17:47 | call to open | potentially untrusted source | +| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:18:1:18:48 | call to new | potentially untrusted source | +| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:18:22:18:47 | call to open | potentially untrusted source | +| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | +| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:22:1:22:38 | call to zcat | potentially untrusted source | +| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:22:23:22:37 | call to open | potentially untrusted source | +| zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:3:1:8:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:3:1:8:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:3:1:8:3 | call to open | potentially untrusted source | +| zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:9:1:11:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:9:1:11:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:9:1:11:3 | call to open | potentially untrusted source | +| zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:12:9:12:43 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:12:9:12:43 | call to open | This file extraction depends on a $@. | zipBombs.rb:12:9:12:43 | call to open | potentially untrusted source | +| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:14:1:14:40 | call to read | potentially untrusted source | +| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:15:1:15:49 | call to extract | potentially untrusted source | +| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:31:3:31:15 | call to extract | potentially untrusted source | +| zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:32:3:32:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:32:3:32:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:32:3:32:29 | call to read | potentially untrusted source | +| zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:48:6:48:32 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:48:6:48:32 | call to read | This file extraction depends on a $@. | zipBombs.rb:48:6:48:32 | call to read | potentially untrusted source | +| zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:52:3:52:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | +| zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:52:3:52:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:52:3:52:29 | call to read | potentially untrusted source | diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref new file mode 100644 index 00000000000..b2ed18ba5ee --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref @@ -0,0 +1 @@ +experimental/CWE-522-DecompressionBombs/BombsV2.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb new file mode 100644 index 00000000000..ab038dd47ec --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb @@ -0,0 +1,24 @@ +require 'zlib' +gzip_path = "Path/To/bomb.gz" +Zlib::GzipReader.open(gzip_path).read +Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + puts uncompressedfile.read +end +Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + uncompressedfile.each do |entry| + puts entry + end +end +uncompressedfile = Zlib::GzipReader.open(gzip_path) +uncompressedfile.each do |entry| + puts entry +end + +Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read +Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| + puts entry +end + +Zlib::GzipReader.zcat(open(gzip_path)) + + diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb new file mode 100644 index 00000000000..c0f15b86fce --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb @@ -0,0 +1,55 @@ +require 'zip' +zipfile_path = "Path/To/bomb.zip" +Zip::InputStream.open(zipfile_path) do |input| + while (entry = input.get_next_entry) + puts :file_name, entry.name + input + end +end +Zip::InputStream.open(zipfile_path) do |input| + input.read +end +input = Zip::InputStream.open(zipfile_path) + +Zip::File.open(zipfile_path).read "10GB" +Zip::File.open(zipfile_path).extract "10GB", "./" + +Zip::File.open(zipfile_path) do |zip_file| + # Handle entries one by one + zip_file.each do |entry| + puts "Extracting #{entry.name}" + raise 'File too large when extracted' if entry.size > MAX_SIZE + # Extract to file or directory based on name in the archive + entry.extract + # Read into memory + entry.get_input_stream.read + end +end + +zip_file = Zip::File.open(zipfile_path) +zip_file.each do |entry| + entry.extract + entry.get_input_stream.read +end + +# Find specific entry +Zip::File.open(zipfile_path) do |zip_file| + zip_file.glob('*.xml').each do |entry| + zip_file.read(entry.name) + entry.extract + end + entry = zip_file.glob('*.csv').first + raise 'File too large when extracted' if entry.size > MAX_SIZE + puts entry.get_input_stream.read +end + +zip_file = Zip::File.open(zipfile_path) +entry = zip_file.glob('*.csv') +puts entry.get_input_stream.read + +zip_file = Zip::File.open(zipfile_path) +zip_file.glob('*') do |entry| + entry.get_input_stream.read +end + + From e2fe0e11f0a9668ba15e5e977de3166861abd09e Mon Sep 17 00:00:00 2001 From: amammad Date: Mon, 26 Jun 2023 17:14:46 +1000 Subject: [PATCH 02/11] fix formatting error/warnings --- .../CWE-522-DecompressionBombs/BombsV1.ql | 72 ++++--------------- .../CWE-522-DecompressionBombs/BombsV2.ql | 9 +-- 2 files changed, 18 insertions(+), 63 deletions(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql index 74c08c6e541..0681cce9e7d 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql @@ -28,7 +28,7 @@ module Zip { } /** - * input in following + * An input in following * ```ruby * input = ip::InputStream.open(path) * Zip::InputStream.open(path) do |input| @@ -38,7 +38,7 @@ module Zip { */ private API::Node instance() { result = - [zipInputStream().getMethod("open").(GetReturnOrGetBlock).getReturnOrGetBlockParameter()] + zipInputStream().getMethod("open").(GetReturnOrGetBlock).getReturnOrGetBlockParameter() } predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { @@ -59,7 +59,7 @@ module Zip { } DataFlow::Node isSink() { - exists(string ioMethods | not ioMethods = ["get_next_entry"] | + exists(string ioMethods | not ioMethods = "get_next_entry" | result = instance().getMethod(ioMethods).getReturn().asSource() ) } @@ -104,12 +104,7 @@ module Zip { */ API::Node instance() { result = - [ - zipFile() - .getMethod(["open", "new"]) - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - ] + zipFile().getMethod(["open", "new"]).(GetReturnOrGetBlock).getReturnOrGetBlockParameter() } /** @@ -189,14 +184,6 @@ module Zip { * # Find specific entry with Zip::File.open(zipfile_path).glob(pattern) */ module Glob { - API::Node instance() { - result = - [ - zipFile().getMethod(["open", "new"]).getReturn().getMethod("glob"), - zipFile().getMethod(["open", "new"]).getBlock().getParameter(0).getMethod("glob") - ] - } - /** * `extract` and `read` can be sink * ```ruby @@ -242,14 +229,12 @@ module Zip { exists(API::Node zipFileOpen | zipFileOpen = zipFile().getMethod(["open", "new"]) | nodeFrom = zipFileOpen.getParameter(0).asSink() and nodeTo = - [ - isAdditionalTaintStepHelper(zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .getMethod("glob") - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter()) - ] + isAdditionalTaintStepHelper(zipFileOpen + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter() + .getMethod("glob") + .(GetReturnOrGetBlock) + .getReturnOrGetBlockParameter()) ) } } @@ -259,38 +244,6 @@ module Zip { */ private API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } - /** - * ```ruby - * returun = inputNode do - * returun.each do |entry| - * outputnode = entry - * end - * ``` - */ - API::Node oneBlockParameter(API::Node nodeMiddle) { - result = - nodeMiddle.getReturn().getMethod(["each", "each_entry", "first"]).getBlock().getParameter(0) - } - - /** - * ```ruby - * inputNode do |param| - * param.each do |entry| - * outputnode = entry - * end - * end - * ``` - */ - API::Node twoBlockParameter(API::Node nodeMiddle) { - result = - nodeMiddle - .getBlock() - .getParameter(0) - .getMethod(["each", "each_entry", "first"]) - .getBlock() - .getParameter(0) - } - DataFlow::Node isAdditionalTaintStepHelper(API::Node nodeMiddle) { result = nodeMiddle.getMethod(_).getReturn().asSource() or result = nodeMiddle.getMethod(_).getReturn().getMethod(_).getReturn().asSource() @@ -324,7 +277,7 @@ module Zlib { ] } - API::Node gzipReaderNew() { result = [gzipReaderInstance().getMethod("new").getReturn()] } + API::Node gzipReaderNew() { result = gzipReaderInstance().getMethod("new").getReturn() } /** * `entry` and `read` can be sink @@ -361,7 +314,7 @@ module Zlib { .getParameter(0) .asSource() or - // _ is one of ["read", "readlines", "readpartial", "readline", "gets"] and more because gzipReader return an IO instance, there are alot of methods and gzipReader is for reading gzip files, so there is low FP rate here if we use _ instead of exact IO method names + // _ is one of ["read", "readlines", "readpartial", "readline", "gets"] and more because gzipReader return an IO instance, there are a lot of methods and gzipReader is for reading gzip files, so there is low FP rate here if we use _ instead of exact IO method names exists(string ioMethods | not ioMethods = ["glob", "each", "each_entry"] | result = gzipReaderNew().getMethod(ioMethods).getReturn().asSource() or result = gzipReaderOpen().getMethod(ioMethods).getReturn().asSource() @@ -464,6 +417,7 @@ class Bombs extends TaintTracking::Configuration { nodeTo = n.getReturn().asSource() ) or + // following can be a global additional step exists(DataFlow::CallNode cn | cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" | diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql index e4c92e6266d..da05732a99f 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql @@ -34,7 +34,7 @@ module DecompressionBombs { } /** - * return values of following methods + * A return values of following methods * `Zlib::GzipReader.open` * `Zlib::GzipReader.zcat` * `Zlib::GzipReader.new` @@ -66,7 +66,7 @@ module DecompressionBombs { } /** - * return values of following methods + * A return values of following methods * `ZipIO.read` * `ZipEntry.extract` */ @@ -105,7 +105,7 @@ module DecompressionBombs { } /** - * return values of following methods + * A return values of following methods * `ZipIO.read` * `ZipEntry.extract` * sanitize the nodes which have `entry.size > someOBJ` @@ -123,7 +123,7 @@ module DecompressionBombs { predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { exists(API::Node zipnodes | zipnodes = zipFile() | - nodeTo = [rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource()] and + nodeTo = rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource() and nodeFrom = zipnodes.getMethod(["new", "open"]).getParameter(0).asSink() ) } @@ -164,6 +164,7 @@ class Bombs extends TaintTracking::Configuration { nodeTo = n.getReturn().asSource() ) or + // following can be a global additional step exists(DataFlow::CallNode cn | cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" | From 9e33b47cbd158ba1f39eed6ded1d44ef582929ac Mon Sep 17 00:00:00 2001 From: amammad Date: Mon, 26 Jun 2023 17:46:22 +1000 Subject: [PATCH 03/11] added more additional steps --- .../CWE-522-DecompressionBombs/BombsV2.ql | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql index da05732a99f..169f1dcf24b 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql @@ -12,6 +12,7 @@ */ import codeql.ruby.AST +import codeql.ruby.frameworks.Files import codeql.ruby.ApiGraphs import codeql.ruby.DataFlow import codeql.ruby.dataflow.RemoteFlowSources @@ -135,6 +136,15 @@ module DecompressionBombs { } } +/** + * A call to `IO.copy_stream` + */ +class IoCopyStream extends DataFlow::CallNode { + IoCopyStream() { this = API::getTopLevelMember("IO").getAMethodCall("copy_stream") } + + DataFlow::Node getAPathArgument() { result = this.getArgument(0) } +} + class Bombs extends TaintTracking::Configuration { Bombs() { this = "Decompression Bombs" } @@ -159,11 +169,21 @@ class Bombs extends TaintTracking::Configuration { nodeTo = n.getReturn().asSource() ) or + exists(File::FileOpen n | + nodeFrom = n.getAPathArgument() and + nodeTo = n + ) + or exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") | nodeFrom = n.getParameter(0).asSink() and nodeTo = n.getReturn().asSource() ) or + exists(IoCopyStream n | + nodeFrom = n.getAPathArgument() and + nodeTo = n + ) + or // following can be a global additional step exists(DataFlow::CallNode cn | cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" From 9540c58c4a97d5279333c6dd005090bf2563cd4c Mon Sep 17 00:00:00 2001 From: amammad Date: Mon, 26 Jun 2023 20:55:11 +1000 Subject: [PATCH 04/11] make one ql file --- .../CWE-522-DecompressionBombs/BombsV1.ql | 433 ------------------ .../{BombsV2.ql => DecompressionBombs.ql} | 0 ...2.expected => DecompressionBombs.expected} | 0 .../DecompressionBombs.qlref | 1 + .../DecompressionBombsV1.expected | 130 ------ .../DecompressionBombsV1.qlref | 1 - .../DecompressionBombsV2.qlref | 1 - 7 files changed, 1 insertion(+), 565 deletions(-) delete mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql rename ruby/ql/src/experimental/CWE-522-DecompressionBombs/{BombsV2.ql => DecompressionBombs.ql} (100%) rename ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/{DecompressionBombsV2.expected => DecompressionBombs.expected} (100%) create mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref delete mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected delete mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref delete mode 100644 ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql deleted file mode 100644 index 0681cce9e7d..00000000000 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql +++ /dev/null @@ -1,433 +0,0 @@ -/** - * @name User-controlled file decompression - * @description User-controlled data that flows into decompression library APIs could be dangerous - * @kind path-problem - * @problem.severity error - * @security-severity 7.8 - * @precision high - * @id rb/user-controlled-file-decompression - * @tags security - * experimental - * external/cwe/cwe-409 - */ - -import codeql.ruby.AST -import codeql.ruby.ApiGraphs -import codeql.ruby.DataFlow -import codeql.ruby.dataflow.RemoteFlowSources -import codeql.ruby.TaintTracking -import DataFlow::PathGraph - -module Zip { - module InputStream { - /** - * `Zip::InputStream` - */ - private API::Node zipInputStream() { - result = [API::getTopLevelMember("Zip").getMember("InputStream")] - } - - /** - * An input in following - * ```ruby - * input = ip::InputStream.open(path) - * Zip::InputStream.open(path) do |input| - * ... - * end - * ``` - */ - private API::Node instance() { - result = - zipInputStream().getMethod("open").(GetReturnOrGetBlock).getReturnOrGetBlockParameter() - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipInputStreamOpen | - zipInputStreamOpen = zipInputStream().getMethod("open") - | - nodeFrom = zipInputStreamOpen.getParameter(0).asSink() and - nodeTo = - [ - zipInputStreamOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .getMethod(_) - .getReturn() - .asSource() - ] - ) - } - - DataFlow::Node isSink() { - exists(string ioMethods | not ioMethods = "get_next_entry" | - result = instance().getMethod(ioMethods).getReturn().asSource() - ) - } - } - - class AdditionalGlobStep extends API::Node { - API::Node additionalGlobStepHelper() { - result = this - or - result = this.getMethod("glob").getReturn() - } - } - - class GetReturnOrGetBlock extends API::Node { - API::Node getReturnOrGetBlockParameter() { - result = this.getBlock().getParameter(0) or - result = this.getReturn() - } - } - - module File { - /** - * ```ruby - * Zip::File.open(path) - * # or - * Zip::File.new(path) - * ``` - */ - module OpenAndNew { - /** - * myZip in following - * - * ```ruby - * myZip = Zip::File.open(path) - * # or - * myZip = Zip::File.new(path) - * - * Zip::File.open(path) do |myZip| - * - * end - * ``` - */ - API::Node instance() { - result = - zipFile().getMethod(["open", "new"]).(GetReturnOrGetBlock).getReturnOrGetBlockParameter() - } - - /** - * `extract` and `read` can be sink - * ```ruby - * ``` - */ - DataFlow::Node isSink() { - result = - instance() - .getMethod(["each", "each_entry", "first"]) - .getBlock() - .getParameter(0) - .getMethod(["extract", "read", "first"]) - .getReturn() - .asSource() - or - result = - instance() - .getMethod(["each", "each_entry", "first"]) - .getBlock() - .getParameter(0) - .getMethod("get_input_stream") - .getReturn() - .asSource() - or - exists(string ioMethods | not ioMethods = ["each", "each_entry", "glob", "first"] | - result = instance().getAMethodCall(ioMethods) - ) - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipFileOpen, API::Node zipFileOpenGlob | - zipFileOpen = zipFile().getMethod(["open", "new"]) - | - nodeFrom = zipFileOpen.getParameter(0).asSink() and - zipFileOpenGlob = zipFileOpen.(GetReturnOrGetBlock).getReturnOrGetBlockParameter() and - nodeTo = - [ - zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .(AdditionalGlobStep) - .additionalGlobStepHelper() - .asSource(), - zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .(AdditionalGlobStep) - .additionalGlobStepHelper() - .getMethod(["read", "extract"]) - .getReturn() - .asSource(), - zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .(AdditionalGlobStep) - .additionalGlobStepHelper() - .getMethod(["each", "each_entry", "first"]) - .getBlock() - .getParameter(0) - .asSource(), - isAdditionalTaintStepHelper(zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .(AdditionalGlobStep) - .additionalGlobStepHelper() - .getMethod(["each", "each_entry", "first"]) - .getBlock() - .getParameter(0)) - ] - ) - } - } - - /** - * # Find specific entry with Zip::File.open(zipfile_path).glob(pattern) - */ - module Glob { - /** - * `extract` and `read` can be sink - * ```ruby - * ``` - */ - DataFlow::Node isSink() { - result = - zipFile() - .getMethod(["open", "new"]) - .getReturn() - .getMethod("glob") - .getBlock() - .getParameter(0) - .getMethod("get_input_stream") - .getReturn() - .asSource() - or - result = - zipFile() - .getMethod(["open", "new"]) - .getReturn() - .getMethod("glob") - .getReturn() - .getMethod("get_input_stream") - .getReturn() - .asSource() - or - result = - zipFile() - .getMethod(["open", "new"]) - .getBlock() - .getParameter(0) - .getMethod("glob") - .getReturn() - .getMethod("first") - .getReturn() - .getMethod("get_input_stream") - .getReturn() - .asSource() - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipFileOpen | zipFileOpen = zipFile().getMethod(["open", "new"]) | - nodeFrom = zipFileOpen.getParameter(0).asSink() and - nodeTo = - isAdditionalTaintStepHelper(zipFileOpen - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter() - .getMethod("glob") - .(GetReturnOrGetBlock) - .getReturnOrGetBlockParameter()) - ) - } - } - - /** - * `Zip::File` - */ - private API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } - - DataFlow::Node isAdditionalTaintStepHelper(API::Node nodeMiddle) { - result = nodeMiddle.getMethod(_).getReturn().asSource() or - result = nodeMiddle.getMethod(_).getReturn().getMethod(_).getReturn().asSource() - } - } - - DataFlow::Node isSink() { - result = [File::Glob::isSink(), File::OpenAndNew::isSink(), InputStream::isSink()] - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - File::Glob::isAdditionalTaintStep(nodeFrom, nodeTo) - or - File::OpenAndNew::isAdditionalTaintStep(nodeFrom, nodeTo) - or - InputStream::isAdditionalTaintStep(nodeFrom, nodeTo) - } -} - -module Zlib { - /** - * `zlib::GzipReader` - */ - API::Node gzipReaderInstance() { result = API::getTopLevelMember("Zlib").getMember("GzipReader") } - - API::Node gzipReaderOpen() { - result = - [ - gzipReaderInstance().getMethod("open").getReturn(), - gzipReaderInstance().getMethod("open").getBlock().getParameter(0) - ] - } - - API::Node gzipReaderNew() { result = gzipReaderInstance().getMethod("new").getReturn() } - - /** - * `entry` and `read` can be sink - * ```ruby - *Zlib::GzipReader.open(gzip_path) do |uncompressedfile| - * uncompressedfile.each do |entry| - * entry - * end - *end - * - *uncompressedfile = Zlib::GzipReader.open(gzip_path) - *uncompressedfile.each do |entry| - * entry - *end - * - *Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read - *Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| - * entry - *end - * ``` - */ - DataFlow::Node isSink() { - result = - gzipReaderOpen() - .getMethod(["glob", "each", "each_entry"]) - .getBlock() - .getParameter(0) - .asSource() - or - result = - gzipReaderNew() - .getMethod(["glob", "each", "each_entry"]) - .getBlock() - .getParameter(0) - .asSource() - or - // _ is one of ["read", "readlines", "readpartial", "readline", "gets"] and more because gzipReader return an IO instance, there are a lot of methods and gzipReader is for reading gzip files, so there is low FP rate here if we use _ instead of exact IO method names - exists(string ioMethods | not ioMethods = ["glob", "each", "each_entry"] | - result = gzipReaderNew().getMethod(ioMethods).getReturn().asSource() or - result = gzipReaderOpen().getMethod(ioMethods).getReturn().asSource() - ) - or - // no need for any other methods after opening gzip with zcat method - result = gzipReaderInstance().getMethod("zcat").getParameter(0).asSink() - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - isAdditionalTaintStepCustomizeMiddleNode(nodeFrom, nodeTo, gzipReaderOpen()) or - isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "open", gzipReaderInstance()) or - isAdditionalTaintStepCustomizeMiddleNode(nodeFrom, nodeTo, gzipReaderNew()) or - isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "new", gzipReaderInstance()) or - isAdditionalTaintStepCustomizeBeginningNode(nodeFrom, nodeTo, "zcat", gzipReaderInstance()) - } - - /** - * `uncompressedfile` is nodeFrom/nodeMiddle - * - * `entry` is nodeTo - * - * ```ruby - * Zlib::GzipReader.open(gzip_path) do |uncompressedfile| - * uncompressedfile.each do |entry| - * entry.bytes - * end - *end - *uncompressedfile = Zlib::GzipReader.open(gzip_path) - *uncompressedfile.each do |entry| - * entry.read - *end - * ``` - */ - predicate isAdditionalTaintStepCustomizeMiddleNode( - DataFlow::Node nodeFrom, DataFlow::Node nodeTo, API::Node nodeMiddle - ) { - nodeFrom = nodeMiddle.asSource() and - nodeTo = - nodeMiddle.getMethod(["glob", "each", "each_entry"]).getBlock().getParameter(0).asSource() - } - - /** - * `gzip_path` is nodeFrom - * - * `compressed_file`/`Zlib::GzipReader.open(gzip_path)` are nodeTo - * - * `Zlib::GzipReader` is nodeInstance - * - * `open` is beginMethodName - * ```ruby - *Zlib::GzipReader.open(gzip_path) do |compressed_file| - * compressed_file.each do |entry| - * entry.bytes - * end - *end - *``` - */ - predicate isAdditionalTaintStepCustomizeBeginningNode( - DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string beginMethodName, API::Node nodeInstance - ) { - nodeFrom = nodeInstance.getMethod(beginMethodName).getParameter(0).asSink() and - nodeTo = - [ - nodeInstance.getMethod(beginMethodName).getBlock().getParameter(0).asSource(), - nodeInstance - .getMethod(beginMethodName) - .getBlock() - .getParameter(0) - .getMethod(_) - .getReturn() - .asSource(), nodeInstance.getMethod(beginMethodName).getReturn().asSource(), - nodeInstance.getMethod(beginMethodName).getReturn().getMethod(_).getReturn().asSource() - ] - } -} - -class Bombs extends TaintTracking::Configuration { - Bombs() { this = "Decompression Bombs" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource or - source instanceof DataFlow::LocalSourceNode - } - - override predicate isSink(DataFlow::Node sink) { sink = [Zip::isSink(), Zlib::isSink()] } - - override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - Zip::isAdditionalTaintStep(nodeFrom, nodeTo) - or - Zlib::isAdditionalTaintStep(nodeFrom, nodeTo) - or - exists(API::Node n | n = API::root().getMember("File").getMethod("open") | - nodeFrom = n.getParameter(0).asSink() and - nodeTo = n.getReturn().asSource() - ) - or - exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") | - nodeFrom = n.getParameter(0).asSink() and - nodeTo = n.getReturn().asSource() - ) - or - // following can be a global additional step - exists(DataFlow::CallNode cn | - cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" - | - nodeFrom = cn.getArgument(0) and - nodeTo = cn - ) - } -} - -from Bombs cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(), - "potentially untrusted source" diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql similarity index 100% rename from ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV2.ql rename to ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected similarity index 100% rename from ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.expected rename to ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref new file mode 100644 index 00000000000..c24a4cc9678 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qlref @@ -0,0 +1 @@ +experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected deleted file mode 100644 index 72a6d137f58..00000000000 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.expected +++ /dev/null @@ -1,130 +0,0 @@ -WARNING: Unused predicate instance (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:192,17-25) -WARNING: Unused predicate oneBlockParameter (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:270,15-32) -WARNING: Unused predicate twoBlockParameter (/home/am/CodeQL-home/codeql-repo/ruby/ql/src/experimental/CWE-522-DecompressionBombs/BombsV1.ql:284,15-32) -edges -| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:8:29:8:33 | entry | -| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:13:27:13:31 | entry | -| file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:18:59:18:63 | entry | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:3:1:3:37 | call to read | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:4:23:4:31 | gzip_path | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:7:23:7:31 | gzip_path | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:1:12:16 | uncompressedfile | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:20:12:51 | call to open | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:17:1:17:53 | call to read | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:18:1:18:48 | call to new | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:22:23:22:37 | call to open | -| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:2:1:2:9 | gzip_path | -| gzipBombs.rb:4:23:4:31 | gzip_path | gzipBombs.rb:5:8:5:28 | call to read | -| gzipBombs.rb:7:23:7:31 | gzip_path | gzipBombs.rb:7:38:7:53 | uncompressedfile | -| gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:3:8:18 | uncompressedfile | -| gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | -| gzipBombs.rb:8:3:8:18 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | -| gzipBombs.rb:12:1:12:16 | uncompressedfile | gzipBombs.rb:13:1:13:16 | uncompressedfile | -| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:1:12:16 | uncompressedfile | -| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:13:27:13:31 | entry | -| gzipBombs.rb:13:1:13:16 | uncompressedfile | gzipBombs.rb:13:27:13:31 | entry | -| gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:53 | call to read | -| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:59:18:63 | entry | -| gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:9:23:9:34 | zipfile_path | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:14:1:14:40 | call to read | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:15:1:15:49 | call to extract | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:17:16:17:27 | zipfile_path | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:29:27:29:38 | zipfile_path | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:36:16:36:27 | zipfile_path | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:48:6:48:27 | call to get_input_stream | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:50:27:50:38 | zipfile_path | -| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:2:1:2:12 | zipfile_path | -| zipBombs.rb:9:23:9:34 | zipfile_path | zipBombs.rb:10:3:10:12 | call to read | -| zipBombs.rb:17:16:17:27 | zipfile_path | zipBombs.rb:23:5:23:17 | call to extract | -| zipBombs.rb:17:16:17:27 | zipfile_path | zipBombs.rb:25:5:25:26 | call to get_input_stream | -| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:31:3:31:15 | call to extract | -| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:32:3:32:24 | call to get_input_stream | -| zipBombs.rb:36:16:36:27 | zipfile_path | zipBombs.rb:38:5:38:29 | call to read | -| zipBombs.rb:36:16:36:27 | zipfile_path | zipBombs.rb:43:8:43:29 | call to get_input_stream | -| zipBombs.rb:50:27:50:38 | zipfile_path | zipBombs.rb:52:3:52:24 | call to get_input_stream | -nodes -| file://:0:0:0:0 | [summary param] self in each(0) | semmle.label | [summary param] self in each(0) | -| gzipBombs.rb:2:1:2:9 | gzip_path | semmle.label | gzip_path | -| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | semmle.label | "Path/To/bomb.gz" | -| gzipBombs.rb:3:1:3:37 | call to read | semmle.label | call to read | -| gzipBombs.rb:4:23:4:31 | gzip_path | semmle.label | gzip_path | -| gzipBombs.rb:5:8:5:28 | call to read | semmle.label | call to read | -| gzipBombs.rb:7:23:7:31 | gzip_path | semmle.label | gzip_path | -| gzipBombs.rb:7:38:7:53 | uncompressedfile | semmle.label | uncompressedfile | -| gzipBombs.rb:8:3:8:18 | uncompressedfile | semmle.label | uncompressedfile | -| gzipBombs.rb:8:29:8:33 | entry | semmle.label | entry | -| gzipBombs.rb:12:1:12:16 | uncompressedfile | semmle.label | uncompressedfile | -| gzipBombs.rb:12:20:12:51 | call to open | semmle.label | call to open | -| gzipBombs.rb:13:1:13:16 | uncompressedfile | semmle.label | uncompressedfile | -| gzipBombs.rb:13:27:13:31 | entry | semmle.label | entry | -| gzipBombs.rb:17:1:17:53 | call to read | semmle.label | call to read | -| gzipBombs.rb:17:22:17:47 | call to open | semmle.label | call to open | -| gzipBombs.rb:18:1:18:48 | call to new | semmle.label | call to new | -| gzipBombs.rb:18:22:18:47 | call to open | semmle.label | call to open | -| gzipBombs.rb:18:59:18:63 | entry | semmle.label | entry | -| gzipBombs.rb:22:23:22:37 | call to open | semmle.label | call to open | -| zipBombs.rb:2:1:2:12 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | semmle.label | "Path/To/bomb.zip" | -| zipBombs.rb:9:23:9:34 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:10:3:10:12 | call to read | semmle.label | call to read | -| zipBombs.rb:14:1:14:40 | call to read | semmle.label | call to read | -| zipBombs.rb:15:1:15:49 | call to extract | semmle.label | call to extract | -| zipBombs.rb:17:16:17:27 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:23:5:23:17 | call to extract | semmle.label | call to extract | -| zipBombs.rb:25:5:25:26 | call to get_input_stream | semmle.label | call to get_input_stream | -| zipBombs.rb:29:27:29:38 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:31:3:31:15 | call to extract | semmle.label | call to extract | -| zipBombs.rb:32:3:32:24 | call to get_input_stream | semmle.label | call to get_input_stream | -| zipBombs.rb:36:16:36:27 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:38:5:38:29 | call to read | semmle.label | call to read | -| zipBombs.rb:43:8:43:29 | call to get_input_stream | semmle.label | call to get_input_stream | -| zipBombs.rb:48:6:48:27 | call to get_input_stream | semmle.label | call to get_input_stream | -| zipBombs.rb:50:27:50:38 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:52:3:52:24 | call to get_input_stream | semmle.label | call to get_input_stream | -subpaths -#select -| gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:3:1:3:37 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:3:1:3:37 | call to read | gzipBombs.rb:3:1:3:37 | call to read | This file extraction depends on a $@. | gzipBombs.rb:3:1:3:37 | call to read | potentially untrusted source | -| gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:5:8:5:28 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:5:8:5:28 | call to read | gzipBombs.rb:5:8:5:28 | call to read | This file extraction depends on a $@. | gzipBombs.rb:5:8:5:28 | call to read | potentially untrusted source | -| gzipBombs.rb:8:29:8:33 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | -| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:7:38:7:53 | uncompressedfile | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:7:38:7:53 | uncompressedfile | potentially untrusted source | -| gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:8:29:8:33 | entry | gzipBombs.rb:8:29:8:33 | entry | This file extraction depends on a $@. | gzipBombs.rb:8:29:8:33 | entry | potentially untrusted source | -| gzipBombs.rb:13:27:13:31 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | -| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:12:20:12:51 | call to open | potentially untrusted source | -| gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:13:27:13:31 | entry | gzipBombs.rb:13:27:13:31 | entry | This file extraction depends on a $@. | gzipBombs.rb:13:27:13:31 | entry | potentially untrusted source | -| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:17:1:17:53 | call to read | potentially untrusted source | -| gzipBombs.rb:17:1:17:53 | call to read | gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:53 | call to read | This file extraction depends on a $@. | gzipBombs.rb:17:22:17:47 | call to open | potentially untrusted source | -| gzipBombs.rb:18:59:18:63 | entry | file://:0:0:0:0 | [summary param] self in each(0) | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | file://:0:0:0:0 | [summary param] self in each(0) | potentially untrusted source | -| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:1:18:48 | call to new | potentially untrusted source | -| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:22:18:47 | call to open | potentially untrusted source | -| gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:59:18:63 | entry | gzipBombs.rb:18:59:18:63 | entry | This file extraction depends on a $@. | gzipBombs.rb:18:59:18:63 | entry | potentially untrusted source | -| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:22:23:22:37 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:23:22:37 | call to open | This file extraction depends on a $@. | gzipBombs.rb:22:23:22:37 | call to open | potentially untrusted source | -| zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:10:3:10:12 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:10:3:10:12 | call to read | zipBombs.rb:10:3:10:12 | call to read | This file extraction depends on a $@. | zipBombs.rb:10:3:10:12 | call to read | potentially untrusted source | -| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:14:1:14:40 | call to read | potentially untrusted source | -| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:15:1:15:49 | call to extract | potentially untrusted source | -| zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:23:5:23:17 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:23:5:23:17 | call to extract | zipBombs.rb:23:5:23:17 | call to extract | This file extraction depends on a $@. | zipBombs.rb:23:5:23:17 | call to extract | potentially untrusted source | -| zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:25:5:25:26 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:25:5:25:26 | call to get_input_stream | zipBombs.rb:25:5:25:26 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:25:5:25:26 | call to get_input_stream | potentially untrusted source | -| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:31:3:31:15 | call to extract | potentially untrusted source | -| zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:32:3:32:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:32:3:32:24 | call to get_input_stream | zipBombs.rb:32:3:32:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:32:3:32:24 | call to get_input_stream | potentially untrusted source | -| zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:38:5:38:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:38:5:38:29 | call to read | zipBombs.rb:38:5:38:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:38:5:38:29 | call to read | potentially untrusted source | -| zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:43:8:43:29 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:43:8:43:29 | call to get_input_stream | zipBombs.rb:43:8:43:29 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:43:8:43:29 | call to get_input_stream | potentially untrusted source | -| zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:48:6:48:27 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:48:6:48:27 | call to get_input_stream | zipBombs.rb:48:6:48:27 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:48:6:48:27 | call to get_input_stream | potentially untrusted source | -| zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:52:3:52:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:52:3:52:24 | call to get_input_stream | zipBombs.rb:52:3:52:24 | call to get_input_stream | This file extraction depends on a $@. | zipBombs.rb:52:3:52:24 | call to get_input_stream | potentially untrusted source | diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref deleted file mode 100644 index 975660828c5..00000000000 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV1.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/CWE-522-DecompressionBombs/BombsV1.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref deleted file mode 100644 index b2ed18ba5ee..00000000000 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombsV2.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/CWE-522-DecompressionBombs/BombsV2.ql \ No newline at end of file From 37af588492051c0d2d21453545d4ba53fb2bb691 Mon Sep 17 00:00:00 2001 From: amammad Date: Mon, 26 Jun 2023 21:16:16 +1000 Subject: [PATCH 05/11] update CVE instance in qhelp --- .../CWE-522-DecompressionBombs/DecompressionBombs.qhelp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp index 71ceda79caa..97248dbcad1 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp @@ -21,7 +21,8 @@
  • -CVE-2023-22898 +CVE-2023-22898 +Gitlab issue
  • A great research to gain more impact by this kind of attack From d44c9d3e74158a74c5c784b66a95f96fda5f3eca Mon Sep 17 00:00:00 2001 From: amammad Date: Thu, 7 Sep 2023 20:36:32 +1000 Subject: [PATCH 06/11] stash --- .../DecompressionBombs.ql | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql index 169f1dcf24b..3ed993bebd5 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql @@ -72,7 +72,10 @@ module DecompressionBombs { * `ZipEntry.extract` */ class ZipSink extends DecompressionBombSink { - ZipSink() { this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource() } + ZipSink() { + this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource() and + not this.getLocation().getFile().getBaseName().matches("%spec%") + } } predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { @@ -84,25 +87,18 @@ module DecompressionBombs { } module ZipFile { - // // Because of additional step and ZipSink predicates, I couldn't use unary predicate - // // I put the explanation because I think there should be a soloution to not use other rubyZipNode predicate - // API::Node rubyZipNode() { - // result = zipFile() or - // result = rubyZipNode().getMethod(_).getReturn() or - // result = rubyZipNode().getMethod(_).getBlock().getParameter(_) or - // result = rubyZipNode().getMethod(_).getParameter(0) or - // result = rubyZipNode().getAnElement() - // } - API::Node rubyZipNode(API::Node n) { - result = n + API::Node rubyZipNode() { + result = zipFile() or - result = rubyZipNode(n).getMethod(_).getReturn() + result = rubyZipNode().getMethod(_) or - result = rubyZipNode(n).getMethod(_).getBlock().getParameter(_) + result = rubyZipNode().getReturn() or - result = rubyZipNode(n).getMethod(_).getParameter(0) + result = rubyZipNode().getParameter(_) or - result = rubyZipNode(n).getAnElement() + result = rubyZipNode().getAnElement() + or + result = rubyZipNode().getBlock() } /** @@ -113,18 +109,16 @@ module DecompressionBombs { */ class ZipSink extends DecompressionBombSink { ZipSink() { - exists(API::Node zipnodes | zipnodes = zipFile() | - this = rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource() and - not exists( - rubyZipNode(zipnodes).getMethod("size").getReturn().getMethod(">").getParameter(0) - ) + exists(API::Node zipnodes | zipnodes = rubyZipNode() | + this = zipnodes.getMethod(["extract", "read"]).getReturn().asSource() and + not exists(zipnodes.getMethod("size").getReturn().getMethod(">").getParameter(0)) ) } } predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipnodes | zipnodes = zipFile() | - nodeTo = rubyZipNode(zipnodes).getMethod(["extract", "read"]).getReturn().asSource() and + exists(API::Node zipnodes | zipnodes = rubyZipNode() | + nodeTo = zipnodes.getMethod(["extract", "read"]).getReturn().asSource() and nodeFrom = zipnodes.getMethod(["new", "open"]).getParameter(0).asSink() ) } @@ -132,7 +126,7 @@ module DecompressionBombs { /** * `Zip::File` */ - private API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } + API::Node zipFile() { result = API::getTopLevelCall("Zip").getMember("File") } } } @@ -148,9 +142,15 @@ class IoCopyStream extends DataFlow::CallNode { class Bombs extends TaintTracking::Configuration { Bombs() { this = "Decompression Bombs" } + override predicate isSanitizer(DataFlow::Node node) { + not node.getLocation().hasLocationInfo("%spec%", _, _, _, _) + } + override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource or - source instanceof DataFlow::LocalSourceNode + source instanceof RemoteFlowSource + // or + // source instanceof DataFlow::LocalSourceNode + // source = API::getTopLevelCall("Zip").getMember("InputStream").getASuccessor*() } override predicate isSink(DataFlow::Node sink) { From 609bb762fe32849a92c0ca2243258cbcda5ce084 Mon Sep 17 00:00:00 2001 From: amammad <77095239+amammad@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:04:11 +0200 Subject: [PATCH 07/11] fix a bug,modularize --- .../DecompressionBombs.ql | 159 ++------------- .../DecompressionBombs.qll | 167 +++++++++++++++ .../DecompressionBombs.expected | 193 ++++++++++-------- .../CWE-522-DecompressionBombs/gzipBombs.rb | 39 ++-- .../CWE-522-DecompressionBombs/zipBombs.rb | 94 ++++----- 5 files changed, 364 insertions(+), 288 deletions(-) create mode 100644 ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql index 3ed993bebd5..da9ffb5302e 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql @@ -11,124 +11,10 @@ * external/cwe/cwe-409 */ -import codeql.ruby.AST -import codeql.ruby.frameworks.Files -import codeql.ruby.ApiGraphs -import codeql.ruby.DataFlow -import codeql.ruby.dataflow.RemoteFlowSources -import codeql.ruby.TaintTracking -import DataFlow::PathGraph - -module DecompressionBombs { - abstract class DecompressionBombSink extends DataFlow::Node { } - - module Zlib { - /** - * `Zlib::GzipReader` - * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. - * - * according to above warning from Doc we don't need to go forward after open() - * or new() methods, we just need the argument node of them - */ - private API::Node gzipReaderInstance() { - result = API::getTopLevelMember("Zlib").getMember("GzipReader") - } - - /** - * A return values of following methods - * `Zlib::GzipReader.open` - * `Zlib::GzipReader.zcat` - * `Zlib::GzipReader.new` - */ - class ZipSink extends DecompressionBombSink { - ZipSink() { - this = gzipReaderInstance().getMethod(["open", "new", "zcat"]).getReturn().asSource() - } - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipnode | zipnode = gzipReaderInstance().getMethod(["open", "new", "zcat"]) | - nodeFrom = zipnode.getParameter(0).asSink() and - nodeTo = zipnode.getReturn().asSource() - ) - } - } - - module ZipInputStream { - /** - * `Zip::InputStream` - * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. - * - * according to above warning from Doc we don't need to go forward after open() - * or new() methods, we just need the argument node of them - */ - private API::Node zipInputStream() { - result = API::getTopLevelMember("Zip").getMember("InputStream") - } - - /** - * A return values of following methods - * `ZipIO.read` - * `ZipEntry.extract` - */ - class ZipSink extends DecompressionBombSink { - ZipSink() { - this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource() and - not this.getLocation().getFile().getBaseName().matches("%spec%") - } - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipnode | zipnode = zipInputStream().getMethod(["open", "new"]) | - nodeFrom = zipnode.getParameter(0).asSink() and - nodeTo = zipnode.getReturn().asSource() - ) - } - } - - module ZipFile { - API::Node rubyZipNode() { - result = zipFile() - or - result = rubyZipNode().getMethod(_) - or - result = rubyZipNode().getReturn() - or - result = rubyZipNode().getParameter(_) - or - result = rubyZipNode().getAnElement() - or - result = rubyZipNode().getBlock() - } - - /** - * A return values of following methods - * `ZipIO.read` - * `ZipEntry.extract` - * sanitize the nodes which have `entry.size > someOBJ` - */ - class ZipSink extends DecompressionBombSink { - ZipSink() { - exists(API::Node zipnodes | zipnodes = rubyZipNode() | - this = zipnodes.getMethod(["extract", "read"]).getReturn().asSource() and - not exists(zipnodes.getMethod("size").getReturn().getMethod(">").getParameter(0)) - ) - } - } - - predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(API::Node zipnodes | zipnodes = rubyZipNode() | - nodeTo = zipnodes.getMethod(["extract", "read"]).getReturn().asSource() and - nodeFrom = zipnodes.getMethod(["new", "open"]).getParameter(0).asSink() - ) - } - - /** - * `Zip::File` - */ - API::Node zipFile() { result = API::getTopLevelCall("Zip").getMember("File") } - } -} +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking +import DecompressionBombs /** * A call to `IO.copy_stream` @@ -139,30 +25,17 @@ class IoCopyStream extends DataFlow::CallNode { DataFlow::Node getAPathArgument() { result = this.getArgument(0) } } -class Bombs extends TaintTracking::Configuration { - Bombs() { this = "Decompression Bombs" } - - override predicate isSanitizer(DataFlow::Node node) { - not node.getLocation().hasLocationInfo("%spec%", _, _, _, _) +module BombsConfig implements DataFlow::ConfigSig { + predicate isBarrier(DataFlow::Node node) { + node.getLocation().hasLocationInfo("%spec%", _, _, _, _) } - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - // or - // source instanceof DataFlow::LocalSourceNode - // source = API::getTopLevelCall("Zip").getMember("InputStream").getASuccessor*() - } + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { - sink instanceof DecompressionBombs::DecompressionBombSink - } + predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink } - override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - DecompressionBombs::ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) - or - DecompressionBombs::ZipInputStream::isAdditionalTaintStep(nodeFrom, nodeTo) - or - DecompressionBombs::Zlib::isAdditionalTaintStep(nodeFrom, nodeTo) + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + any(DecompressionBomb::AdditionalTaintStep ats).isAdditionalTaintStep(nodeFrom, nodeTo) or exists(API::Node n | n = API::root().getMember("File").getMethod("open") | nodeFrom = n.getParameter(0).asSink() and @@ -194,7 +67,11 @@ class Bombs extends TaintTracking::Configuration { } } -from Bombs cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(), +module Bombs = TaintTracking::Global; + +import Bombs::PathGraph + +from Bombs::PathNode source, Bombs::PathNode sink +where Bombs::flowPath(source, sink) +select sink.getNode(), source, sink, "This file Decompression depends on a $@.", source.getNode(), "potentially untrusted source" diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll new file mode 100644 index 00000000000..d51dffe2d90 --- /dev/null +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll @@ -0,0 +1,167 @@ +import codeql.ruby.AST +import codeql.ruby.frameworks.Files +import codeql.ruby.ApiGraphs +import codeql.ruby.DataFlow +import codeql.ruby.dataflow.RemoteFlowSources + +module DecompressionBomb { + /** + * The Sinks of uncontrolled data decompression + */ + class Sink extends DataFlow::Node { + Sink() { this = any(Range r).sink() } + } + + /** + * The additional taint steps that need for creating taint tracking or dataflow. + */ + abstract class AdditionalTaintStep extends string { + AdditionalTaintStep() { this = "AdditionalTaintStep" } + + /** + * Holds if there is a additional taint step between pred and succ. + */ + abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ); + } + + /** + * A abstract class responsible for extending new decompression sinks + */ + abstract class Range extends API::Node { + /** + * Gets the sink of responsible for decompression node + * + * it can be a path, stream of compressed data, + * or a call to function that use pipe + */ + abstract DataFlow::Node sink(); + } +} + +module Zlib { + /** + * Gets a node of `Zlib::GzipReader` member + * + * Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. + */ + private API::Node gzipReaderInstance() { + result = API::getTopLevelMember("Zlib").getMember("GzipReader") + } + + /** + * A return values of following methods + * `Zlib::GzipReader.open` + * `Zlib::GzipReader.zcat` + * `Zlib::GzipReader.new` + */ + class DecompressionBombSink extends DecompressionBomb::Range { + DecompressionBombSink() { this = gzipReaderInstance().getMethod(["open", "new", "zcat"]) } + + override DataFlow::Node sink() { result = this.getReturn().asSource() } + } + + /** + * The additional taint steps that need for creating taint tracking or dataflow for `Zlib`. + */ + class AdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep { + AdditionalTaintStep() { this = "AdditionalTaintStep" } + + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node openOrNewOrZcat | + openOrNewOrZcat = gzipReaderInstance().getMethod(["open", "new", "zcat"]) + | + nodeFrom = openOrNewOrZcat.getParameter(0).asSink() and + nodeTo = openOrNewOrZcat.getReturn().asSource() + ) + } + } +} + +module ZipInputStream { + /** + * Gets a node of `Zip::InputStream` member + * + * Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. + */ + private API::Node zipInputStream() { + result = API::getTopLevelMember("Zip").getMember("InputStream") + } + + /** + * The return values of following methods + * `ZipIO.read` + * `ZipEntry.extract` + */ + class DecompressionBombSink extends DecompressionBomb::Range { + DecompressionBombSink() { this = zipInputStream().getMethod(["open", "new"]) } + + override DataFlow::Node sink() { result = this.getReturn().asSource() } + } + + /** + * The additional taint steps that need for creating taint tracking or dataflow for `Zip`. + */ + class AdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep { + AdditionalTaintStep() { this = "AdditionalTaintStep" } + + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node openOrNew | openOrNew = zipInputStream().getMethod(["open", "new"]) | + nodeFrom = openOrNew.getParameter(0).asSink() and + nodeTo = openOrNew.getReturn().asSource() + ) + } + } +} + +module ZipFile { + /** + * Gets a node of `Zip::File` member + */ + API::Node zipFile() { result = API::getTopLevelMember("Zip").getMember("File") } + + /** + * Gets nodes that have a `base` at the beginning + */ + API::Node rubyZipNode(API::Node base) { + result = base + or + result = rubyZipNode(base).getMethod(_) + or + result = rubyZipNode(base).getReturn() + or + result = rubyZipNode(base).getParameter(_) + or + result = rubyZipNode(base).getAnElement() + or + result = rubyZipNode(base).getBlock() + } + + /** + * The return values of following methods + * `ZipIO.read` + * `ZipEntry.extract` + * A sanitizer exists inside the nodes which have `entry.size > someOBJ` + */ + class DecompressionBombSink extends DecompressionBomb::Range { + DecompressionBombSink() { this = rubyZipNode(zipFile()) } + + override DataFlow::Node sink() { + result = this.getMethod(["extract", "read"]).getReturn().asSource() and + not exists(this.getMethod("size").getReturn().getMethod(">").getParameter(0)) + } + } + + /** + * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`. + */ + class AdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep { + AdditionalTaintStep() { this = "AdditionalTaintStep" } + + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipFile | zipFile = zipFile().getMethod("open") | + nodeFrom = zipFile.getParameter(0).asSink() and + nodeTo = rubyZipNode(zipFile).getMethod(["extract", "read"]).getReturn().asSource() + ) + } + } +} diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected index c9f13eb5272..3320a40a534 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected @@ -1,87 +1,114 @@ edges -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:3:1:3:32 | call to open | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:4:1:6:3 | call to open | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:7:1:11:3 | call to open | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:12:20:12:51 | call to open | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:17:1:17:48 | call to new | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:18:1:18:48 | call to new | -| gzipBombs.rb:2:1:2:9 | gzip_path | gzipBombs.rb:22:1:22:38 | call to zcat | -| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:2:1:2:9 | gzip_path | -| gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:48 | call to new | -| gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | -| gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:1:22:38 | call to zcat | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:3:1:8:3 | call to open | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:9:1:11:3 | call to open | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:12:9:12:43 | call to open | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:14:1:14:40 | call to read | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:15:1:15:49 | call to extract | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:29:27:29:38 | zipfile_path | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:48:6:48:32 | call to read | -| zipBombs.rb:2:1:2:12 | zipfile_path | zipBombs.rb:50:27:50:38 | zipfile_path | -| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:2:1:2:12 | zipfile_path | -| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:31:3:31:15 | call to extract | -| zipBombs.rb:29:27:29:38 | zipfile_path | zipBombs.rb:32:3:32:29 | call to read | -| zipBombs.rb:50:27:50:38 | zipfile_path | zipBombs.rb:52:3:52:29 | call to read | +| gzipBombs.rb:4:3:4:11 | gzip_path | gzipBombs.rb:6:25:6:33 | gzip_path | +| gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:4:15:4:27 | ...[...] | +| gzipBombs.rb:4:15:4:27 | ...[...] | gzipBombs.rb:4:3:4:11 | gzip_path | +| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:6:3:6:34 | call to open | +| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:7:25:7:33 | gzip_path | +| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:7:3:9:5 | call to open | +| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:10:25:10:33 | gzip_path | +| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:10:3:14:5 | call to open | +| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:15:44:15:52 | gzip_path | +| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:15:22:15:53 | call to open | +| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:20:34:20:42 | gzip_path | +| gzipBombs.rb:20:24:20:49 | call to open | gzipBombs.rb:20:3:20:50 | call to new | +| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:20:24:20:49 | call to open | +| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:21:34:21:42 | gzip_path | +| gzipBombs.rb:21:24:21:49 | call to open | gzipBombs.rb:21:3:21:50 | call to new | +| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:21:24:21:49 | call to open | +| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:25:30:25:38 | gzip_path | +| gzipBombs.rb:25:25:25:39 | call to open | gzipBombs.rb:25:3:25:40 | call to zcat | +| gzipBombs.rb:25:30:25:38 | gzip_path | gzipBombs.rb:25:25:25:39 | call to open | +| zipBombs.rb:4:3:4:14 | zipfile_path | zipBombs.rb:6:25:6:36 | zipfile_path | +| zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:4:18:4:30 | ...[...] | +| zipBombs.rb:4:18:4:30 | ...[...] | zipBombs.rb:4:3:4:14 | zipfile_path | +| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:6:3:11:5 | call to open | +| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:12:25:12:36 | zipfile_path | +| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:12:3:14:5 | call to open | +| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:15:33:15:44 | zipfile_path | +| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:15:11:15:45 | call to open | +| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:17:18:17:29 | zipfile_path | +| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:17:3:17:42 | call to read | +| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:18:18:18:29 | zipfile_path | +| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:18:3:18:51 | call to extract | +| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:20:18:20:29 | zipfile_path | +| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:28:7:28:33 | call to read | +| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:32:29:32:40 | zipfile_path | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:34:5:34:17 | call to extract | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:35:5:35:31 | call to read | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:39:18:39:29 | zipfile_path | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:41:7:41:31 | call to read | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:42:7:42:19 | call to extract | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:46:10:46:36 | call to read | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:49:29:49:40 | zipfile_path | +| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:51:8:51:34 | call to read | +| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:53:29:53:40 | zipfile_path | +| zipBombs.rb:53:29:53:40 | zipfile_path | zipBombs.rb:55:5:55:31 | call to read | nodes -| gzipBombs.rb:2:1:2:9 | gzip_path | semmle.label | gzip_path | -| gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | semmle.label | "Path/To/bomb.gz" | -| gzipBombs.rb:3:1:3:32 | call to open | semmle.label | call to open | -| gzipBombs.rb:4:1:6:3 | call to open | semmle.label | call to open | -| gzipBombs.rb:7:1:11:3 | call to open | semmle.label | call to open | -| gzipBombs.rb:12:20:12:51 | call to open | semmle.label | call to open | -| gzipBombs.rb:17:1:17:48 | call to new | semmle.label | call to new | -| gzipBombs.rb:17:22:17:47 | call to open | semmle.label | call to open | -| gzipBombs.rb:18:1:18:48 | call to new | semmle.label | call to new | -| gzipBombs.rb:18:22:18:47 | call to open | semmle.label | call to open | -| gzipBombs.rb:22:1:22:38 | call to zcat | semmle.label | call to zcat | -| gzipBombs.rb:22:23:22:37 | call to open | semmle.label | call to open | -| zipBombs.rb:2:1:2:12 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | semmle.label | "Path/To/bomb.zip" | -| zipBombs.rb:3:1:8:3 | call to open | semmle.label | call to open | -| zipBombs.rb:9:1:11:3 | call to open | semmle.label | call to open | -| zipBombs.rb:12:9:12:43 | call to open | semmle.label | call to open | -| zipBombs.rb:14:1:14:40 | call to read | semmle.label | call to read | -| zipBombs.rb:15:1:15:49 | call to extract | semmle.label | call to extract | -| zipBombs.rb:29:27:29:38 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:31:3:31:15 | call to extract | semmle.label | call to extract | -| zipBombs.rb:32:3:32:29 | call to read | semmle.label | call to read | -| zipBombs.rb:48:6:48:32 | call to read | semmle.label | call to read | -| zipBombs.rb:50:27:50:38 | zipfile_path | semmle.label | zipfile_path | -| zipBombs.rb:52:3:52:29 | call to read | semmle.label | call to read | +| gzipBombs.rb:4:3:4:11 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:4:15:4:20 | call to params | semmle.label | call to params | +| gzipBombs.rb:4:15:4:27 | ...[...] | semmle.label | ...[...] | +| gzipBombs.rb:6:3:6:34 | call to open | semmle.label | call to open | +| gzipBombs.rb:6:25:6:33 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:7:3:9:5 | call to open | semmle.label | call to open | +| gzipBombs.rb:7:25:7:33 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:10:3:14:5 | call to open | semmle.label | call to open | +| gzipBombs.rb:10:25:10:33 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:15:22:15:53 | call to open | semmle.label | call to open | +| gzipBombs.rb:15:44:15:52 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:20:3:20:50 | call to new | semmle.label | call to new | +| gzipBombs.rb:20:24:20:49 | call to open | semmle.label | call to open | +| gzipBombs.rb:20:34:20:42 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:21:3:21:50 | call to new | semmle.label | call to new | +| gzipBombs.rb:21:24:21:49 | call to open | semmle.label | call to open | +| gzipBombs.rb:21:34:21:42 | gzip_path | semmle.label | gzip_path | +| gzipBombs.rb:25:3:25:40 | call to zcat | semmle.label | call to zcat | +| gzipBombs.rb:25:25:25:39 | call to open | semmle.label | call to open | +| gzipBombs.rb:25:30:25:38 | gzip_path | semmle.label | gzip_path | +| zipBombs.rb:4:3:4:14 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:4:18:4:23 | call to params | semmle.label | call to params | +| zipBombs.rb:4:18:4:30 | ...[...] | semmle.label | ...[...] | +| zipBombs.rb:6:3:11:5 | call to open | semmle.label | call to open | +| zipBombs.rb:6:25:6:36 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:12:3:14:5 | call to open | semmle.label | call to open | +| zipBombs.rb:12:25:12:36 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:15:11:15:45 | call to open | semmle.label | call to open | +| zipBombs.rb:15:33:15:44 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:17:3:17:42 | call to read | semmle.label | call to read | +| zipBombs.rb:17:18:17:29 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:18:3:18:51 | call to extract | semmle.label | call to extract | +| zipBombs.rb:18:18:18:29 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:20:18:20:29 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:28:7:28:33 | call to read | semmle.label | call to read | +| zipBombs.rb:32:29:32:40 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:34:5:34:17 | call to extract | semmle.label | call to extract | +| zipBombs.rb:35:5:35:31 | call to read | semmle.label | call to read | +| zipBombs.rb:39:18:39:29 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:41:7:41:31 | call to read | semmle.label | call to read | +| zipBombs.rb:42:7:42:19 | call to extract | semmle.label | call to extract | +| zipBombs.rb:46:10:46:36 | call to read | semmle.label | call to read | +| zipBombs.rb:49:29:49:40 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:51:8:51:34 | call to read | semmle.label | call to read | +| zipBombs.rb:53:29:53:40 | zipfile_path | semmle.label | zipfile_path | +| zipBombs.rb:55:5:55:31 | call to read | semmle.label | call to read | subpaths #select -| gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:3:1:3:32 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:3:1:3:32 | call to open | gzipBombs.rb:3:1:3:32 | call to open | This file extraction depends on a $@. | gzipBombs.rb:3:1:3:32 | call to open | potentially untrusted source | -| gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:4:1:6:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:4:1:6:3 | call to open | gzipBombs.rb:4:1:6:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:4:1:6:3 | call to open | potentially untrusted source | -| gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:7:1:11:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:7:1:11:3 | call to open | gzipBombs.rb:7:1:11:3 | call to open | This file extraction depends on a $@. | gzipBombs.rb:7:1:11:3 | call to open | potentially untrusted source | -| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:12:20:12:51 | call to open | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:20:12:51 | call to open | gzipBombs.rb:12:20:12:51 | call to open | This file extraction depends on a $@. | gzipBombs.rb:12:20:12:51 | call to open | potentially untrusted source | -| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:17:1:17:48 | call to new | potentially untrusted source | -| gzipBombs.rb:17:1:17:48 | call to new | gzipBombs.rb:17:22:17:47 | call to open | gzipBombs.rb:17:1:17:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:17:22:17:47 | call to open | potentially untrusted source | -| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:18:1:18:48 | call to new | potentially untrusted source | -| gzipBombs.rb:18:1:18:48 | call to new | gzipBombs.rb:18:22:18:47 | call to open | gzipBombs.rb:18:1:18:48 | call to new | This file extraction depends on a $@. | gzipBombs.rb:18:22:18:47 | call to open | potentially untrusted source | -| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:2:13:2:29 | "Path/To/bomb.gz" | potentially untrusted source | -| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:22:1:22:38 | call to zcat | potentially untrusted source | -| gzipBombs.rb:22:1:22:38 | call to zcat | gzipBombs.rb:22:23:22:37 | call to open | gzipBombs.rb:22:1:22:38 | call to zcat | This file extraction depends on a $@. | gzipBombs.rb:22:23:22:37 | call to open | potentially untrusted source | -| zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:3:1:8:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:3:1:8:3 | call to open | zipBombs.rb:3:1:8:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:3:1:8:3 | call to open | potentially untrusted source | -| zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:9:1:11:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:9:1:11:3 | call to open | zipBombs.rb:9:1:11:3 | call to open | This file extraction depends on a $@. | zipBombs.rb:9:1:11:3 | call to open | potentially untrusted source | -| zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:12:9:12:43 | call to open | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:12:9:12:43 | call to open | zipBombs.rb:12:9:12:43 | call to open | This file extraction depends on a $@. | zipBombs.rb:12:9:12:43 | call to open | potentially untrusted source | -| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | zipBombs.rb:14:1:14:40 | call to read | This file extraction depends on a $@. | zipBombs.rb:14:1:14:40 | call to read | potentially untrusted source | -| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | zipBombs.rb:15:1:15:49 | call to extract | This file extraction depends on a $@. | zipBombs.rb:15:1:15:49 | call to extract | potentially untrusted source | -| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | zipBombs.rb:31:3:31:15 | call to extract | This file extraction depends on a $@. | zipBombs.rb:31:3:31:15 | call to extract | potentially untrusted source | -| zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:32:3:32:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:32:3:32:29 | call to read | zipBombs.rb:32:3:32:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:32:3:32:29 | call to read | potentially untrusted source | -| zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:48:6:48:32 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:48:6:48:32 | call to read | zipBombs.rb:48:6:48:32 | call to read | This file extraction depends on a $@. | zipBombs.rb:48:6:48:32 | call to read | potentially untrusted source | -| zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | zipBombs.rb:52:3:52:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:2:16:2:33 | "Path/To/bomb.zip" | potentially untrusted source | -| zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:52:3:52:29 | call to read | zipBombs.rb:52:3:52:29 | call to read | This file extraction depends on a $@. | zipBombs.rb:52:3:52:29 | call to read | potentially untrusted source | +| gzipBombs.rb:6:3:6:34 | call to open | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:6:3:6:34 | call to open | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:7:3:9:5 | call to open | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:7:3:9:5 | call to open | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:10:3:14:5 | call to open | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:10:3:14:5 | call to open | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:15:22:15:53 | call to open | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:15:22:15:53 | call to open | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:20:3:20:50 | call to new | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:20:3:20:50 | call to new | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:21:3:21:50 | call to new | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:21:3:21:50 | call to new | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| gzipBombs.rb:25:3:25:40 | call to zcat | gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:25:3:25:40 | call to zcat | This file Decompression depends on a $@. | gzipBombs.rb:4:15:4:20 | call to params | potentially untrusted source | +| zipBombs.rb:6:3:11:5 | call to open | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:6:3:11:5 | call to open | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:12:3:14:5 | call to open | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:12:3:14:5 | call to open | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:15:11:15:45 | call to open | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:15:11:15:45 | call to open | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:17:3:17:42 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:17:3:17:42 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:18:3:18:51 | call to extract | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:18:3:18:51 | call to extract | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:28:7:28:33 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:28:7:28:33 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:34:5:34:17 | call to extract | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:34:5:34:17 | call to extract | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:35:5:35:31 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:35:5:35:31 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:41:7:41:31 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:41:7:41:31 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:42:7:42:19 | call to extract | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:42:7:42:19 | call to extract | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:46:10:46:36 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:46:10:46:36 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:51:8:51:34 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:51:8:51:34 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | +| zipBombs.rb:55:5:55:31 | call to read | zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:55:5:55:31 | call to read | This file Decompression depends on a $@. | zipBombs.rb:4:18:4:23 | call to params | potentially untrusted source | diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb index ab038dd47ec..bf9bb7b329d 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/gzipBombs.rb @@ -1,24 +1,27 @@ require 'zlib' -gzip_path = "Path/To/bomb.gz" -Zlib::GzipReader.open(gzip_path).read -Zlib::GzipReader.open(gzip_path) do |uncompressedfile| - puts uncompressedfile.read -end -Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + +class TestController < ActionController::Base + gzip_path = params[:path] + + Zlib::GzipReader.open(gzip_path).read + Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + puts uncompressedfile.read + end + Zlib::GzipReader.open(gzip_path) do |uncompressedfile| + uncompressedfile.each do |entry| + puts entry + end + end + uncompressedfile = Zlib::GzipReader.open(gzip_path) uncompressedfile.each do |entry| puts entry end -end -uncompressedfile = Zlib::GzipReader.open(gzip_path) -uncompressedfile.each do |entry| - puts entry + + Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read + Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| + puts entry + end + + Zlib::GzipReader.zcat(open(gzip_path)) end -Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read -Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| - puts entry -end - -Zlib::GzipReader.zcat(open(gzip_path)) - - diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb index c0f15b86fce..5aab5ce6382 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/zipBombs.rb @@ -1,55 +1,57 @@ require 'zip' -zipfile_path = "Path/To/bomb.zip" -Zip::InputStream.open(zipfile_path) do |input| - while (entry = input.get_next_entry) - puts :file_name, entry.name - input + +class TestController < ActionController::Base + zipfile_path = params[:path] + + Zip::InputStream.open(zipfile_path) do |input| + while (entry = input.get_next_entry) + puts :file_name, entry.name + input + end end -end -Zip::InputStream.open(zipfile_path) do |input| - input.read -end -input = Zip::InputStream.open(zipfile_path) + Zip::InputStream.open(zipfile_path) do |input| + input.read + end + input = Zip::InputStream.open(zipfile_path) -Zip::File.open(zipfile_path).read "10GB" -Zip::File.open(zipfile_path).extract "10GB", "./" + Zip::File.open(zipfile_path).read "10GB" + Zip::File.open(zipfile_path).extract "10GB", "./" -Zip::File.open(zipfile_path) do |zip_file| - # Handle entries one by one + Zip::File.open(zipfile_path) do |zip_file| + # Handle entries one by one + zip_file.each do |entry| + puts "Extracting #{entry.name}" + raise 'File too large when extracted' if entry.size > MAX_SIZE + # Extract to file or directory based on name in the archive + entry.extract + # Read into memory + entry.get_input_stream.read + end + end + + zip_file = Zip::File.open(zipfile_path) zip_file.each do |entry| - puts "Extracting #{entry.name}" - raise 'File too large when extracted' if entry.size > MAX_SIZE - # Extract to file or directory based on name in the archive entry.extract - # Read into memory + entry.get_input_stream.read + end + + # Find specific entry + Zip::File.open(zipfile_path) do |zip_file| + zip_file.glob('*.xml').each do |entry| + zip_file.read(entry.name) + entry.extract + end + entry = zip_file.glob('*.csv').first + raise 'File too large when extracted' if entry.size > MAX_SIZE + puts entry.get_input_stream.read + end + + zip_file = Zip::File.open(zipfile_path) + entry = zip_file.glob('*.csv') + puts entry.get_input_stream.read + + zip_file = Zip::File.open(zipfile_path) + zip_file.glob('*') do |entry| entry.get_input_stream.read end end - -zip_file = Zip::File.open(zipfile_path) -zip_file.each do |entry| - entry.extract - entry.get_input_stream.read -end - -# Find specific entry -Zip::File.open(zipfile_path) do |zip_file| - zip_file.glob('*.xml').each do |entry| - zip_file.read(entry.name) - entry.extract - end - entry = zip_file.glob('*.csv').first - raise 'File too large when extracted' if entry.size > MAX_SIZE - puts entry.get_input_stream.read -end - -zip_file = Zip::File.open(zipfile_path) -entry = zip_file.glob('*.csv') -puts entry.get_input_stream.read - -zip_file = Zip::File.open(zipfile_path) -zip_file.glob('*') do |entry| - entry.get_input_stream.read -end - - From 2e4e5ef48095b97fed9741fe123a9655accce356 Mon Sep 17 00:00:00 2001 From: amammad <77095239+amammad@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:50:00 +0200 Subject: [PATCH 08/11] fix a comment --- .../CWE-522-DecompressionBombs/DecompressionBombs.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll index d51dffe2d90..cda39e14446 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll @@ -81,16 +81,18 @@ module ZipInputStream { /** * Gets a node of `Zip::InputStream` member * - * Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes. + * Note that if you use the lower level Zip::InputStream interface, rubyZip does not check the entry sizes. */ private API::Node zipInputStream() { result = API::getTopLevelMember("Zip").getMember("InputStream") } /** - * The return values of following methods - * `ZipIO.read` - * `ZipEntry.extract` + * The methods + * `Zip::InputStream.read` + * `Zip::InputStream.extract` + * + * as source of decompression bombs, they need an additional taint step for a dataflow or taint tracking query */ class DecompressionBombSink extends DecompressionBomb::Range { DecompressionBombSink() { this = zipInputStream().getMethod(["open", "new"]) } From 2097a001b990a49ddc12b0fd1157fd75f379a63e Mon Sep 17 00:00:00 2001 From: amammad <77095239+amammad@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:01:51 +0100 Subject: [PATCH 09/11] apply code review suggestions, fix qldoc, add experimental additional taint steps that can improve performance --- .../DecompressionBombs.qhelp | 2 +- .../DecompressionBombs.ql | 22 ++-- .../DecompressionBombs.qll | 108 +++++++++++++----- 3 files changed, 85 insertions(+), 47 deletions(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp index 97248dbcad1..44d8721928e 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp @@ -12,7 +12,7 @@

    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.

    -

    Please read official RubyZip Documentation here +

    Please read official RubyZip Documentation here

    Rubyzip: According to official Documentation

    diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql index da9ffb5302e..1e245ca6edd 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql @@ -11,6 +11,7 @@ * external/cwe/cwe-409 */ +import ruby private import codeql.ruby.Concepts private import codeql.ruby.DataFlow private import codeql.ruby.TaintTracking @@ -26,10 +27,6 @@ class IoCopyStream extends DataFlow::CallNode { } module BombsConfig implements DataFlow::ConfigSig { - predicate isBarrier(DataFlow::Node node) { - node.getLocation().hasLocationInfo("%spec%", _, _, _, _) - } - predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink } @@ -37,29 +34,24 @@ module BombsConfig implements DataFlow::ConfigSig { predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { any(DecompressionBomb::AdditionalTaintStep ats).isAdditionalTaintStep(nodeFrom, nodeTo) or - exists(API::Node n | n = API::root().getMember("File").getMethod("open") | + exists(API::Node n | n = API::getTopLevelMember("File").getMethod("open") | nodeFrom = n.getParameter(0).asSink() and nodeTo = n.getReturn().asSource() ) or - exists(File::FileOpen n | - nodeFrom = n.getAPathArgument() and - nodeTo = n - ) + nodeFrom = nodeTo.(File::FileOpen).getAPathArgument() or - exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") | + exists(API::Node n | n = API::getTopLevelMember("StringIO").getMethod("new") | nodeFrom = n.getParameter(0).asSink() and nodeTo = n.getReturn().asSource() ) or - exists(IoCopyStream n | - nodeFrom = n.getAPathArgument() and - nodeTo = n - ) + nodeFrom = nodeTo.(IoCopyStream).getAPathArgument() or // following can be a global additional step exists(DataFlow::CallNode cn | - cn.getMethodName() = "open" and cn.getReceiver().toString() = "self" + cn.getMethodName() = "open" and + cn.getReceiver().getExprNode().getExpr() instanceof Ast::SelfVariableAccess | nodeFrom = cn.getArgument(0) and nodeTo = cn diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll index cda39e14446..c9622f5ed09 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll @@ -6,11 +6,12 @@ import codeql.ruby.dataflow.RemoteFlowSources module DecompressionBomb { /** - * The Sinks of uncontrolled data decompression + * A abstract class responsible for extending new decompression sinks + * + * can be a path, stream of compressed data, + * or a call to function that use pipe */ - class Sink extends DataFlow::Node { - Sink() { this = any(Range r).sink() } - } + abstract class Sink extends DataFlow::Node { } /** * The additional taint steps that need for creating taint tracking or dataflow. @@ -23,19 +24,6 @@ module DecompressionBomb { */ abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ); } - - /** - * A abstract class responsible for extending new decompression sinks - */ - abstract class Range extends API::Node { - /** - * Gets the sink of responsible for decompression node - * - * it can be a path, stream of compressed data, - * or a call to function that use pipe - */ - abstract DataFlow::Node sink(); - } } module Zlib { @@ -54,10 +42,10 @@ module Zlib { * `Zlib::GzipReader.zcat` * `Zlib::GzipReader.new` */ - class DecompressionBombSink extends DecompressionBomb::Range { - DecompressionBombSink() { this = gzipReaderInstance().getMethod(["open", "new", "zcat"]) } - - override DataFlow::Node sink() { result = this.getReturn().asSource() } + class DecompressionBombSink extends DecompressionBomb::Sink { + DecompressionBombSink() { + this = gzipReaderInstance().getMethod(["open", "new", "zcat"]).getReturn().asSource() + } } /** @@ -94,10 +82,10 @@ module ZipInputStream { * * as source of decompression bombs, they need an additional taint step for a dataflow or taint tracking query */ - class DecompressionBombSink extends DecompressionBomb::Range { - DecompressionBombSink() { this = zipInputStream().getMethod(["open", "new"]) } - - override DataFlow::Node sink() { result = this.getReturn().asSource() } + class DecompressionBombSink extends DecompressionBomb::Sink { + DecompressionBombSink() { + this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource() + } } /** @@ -144,12 +132,14 @@ module ZipFile { * `ZipEntry.extract` * A sanitizer exists inside the nodes which have `entry.size > someOBJ` */ - class DecompressionBombSink extends DecompressionBomb::Range { - DecompressionBombSink() { this = rubyZipNode(zipFile()) } - - override DataFlow::Node sink() { - result = this.getMethod(["extract", "read"]).getReturn().asSource() and - not exists(this.getMethod("size").getReturn().getMethod(">").getParameter(0)) + class DecompressionBombSink extends DecompressionBomb::Sink { + DecompressionBombSink() { + exists(API::Node rubyZip | rubyZip = rubyZipNode(zipFile()) | + this = rubyZip.getMethod(["extract", "read"]).getReturn().asSource() and + not exists( + rubyZip.getMethod("size").getReturn().getMethod([">", " <", "<=", " >="]).getParameter(0) + ) + ) } } @@ -166,4 +156,60 @@ module ZipFile { ) } } + + predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(API::Node zipFile | zipFile = zipFile().getMethod("open") | + nodeFrom = zipFile.getParameter(0).asSink() and + nodeTo = rubyZipNode(zipFile).getMethod(["extract", "read"]).getReturn().asSource() + ) + } + // /** + // * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`. + // */ + // class AdditionalTaintStep1 extends DecompressionBomb::AdditionalTaintStep { + // AdditionalTaintStep1() { this = "AdditionalTaintStep" } + // override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // exists(API::Node zipFile | zipFile = zipFile().getMethod("open") | + // nodeFrom = zipFile.getParameter(0).asSink() and + // nodeTo = zipFile.asSource() + // ) + // } + // } + // API::Node rubyZipNode2() { + // result = zipFile().getMethod("open") + // or + // result = rubyZipNode2().getMethod(_) + // or + // result = rubyZipNode2().getReturn() + // or + // result = rubyZipNode2().getParameter(_) + // or + // result = rubyZipNode2().getAnElement() + // or + // result = rubyZipNode2().getBlock() + // } + // /** + // * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`. + // */ + // class AdditionalTaintStep2 extends DecompressionBomb::AdditionalTaintStep { + // AdditionalTaintStep2() { this = "AdditionalTaintStep" } + // override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // exists(API::Node zipFileOpen | zipFileOpen = rubyZipNode2() | + // nodeFrom = zipFileOpen.getReturn().asSource() and + // nodeTo = zipFileOpen.getMethod(["extract", "read"]).getReturn().asSource() + // ) + // } + // } + // /** + // * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`. + // */ + // class AdditionalTaintStep3 extends DecompressionBomb::AdditionalTaintStep { + // AdditionalTaintStep3() { this = "AdditionalTaintStep" } + // override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // exists(API::Node zipFileOpen | zipFileOpen = rubyZipNode2() | + // nodeFrom = zipFileOpen.asCall() and + // nodeTo = zipFileOpen.getMethod(["extract", "read"]).getReturn().asSource() + // ) + // } + // } } From f06c3fddd95543396641971efe421b1f23069cda Mon Sep 17 00:00:00 2001 From: am0o0 <77095239+am0o0@users.noreply.github.com> Date: Thu, 16 May 2024 15:12:31 +0200 Subject: [PATCH 10/11] fix qhelp, fix duplicate query id --- .../CWE-522-DecompressionBombs/DecompressionBombs.qhelp | 3 +-- .../CWE-522-DecompressionBombs/DecompressionBombs.ql | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp index 44d8721928e..01741e519aa 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp @@ -10,9 +10,8 @@

    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.

    - -

    Please read official RubyZip Documentation here

    +

    Rubyzip: According to official Documentation

    diff --git a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql index 1e245ca6edd..5ab6d29af7b 100644 --- a/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql +++ b/ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql @@ -5,7 +5,7 @@ * @problem.severity error * @security-severity 7.8 * @precision high - * @id rb/user-controlled-file-decompression + * @id rb/user-controlled-data-decompression * @tags security * experimental * external/cwe/cwe-409 From dcadda23cd3367900f58ce48f4ab58f83e215eba Mon Sep 17 00:00:00 2001 From: am0o0 <77095239+am0o0@users.noreply.github.com> Date: Thu, 16 May 2024 15:15:27 +0200 Subject: [PATCH 11/11] update expected file --- .../DecompressionBombs.expected | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected index 3320a40a534..7f3fab2ea87 100644 --- a/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected +++ b/ruby/ql/test/query-tests/experimental/CWE-522-DecompressionBombs/DecompressionBombs.expected @@ -1,48 +1,48 @@ edges -| gzipBombs.rb:4:3:4:11 | gzip_path | gzipBombs.rb:6:25:6:33 | gzip_path | -| gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:4:15:4:27 | ...[...] | -| gzipBombs.rb:4:15:4:27 | ...[...] | gzipBombs.rb:4:3:4:11 | gzip_path | -| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:6:3:6:34 | call to open | -| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:7:25:7:33 | gzip_path | -| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:7:3:9:5 | call to open | -| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:10:25:10:33 | gzip_path | -| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:10:3:14:5 | call to open | -| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:15:44:15:52 | gzip_path | -| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:15:22:15:53 | call to open | -| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:20:34:20:42 | gzip_path | -| gzipBombs.rb:20:24:20:49 | call to open | gzipBombs.rb:20:3:20:50 | call to new | -| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:20:24:20:49 | call to open | -| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:21:34:21:42 | gzip_path | -| gzipBombs.rb:21:24:21:49 | call to open | gzipBombs.rb:21:3:21:50 | call to new | -| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:21:24:21:49 | call to open | -| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:25:30:25:38 | gzip_path | -| gzipBombs.rb:25:25:25:39 | call to open | gzipBombs.rb:25:3:25:40 | call to zcat | -| gzipBombs.rb:25:30:25:38 | gzip_path | gzipBombs.rb:25:25:25:39 | call to open | -| zipBombs.rb:4:3:4:14 | zipfile_path | zipBombs.rb:6:25:6:36 | zipfile_path | -| zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:4:18:4:30 | ...[...] | -| zipBombs.rb:4:18:4:30 | ...[...] | zipBombs.rb:4:3:4:14 | zipfile_path | -| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:6:3:11:5 | call to open | -| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:12:25:12:36 | zipfile_path | -| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:12:3:14:5 | call to open | -| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:15:33:15:44 | zipfile_path | -| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:15:11:15:45 | call to open | -| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:17:18:17:29 | zipfile_path | -| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:17:3:17:42 | call to read | -| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:18:18:18:29 | zipfile_path | -| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:18:3:18:51 | call to extract | -| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:20:18:20:29 | zipfile_path | -| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:28:7:28:33 | call to read | -| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:32:29:32:40 | zipfile_path | -| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:34:5:34:17 | call to extract | -| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:35:5:35:31 | call to read | -| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:39:18:39:29 | zipfile_path | -| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:41:7:41:31 | call to read | -| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:42:7:42:19 | call to extract | -| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:46:10:46:36 | call to read | -| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:49:29:49:40 | zipfile_path | -| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:51:8:51:34 | call to read | -| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:53:29:53:40 | zipfile_path | -| zipBombs.rb:53:29:53:40 | zipfile_path | zipBombs.rb:55:5:55:31 | call to read | +| gzipBombs.rb:4:3:4:11 | gzip_path | gzipBombs.rb:6:25:6:33 | gzip_path | provenance | | +| gzipBombs.rb:4:15:4:20 | call to params | gzipBombs.rb:4:15:4:27 | ...[...] | provenance | | +| gzipBombs.rb:4:15:4:27 | ...[...] | gzipBombs.rb:4:3:4:11 | gzip_path | provenance | | +| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:6:3:6:34 | call to open | provenance | | +| gzipBombs.rb:6:25:6:33 | gzip_path | gzipBombs.rb:7:25:7:33 | gzip_path | provenance | | +| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:7:3:9:5 | call to open | provenance | | +| gzipBombs.rb:7:25:7:33 | gzip_path | gzipBombs.rb:10:25:10:33 | gzip_path | provenance | | +| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:10:3:14:5 | call to open | provenance | | +| gzipBombs.rb:10:25:10:33 | gzip_path | gzipBombs.rb:15:44:15:52 | gzip_path | provenance | | +| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:15:22:15:53 | call to open | provenance | | +| gzipBombs.rb:15:44:15:52 | gzip_path | gzipBombs.rb:20:34:20:42 | gzip_path | provenance | | +| gzipBombs.rb:20:24:20:49 | call to open | gzipBombs.rb:20:3:20:50 | call to new | provenance | | +| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:20:24:20:49 | call to open | provenance | | +| gzipBombs.rb:20:34:20:42 | gzip_path | gzipBombs.rb:21:34:21:42 | gzip_path | provenance | | +| gzipBombs.rb:21:24:21:49 | call to open | gzipBombs.rb:21:3:21:50 | call to new | provenance | | +| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:21:24:21:49 | call to open | provenance | | +| gzipBombs.rb:21:34:21:42 | gzip_path | gzipBombs.rb:25:30:25:38 | gzip_path | provenance | | +| gzipBombs.rb:25:25:25:39 | call to open | gzipBombs.rb:25:3:25:40 | call to zcat | provenance | | +| gzipBombs.rb:25:30:25:38 | gzip_path | gzipBombs.rb:25:25:25:39 | call to open | provenance | | +| zipBombs.rb:4:3:4:14 | zipfile_path | zipBombs.rb:6:25:6:36 | zipfile_path | provenance | | +| zipBombs.rb:4:18:4:23 | call to params | zipBombs.rb:4:18:4:30 | ...[...] | provenance | | +| zipBombs.rb:4:18:4:30 | ...[...] | zipBombs.rb:4:3:4:14 | zipfile_path | provenance | | +| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:6:3:11:5 | call to open | provenance | | +| zipBombs.rb:6:25:6:36 | zipfile_path | zipBombs.rb:12:25:12:36 | zipfile_path | provenance | | +| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:12:3:14:5 | call to open | provenance | | +| zipBombs.rb:12:25:12:36 | zipfile_path | zipBombs.rb:15:33:15:44 | zipfile_path | provenance | | +| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:15:11:15:45 | call to open | provenance | | +| zipBombs.rb:15:33:15:44 | zipfile_path | zipBombs.rb:17:18:17:29 | zipfile_path | provenance | | +| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:17:3:17:42 | call to read | provenance | | +| zipBombs.rb:17:18:17:29 | zipfile_path | zipBombs.rb:18:18:18:29 | zipfile_path | provenance | | +| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:18:3:18:51 | call to extract | provenance | | +| zipBombs.rb:18:18:18:29 | zipfile_path | zipBombs.rb:20:18:20:29 | zipfile_path | provenance | | +| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:28:7:28:33 | call to read | provenance | | +| zipBombs.rb:20:18:20:29 | zipfile_path | zipBombs.rb:32:29:32:40 | zipfile_path | provenance | | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:34:5:34:17 | call to extract | provenance | | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:35:5:35:31 | call to read | provenance | | +| zipBombs.rb:32:29:32:40 | zipfile_path | zipBombs.rb:39:18:39:29 | zipfile_path | provenance | | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:41:7:41:31 | call to read | provenance | | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:42:7:42:19 | call to extract | provenance | | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:46:10:46:36 | call to read | provenance | | +| zipBombs.rb:39:18:39:29 | zipfile_path | zipBombs.rb:49:29:49:40 | zipfile_path | provenance | | +| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:51:8:51:34 | call to read | provenance | | +| zipBombs.rb:49:29:49:40 | zipfile_path | zipBombs.rb:53:29:53:40 | zipfile_path | provenance | | +| zipBombs.rb:53:29:53:40 | zipfile_path | zipBombs.rb:55:5:55:31 | call to read | provenance | | nodes | gzipBombs.rb:4:3:4:11 | gzip_path | semmle.label | gzip_path | | gzipBombs.rb:4:15:4:20 | call to params | semmle.label | call to params |