Completed Client-Side Security and Privacy Review
Scenario
Atlas wants to add analytics, session replay, and attribution tracking to /account/billing and /reports/revenue. The billing route includes payment method details, invoice history, account owner names, and support links. The reports route includes customer segmentation filters and export events.
This completed review demonstrates the expected depth for review-packets/client-side-security-privacy-review-packet.
Review summary
Decision: approve analytics on both routes with typed first-party event collection. Block session replay on billing for launch. Allow replay on reports only after sensitive table cells and filter values are blocked. Defer attribution API integration until browser support and product requirements are revalidated.
Launch status:
| Area | Status | Reason |
|---|---|---|
| Product analytics | Approved with conditions | typed events and sensitive-field rejection are required |
| Session replay on billing | Blocked | sensitive form and payment-adjacent data create unacceptable capture risk |
| Session replay on reports | Conditional | can launch only with blocked selectors and masking fixture evidence |
| Attribution | Deferred | current browser/API support is unstable enough to require a separate pilot |
| Third-party script loading | Conditional | each script needs owner, consent category, load strategy, and kill switch |
Browser trust boundary
Trust decisions:
- user input, URL parameters, storage values, and third-party script behavior are untrusted
- frontend role checks are UX hints only
- protected operations remain server-authorized
- telemetry SDK rejects unknown event names and sensitive fields before network emission
- replay cannot run on pages that contain payment method editing, security settings, or support messages until explicit blocking fixtures pass
Sensitive data flow map
| Data | Appears in UI | Allowed in URL | Allowed in analytics | Allowed in replay | Allowed in logs |
|---|---|---|---|---|---|
| account id | yes | hashed only if needed | hashed or internal surrogate | no visible raw value | trace id only |
| yes | no | no | masked/blocked | no | |
| payment last four | yes | no | no | blocked | no |
| invoice id | yes | no | hashed only | masked | trace id correlation |
| report filter names | yes | non-sensitive values only | category names only | masked if customer-specific | no raw values |
| export format | yes | yes | yes | yes | yes |
| free-form support text | yes | no | no | blocked | no |
Event schema decision
Approved events:
| Event | Purpose | Allowed properties |
|---|---|---|
billing.invoice_downloaded | product analytics | invoice age bucket, tenant class, file type |
billing.payment_update_started | funnel analytics | tenant class, route version |
billing.payment_update_completed | funnel analytics | success/failure class, route version |
reports.filter_applied | product analytics | filter categories, count, route version |
reports.export_requested | product analytics | format, row count bucket, tenant class |
reports.export_completed | reliability/product analytics | duration bucket, success/failure class |
Rejected properties:
- raw email
- full URL
- account name
- customer name
- payment data
- free-form text
- unbounded metadata objects
Third-party script decision
| Script | Route | Decision | Conditions |
|---|---|---|---|
| analytics vendor | billing, reports | allow through first-party SDK only | no direct browser collection on sensitive routes |
| session replay | billing | block | revisit with security/privacy sign-off |
| session replay | reports | conditional | blocked selectors, masking tests, kill switch |
| attribution pixel | acquisition only | not allowed on billing/reports | separate pilot outside account app |
| support widget | billing | allow on support panel only | no access to payment form DOM, lazy load, owner and kill switch |
CSP and browser controls
Required:
- CSP in report-only during beta, enforcement before GA
script-srclimited to owned app, approved CDN, and nonce/hash strategy where applicableframe-ancestorsdenies unapproved embedding- opener policy prevents unsafe opener access
- referrer policy avoids leaking sensitive URLs
- cookies use appropriate
Secure,HttpOnly, andSameSiteattributes - source maps are not publicly accessible for sensitive production bundles unless policy approves
Launch blockers
- Unknown event properties are currently accepted by the telemetry SDK.
- Session replay masking fixture does not cover invoice table, customer filter chips, and support text.
- Support widget has no documented kill switch.
- CSP reports have no triage owner.
Conditional approval plan
| Condition | Owner | Due |
|---|---|---|
| implement event allowlist and sensitive-field rejection | data platform | before beta |
| add replay blocking fixtures for reports route | frontend platform | before beta |
| add support widget kill switch | account team | before beta |
| assign CSP report triage owner | security engineering | before beta |
| review attribution pilot separately | growth platform | after GA |
Monitoring
Track:
- rejected analytics payloads by field type
- session replay blocked/masked fixture pass rate
- CSP violation count by release
- third-party script load failures
- third-party script transfer and main-thread cost
- support widget activation and disable events
- sensitive-data scanner findings in logs/events
Incident runbook
If sensitive data appears in analytics or replay:
- Disable affected destination or route-level SDK flag.
- Preserve release, configuration, and event schema history.
- Identify event/replay ids and affected cohorts.
- Notify security/privacy/legal owners.
- Purge downstream data where policy requires.
- Add regression fixture for the leaked field.
- Keep destination disabled until fixture and schema checks pass.
Final decision
The review approves measurement only when routed through first-party typed collection with consent-aware loading, sensitive-field rejection, and operational kill switches. The billing route is treated as deny-by-default for replay. Attribution is deferred because browser support and API stability are not strong enough to justify production coupling without a focused pilot.