SEC2-087: The API token sits in storage.local in clear, outside Thunderbird's primary password, and the privacy text does not say so #16
Labels
No labels
bug
documentation
enhancement
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-thunderbird#16
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-10-16 in a read-only security review of
postulo-thunderbirdat4a10c95, checked by a second, independent pass.Severity: Informational · Category: Data Exposure · Reference: CWE-312 (Cleartext Storage of Sensitive Information); OWASP A02:2021 (Cryptographic Failures)
Location:
src/lib/thunderbird.js:8-15README.md("Privacy, as it stands")src/_locales/en/messages.json:10-12(tokenHint: "It is kept in this Thunderbird profile only.")Issue
The bearer token is written and read as a plain string:
storage.localis a per-extension IndexedDB file inside the profile folder. Thunderbird'sprimary password encrypts the accounts' mail passwords in
logins.json, not extension storage,so the Postulo token is readable by anything that can read the profile folder (another process of
the same user, a profile backup, a synced folder) even when a primary password is set. The README
and
tokenHintsay the token "stays in this Thunderbird profile", which is true, but a person whohas set a primary password may read that as "protected like my mail passwords". No WebExtension
API offers encrypted storage, so this is a documentation point, not a code defect; the
compensating controls are the token's narrow scope and revocation from Settings.
The token is otherwise handled well: it travels only in the
Authorizationheader toendpoint(instance, ...)(api.js:57-65, 161-164), never in a URL, never logged (noconsole.*call in
src/), and only to the origin the person granted (options.js:44, 56,thunderbird.js:28-30).Impact
Someone with read access to the profile folder holds a working
capturestoken (and, once #9lands, perhaps a
listings:bindone as well) until the person revokes it. Scoped and revocable,so limited; recorded so that the privacy text matches the storage.
Fix
In the README's "Privacy, as it stands" (and the PRIVACY.md that #1 plans) add one sentence: the
token is stored unencrypted in the profile's extension storage, Thunderbird's primary password
does not cover it, and anyone who can read the profile folder can use it until it is revoked
under Settings → API tokens. Optionally, add a "Forget token" button on the options page that
clears it from
storage.local(today the only way is to overwrite it).Done when the README says so and, if the control is added, a test asserts that the options page
has it and that pressing it leaves no
tokenin storage.Related
#8 (open) mentions
storage.localfor the display-language setting only. The scope question forthis same token is the issue on the mail-client row of the threat model.