Skip to content

Added iOS Health SQLite (healthdb_secure.sqlite) plugin - #5027

Open
annisadp wants to merge 3 commits into
log2timeline:mainfrom
annisadp:main
Open

Added iOS Health SQLite (healthdb_secure.sqlite) plugin#5027
annisadp wants to merge 3 commits into
log2timeline:mainfrom
annisadp:main

Conversation

@annisadp

@annisadp annisadp commented Jan 18, 2026

Copy link
Copy Markdown

The parser created consists of achievements, all watch sleep latest, all watch sleep, headphone audio levels, heart rate, height, resting heart rate, steps, source devices latest, source devices, watch by sleep period latest, watch by sleep period, watch worn data, weight, workouts latest, workouts and wrist temperature

One line description of pull request

Adds 17 new SQLite plugins for extracting iOS Health artifacts (iOS 13.3.1, iOS 13.4.1, iOS 15, iOS 16, iOS 17). Includes unit tests, formatters, ios.yaml and SQLite database

Description:

Plugins including:
plaso/plaso/parsers/sqlite plugins/ios_health_achievements.py
plaso/plaso/parsers/sqlite plugins/ios_health_all_watch_sleep_latest.py
plaso/plaso/parsers/sqlite plugins/ios_health_all_watch_sleep.py
plaso/plaso/parsers/sqlite plugins/ios_health_headphone_audio_levels.py
plaso/plaso/parsers/sqlite plugins/ios_health_heart_rate.py
plaso/plaso/parsers/sqlite plugins/ios_health_height.py
plaso/plaso/parsers/sqlite plugins/ios_health_resting_heart_rate.py
plaso/plaso/parsers/sqlite plugins/ios_health_steps.py
plaso/plaso/parsers/sqlite plugins/ios_health_source_devices_latest.py
plaso/plaso/parsers/sqlite plugins/ios_health_source_devices.py
plaso/plaso/parsers/sqlite plugins/ios_health_watch_by_sleep_period_latest.py
plaso/plaso/parsers/sqlite plugins/ios_health_watch_by_sleep_period.py
plaso/plaso/parsers/sqlite plugins/ios_health_watch_worn.py
plaso/plaso/parsers/sqlite plugins/ios_health_weight.py
plaso/plaso/parsers/sqlite plugins/ios_health_workout_latest.py
plaso/plaso/parsers/sqlite plugins/ios_health_workouts.py
plaso/plaso/parsers/sqlite plugins/ios_health_wrist_temperature.py

Unit Test including:
plaso/ test /parsers/sqlite plugins/ios_health_achievements.py
plaso/ test /parsers/sqlite plugins/ios_health_all_watch_sleep_latest.py
plaso/ test /parsers/sqlite plugins/ios_health_all_watch_sleep.py
plaso/ test /parsers/sqlite plugins/ios_health_headphone_audio_levels.py
plaso/ test /parsers/sqlite plugins/ios_health_heart_rate.py
plaso/ test /parsers/sqlite plugins/ios_health_height.py
plaso/ test /parsers/sqlite plugins/ios_health_resting_heart_rate.py
plaso/ test /parsers/sqlite plugins/ios_health_steps.py
plaso/ test /parsers/sqlite plugins/ios_health_source_devices_latest.py
plaso/ test /parsers/sqlite plugins/ios_health_source_devices.py
plaso/ test /parsers/sqlite plugins/ios_health_watch_by_sleep_period_latest.py
plaso/ test /parsers/sqlite plugins/ios_health_ watch_by_sleep_period.py
plaso/ test /parsers/sqlite plugins/ios_health_watch_worn.py
plaso/ test /parsers/sqlite plugins/ios_health_weight.py
plaso/ test /parsers/sqlite plugins/ios_health_workout_latest.py
plaso/ test /parsers/sqlite plugins/ios_health_workouts.py
plaso/ test /parsers/sqlite plugins/ios_health_wrist_temperature.py

SQLite database froom Digital Corpora places on plaso/test_data/:
healthdb_secure_iOS_13_3_1.sqlite
healthdb_secure_iOS_13_4_1.sqlite
healthdb_secure_iOS_15.sqlite
healthdb_secure_iOS_16.sqlite
healthdb_secure_iOS_17.sqlite

Notes:

All contributions to Plaso undergo code review.
This makes sure that the code has appropriate test coverage and conforms to the
Plaso style guide.

One of the maintainers will examine your code, and may request changes. Check off the items below in
order, and then a maintainer will review your code.

Checklist:

  • No new new dependencies are required or l2tdevtools has been updated.
  • Test data has a Plaso compatible license. If the test data was not authored by you (the contributor), make sure to mention its orginal source in ACKNOWLEDGEMENTS.
  • Reviewer assigned.
  • Automated checks (GitHub Actions, AppVeyor) pass.

@joachimmetz joachimmetz self-assigned this Feb 7, 2026
@joachimmetz
joachimmetz self-requested a review February 7, 2026 09:10
@joachimmetz

Copy link
Copy Markdown
Member

Thanks for the changes, unfortunately "Digital Corpora" is public domain. For context see https://opensource.org/blog/public-domain-is-not-open-source

@joachimmetz joachimmetz added the pending reporter input Issue is pending input from the reporter label Feb 7, 2026
@joachimmetz

Copy link
Copy Markdown
Member

@annisadp can you rebase this PR with current HEAD and replace the test files with ones that are compatible with the Plaso license?

@joachimmetz joachimmetz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pending questions/comments

@joachimmetz joachimmetz removed the pending reporter input Issue is pending input from the reporter label May 12, 2026
@joachimmetz

Copy link
Copy Markdown
Member

rebased with HEAD

@joachimmetz

Copy link
Copy Markdown
Member

Looked into it a bit closer, we likely can use the test files, but they require a mention in the acknowledgement

"""Tests the Process function on a healthdb_secure.sqlite file."""
plugin = ios_health_all_watch_sleep.IOSHealthAllWatchSleepPlugin()
storage_writer = self._ParseDatabaseFileWithPlugin(
['healthdb_secure.sqlite'], plugin)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file does not exist, I assume this is healthdb_secure_iOS_13_3_1.sqlite ?

from plaso.parsers.sqlite_plugins import ios_health_watch_by_sleep_period_latest
from plaso.parsers.sqlite_plugins import ios_health_watch_by_sleep_period
from plaso.parsers.sqlite_plugins import ios_health_watch_worn_data
from plaso.parsers.sqlite_plugins import ios_health_workout_latest

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s missing, should be ios_health_workouts_latest

Comment thread plaso/parsers/sqlite_plugins/__init__.py

if __name__ == '__main__':
unittest.main()

No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace

@joachimmetz

Copy link
Copy Markdown
Member

Given these appear to be different plugins for the same database, they should be merged into 1 plugin that creates different events

@joachimmetz joachimmetz added this to the Parking/Temporary release milestone May 12, 2026
@joachimmetz joachimmetz changed the title Add iOS Health plugins Added iOS Health SQLite (healthdb_secure.sqlite) plugin May 12, 2026
@codecov

codecov Bot commented May 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.84810% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.18%. Comparing base (2738b78) to head (a132abc).
⚠️ Report is 33 commits behind head on main.

Files with missing lines Patch % Lines
...rsers/sqlite_plugins/ios_health_workouts_latest.py 83.45% 22 Missing ⚠️
...plugins/ios_health_watch_by_sleep_period_latest.py 87.36% 12 Missing ⚠️
...sqlite_plugins/ios_health_watch_by_sleep_period.py 82.60% 8 Missing ⚠️
...rsers/sqlite_plugins/ios_health_watch_worn_data.py 90.24% 4 Missing ⚠️
...qlite_plugins/ios_health_all_watch_sleep_latest.py 93.93% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5027      +/-   ##
==========================================
+ Coverage   85.16%   85.18%   +0.01%     
==========================================
  Files         456      462       +6     
  Lines       40682    41073     +391     
==========================================
+ Hits        34646    34986     +340     
- Misses       6036     6087      +51     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

QUERIES = [
(
(
"WITH surface_temp AS (SELECT mv.object_id, mv.numerical_value "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both surface_temp and alg_ver return NULL values, is this query WAI?

return None
text = str(value).replace("\xa0", " ").strip()
try:
fixed = text.encode("latin-1", errors="ignore").decode(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not transparant to the user and therefore debatable if forensically sound.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seconds = total % 60
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

def _DistanceToKmMiles(self, total_distance):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add this as an output formatter?


if duration_seconds is not None:
try:
event_data.duration_in_minutes = float(duration_seconds) / 60.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculated but not used?

QUERIES = [
(
(
"WITH TimeData AS (SELECT s.start_date AS start_cocoa, s.end_date AS "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not do this in an analysis plugin?

except (TypeError, ValueError):
code = None

if code not in (2, 3, 4, 5):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not transparent to the user or the reviewer. What is the reason for skipping these codes?

@joachimmetz

Copy link
Copy Markdown
Member

Heads up I'm splitting this PR, breaking out #5149 what has been refactored so far.

There is more work needed on the remainder in this PR

annisadp and others added 2 commits July 7, 2026 14:22
The parser created consists of achievements, all watch sleep, headphone audio levels, heart rate, height, resting heart rate, steps, source devices, all watch by sleep period, watch worn, weight, workouts and wrist temperature
@@ -0,0 +1,129 @@
"""SQLite parser plugin for iOS Health - All Watch Sleep Data (iOS 17)."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self, this is basically the same as ios_health._ParseAllWatchSleepSample with the exception of filtering on several category values

self.manufacturer = None
self.model = None
self.software = None
self.sync_identity = None

@joachimmetz joachimmetz Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: Only major difference to IOSHealthSourceDevicesEventData is sync_identity, which does not seem to be in previous versions of the database schema


QUERIES = [
(
(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given this also uses sample type 63 as input should this be an analysis plugin?

@joachimmetz joachimmetz added pending reporter input Issue is pending input from the reporter needs work PR is incomplete and needs work labels Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs work PR is incomplete and needs work pending reporter input Issue is pending input from the reporter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants