{"title":"Tar Slip via unsafe tarfile extraction","language":"Python","severity":"Critical","cwe":"CWE-22","source_lines":[2],"flow_lines":[2,7],"sink_lines":[7],"vulnerable_code":"import tarfile\nimport os\n\ndef restore_iot_firmware_backup(backup_archive, device_id):\n    restore_path = f\"/opt/iot/devices/{device_id}/firmware\"\n    os.makedirs(restore_path, exist_ok=True)\n    with tarfile.open(backup_archive, 'r:gz') as tar:\n        for member in tar.getmembers():\n            tar.extract(member, path=restore_path)\n    return {\"status\": \"restored\", \"device\": device_id, \"location\": restore_path}","explanation":"The code extracts tar archive members without validating their paths, allowing path traversal attacks (Tar Slip). A malicious tar archive can contain members with paths like '../../../etc/passwd' that escape the intended restore_path directory when extracted, potentially overwriting critical system files.","remediation":"The fix resolves each tar member's full path and verifies it stays within the intended restore directory before extraction. It also validates symlink targets to prevent indirect path traversal through symbolic links. Any member that would escape the target directory raises a ValueError, preventing the extraction entirely.","secure_code":"import tarfile\nimport os\n\ndef restore_iot_firmware_backup(backup_archive, device_id):\n    restore_path = os.path.realpath(f\"/opt/iot/devices/{device_id}/firmware\")\n    os.makedirs(restore_path, exist_ok=True)\n    with tarfile.open(backup_archive, 'r:gz') as tar:\n        for member in tar.getmembers():\n            member_path = os.path.realpath(os.path.join(restore_path, member.name))\n            if not member_path.startswith(restore_path + os.sep) and member_path != restore_path:\n                raise ValueError(f\"Illegal path in archive: {member.name} resolves outside target directory\")\n            if member.issym() or member.islnk():\n                link_target = os.path.realpath(os.path.join(restore_path, os.path.dirname(member.name), member.linkname))\n                if not link_target.startswith(restore_path + os.sep) and link_target != restore_path:\n                    raise ValueError(f\"Illegal symlink target in archive: {member.name} -> {member.linkname}\")\n            tar.extract(member, path=restore_path)\n    return {\"status\": \"restored\", \"device\": device_id, \"location\": restore_path}"}