From ad68a5e4e936b942d28f53799b460518a1764200 Mon Sep 17 00:00:00 2001 From: Taus Date: Tue, 9 Dec 2025 22:50:51 +0000 Subject: [PATCH] Python: Add modelling for `zstd.compression` See https://docs.python.org/3/library/compression.zstd.html for information about this library. As far as I can tell, the `zstd` library is not vulnerable to things like ZipSlip, but it _could_ be vulnerable to a decompression bomb attack, so I extended those models accordingly. --- .../python/security/DecompressionBomb.qll | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll index a2e50d0ade5..87a47ebeb00 100644 --- a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll +++ b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll @@ -364,6 +364,46 @@ module Lzma { } } +/** Provides sinks and additional taint steps related to the `zstd` library in Python 3.14+. */ +module Zstd { + private API::Node zstdInstance() { + result = API::moduleImport("compression").getMember("zstd").getMember(["ZstdFile", "open"]) + } + + /** + * The Decompression Sinks of `zstd` library + * + * `zstd.open(sink)` + * `zstd.ZstdFile(sink)` + * + * only read mode is sink + */ + class DecompressionSink extends DecompressionBomb::Sink { + DecompressionSink() { + exists(API::CallNode zstdCall | zstdCall = zstdInstance().getACall() | + this = zstdCall.getParameter(0, "filename").asSink() and + ( + not exists( + zstdCall + .getParameter(1, "mode") + .getAValueReachingSink() + .asExpr() + .(StringLiteral) + .getText() + ) or + zstdCall + .getParameter(1, "mode") + .getAValueReachingSink() + .asExpr() + .(StringLiteral) + .getText() + .matches("%r%") + ) + ) + } + } +} + /** * `io.TextIOWrapper(ip, encoding='utf-8')` like following: * ```python