Skip to content

Commit 47888a8

Browse files
committed
Merge branch 'main' of github.com:n24q02m/qwen3-embed
2 parents 387bbc0 + c1d0c76 commit 47888a8

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

qwen3_embed/common/model_management.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,20 @@ def decompress_to_cache(cls, targz_path: str, cache_dir: str) -> str:
372372
# SECURITY: Validate symlink and hardlink targets to prevent
373373
# arbitrary file writes outside the extraction directory.
374374
if member.issym() or member.islnk():
375-
if os.path.isabs(member.linkname):
375+
if os.path.isabs(member.linkname) or member.linkname.startswith("/"):
376376
raise tarfile.TarError(
377377
f"Attempted absolute path traversal in symlink/hardlink: {member.name} -> {member.linkname}"
378378
)
379-
link_target_path = os.path.abspath(
380-
os.path.join(os.path.dirname(member_path), member.linkname)
381-
)
379+
if member.issym():
380+
# Symlinks resolve relative to the directory containing the link
381+
link_target_path = os.path.abspath(
382+
os.path.join(os.path.dirname(member_path), member.linkname)
383+
)
384+
else:
385+
# Hardlinks (LNKTYPE) resolve relative to the extraction root
386+
link_target_path = os.path.abspath(
387+
os.path.join(target_dir, member.linkname)
388+
)
382389
if (
383390
not link_target_path.startswith(target_dir + os.sep)
384391
and link_target_path != target_dir

tests/test_model_management.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,29 @@ def test_decompress_symlink_absolute_prevention(self, tmp_path):
459459

460460
assert not cache_dir.exists()
461461

462+
def test_decompress_hardlink_traversal_prevention(self, tmp_path):
463+
"""Tar slip via hardlink resolved relative to extraction root is caught.
464+
465+
Hardlinks in tar archives resolve linkname relative to the archive root
466+
(extraction directory), NOT relative to the containing directory like
467+
symlinks. A nested hardlink with '../evil.txt' linkname must be detected
468+
as escaping the target directory.
469+
"""
470+
cache_dir = tmp_path / "tmp_cache_dir_hardlink_nested"
471+
cache_dir.mkdir()
472+
473+
malicious_tar = tmp_path / "malicious_hardlink_nested.tar.gz"
474+
with tarfile.open(malicious_tar, "w:gz") as tar:
475+
info = tarfile.TarInfo(name="sub/nested/evil_hardlink")
476+
info.type = tarfile.LNKTYPE
477+
info.linkname = "../evil.txt"
478+
tar.addfile(info)
479+
480+
with pytest.raises(tarfile.TarError):
481+
ModelManagement.decompress_to_cache(str(malicious_tar), str(cache_dir))
482+
483+
assert not cache_dir.exists()
484+
462485
def test_decompress_mid_extraction_failure(self, tmp_path):
463486
"""Mid-extraction TarError is re-raised and cache dir is removed."""
464487
tar_path = make_tar_gz(tmp_path, inner_name="model.onnx")

0 commit comments

Comments
 (0)