I wrote the privacy rule, enforced it, commented it, and shipped the leak anyway

I wrote the privacy rule, enforced it, commented it, and shipped the leak anyway

2026年8月23日2 次浏览来源:Dev.to阅读原文

This is a submission for DEV's Summer Bug Smash: Smash Stories.

TL;DR.

I wrote a scrubbing policy before writing any instrumentation code.

I enforced it in a hook.

I unit tested it.

I wrote a comment above the one obviously sensitive line saying exactly what it must never do.

Then I intercepted the actual bytes leaving the browser and found a stranger's shoulder injury in them.

Every guarantee I had written was about data my code hands to the SDK.

None of them were about data the SDK collects on its own.

The setup WhyRep is a workout tracker built local-first.

Training data is created and read on the device, the tracker works offline with no account, and that is not a marketing line, it is the architecture.

It is also the thing people decide to trust or not trust in about four seconds on the landing page.

So when I added Sentry, the scrubbing policy came before the code.

Written down, in the repo, as a list of things that may never appear in an event: exercise names, weights, reps, RIR, session notes, chat content.

Never.

On Android I enforced it twice.

A hook that strips the forbidden fields, and a unit test that constructs an event carrying each one and asserts it comes out stripped.

Green.

Good.

Then I wired up the landing site's share-link page.

It decodes , where the payload is somebody's entire workout template, base64 in the URL fragment.

I was careful there too.

On a decode failure it reports a coarse reason tag and never the payload: I wrote that comment because it was the obviously sensitive thing and I wanted the next person to see it before they "helpfully" added the payload for debugging.

Read the comment again.

It is correct.

It is well-reasoned.

It describes exactly the right danger.

And the leak happened six inches away from it.

What I missed The payload is in the URL.

Sentry's browser SDK attaches to every event, every transaction and every replay, by itself, as standard context.

It is a completely reasonable default.

It is the single most useful piece of context a browser error can carry.

And the fragment goes with it.

Every guarantee I had written was about data my code passes to Sentry.

None of them were about data the SDK collects on its own.

Those are two different data paths and I had only ever thought about one of them.

I only found it because I stopped reading my own code and intercepted the actual bytes leaving the browser.

A Puppeteer test that grabs the outbound envelope body and greps it for known-sensitive strings: It failed on the first run.

Decoded, the intercepted request contained , , and a note reading .

Base64 is not encryption.

That is a lifter's shoulder injury, in a third-party service, from a page whose privacy policy said the site had no third-party trackers.

Both panels are the same request.

The top one is the only part my policy had an opinion about.

Then the same grep found a second one Once the interceptor existed I pointed it at the rest of the site, which is how I found the one I would not have gone looking for.

The waitlist dedupes signups by using the email address as the Firestore document id.

That is a defensible design.

It also puts the email in the request path: Sentry's automatic fetch breadcrumb records request URLs verbatim.

So every waitlist signup email would have been attached to any error that happened afterwards in that session.

Not an event I sent.

A breadcrumb the SDK collected, riding along on somebody else's unrelated exception.

And that one is worse than the first, for a reason worth naming.

The template in the fragment is the user's own data, shared by the user, from a page they opened deliberately.

The waitlist email is a third party's data, collected in confidence, leaking through a mechanism that has nothing to do with the feature it was collected for.

Not my data.

Other people's.

The fix It is unglamorous, which is fine.

Three hooks: All three are needed and that is the part I would underline.

Errors, performance data and breadcrumbs take three different pa

分享