diff --git a/admin/image_manifest.json b/admin/image_manifest.json index c02709364..d14561b01 100644 --- a/admin/image_manifest.json +++ b/admin/image_manifest.json @@ -5,6 +5,7 @@ "description": "iPhone 8 extraction with sample data for testing", "local_image_paths": [ "~/Documents/phone-images/Josh/iOS_15_Public_Image.tar", + "~/Documents/phone-images/Josh/iOS_15_Public_Image.zip", "/home/user/images/iphone_001.zip" ], "file_path_list": "admin/data/filepath-lists/josh-hickman-ios15.csv.zip", diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.zip new file mode 100644 index 000000000..b380e19db Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.zip differ diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.josh_ios15_ffs.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.josh_ios15_ffs.zip new file mode 100644 index 000000000..75dd8de4d Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.josh_ios15_ffs.zip differ diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.mvs_ios_2023.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.mvs_ios_2023.zip new file mode 100644 index 000000000..2bb18de52 Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.mvs_ios_2023.zip differ diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.zip new file mode 100644 index 000000000..b380e19db Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.zip differ diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.zip new file mode 100644 index 000000000..75dd8de4d Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.zip differ diff --git a/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.mvs_ios_2023.zip b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.mvs_ios_2023.zip new file mode 100644 index 000000000..2bb18de52 Binary files /dev/null and b/admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.mvs_ios_2023.zip differ diff --git a/admin/test/cases/testdata.cloudkitSharing.json b/admin/test/cases/testdata.cloudkitSharing.json new file mode 100644 index 000000000..8d9543948 --- /dev/null +++ b/admin/test/cases/testdata.cloudkitSharing.json @@ -0,0 +1,143 @@ +{ + "mvs_ios_2023": { + "description": "", + "maker": "", + "make_data": { + "input_data_path": "/Users/jameshabben/Documents/phone-images/magnet/00008101-0010541A1130001E_files_full-001.zip", + "os": "macOS-15.7.4-x86_64-i386-64bit", + "timestamp": "2026-05-27T15:33:22.409614", + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "artifacts": { + "cloudkit_sharing": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + }, + "cloudkit_participants": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + } + }, + "image_name": "mvs_ios_2023", + "image_info": { + "creation_date": "2023-01-01", + "os_name": "iPhone OS", + "os_version": "14.7.1", + "device_model": "Unknown", + "extraction_method": "Full Filesystem", + "extraction_tool": "Magnet" + } + }, + "josh_ios15_ffs": { + "description": "", + "maker": "", + "make_data": { + "input_data_path": "/Users/jameshabben/Documents/phone-images/Josh/iOS_15_Public_Image.zip", + "os": "macOS-15.7.4-x86_64-i386-64bit", + "timestamp": "2026-05-28T06:45:31.943111", + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "artifacts": { + "cloudkit_sharing": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + }, + "cloudkit_participants": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + } + }, + "image_name": "josh_ios15_ffs", + "image_info": { + "creation_date": "2023-05-20", + "os_name": "iPhone OS", + "os_version": "15.3.1", + "device_model": "iPhone 8", + "extraction_method": "Full Filesystem", + "extraction_tool": "Cellebrite" + } + }, + "belkasoft_ctf6_ios_device1": { + "description": "", + "maker": "", + "make_data": { + "input_data_path": "/Users/jameshabben/Documents/phone-images/belkasoft/BelkaCTF_6_CASE240405_D201AP.tar", + "os": "macOS-15.7.4-x86_64-i386-64bit", + "timestamp": "2026-05-28T09:04:21.116050", + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "artifacts": { + "cloudkit_sharing": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + }, + "cloudkit_participants": { + "search_patterns": [ + "*NoteStore.sqlite*" + ], + "file_count": 3, + "expected_output": { + "headers": [], + "data": [] + } + } + }, + "image_name": "belkasoft_ctf6_ios_device1", + "image_info": { + "creation_date": "2023-01-01", + "os_name": "iPhone OS", + "os_version": "16.3", + "device_model": "Unknown", + "extraction_method": "Unknown", + "extraction_tool": "Unknown" + } + } +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.20260528160449.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.20260528160449.json new file mode 100644 index 000000000..517a1f1c6 --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.20260528160449.json @@ -0,0 +1,56 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Share Participants", + "function_name": "cloudkit_participants", + "case_number": "belkasoft_ctf6_ios_device1", + "number_of_columns": 29, + "number_of_rows": 0, + "total_data_size_bytes": 0, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.belkasoft_ctf6_ios_device1.zip", + "start_time": "2026-05-28T16:04:49.561607+00:00", + "end_time": "2026-05-28T16:04:49.646844+00:00", + "run_time_seconds": 0.0018002986907958984, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Share Record ID", + "Root Record ID", + "Participant ID", + "Participant User Record ID", + "Email Address", + "Phone Number", + "Participant Type", + "Acceptance Status", + "Permission", + "Original Participant Type", + "Original Acceptance Status", + "Original Permission", + "Is Current User", + "Inviter ID", + "Has iCloud Account", + "Invitation Token Status", + "Wants New Invitation Token", + "Is Anonymous Invited Participant", + "Created In Process", + "Accepted In Process", + "Name Prefix", + "First Name", + "Middle Name", + "Last Name", + "Name Suffix", + "Nickname" + ], + "data": [] +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.josh_ios15_ffs.20260528141238.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.josh_ios15_ffs.20260528141238.json new file mode 100644 index 000000000..bf2ae8b4e --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.josh_ios15_ffs.20260528141238.json @@ -0,0 +1,119 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Share Participants", + "function_name": "cloudkit_participants", + "case_number": "josh_ios15_ffs", + "number_of_columns": 29, + "number_of_rows": 2, + "total_data_size_bytes": 622, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.josh_ios15_ffs.zip", + "start_time": "2026-05-28T14:12:38.365323+00:00", + "end_time": "2026-05-28T14:12:38.464849+00:00", + "run_time_seconds": 0.006311893463134766, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Share Record ID", + "Root Record ID", + "Participant ID", + "Participant User Record ID", + "Email Address", + "Phone Number", + "Participant Type", + "Acceptance Status", + "Permission", + "Original Participant Type", + "Original Acceptance Status", + "Original Permission", + "Is Current User", + "Inviter ID", + "Has iCloud Account", + "Invitation Token Status", + "Wants New Invitation Token", + "Is Anonymous Invited Participant", + "Created In Process", + "Accepted In Process", + "Name Prefix", + "First Name", + "Middle Name", + "Last Name", + "Name Suffix", + "Nickname" + ], + "data": [ + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_participants_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 29, + "B766596C-E7EB-48E6-93B2-EDBAD4B63F48", + "", + "", + "968CA14F-2069-42E4-AA13-D9A2B932654E", + "__defaultOwner__", + "thisisdfir@gmail.com", + "", + 1, + 2, + 3, + "", + 1, + 2, + true, + "", + "", + "", + "", + "", + false, + false, + "", + "This Is", + "", + "DFIR", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_participants_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 29, + "B766596C-E7EB-48E6-93B2-EDBAD4B63F48", + "", + "", + "99293E2A-4188-45B2-BE50-2B21BD9C157E", + "", + "", + "19198887386", + 2, + 1, + 3, + "", + 1, + 2, + false, + "_2059b5c2ab5206967ff351966be73cf9", + "", + "", + "", + "", + false, + false, + "", + "", + "", + "", + "", + "" + ] + ] +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.mvs_ios_2023.20260527230714.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.mvs_ios_2023.20260527230714.json new file mode 100644 index 000000000..dfdb6602b --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_participants.mvs_ios_2023.20260527230714.json @@ -0,0 +1,56 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Share Participants", + "function_name": "cloudkit_participants", + "case_number": "mvs_ios_2023", + "number_of_columns": 29, + "number_of_rows": 0, + "total_data_size_bytes": 0, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_participants.mvs_ios_2023.zip", + "start_time": "2026-05-27T23:07:14.191654+00:00", + "end_time": "2026-05-27T23:07:14.446828+00:00", + "run_time_seconds": 0.009323835372924805, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Share Record ID", + "Root Record ID", + "Participant ID", + "Participant User Record ID", + "Email Address", + "Phone Number", + "Participant Type", + "Acceptance Status", + "Permission", + "Original Participant Type", + "Original Acceptance Status", + "Original Permission", + "Is Current User", + "Inviter ID", + "Has iCloud Account", + "Invitation Token Status", + "Wants New Invitation Token", + "Is Anonymous Invited Participant", + "Created In Process", + "Accepted In Process", + "Name Prefix", + "First Name", + "Middle Name", + "Last Name", + "Name Suffix", + "Nickname" + ], + "data": [] +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.20260528160449.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.20260528160449.json new file mode 100644 index 000000000..3e99b6fb4 --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.20260528160449.json @@ -0,0 +1,44 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Shares", + "function_name": "cloudkit_sharing", + "case_number": "belkasoft_ctf6_ios_device1", + "number_of_columns": 17, + "number_of_rows": 0, + "total_data_size_bytes": 0, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.belkasoft_ctf6_ios_device1.zip", + "start_time": "2026-05-28T16:04:49.459546+00:00", + "end_time": "2026-05-28T16:04:49.560998+00:00", + "run_time_seconds": 0.0020220279693603516, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Record ID", + "Root Record ID", + "Record Type", + "Creation Date", + "Creator User Record ID", + "Modified Date", + "Last Modified User Record ID", + "Modified By Device", + "Container Identifier", + "Displayed Hostname", + "Public Permission", + "Participant Visibility", + "Allows Anonymous Access", + "Known To Server" + ], + "data": [] +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.20260528141238.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.20260528141238.json new file mode 100644 index 000000000..105833cd2 --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.20260528141238.json @@ -0,0 +1,558 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Shares", + "function_name": "cloudkit_sharing", + "case_number": "josh_ios15_ffs", + "number_of_columns": 17, + "number_of_rows": 27, + "total_data_size_bytes": 6774, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.josh_ios15_ffs.zip", + "start_time": "2026-05-28T14:12:38.102705+00:00", + "end_time": "2026-05-28T14:12:38.364545+00:00", + "run_time_seconds": 0.04447531700134277, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Record ID", + "Root Record ID", + "Record Type", + "Creation Date", + "Creator User Record ID", + "Modified Date", + "Last Modified User Record ID", + "Modified By Device", + "Container Identifier", + "Displayed Hostname", + "Public Permission", + "Participant Visibility", + "Allows Anonymous Access", + "Known To Server" + ], + "data": [ + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 1, + "TrashFolder-CloudKit", + "TrashFolder-CloudKit", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 2, + "79327CCA-8D1E-4F0A-B32B-871C0CC1C1AF", + "_2059b5c2ab5206967ff351966be73cf9", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 3, + "DefaultFolder-CloudKit", + "DefaultFolder-CloudKit", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 4, + "DeviceMigrationState_79327CCA-8D1E-4F0A-B32B-871C0CC1C1AF", + "79327CCA-8D1E-4F0A-B32B-871C0CC1C1AF", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 8, + "BC49B362-F4C5-462A-80CB-03FF75C3D93E", + "BC49B362-F4C5-462A-80CB-03FF75C3D93E", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 9, + "1BE7A9A7-1675-4D78-AF2B-4EBC0BDFE651", + "1BE7A9A7-1675-4D78-AF2B-4EBC0BDFE651", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 10, + "6CFCD8F3-864D-4064-9D03-F653F6C1CC23", + "6CFCD8F3-864D-4064-9D03-F653F6C1CC23", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 11, + "ED99B707-E353-401A-AAA8-5610066BD280", + "ED99B707-E353-401A-AAA8-5610066BD280", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 12, + "ECED25E5-D8A0-41F3-9B69-63F964CF8955", + "ECED25E5-D8A0-41F3-9B69-63F964CF8955", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 13, + "DeviceMigrationState_FD0781A4-787F-4213-AF9E-D6DC5C257EAA", + "FD0781A4-787F-4213-AF9E-D6DC5C257EAA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 14, + "DeviceMigrationState_8A9D0A33-0A6C-4839-BC60-0797A9AA6F42", + "8A9D0A33-0A6C-4839-BC60-0797A9AA6F42", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 15, + "DeviceMigrationState_9863A009-364F-4B8A-8ECE-86602035DB04", + "9863A009-364F-4B8A-8ECE-86602035DB04", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 16, + "DeviceMigrationState_4F1E99F0-D1E7-4AE6-9004-FF4F021F890C", + "4F1E99F0-D1E7-4AE6-9004-FF4F021F890C", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 17, + "DeviceMigrationState_377145C8-3B1B-4A88-BFB2-EC9CFFE09AA9", + "377145C8-3B1B-4A88-BFB2-EC9CFFE09AA9", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 18, + "DeviceMigrationState_5AB739BD-C51F-42A3-8677-E90F6D46CCB9", + "5AB739BD-C51F-42A3-8677-E90F6D46CCB9", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 19, + "DeviceMigrationState_84323F17-088E-4DA5-9FB7-0B38B8DC141E", + "84323F17-088E-4DA5-9FB7-0B38B8DC141E", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 20, + "DeviceMigrationState_D45020EE-7A2C-4FC9-A084-437C4186F0D3", + "D45020EE-7A2C-4FC9-A084-437C4186F0D3", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 21, + "2909234A-0865-4D1A-A5DE-840F123FAD42", + "2909234A-0865-4D1A-A5DE-840F123FAD42", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 22, + "DeviceMigrationState_1589F4EC-8F6C-4F37-929F-C6F121B36A59", + "1589F4EC-8F6C-4F37-929F-C6F121B36A59", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 23, + "DeviceMigrationState_831D9440-6E53-431E-BD97-72ACF24018E8", + "831D9440-6E53-431E-BD97-72ACF24018E8", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 24, + "DeviceMigrationState_01710F6A-6ED2-42B8-96FF-88711C44F15D", + "01710F6A-6ED2-42B8-96FF-88711C44F15D", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 25, + "DeviceMigrationState_CDEF4DD4-1AA6-4AF9-82DA-A528FD2667D9", + "CDEF4DD4-1AA6-4AF9-82DA-A528FD2667D9", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 26, + "DeviceMigrationState_CA09F0EE-68C7-415B-8971-2E40AB20F681", + "CA09F0EE-68C7-415B-8971-2E40AB20F681", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 27, + "DeviceMigrationState_D2EA806B-018D-4F65-9CE1-10C0BCE7D7EA", + "D2EA806B-018D-4F65-9CE1-10C0BCE7D7EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 28, + "DeviceMigrationState_91D992C1-101E-48C2-A7ED-F9844BC49BCB", + "91D992C1-101E-48C2-A7ED-F9844BC49BCB", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 29, + "B766596C-E7EB-48E6-93B2-EDBAD4B63F48", + "B766596C-E7EB-48E6-93B2-EDBAD4B63F48", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + [ + "admin/test/temp/extract_cloudkitSharing_cloudkit_sharing_1779977558/filesystem2/mobile/Containers/Shared/AppGroup/B120CD27-5591-4ED6-A1EC-E73CFFB4F8D3/NoteStore.sqlite", + 31, + "EBBBFEF8-6284-4580-9887-2EBA8B25C736", + "EBBBFEF8-6284-4580-9887-2EBA8B25C736", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + ] +} \ No newline at end of file diff --git a/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.mvs_ios_2023.20260527230714.json b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.mvs_ios_2023.20260527230714.json new file mode 100644 index 000000000..72a9268f0 --- /dev/null +++ b/admin/test/results/cloudkitSharing/cloudkitSharing.cloudkit_sharing.mvs_ios_2023.20260527230714.json @@ -0,0 +1,44 @@ +{ + "metadata": { + "module_name": "cloudkitSharing", + "artifact_name": "CloudKit Shares", + "function_name": "cloudkit_sharing", + "case_number": "mvs_ios_2023", + "number_of_columns": 17, + "number_of_rows": 0, + "total_data_size_bytes": 0, + "media_checkins": 0, + "media_embedded_checkins": 0, + "input_zip_path": "admin/test/cases/data/cloudkitSharing/testdata.cloudkitSharing.cloudkit_sharing.mvs_ios_2023.zip", + "start_time": "2026-05-27T23:07:14.447308+00:00", + "end_time": "2026-05-27T23:07:14.543199+00:00", + "run_time_seconds": 0.0016520023345947266, + "last_commit": { + "hash": "b2a401886e75c564e525dce398cb885d4e7eb2a6", + "author_name": "Kevin - Stark 4N6", + "author_email": "48143894+stark4n6@users.noreply.github.com", + "date": "2025-02-06T19:47:52-05:00", + "message": "Minor Fixes" + } + }, + "headers": [ + "Source File", + "Source Z_PK", + "ZIDENTIFIER", + "Record ID", + "Root Record ID", + "Record Type", + "Creation Date", + "Creator User Record ID", + "Modified Date", + "Last Modified User Record ID", + "Modified By Device", + "Container Identifier", + "Displayed Hostname", + "Public Permission", + "Participant Visibility", + "Allows Anonymous Access", + "Known To Server" + ], + "data": [] +} \ No newline at end of file diff --git a/scripts/artifacts/cloudkitParticipants.py b/scripts/artifacts/cloudkitParticipants.py deleted file mode 100644 index c06e0725c..000000000 --- a/scripts/artifacts/cloudkitParticipants.py +++ /dev/null @@ -1,80 +0,0 @@ -import glob -import os -import nska_deserialize as nd -import sqlite3 -import datetime -import io - -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, tsv, timeline, is_platform_windows, open_sqlite_db_readonly - - -def get_cloudkitParticipants(files_found, report_folder, seeker, wrap_text, timezone_offset): - - user_dictionary = {} - - for file_found in files_found: - file_found = str(file_found) - - # Can add a separate section for each file this information is found in. - # This is for Apple Notes. - if file_found.endswith('NoteStore.sqlite'): - db = open_sqlite_db_readonly(file_found) - cursor = db.cursor() - cursor.execute(''' - SELECT Z_PK, ZSERVERSHAREDATA - FROM - ZICCLOUDSYNCINGOBJECT - WHERE - ZSERVERSHAREDATA NOT NULL - ''') - - all_rows = cursor.fetchall() - for row in all_rows: - - filename = os.path.join(report_folder, 'zserversharedata_'+str(row[0])+'.bplist') - output_file = open(filename, "wb") - output_file.write(row[1]) - output_file.close() - - deserialized_plist = nd.deserialize_plist(io.BytesIO(row[1])) - for item in deserialized_plist: - if 'Participants' in item: - for participant in item['Participants']: - record_id = participant['UserIdentity']['UserRecordID']['RecordName'] - email_address = participant['UserIdentity']['LookupInfo']['EmailAddress'] - phone_number = participant['UserIdentity']['LookupInfo']['PhoneNumber'] - first_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.givenName'] - middle_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.middleName'] - last_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.familyName'] - name_prefix = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.namePrefix'] - name_suffix = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.nameSuffix'] - nickname = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.nickname'] - - user_dictionary[record_id] = [record_id, email_address, phone_number, name_prefix, first_name, middle_name, last_name, name_suffix, nickname] - db.close() - - # Build the array after dealing with all the files - user_list = list(user_dictionary.values()) - - if len(user_list) > 0: - description = 'CloudKit Participants - Cloudkit accounts participating in CloudKit shares.' - report = ArtifactHtmlReport('Participants') - report.start_artifact_report(report_folder, 'Participants', description) - report.add_script() - user_headers = ('Record ID','Email Address','Phone Number','Name Prefix','First Name','Middle Name','Last Name','Name Suffix','Nickname') - report.write_artifact_data_table(user_headers, user_list, '', write_location=False) - report.end_artifact_report() - - tsvname = 'Cloudkit Participants' - tsv(report_folder, user_headers, user_list, tsvname) - else: - logfunc('No Cloudkit - Cloudkit Participants data available') - - -__artifacts__ = { - "cloudkitparticipants": ( - "Cloudkit", - ('*NoteStore.sqlite*'), # TODO confirm this is the correct file ref see issue #322 - get_cloudkitParticipants) -} \ No newline at end of file diff --git a/scripts/artifacts/cloudkitSharing.py b/scripts/artifacts/cloudkitSharing.py index f873fefdf..ac8322e9f 100644 --- a/scripts/artifacts/cloudkitSharing.py +++ b/scripts/artifacts/cloudkitSharing.py @@ -1,169 +1,235 @@ +""" cloudkitSharing """ __artifacts_v2__ = { - "cloudkitsharing": { - "name": "Cloudkit Sharing", - "description": "This module processes data related to CloudKit sharing, encompassing information on notes " - "shared via CloudKit and the accounts participating in CloudKit shares. It allows for the " - "retrieval of Record ID from the ZICCLOUDSYNCINGOBJECT.ZIDENTIFIER column for shared notes, " - "and provides details on CloudKit participants.", + "cloudkit_sharing": { + "name": "CloudKit Shares", + "description": "Processes CloudKit sharing data from NoteStore.sqlite", "author": "@DFIRScience", - "version": "0.3", - "date": "2022-08-09", - "requirements": "", + "creation_date": "2022-08-09", + "last_update_date": "2026-05-28", + "requirements": "none", "category": "Cloudkit", "notes": "", "paths": ('*NoteStore.sqlite*',), - "function": "get_cloudkitSharing" + "output_types": "standard", + "artifact_icon": "share-2" + }, + "cloudkit_participants": { + "name": "CloudKit Share Participants", + "description": "Processes CloudKit participant data from NoteStore.sqlite", + "author": "@DFIRScience", + "creation_date": "2022-08-09", + "last_update_date": "2026-05-28", + "requirements": "none", + "category": "Cloudkit", + "notes": "", + "paths": ('*NoteStore.sqlite*',), + "output_types": "standard", + "artifact_icon": "users" } } -import glob import os -import nska_deserialize as nd -import sqlite3 -import datetime import io +import nska_deserialize as nd +from scripts.ilapfuncs import artifact_processor, open_sqlite_db_readonly -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, tsv, timeline, is_platform_windows, open_sqlite_db_readonly +def deep_get(data, path, default=''): + """Safe nested dictionary access.""" + for key in path: + if not isinstance(data, dict): + return default + data = data.get(key) + if data is None: + return default + return data + + +def write_debug_bplist(report_folder, prefix, z_pk, data): + """Writes extracted blobs to the report folder for validation.""" + filename = os.path.join(report_folder, f'{prefix}_{z_pk}.bplist') + with open(filename, "wb") as f: + f.write(data) -def get_cloudkitSharing(files_found, report_folder, seeker, wrap_text, timezone_offset): - for file_found in files_found: - file_found = str(file_found) - if file_found.endswith('NoteStore.sqlite'): - get_cloudkitServerRecordData(file_found, report_folder, seeker, wrap_text) - get_cloudkitServerSharedData(file_found, report_folder, seeker, wrap_text) - - -def get_cloudkitServerSharedData(file_found, report_folder, seeker, wrap_text): - user_dictionary = {} - - db = open_sqlite_db_readonly(file_found) - cursor = db.cursor() - cursor.execute(''' - SELECT Z_PK, ZSERVERSHAREDATA - FROM - ZICCLOUDSYNCINGOBJECT - WHERE - ZSERVERSHAREDATA NOT NULL - ''') - - all_rows = cursor.fetchall() - for row in all_rows: - - filename = os.path.join(report_folder, 'zserversharedata_' + str(row[0]) + '.bplist') - output_file = open(filename, "wb") - output_file.write(row[1]) - output_file.close() - - deserialized_plist = nd.deserialize_plist(io.BytesIO(row[1])) - for item in deserialized_plist: - if 'Participants' in item: - for participant in item['Participants']: - record_id = participant['UserIdentity']['UserRecordID']['RecordName'] - email_address = participant['UserIdentity']['LookupInfo']['EmailAddress'] - phone_number = participant['UserIdentity']['LookupInfo']['PhoneNumber'] - first_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.givenName'] - middle_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.middleName'] - last_name = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.familyName'] - name_prefix = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.namePrefix'] - name_suffix = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.nameSuffix'] - nickname = participant['UserIdentity']['NameComponents']['NS.nameComponentsPrivate']['NS.nickname'] - - user_dictionary[record_id] = [record_id, email_address, phone_number, name_prefix, first_name, - middle_name, last_name, name_suffix, nickname] - db.close() - - # Build the array after dealing with all the files - user_list = list(user_dictionary.values()) - - if len(user_list) > 0: - description = 'CloudKit Participants - Cloudkit accounts participating in CloudKit shares.' - report = ArtifactHtmlReport('Participants') - report.start_artifact_report(report_folder, 'Participants', description) - report.add_script() - user_headers = ( - 'Record ID', 'Email Address', 'Phone Number', 'Name Prefix', 'First Name', 'Middle Name', 'Last Name', - 'Name Suffix', 'Nickname') - report.write_artifact_data_table(user_headers, user_list, '', write_location=False) - report.end_artifact_report() - - tsvname = 'Cloudkit Participants' - tsv(report_folder, user_headers, user_list, tsvname) - else: - logfunc('No Cloudkit - Cloudkit Participants data available') - - -def get_cloudkitServerRecordData(file_found, report_folder, seeker, wrap_text): - db = open_sqlite_db_readonly(file_found) - cursor = db.cursor() - cursor.execute(''' - select z_pk, zserverrecorddata - from - ziccloudsyncingobject - where - zserverrecorddata not null - ''') - - note_data = [] - all_rows = cursor.fetchall() - result_number = len(all_rows) - if result_number > 0: - - for row in all_rows: - - filename = os.path.join(report_folder, 'zserverrecorddata_' + str(row[0]) + '.bplist') - output_file = open(filename, "wb") - output_file.write(row[1]) - output_file.close() - - deserialized_plist = nd.deserialize_plist(io.BytesIO(row[1])) - creator_id = '' - last_modified_id = '' - creation_date = '' - last_modified_date = '' - last_modified_device = '' - record_type = '' - record_id = '' - for item in deserialized_plist: - if 'RecordCtime' in item: - creation_date = item['RecordCtime'] - elif 'RecordMtime' in item: - last_modified_date = item['RecordMtime'] - elif 'LastModifiedUserRecordID' in item: - last_modified_id = item['LastModifiedUserRecordID']['RecordName'] - elif 'CreatorUserRecordID' in item: - creator_id = item['CreatorUserRecordID']['RecordName'] - elif 'ModifiedByDevice' in item: - last_modified_device = item['ModifiedByDevice'] - elif 'RecordType' in item: - record_type = item['RecordType'] - elif 'RecordID' in item: - record_id = item['RecordID']['RecordName'] - - note_data.append([record_id, record_type, creation_date, creator_id, last_modified_date, last_modified_id, - last_modified_device]) - - description = 'CloudKit Note Sharing - Notes information shared via CloudKit. Look up the Record ID in the ZICCLOUDSYYNCINGOBJECT.ZIDENTIFIER column. ' - report = ArtifactHtmlReport('Note Sharing') - report.start_artifact_report(report_folder, 'Note Sharing', description) - report.add_script() - note_headers = ( - 'Record ID', 'Record Type', 'Creation Date', 'Creator ID', 'Modified Date', 'Modifier ID', 'Modifier Device') - report.write_artifact_data_table(note_headers, note_data, file_found) - report.end_artifact_report() - - tsvname = 'Cloudkit Note Sharing' - tsv(report_folder, note_headers, note_data, tsvname) - else: - logfunc('No Cloudkit - Cloudkit Note Sharing data available') - - db.close() - -# __artifacts__ = { -# "cloudkitsharing": ( -# "Cloudkit", -# ('*NoteStore.sqlite*'), -# get_cloudkitSharing) -# } +@artifact_processor +def cloudkit_sharing(context): + """ See artifact description """ + data_list = [] + + for file_found in context.get_files_found(): + file_found = str(file_found) + if not file_found.endswith('NoteStore.sqlite'): + continue + + # Dictionary to merge share and record data by Z_PK + shares = {} + + db = open_sqlite_db_readonly(file_found) + cursor = db.cursor() + + # 1. Process Server Record Data + cursor.execute('SELECT Z_PK, ZIDENTIFIER, ZSERVERRECORDDATA FROM ZICCLOUDSYNCINGOBJECT WHERE ZSERVERRECORDDATA IS NOT NULL') + for row in cursor: + z_pk, z_id, blob = row + write_debug_bplist(context.get_report_folder(), 'zserverrecorddata', z_pk, blob) + + deserialized = nd.deserialize_plist(io.BytesIO(blob)) + + record_items = [] + if isinstance(deserialized, list): + record_items = [x for x in deserialized if isinstance(x, dict) and 'RecordID' in x] + elif isinstance(deserialized, dict) and 'RecordID' in deserialized: + record_items = [deserialized] + + for item in record_items: + shares[z_pk] = { + 'z_id': z_id, + 'record_id': deep_get(item, ['RecordID', 'RecordName']), + 'record_type': item.get('RecordType', ''), + 'ctime': item.get('RecordCtime', ''), + 'creator': deep_get(item, ['CreatorUserRecordID', 'RecordName']), + 'mtime': item.get('RecordMtime', ''), + 'modifier': deep_get(item, ['LastModifiedUserRecordID', 'RecordName']), + 'device': item.get('ModifiedByDevice', ''), + 'root_id': '', # Filled by share data if available + 'container': '', + 'hostname': '', + 'permission': '', + 'visibility': '', + 'anon': '', + 'known': '' + } + break # Only take the first matching record item per Z_PK + + # 2. Process Server Share Data + cursor.execute('SELECT Z_PK, ZIDENTIFIER, ZSERVERSHAREDATA FROM ZICCLOUDSYNCINGOBJECT WHERE ZSERVERSHAREDATA IS NOT NULL') + for row in cursor: + z_pk, z_id, blob = row + write_debug_bplist(context.get_report_folder(), 'zserversharedata', z_pk, blob) + + deserialized = nd.deserialize_plist(io.BytesIO(blob)) + + share_items = [] + if isinstance(deserialized, list): + share_items = [x for x in deserialized if isinstance(x, dict) and 'RecordID' in x] + elif isinstance(deserialized, dict) and 'RecordID' in deserialized: + share_items = [deserialized] + + for item in share_items: + if z_pk not in shares: + shares[z_pk] = { + 'z_id': z_id, + 'record_id': deep_get(item, ['RecordID', 'RecordName']), + 'record_type': '', + 'ctime': '', + 'creator': '', + 'mtime': '', + 'modifier': '', + 'device': '' + } + + shares[z_pk].update({ + 'root_id': deep_get(item, ['RootRecordID', 'RecordName']), + 'container': deep_get(item, ['ContainerID', 'ContainerIdentifier']), + 'hostname': item.get('DisplayedHostname', ''), + 'permission': item.get('PublicPermission', ''), + 'visibility': item.get('ParticipantVisibility', ''), + 'anon': item.get('AllowsAnonymousAccess', ''), + 'known': item.get('KnownToServer', '') + }) + break # Only take the first matching share item per Z_PK + + db.close() + + for z_pk, s in shares.items(): + data_list.append(( + file_found, z_pk, s['z_id'], s['record_id'], s['root_id'], s['record_type'], + s['ctime'], s['creator'], s['mtime'], s['modifier'], s['device'], + s['container'], s['hostname'], s['permission'], s['visibility'], + s['anon'], s['known'] + )) + + data_headers = ( + 'Source File', 'Source Z_PK', 'ZIDENTIFIER', 'Record ID', 'Root Record ID', 'Record Type', + ('Creation Date', 'datetime'), 'Creator User Record ID', ('Modified Date', 'datetime'), + 'Last Modified User Record ID', 'Modified By Device', 'Container Identifier', + 'Displayed Hostname', 'Public Permission', 'Participant Visibility', + 'Allows Anonymous Access', 'Known To Server' + ) + return data_headers, data_list, 'NoteStore.sqlite' + + +@artifact_processor +def cloudkit_participants(context): + """ See artifact description """ + data_list = [] + for file_found in context.get_files_found(): + file_found = str(file_found) + if not file_found.endswith('NoteStore.sqlite'): + continue + + db = open_sqlite_db_readonly(file_found) + cursor = db.cursor() + cursor.execute('SELECT Z_PK, ZIDENTIFIER, ZSERVERSHAREDATA FROM ZICCLOUDSYNCINGOBJECT WHERE ZSERVERSHAREDATA IS NOT NULL') + + for row in cursor: + z_pk, z_id, blob = row + deserialized = nd.deserialize_plist(io.BytesIO(blob)) + + share_items = [] + if isinstance(deserialized, list): + share_items = [x for x in deserialized if isinstance(x, dict) and 'Participants' in x] + elif isinstance(deserialized, dict) and 'Participants' in deserialized: + share_items = [deserialized] + + for item in share_items: + share_record_id = deep_get(item, ['RecordID', 'RecordName']) + root_record_id = deep_get(item, ['RootRecordID', 'RecordName']) + + participants = item.get('Participants') or [] + for p in participants: + if not isinstance(p, dict): + continue + ui = p.get('UserIdentity') or {} + name_priv = deep_get(ui, ['NameComponents', 'NS.nameComponentsPrivate'], {}) + + data_list.append(( + file_found, z_pk, z_id, share_record_id, root_record_id, + p.get('ParticipantID', ''), + deep_get(ui, ['UserRecordID', 'RecordName']), + deep_get(ui, ['LookupInfo', 'EmailAddress']), + deep_get(ui, ['LookupInfo', 'PhoneNumber']), + p.get('Type', ''), + p.get('AcceptanceStatus', ''), + p.get('Permission', ''), + p.get('OriginalType', ''), + p.get('OriginalAcceptanceStatus', ''), + p.get('OriginalPermission', ''), + p.get('IsCurrentUser', ''), + p.get('InviterID', ''), + p.get('HasICloudAccount', ''), + p.get('InvitationTokenStatus', ''), + p.get('WantsNewInvitationToken', ''), + p.get('IsAnonymousInvitedParticipant', ''), + p.get('CreatedInProcess', ''), + p.get('AcceptedInProcess', ''), + name_priv.get('NS.namePrefix', ''), + name_priv.get('NS.givenName', ''), + name_priv.get('NS.middleName', ''), + name_priv.get('NS.familyName', ''), + name_priv.get('NS.nameSuffix', ''), + name_priv.get('NS.nickname', '') + )) + db.close() + + data_headers = ( + 'Source File', 'Source Z_PK', 'ZIDENTIFIER', 'Share Record ID', 'Root Record ID', + 'Participant ID', 'Participant User Record ID', 'Email Address', ('Phone Number', 'phonenumber'), + 'Participant Type', 'Acceptance Status', 'Permission', 'Original Participant Type', + 'Original Acceptance Status', 'Original Permission', 'Is Current User', 'Inviter ID', + 'Has iCloud Account', 'Invitation Token Status', 'Wants New Invitation Token', + 'Is Anonymous Invited Participant', 'Created In Process', 'Accepted In Process', + 'Name Prefix', 'First Name', 'Middle Name', 'Last Name', 'Name Suffix', 'Nickname' + ) + return data_headers, data_list, 'NoteStore.sqlite'