Open Source / ALEAPP #1785

Reading WhatsApp Business stores in ALEAPP

WhatsApp Business keeps its data in its own package folder, which the standard WhatsApp artifacts do not open. This pull request adds artifacts that read it, and states plainly that no real Business store has been parsed yet.

  • Daniel Addai, SectraTEK Lab
  • In review
  • CC BY 4.0

View pull request #1785 on GitHub (opens in a new tab)

Summary

WhatsApp Business is a separate Android app with its own package folder, com.whatsapp.w4b. A report built only from the standard WhatsApp artifacts does not open that folder. This contribution adds nine artifacts that read it, and it is also an example of how a contribution should describe what has not been checked.

  • One new module, WhatsAppBusiness.py, adds nine artifacts: contacts, call logs, legacy messages, one-to-one messages, group messages, group details, channels, channel messages and user profile.
  • Each one runs the WhatsApp artifact of the same name, with paths that match only com.whatsapp.w4b. The two apps are read on their own and never mixed.
  • Status: In review. The pull request is open as a draft. On 25 October 2026 the maintainer asked for a run on real WhatsApp Business data before it can go in.
  • No real Business store has been parsed yet. The pull request says so in its checklist, and each artifact repeats it in its notes.
  • 9artifacts added
  • 43files changed
  • 1,640lines added
  • 18synthetic cases

Methodology note

Drafted with AI assistance and reviewed by the author before publication. The pull request was also prepared with AI assistance, as its description states.

The issue

Picture an examiner with a full file system extraction of a phone used for a small shop. The shopkeeper talks to customers in WhatsApp Business, a different app from the one most people have. The examiner runs the parser and gets a WhatsApp section, or none. The Business chats are in the extraction, under com.whatsapp.w4b, but no artifact looks there. The absence in the report reads as absence on the phone, and it is neither.

The reverse risk is just as real. If a Business artifact matched paths loosely, a phone with both apps would show two apps' chats in one table, and an examiner could attribute a conversation to the wrong app. The two apps need separate stores, separate artifacts and a clear label.

An earlier attempt at the same idea, pull request #1499 by another contributor, was closed by the maintainer with change requests. This work uses the same wrapper approach, tries to answer each of those requests, and credits the idea to that pull request.

Before and after: the Business package folderA two-panel view. Before, the WhatsApp artifacts read the com.whatsapp folder and nothing reads the com.whatsapp.w4b folder. With this change, nine WhatsApp artifacts read com.whatsapp and nine WhatsApp Business artifacts read com.whatsapp.w4b, each with paths that match only its own package.Before this changecom.whatsappdatabases/msgstore.db wa.dbcom.whatsapp.w4bdatabases/msgstore.db wa.dbWhatsApp artifacts (9)contacts, calls, messages, groups, profileNo artifact reads this storeWith this changecom.whatsappdatabases/msgstore.db wa.dbcom.whatsapp.w4bdatabases/msgstore.db wa.dbWhatsApp artifacts (9)paths match com.whatsapp onlyWhatsApp Business artifacts (9)paths match com.whatsapp.w4b onlyTwo apps, two packagefolders, kept apartBusiness rows are nevermixed into WhatsApp rows
Figure 1Where the Business package folder stands in a report, before and after the change. The file names shown are the ones the Business artifacts look for; the Business names are assumptions, not observed values. Source: PR body (What this changes); diff, scripts/artifacts/WhatsAppBusiness.py (diagram).
Show this figure as a table
Figure 1 as a table
StateFolderRead by
Beforecom.whatsappWhatsApp artifacts (9)
Beforecom.whatsapp.w4bNo artifact
With this changecom.whatsappWhatsApp artifacts (9)
With this changecom.whatsapp.w4bWhatsApp Business artifacts (9)

The essence

The module is a thin layer. Each Business artifact calls the existing WhatsApp artifact of the same name through __wrapped__, the way justalk.py already runs its Kids variants. Because the Business paths match only com.whatsapp.w4b, the shared code never sees a com.whatsapp file during a Business run.

One supporting change moved the profile code in WhatsApp.py into _user_profile_rows, so the Business profile can match its own preference file names. The output of WhatsApp - User Profile is unchanged. A second change lets the test harness record a wrapper's shared implementation.

Artifacts added by the pull request
ArtifactReads (under com.whatsapp.w4b)Note in the artifact
WhatsApp Business - Contactsdatabases/wa.dbChannel and status@broadcast jids left out, as in WhatsApp
WhatsApp Business - Call Logsmsgstore.db, wa.dbLID jids resolved through jid_map
WhatsApp Business - Messagesmsgstore.db, wa.dbLegacy schema with messages.data only
WhatsApp Business - One To One Messagesmsgstore.db, wa.db, media foldersModern schema; media folder location assumed
WhatsApp Business - Group Messagesmsgstore.db, wa.db, media foldersModern schema; media folder location assumed
WhatsApp Business - Group Detailsmsgstore.db, wa.db, files/AvatarsAvatars folder assumed
WhatsApp Business - Channelsmsgstore.dbNewsletter table assumed to be the same
WhatsApp Business - Channel Messagesmsgstore.db, media foldersMedia folder location assumed
WhatsApp Business - User Profileshared_prefs files ending preferences_light.xml, startup_prefs.xml, or named com.whatsapp.w4b_preferences.xmlFive keys; file name not verified on a device

Our methodology

The method here is mostly about limits. We could not read a real Business store, so the work is designed to show exactly how far the evidence goes.

Keep the two apps apart. A new test file, test_whatsapp_business.py, lists the files of a device that holds both apps and checks that every Business artifact matches only Business files and that no WhatsApp artifact matches a Business file. It also checks that every WhatsApp artifact has a Business twin, that the conversation views stay equal to the WhatsApp ones, and that each function is its own artifact processor. A run test builds synthetic databases for both packages and reads them.

Show the shared code did not change. The refactored WhatsApp.py was run against upstream on three real WhatsApp extractions and every WhatsApp output file was byte-identical. The Business artifacts were then run on copies of two real WhatsApp databases placed under com.whatsapp.w4b, and gave the same row counts and identical output bodies, paths aside. This shows the wrapper reads the same schema in the same way. It does not show the Business app uses that schema, and the pull request says so.

Record what fails. A comparison run with a second copy of the data reads one store, not two, because the WhatsApp code reads only the first msgstore.db and wa.db it is given. The nine Business legs of that test are recorded as known failures; the fix belongs in the shared code.

Say what is assumed. Every note ends with a validation boundary. The media folder, the avatar folder, the preference file name and the channels table are listed as assumed. After review, the profile match was widened to also accept com.whatsapp.w4b_preferences.xml, a name taken from a vendor's published acquisition guide. That is a second-hand description of an extraction, and the note says so.

Fix review findings. Two User Profile baselines were first recorded on Windows with backslash paths and would fail on macOS and on the Linux runner. They were re-recorded with forward slashes and now pass the repository runner.

Pipeline of a WhatsApp Business artifactA four-step flow from an extraction file under com.whatsapp.w4b, through a Business artifact with a path filter, through the shared WhatsApp code called with __wrapped__, to a report category named WhatsApp Business. Below, three supporting items: the profile code split, the new test file, and a recorded known failure for a second Android user.Data flow of one Business artifactExtraction file*/com.whatsapp.w4b/databases/msgstore.db*Business artifactget_whatsapp_business_*path filter: w4b onlyShared WhatsApp codeget_whatsapp_*.__wrapped__(context)Reportcategory: WhatsAppBusinessAround the flowProfile code split_user_profile_rows(context, names)WhatsApp output unchangedTest filetest_whatsapp_business.py6 new tests: paths, views, runRecorded known failuresecond Android user is dropped(shared WhatsApp code)
Figure 2The Business artifacts add no parsing of their own. The path filter decides which package folder is read; the shared WhatsApp code does the reading. Source: PR body; diff, scripts/artifacts/WhatsAppBusiness.py, scripts/artifacts/WhatsApp.py, admin/test/scripts/test_whatsapp_business.py (diagram).
Evidence ladder for the Business artifactsThree checks in order. Two are done: the refactored WhatsApp code matches upstream output on three extractions, and the Business artifacts match the WhatsApp artifacts on relabelled copies of two real WhatsApp databases. The third, a run on a real WhatsApp Business extraction, is pending.Check runWhat it showsStatusRefactored WhatsApp.py against upstream main3 extractions; every WhatsApp TSV byte-identicalShared code unchangedDoneBusiness artifacts on relabelled real WhatsApp data2 databases copied under com.whatsapp.w4b; same counts, same bodiesWrapper reads the same schema thesame wayDoneBusiness artifacts on a real Business extractionrequested by the maintainer; no such extraction availableThat the Business app uses thatschemaPendingA dashed box marks a check that has not been done.
Figure 3What has been run and what it shows. The first two checks use real WhatsApp (not Business) data and show that the new code reads what the existing code reads. Only the third check would show anything about the Business app. Source: PR body (For a new or changed artifact; Test record); maintainer review of 25 October 2026. Real WhatsApp counts, no synthetic values.
Show this figure as a table
Figure 3 as a table
CheckResultShows
Refactored WhatsApp.py vs upstream main, 3 extractionsEvery WhatsApp TSV byte-identicalShared code unchanged
Business artifacts on relabelled copies of 2 real WhatsApp databasesSame row counts; TSV bodies byte-identical (paths aside)Wrapper reads the same schema the same way
Business artifacts on a real Business extractionNot done; none availableWould show whether the Business app uses that schema

The value

For an examiner the value is a route to a store that was invisible. If a phone carries com.whatsapp.w4b, the report can list the Business conversations, calls, groups, channels and profile values in their own category. It can answer a plain question: does this device hold a Business store, and what does the shared reader return from it?

The limit is just as important. Until the code has run on a real Business store, the rows are indicators to be verified against the app or another source. The pull request is built so that the report itself carries that warning in every artifact's notes. We think that is the right default for an app nobody on the project could test.

What is pending. The maintainer asked for a known data extraction: WhatsApp Business installed on a test phone or a rooted emulator with a test account, a few messages, a call, a group, a channel and a profile, then a full file system extraction, with counts added to sample_data. The author replied that no such extraction exists yet and asked for the pull request to be marked as a draft until one does. A public dataset on WhatsApp Business was checked and does not help: it is an unrooted quick image with no Business databases or preferences. The baseline fix was pushed and awaits the repository checks. Continuous integration runs for first-time contributors need a maintainer's approval, so none had run when the pull request was written.

None of this makes the artifacts wrong. It means the claim stays narrow, and it keeps a reviewer from having to trust an app we could not test.

What the Business artifacts assumeA matrix of five items the artifacts depend on, with three columns: named in the public app package by a strings check, run on constructed or relabelled data, and seen in a real Business store. Every item is run on constructed data and none has been seen in a real Business store.Item the artifacts depend onNamed in the apppackage (strings only)Run on constructedor relabelled dataSeen in a realBusiness storemsgstore.db and wa.dbfile names, tables and columnsNames onlyYesNoPreferences file and keysstartup_prefs; w4b_preferences name is second-handPartlyYesNoMedia folderWhatsApp Business/MediaNot listedYesNoAvatars folderfiles/AvatarsName onlyYesNoChannels tablethe newsletter tableFields onlyYesNo
Figure 4The artifact notes state each of these as assumed. Names found by a strings check show that the app refers to a name, not what it writes to disk. Source: PR body; author comment of 25 October 2026 (public dataset and package check); diff notes of WhatsAppBusiness.py (diagram).
Show this figure as a table
Figure 4 as a table
ItemNamed in app package (strings)Run on constructed dataSeen in a real Business store
msgstore.db and wa.dbNames onlyYesNo
Preferences file and keysPartly (startup_prefs)YesNo
Media folderNot listedYesNo
Avatars folderName onlyYesNo
Channels tableFields onlyYesNo

Where this sits in the Lab's research

No Lab report covers this yet; the method above is from the pull request. It is a case of recording a negative result: a contribution that states, in the code and in the report, what has not been validated. The Lab's other posts in this series show the same checks (synthetic fixtures, independent recounts, behavior on missing data) applied to artifacts that did have real data.

Sources

Sources for the numbers and claims on this page
Claim or numberWhere it comes from
Nine artifacts and their namesPR body (What this changes); diff, scripts/artifacts/WhatsAppBusiness.py
Wrapper through __wrapped__; paths match only com.whatsapp.w4bPR body; diff, WhatsAppBusiness.py (functions and paths)
1,640 lines added, 6 removed, 43 files changed, 4 commitsPR metadata (gh pr view, 5 November 2026)
Profile code moved into _user_profile_rows; output unchangedPR body; diff, scripts/artifacts/WhatsApp.py
18 synthetic cases; run_test_cases PASS=150 (132 on upstream plus 18)PR body (Test record); diff, admin/test/cases/testdata.WhatsAppBusiness.json
6 new tests in test_whatsapp_business.pyPR body (Test record: 1,582 tests on the branch against 1,576 on upstream)
Four Samsung Android 16 extractions, none with com.whatsapp.w4bPR body (checklist: unverified on real data); maintainer review
Real WhatsApp comparison: three extractions, TSVs byte-identical; two databases relabelled, same countsPR body (checklist)
Second-copy comparison reads one store; nine known-failure legsPR body (checklist); diff, admin/test/cases/known_failures.json
Changes requested 25 October 2026; baselines with backslash pathsPR reviews (maintainer)
Baselines fixed; no real extraction available; public dataset checked; draft requestedPR comments (author, 25 October 2026)
Preference file name from a vendor guide; not verifiedPR comments; diff notes of get_whatsapp_business_user_profile
Earlier pull request #1499 closed with change requestsPR body (Prior work)
Open as a draft; no activity after 25 October 2026PR metadata (isDraft, updatedAt)
No Lab report covers thisLab report inventory (no paper maps to this pull request)