{"record":{"id":"b7edc4a8c111c9cb","repo":"kovidgoyal/kitty","slug":"attempted-path-traversal-in-tar-file-member-name","errorCode":null,"errorMessage":"Attempted path traversal in tar file: {member.name}","messagePattern":"Attempted path traversal in tar file: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"kitty/utils.py","lineNumber":1137,"sourceCode":"        log_error(e)\n        url = 'Unparseable URL: ' + url\n    return url\n\n\ndef extract_all_from_tarfile_safely(tf: 'tarfile.TarFile', dest: str) -> None:\n    # Ensure that all extracted items are within dest\n\n    def is_within_directory(directory: str, target: str) -> bool:\n        abs_directory = os.path.abspath(directory)\n        abs_target = os.path.abspath(target)\n        prefix = os.path.commonprefix((abs_directory, abs_target))\n        return prefix == abs_directory\n\n    def safe_extract(tar: 'tarfile.TarFile', path: str = '.', numeric_owner: bool = False) -> None:\n        for member in tar.getmembers():\n            member_path = os.path.join(path, member.name)\n            if not is_within_directory(path, member_path):\n                raise ValueError(f'Attempted path traversal in tar file: {member.name}')\n        tar.extractall(path, tar.getmembers(), numeric_owner=numeric_owner)\n\n    safe_extract(tf, dest)\n\n\ndef is_png(path: str) -> bool:\n    if path:\n        with suppress(Exception), open(path, 'rb') as f:\n            header = f.read(8)\n            return header.startswith(b'\\211PNG\\r\\n\\032\\n')\n    return False\n\n\ndef cmdline_for_hold(cmd: Sequence[str] = (), opts: Optional['Options'] = None) -> list[str]:\n    if opts is None:\n        with suppress(RuntimeError):\n            opts = get_options()\n    if opts is None:","sourceCodeStart":1119,"sourceCodeEnd":1155,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kitty/utils.py#L1119-L1155","documentation":"safe_extract() (used by extract_all_from_tarfile_safely) walks every tar member and verifies that os.path.join(dest, member.name) stays inside the destination directory. A member whose absolute path or ../ components escapes the destination raises ValueError — this is an explicit defense against the classic tar path-traversal (TarSlip) attack before extractall runs.","triggerScenarios":"A tar archive containing entries like '../../etc/passwd', '/etc/passwd', or symlinks whose resolved member path falls outside the extraction directory; the check runs on every member of any tar passed to extract_all_from_tarfile_safely.","commonSituations":"Processing untrusted/user-uploaded tarballs (kitty e.g. does this for remote resource tars); a legitimately-created archive with absolute paths baked in by an old tar version.","solutions":["Reject or quarantine the untrusted archive — the error is a security stop, do not bypass it","Re-pack the archive from within the source directory so members are relative paths","Sanitize members (strip leading '/' and resolve '..') before building the tar, if you control creation","Never wrap the call in a blind except that falls back to tar.extractall"],"exampleFix":"# before\ntar.add('/etc/config.conf')  # stores absolute path\n# after\ntar.add('/etc/config.conf', arcname='config.conf')  # relative member","handlingStrategy":"validation","validationCode":"import os\nfor m in tar.getmembers():\n    dest = os.path.realpath(os.path.join(extract_dir, m.name))\n    if not dest.startswith(os.path.realpath(extract_dir) + os.sep):\n        raise ValueError(f'unsafe member {m.name}')","typeGuard":null,"tryCatchPattern":"try:\n    extract_all_from_tarfile_safely(tf, dest)\nexcept ValueError as e:\n    if 'path traversal' in str(e):\n        reject_archive(tf)  # quarantine / alert; never fall back to extractall\n    else:\n        raise","preventionTips":["Treat this error as a hard security failure — never catch-and-continue","Only accept archives from trusted producers; re-pack untrusted ones with sanitized members","Pre-scan members for absolute paths and '..' before extraction"],"tags":["security","tar","path-traversal","tarslip"],"backgroundTag":"tarslip-path-traversal","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}