Screenshot sanitization — sensitive data masked before capture
Screenshot sanitization — sensitive data masked before capture
Status: Delivered
CAS: CAS-2847
Delivered: 2026-05-15
PRs: #786
What’s new
When a user files a bug report through Astrid, Casaconomy now automatically masks sensitive content — names, email addresses, phone numbers, transaction amounts, payee names, and device identifiers — before the screenshot is captured. The mask is applied only for the instant of capture; the DOM restores to its live state immediately after, with no visible flicker.
How to use it
There is nothing to configure. Masking is automatic whenever a bug report is filed through the Astrid chat sheet.
As a developer adding a new component: mark sensitive text elements with
the data-redact attribute:
<span data-redact="email">{user.email}</span><span data-redact="currency">{formatAmount(tx.amount)}</span><span data-redact="name">{user.name}</span><span data-redact="payee">{tx.payeeName}</span><span data-redact="account">{account.iban}</span><span data-redact="phone">{user.phone}</span><span data-redact="text">{anyOtherSensitiveText}</span>Each type produces a layout-preserving placeholder (first letter kept for
name; country code kept for account; digits replaced with • for
currency, phone, email; full block for payee/text).
Components covered in the initial pass:
| Screen | Redacted fields |
|---|---|
| Transactions list | Payee name, transaction total |
| Settings → Users (desktop) | Name, initials, email, phone |
| Settings → Users (mobile) | Name, initials, email, phone |
| Settings → License | Device name, device ID |
What changed under the hood
src/utils/screenshot/redact.ts— new module with three exports:maskText(per-type masking logic),redactDom(walks[data-redact]elements, replaces text, returns a restore function), andwithRedaction(wraps an async capture callback — masks before, restores infinallyeven on error).useReportIssueStore.ts—captureScreenshotWithPreviewnow runs the html2canvas → toBlob pipeline insidewithRedaction(document.body).- Only leaf text nodes are processed; elements with child element nodes (icons, nested components) are skipped to avoid destroying their structure.
- 26 unit tests in
test/utils/screenshot/redact.test.tscover all attribute types and edge cases.
Why we built it
Bug-report screenshots capture the live WKWebView state, which at time of capture can contain currency amounts, IBAN numbers, email addresses, phone numbers, and names. Moving screenshot storage to Cloudflare R2 (CAS-2833/2848) removed the GitHub coupling, but the image itself was still raw user data in a bucket. The right fix is to sanitize at source so that the screenshot arriving in the bug report is a debug artifact — showing layout, state, and UI structure — rather than a privacy artifact containing personal or financial data.
Known limitations / follow-on work
- More components to annotate — the initial pass covers the highest-risk
screens. Follow-ups:
TransactionGroupContentandTransactionSummaryTabletotals,ProviderModalAPI headers,RuleCardmatch-query literals,BankAccountFormIBAN/BIC fields. - Input value redaction —
data-redactcurrently targetstextContentonly.<TextInput>values (e.g. license key field) require a separateinput.valueredaction path, deferred to a follow-up. - Settings → Users form modal — fields visible in the edit modal are not yet annotated; the modal was not open during the initial component audit.