From a5848e589a7953dc3ba345802404c4932ea0ccfe Mon Sep 17 00:00:00 2001 From: rahulreddykarne <33528858+rahulreddykarne@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:14:50 -0500 Subject: [PATCH] Add tarfile-unsafe-extraction rule (CWE-22 tarslip) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a rule detecting tar-archive extraction via extractall()/extract() with no filter= argument - the CVE-2007-4559 "tarslip" path-traversal class (CWE-22). Motivating case: CVE-2026-43637. Validated against torchvision, keras, scikit-learn, and cornac: 1 true positive (torchvision's _extract_tar) and 0 false positives. Ignores every filter= form and all zipfile usage. Known limitation: matches inline open→extract usage and helpers typed tarfile.TarFile; does not follow archives passed into untyped helper functions, since the open-source Semgrep taint engine is intra-function. --- .../audit/tarfile-unsafe-extraction.py | 86 +++++++++++++++++++ .../audit/tarfile-unsafe-extraction.yaml | 68 +++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 python/lang/security/audit/tarfile-unsafe-extraction.py create mode 100644 python/lang/security/audit/tarfile-unsafe-extraction.yaml diff --git a/python/lang/security/audit/tarfile-unsafe-extraction.py b/python/lang/security/audit/tarfile-unsafe-extraction.py new file mode 100644 index 0000000000..ae52833303 --- /dev/null +++ b/python/lang/security/audit/tarfile-unsafe-extraction.py @@ -0,0 +1,86 @@ +import tarfile +import zipfile + + +def unsafe_context_manager(archive_path, dest): + with tarfile.open(archive_path) as tar: + # ruleid: tarfile-unsafe-extraction + tar.extractall(dest) + + +def unsafe_assigned(archive_path, dest): + tar = tarfile.open(archive_path) + # ruleid: tarfile-unsafe-extraction + tar.extractall(path=dest) + tar.close() + + +def unsafe_chained(archive_path, dest): + # ruleid: tarfile-unsafe-extraction + tarfile.open(archive_path).extractall(dest) + + +def unsafe_single_member(archive_path, dest, member): + with tarfile.open(archive_path) as tar: + # ruleid: tarfile-unsafe-extraction + tar.extract(member, dest) + + +def unsafe_downloaded_cache(url, dest): + # models CVE-2026-43637: extracting an untrusted downloaded archive + local = download(url) + with tarfile.open(local) as tar: + # ruleid: tarfile-unsafe-extraction + tar.extractall(dest) + + +# ----- safe: extraction filter present (Python 3.12+) ----- + +def safe_data_filter(archive_path, dest): + with tarfile.open(archive_path) as tar: + # ok: tarfile-unsafe-extraction + tar.extractall(dest, filter="data") + + +def safe_data_filter_symbol(archive_path, dest): + with tarfile.open(archive_path) as tar: + # ok: tarfile-unsafe-extraction + tar.extractall(path=dest, filter=tarfile.data_filter) + + +def safe_member_filter(archive_path, dest, member): + with tarfile.open(archive_path) as tar: + # ok: tarfile-unsafe-extraction + tar.extract(member, dest, filter="tar") + + +def safe_chained_filter(archive_path, dest): + # ok: tarfile-unsafe-extraction + tarfile.open(archive_path).extractall(dest, filter="data") + + +# ----- safe: not a tarfile object (zipfile sanitizes '..' since 2.7.4) ----- + +def zip_extract(archive_path, dest): + with zipfile.ZipFile(archive_path) as zf: + # ok: tarfile-unsafe-extraction + zf.extractall(dest) + + +# ----- typed helper parameter (archive opened by the caller) ----- + +def unsafe_typed_helper(tar: tarfile.TarFile, dest): + # ruleid: tarfile-unsafe-extraction + tar.extractall(dest) + + +from tarfile import TarFile + +def unsafe_typed_helper_bare(tar: TarFile, dest, member): + # ruleid: tarfile-unsafe-extraction + tar.extract(member, dest) + + +def safe_typed_helper(tar: tarfile.TarFile, dest): + # ok: tarfile-unsafe-extraction + tar.extractall(dest, filter="data") diff --git a/python/lang/security/audit/tarfile-unsafe-extraction.yaml b/python/lang/security/audit/tarfile-unsafe-extraction.yaml new file mode 100644 index 0000000000..d431a05c2f --- /dev/null +++ b/python/lang/security/audit/tarfile-unsafe-extraction.yaml @@ -0,0 +1,68 @@ +rules: + - id: tarfile-unsafe-extraction + patterns: + - pattern-either: + - patterns: + - pattern-inside: | + with tarfile.open(...) as $TAR: + ... + - pattern-either: + - pattern: $TAR.extractall(...) + - pattern: $TAR.extract(...) + - patterns: + - pattern-inside: | + $TAR = tarfile.open(...) + ... + - pattern-either: + - pattern: $TAR.extractall(...) + - pattern: $TAR.extract(...) + - pattern: tarfile.open(...).extractall(...) + - pattern: tarfile.open(...).extract(...) + - patterns: + - pattern-inside: | + def $FUNC(..., $TAR: tarfile.TarFile, ...): + ... + - pattern-either: + - pattern: $TAR.extractall(...) + - pattern: $TAR.extract(...) + - patterns: + - pattern-inside: | + def $FUNC(..., $TAR: TarFile, ...): + ... + - pattern-either: + - pattern: $TAR.extractall(...) + - pattern: $TAR.extract(...) + - pattern-not: $ANY.extractall(..., filter=$F, ...) + - pattern-not: $ANY.extract(..., filter=$F, ...) + - pattern-not: tarfile.open(...).extractall(..., filter=$F, ...) + - pattern-not: tarfile.open(...).extract(..., filter=$F, ...) + message: >- + Extracting a tar archive with `extractall()`/`extract()` and no `filter=` + argument does not contain extracted paths. A crafted archive can use + `../` sequences, absolute paths, or symlink/hardlink members to write + files outside the destination directory (CVE-2007-4559 "tarslip" class; + e.g. CVE-2026-43637). If the archive is attacker-influenced (downloaded, + uploaded, or fetched from a cache), this is arbitrary file write and can + lead to code execution. On Python 3.12+ pass `filter='data'` + (or `filter=tarfile.data_filter`); otherwise validate every member so its + resolved path stays within the destination and reject absolute paths, + `..`, and links before extracting. + metadata: + owasp: + - A01:2021 - Broken Access Control + - A05:2021 - Security Misconfiguration + cwe: + - "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')" + category: security + technology: + - python + references: + - https://docs.python.org/3/library/tarfile.html#extraction-filters + - https://nvd.nist.gov/vuln/detail/CVE-2007-4559 + subcategory: + - audit + likelihood: MEDIUM + impact: HIGH + confidence: MEDIUM + languages: [python] + severity: WARNING