Skip to content

Commit e313904

Browse files
fix(tar) absolute symlink extraction
Absolute targets were converted before, but it was too simple to be right other than when the link target was in the same directory. With this change absolute symlinks like /sbin/ls -> /bin/busybox are converted to /sbin/ls -> ../bin/busybox Co-authored-by: Andrew Fasano <fasano@mit.edu>
1 parent 5bd59b0 commit e313904

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed

unblob/handlers/archive/_safe_tarfile.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def extract(self, tarinfo: tarfile.TarInfo, extract_root: Path): # noqa: C901
6363
"Absolute path.",
6464
"Converted to extraction relative path.",
6565
)
66-
tarinfo.name = f"./{tarinfo.name}"
66+
tarinfo.name = str(Path(tarinfo.name).relative_to("/"))
6767

6868
# prevent traversal attempts through file name
6969
if not is_safe_path(basedir=extract_root, path=extract_root / tarinfo.name):
@@ -77,12 +77,33 @@ def extract(self, tarinfo: tarfile.TarInfo, extract_root: Path): # noqa: C901
7777
# prevent traversal attempts through links
7878
if tarinfo.islnk() or tarinfo.issym():
7979
if Path(tarinfo.linkname).is_absolute():
80+
81+
def calculate_linkname():
82+
root = extract_root.resolve()
83+
path = (extract_root / tarinfo.name).resolve()
84+
85+
if path.parts[: len(root.parts)] != root.parts:
86+
return None
87+
88+
depth = max(0, len(path.parts) - len(root.parts) - 1)
89+
return ("/".join([".."] * depth) or ".") + tarinfo.linkname
90+
91+
relative_linkname = calculate_linkname()
92+
if relative_linkname is None:
93+
self.record_problem(
94+
tarinfo,
95+
"Absolute path conversion to extraction relative failed - would escape root.",
96+
"Skipped.",
97+
)
98+
return
99+
100+
assert not Path(relative_linkname).is_absolute()
80101
self.record_problem(
81102
tarinfo,
82103
"Absolute path as link target.",
83104
"Converted to extraction relative path.",
84105
)
85-
tarinfo.linkname = f"./{tarinfo.linkname}"
106+
tarinfo.linkname = relative_linkname
86107

87108
resolved_path = (extract_root / tarinfo.name).parent / tarinfo.linkname
88109
if not is_safe_path(basedir=extract_root, path=resolved_path):

0 commit comments

Comments
 (0)