Skip to content

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:

ScreenRedacted fields
Transactions listPayee name, transaction total
Settings → Users (desktop)Name, initials, email, phone
Settings → Users (mobile)Name, initials, email, phone
Settings → LicenseDevice 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), and withRedaction (wraps an async capture callback — masks before, restores in finally even on error).
  • useReportIssueStore.tscaptureScreenshotWithPreview now runs the html2canvas → toBlob pipeline inside withRedaction(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.ts cover 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: TransactionGroupContent and TransactionSummaryTable totals, ProviderModal API headers, RuleCard match-query literals, BankAccountForm IBAN/BIC fields.
  • Input value redactiondata-redact currently targets textContent only. <TextInput> values (e.g. license key field) require a separate input.value redaction 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.