From ffae240355a9245450ecbbc9f089c592b3423000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Tue, 8 Sep 2026 01:03:05 +0100 Subject: [PATCH] Skip AppleDouble sidecars when listing a sysdiagnose (#922) Device-generated sysdiagnose archives carry a ._name entry beside every file that has extended attributes, an ACL or Finder info; one iOS 26 archive held 1234 of them among 3648 members, and the count grows with each release. bsdtar folds them back into the file on extraction and hides them from listings, but tarfile returns them as regular members, so check-sysdiagnose extracted them and handed them to every module. A module that globs for plists or logs then tries to parse AppleDouble headers and logs one warning per sidecar. Leave them out of the file list, both for archives and for folders extracted on a system that keeps them as files. --- src/mvt/ios/cmd_check_sysdiagnose.py | 7 +++++++ tests/test_cmd_check_sysdiagnose.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/mvt/ios/cmd_check_sysdiagnose.py b/src/mvt/ios/cmd_check_sysdiagnose.py index c700e6bb..26e4d49f 100644 --- a/src/mvt/ios/cmd_check_sysdiagnose.py +++ b/src/mvt/ios/cmd_check_sysdiagnose.py @@ -88,6 +88,8 @@ class CmdIOSCheckSysdiagnose(Command): parent_path = Path(self.target_path).absolute().parent for root, _, filenames in os.walk(self.target_path): for filename in filenames: + if filename.startswith("._"): + continue absolute_path = os.path.join(root, filename) file_path = os.path.relpath(absolute_path, parent_path) self.sysdiagnose_files.append(file_path) @@ -137,6 +139,11 @@ class CmdIOSCheckSysdiagnose(Command): if not member_path.parts: continue + # AppleDouble sidecars (._name) carry a file's extended attributes, + # not sysdiagnose content. Device archives hold hundreds of them; + # bsdtar hides them from listings, tarfile does not. + if member_path.name.startswith("._"): + continue archive_roots.add(member_path.parts[0]) if member.isdir(): diff --git a/tests/test_cmd_check_sysdiagnose.py b/tests/test_cmd_check_sysdiagnose.py index 020c8616..b00b648b 100644 --- a/tests/test_cmd_check_sysdiagnose.py +++ b/tests/test_cmd_check_sysdiagnose.py @@ -34,6 +34,7 @@ def _create_sysdiagnose_folder(tmp_path): "sysdiagnose_2024.01.02_03-04-05+0200.tar.gz", encoding="utf-8" ) (folder / "report.ips").write_text('{"bug_type": 210}\nbody', encoding="utf-8") + (folder / "._artifact.txt").write_bytes(b"\x00\x05\x16\x07AppleDouble") return folder @@ -67,6 +68,7 @@ def test_check_sysdiagnose_from_folder(tmp_path): assert _test_module(command).ips_files == [ {"file_path": str(tmp_path / "sysdiagnose" / "report.ips"), "bug_type": 210} ] + assert "sysdiagnose/._artifact.txt" not in command.sysdiagnose_files def test_check_sysdiagnose_from_archive_closes_archive(tmp_path): @@ -83,6 +85,7 @@ def test_check_sysdiagnose_from_archive_closes_archive(tmp_path): } ] assert command.sysdiagnose_archive is None + assert "sysdiagnose/._artifact.txt" not in command.sysdiagnose_files def test_archive_is_extracted_once_and_unsafe_members_are_skipped(tmp_path):