Skip to content

Commit 37c7d07

Browse files
authored
Securely extract PyPI .tar.gz archives (#102)
* Add Semgrep rule and run custom Semgrep rules in CI for SAST * Securely extract remote .tar.gz files
1 parent 8e814de commit 37c7d07

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
rules:
2+
- id: insecure-shutil-unpack-archive-use
3+
message: The Python 'shutil' shutil.extract_archive is vulnerable to
4+
arbitrary file overwrites
5+
languages:
6+
- python
7+
severity: ERROR
8+
metadata:
9+
category: security
10+
technology:
11+
- python
12+
owasp:
13+
- A06:2017 - Security Misconfiguration
14+
- A05:2021 - Security Misconfiguration
15+
cwe:
16+
- "CWE-22: Improper Limitation of a Pathname to a Restricted Directory
17+
('Path Traversal')"
18+
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
19+
pattern-either:
20+
- pattern: |
21+
shutil.unpack_archive(...)

.github/workflows/semgrep.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,17 @@ jobs:
2626
- uses: actions/checkout@v3
2727

2828
- run: semgrep --config auto --sarif --output semgrep.sarif ./guarddog
29+
- run: semgrep --config .github/semgrep-rules --sarif --output semgrep-custom.sarif ./guarddog
2930

3031
- name: Upload SARIF file for GitHub Advanced Security Dashboard
3132
uses: github/codeql-action/upload-sarif@v2
3233
with:
34+
category: semgrep-builtin
3335
sarif_file: semgrep.sarif
34-
if: always()
36+
37+
- name: Upload SARIF file for custom Semgrep rules for GitHub Advanced Security Dashboard
38+
uses: github/codeql-action/upload-sarif@v2
39+
with:
40+
category: semgrep-custom
41+
sarif_file: semgrep-custom.sarif
42+

guarddog/scanners/package_scanner.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import json
22
import os
3-
import shutil
4-
import tarsafe # type: ignore
3+
import tarsafe # type:ignore
54
import tempfile
65
import requests
76

@@ -160,5 +159,8 @@ def download_compressed(self, url, zippath, unzippedpath):
160159
with open(zippath, "wb") as f:
161160
f.write(response.raw.read())
162161

163-
shutil.unpack_archive(zippath, unzippedpath)
164-
os.remove(zippath)
162+
if zippath.endswith('.tar.gz'):
163+
tarsafe.open(zippath).extractall(unzippedpath)
164+
os.remove(zippath)
165+
else:
166+
raise ValueError("unsupported archive extension: " + zippath)

0 commit comments

Comments
 (0)