From b055398e485daae838ba3c55fd611cc303f0a854 Mon Sep 17 00:00:00 2001 From: Brigs Date: Sat, 12 Sep 2026 21:13:45 -0400 Subject: [PATCH] Type the timestamp columns of the cloud service and snapshot artifacts Ph085 and Ph086 returned TimestampUTC as an untyped header holding datetime objects, as did get_snapshot_creationDate for Creation Date, Expiration Date and Last Used Date and get_snapshot_lastUsedDate for Expiration Date, so LAVA stored those values as text rather than as timestamps. They are now typed datetime, and get_snapshot_lastUsedDate compares header names so the typed tuples still line up. The Ph085 and Ph086 descriptions now cite the Scott Koenig post that documents cloudServiceEnableLog.plist and say the plist records enabling and disabling. Co-Authored-By: Claude Opus 5 --- .../Ph085acntsdcloudServiceEnableLogplist.py | 10 +++++----- .../Ph086astsdcloudServiceEnableLogplist.py | 8 ++++---- scripts/artifacts/applicationStateDB.py | 13 +++++++------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/scripts/artifacts/Ph085acntsdcloudServiceEnableLogplist.py b/scripts/artifacts/Ph085acntsdcloudServiceEnableLogplist.py index 421dd0eb8..ad68c8dc1 100644 --- a/scripts/artifacts/Ph085acntsdcloudServiceEnableLogplist.py +++ b/scripts/artifacts/Ph085acntsdcloudServiceEnableLogplist.py @@ -2,12 +2,12 @@ 'Ph085accountsdcloudServiceEnableLogPlist': { 'name': 'Ph085-accountsd-cloud-Service-Enable-Log-Plist', 'description': 'Parses basic data from */PhotoData/private/com.apple.accountsd/cloudServiceEnableLog.plist' -' which is a plist that tracks when Cloud Photos Library (CPL) and Shared Albums have been' -' enabled. Based on research and published blogs written by Scott Koenig' -' https://theforensicscooter.com/2024/05/18/ileapp-parsers-photos-sqlite-queries/', +' which is a plist that records when iCloud Photo Library (CPL) and Shared Albums were enabled or' +' disabled. Based on research and published blogs written by Scott Koenig' +' https://theforensicscooter.com/2022/05/02/photos-sqlite-query-documentation-notable-artifacts/', 'author': 'Scott Koenig', 'creation_date': '2026-05-28', -'last_update_date': '2026-07-27', +'last_update_date': '2026-09-12', 'version': '5.0', 'date': '2025-01-05', 'requirements': 'Acquisition that contains accountsd cloudServiceEnableLog.plist', @@ -60,7 +60,7 @@ def Ph085accountsdcloudServiceEnableLogPlist(context): data_list.append((timestamputc, servicetype, enabledstate)) data_headers = ( - 'TimestampUTC', + ('TimestampUTC', 'datetime'), 'Service-Type', 'Enabled-State') return data_headers, data_list, source_path diff --git a/scripts/artifacts/Ph086astsdcloudServiceEnableLogplist.py b/scripts/artifacts/Ph086astsdcloudServiceEnableLogplist.py index a4b8a0f1c..5a3493c46 100644 --- a/scripts/artifacts/Ph086astsdcloudServiceEnableLogplist.py +++ b/scripts/artifacts/Ph086astsdcloudServiceEnableLogplist.py @@ -2,14 +2,14 @@ 'Ph086assetsdcloudServiceEnableLogPlist': { 'name': 'Ph086-assetsd-cloud-Service-Enable-Log-Plist', 'description': 'Parses basic data from */PhotoData/private/com.apple.assetsd/cloudServiceEnableLog.plist' -' which is a plist that tracks when Cloud Photos Library (CPL) has been enabled.' +' which is a plist that records when iCloud Photo Library (CPL) was enabled or disabled.' ' Based on research and published blogs written by Scott Koenig' -' https://theforensicscooter.com/2024/05/18/ileapp-parsers-photos-sqlite-queries/', +' https://theforensicscooter.com/2022/05/02/photos-sqlite-query-documentation-notable-artifacts/', 'author': 'Scott Koenig', 'creation_date': '2026-05-28', 'version': '5.0', 'date': '2025-01-05', -'last_update_date': '2026-07-31', +'last_update_date': '2026-09-12', 'requirements': 'Acquisition that contains assetsd cloudServiceEnableLog.plist', 'category': 'Photos.sqlite', 'notes': '', @@ -60,7 +60,7 @@ def Ph086assetsdcloudServiceEnableLogPlist(context): data_list.append((timestamputc, servicetype, enabledstate)) data_headers = ( - 'TimestampUTC', + ('TimestampUTC', 'datetime'), 'Service-Type', 'Enabled-State') return data_headers, data_list, source_path diff --git a/scripts/artifacts/applicationStateDB.py b/scripts/artifacts/applicationStateDB.py index 760c0651f..2fbac1b5b 100644 --- a/scripts/artifacts/applicationStateDB.py +++ b/scripts/artifacts/applicationStateDB.py @@ -74,7 +74,7 @@ "not by itself prove foreground application use or that the user viewed the image contents.", "author": "@mxkrt - @AlexisBrignoni", "creation_date": "2025-08-04", - "last_update_date": "2026-08-21", + "last_update_date": "2026-09-12", "requirements": "none", "category": "Device Usage", "notes": "SplashBoard runtime headers expose creationDate and lastUsedDate properties on " @@ -112,7 +112,7 @@ "at that time.", "author": "@mxkrt - @AlexisBrignoni", "creation_date": "2025-08-04", - "last_update_date": "2026-08-21", + "last_update_date": "2026-09-12", "requirements": "none", "category": "Device Usage", "notes": "The property name is sourced from the runtime-derived SplashBoard header. Its forensic meaning is " @@ -173,8 +173,8 @@ 'contentType imageOpaque requiredOSVersion') # display headers for the snapshot analysis results -_snapshot_headers = ('Creation Date', 'Bundle ID', 'Snapshot Group', - 'Snapshot Index', 'Expiration Date', 'Last Used Date', +_snapshot_headers = (('Creation Date', 'datetime'), 'Bundle ID', 'Snapshot Group', + 'Snapshot Index', ('Expiration Date', 'datetime'), ('Last Used Date', 'datetime'), 'Launch Interface Identifier', 'Relative Path', 'Group ID', 'Image Scale', 'Fullscreen', 'Name', 'Interface Orientation', 'File Location', @@ -248,8 +248,9 @@ def get_snapshot_lastUsedDate(context): new_data_list.append(new_entry) # swap Last Used Date and Creation Date in headers as well - last_idx = _snapshot_headers.index('Last Used Date') - new_headers = [hdr for hdr in _snapshot_headers[1:] if hdr != 'Last Used Date'] + names = [hdr[0] if isinstance(hdr, tuple) else hdr for hdr in _snapshot_headers] + last_idx = names.index('Last Used Date') + new_headers = [hdr for hdr, name in zip(_snapshot_headers[1:], names[1:]) if name != 'Last Used Date'] new_headers.insert(0, ('Last Used Date', 'datetime')) new_headers.insert(last_idx, ('Creation Date', 'datetime'))