Skip to content

Commit e827184

Browse files
authored
Merge pull request #1368 from ousamabenyounes/fix/issue-1250
fix(install): reject archive with path traversal before extraction (#1250)
2 parents fef1ba5 + ac9b22c commit e827184

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

install.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ install() {
9898
error "Failed to download binary"
9999
fi
100100

101+
# Verify archive contents before extraction (CWE-22 path traversal).
102+
# Reject any entry with an absolute path or a ".." component.
103+
info "Verifying archive..."
104+
if tar -tzf "$ARCHIVE" | grep -qE '^/|(^|/)\.\.(/|$)'; then
105+
error "Archive contains unsafe paths (absolute or directory traversal) — refusing to extract"
106+
fi
107+
101108
info "Extracting..."
102109
tar -xzf "$ARCHIVE" -C "$TEMP_DIR"
103110

scripts/test-install.sh

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env sh
2+
# Tests for install.sh path traversal check (issue #1250, CWE-22).
3+
#
4+
# Verifies:
5+
# 1. Safe archives (single binary, "./prefix", subdirs) are accepted.
6+
# 2. Archives with absolute paths are rejected pre-extraction.
7+
# 3. Archives with ".." components are rejected pre-extraction.
8+
# 4. The check is still present in install.sh (regression guard).
9+
10+
set -eu
11+
12+
REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd)
13+
INSTALL_SH="$REPO_ROOT/install.sh"
14+
15+
if [ ! -f "$INSTALL_SH" ]; then
16+
echo "FAIL: install.sh not found at $INSTALL_SH"
17+
exit 1
18+
fi
19+
20+
if ! command -v python3 >/dev/null 2>&1; then
21+
echo "SKIP: python3 not available — crafted tarball tests require python3"
22+
exit 0
23+
fi
24+
25+
TMPDIR=$(mktemp -d)
26+
trap 'rm -rf "$TMPDIR"' EXIT
27+
28+
# The check replicated from install.sh (keep in sync with install.sh).
29+
# Returns 0 when archive is safe, 1 when unsafe.
30+
check_archive() {
31+
if tar -tzf "$1" | grep -qE '^/|(^|/)\.\.(/|$)'; then
32+
return 1
33+
fi
34+
return 0
35+
}
36+
37+
# --- Build safe archive using standard tar ---
38+
mkdir -p "$TMPDIR/safe_src"
39+
printf '#!/bin/sh\necho rtk\n' > "$TMPDIR/safe_src/rtk"
40+
(cd "$TMPDIR/safe_src" && tar -czf "$TMPDIR/safe.tgz" rtk)
41+
42+
# --- Build crafted malicious archives with python ---
43+
python3 - "$TMPDIR" <<'PY'
44+
import sys, tarfile, io
45+
46+
base = sys.argv[1]
47+
48+
49+
def make(name, entry):
50+
with tarfile.open(f"{base}/{name}", "w:gz") as t:
51+
info = tarfile.TarInfo(name=entry)
52+
data = b"pwned"
53+
info.size = len(data)
54+
t.addfile(info, io.BytesIO(data))
55+
56+
57+
make("traversal.tgz", "../etc/evil")
58+
make("absolute.tgz", "/tmp/evil_abs")
59+
make("middle.tgz", "rtk/../../../etc/evil")
60+
make("end_dotdot.tgz", "rtk/..")
61+
PY
62+
63+
FAIL=0
64+
pass() { printf ' PASS: %s\n' "$1"; }
65+
fail() { printf ' FAIL: %s\n' "$1"; FAIL=1; }
66+
67+
echo "==> Functional checks"
68+
69+
if check_archive "$TMPDIR/safe.tgz"; then
70+
pass "safe archive accepted"
71+
else
72+
fail "safe archive rejected (false positive)"
73+
fi
74+
75+
for bad in traversal absolute middle end_dotdot; do
76+
if check_archive "$TMPDIR/$bad.tgz"; then
77+
fail "$bad archive accepted (should be rejected)"
78+
else
79+
pass "$bad archive rejected"
80+
fi
81+
done
82+
83+
echo "==> Regression guard"
84+
85+
if grep -qF 'tar -tzf' "$INSTALL_SH" && grep -qF '\.\.' "$INSTALL_SH"; then
86+
pass "install.sh still contains the path-traversal check"
87+
else
88+
fail "install.sh is missing the path-traversal check — was it removed?"
89+
fi
90+
91+
echo ""
92+
if [ "$FAIL" -eq 0 ]; then
93+
echo "All install.sh path traversal tests passed"
94+
exit 0
95+
else
96+
echo "Some tests failed"
97+
exit 1
98+
fi

0 commit comments

Comments
 (0)