Skip to content
TODD BROWN
« Writing

Adversarial audits left in the code

I built a HubSpot CRM data-mirror integration for a national amateur sports league and left three rounds of adversarial audit findings in the shipped code — permanent, dated comments, severity-coded, with 8 findings still open and visible. One of those audits caught a form field silently dropped for two months.

The integration went live on 29 May 2026 as one deliverable inside a six-week revenue-operations rebuild covering roughly 2,400 teams. It's a one-way backend sync: team-application lifecycle events copied out of the league's Python/Django registration system into a HubSpot CRM I built for them from scratch during the same engagement. I designed and wrote the integration; the registration system's own outside development agency reviewed, merged and deployed it.

What it does, and how small it is

The data mirror is additive only, reversible by design. Four patch points in the registration system — three required plus one conditional legacy route — each a small change (6–12 lines) to an existing file: a serializer, an e-signature service, an insert path, and a model file. My estimate put the total lines added to existing client code, across all patch points and settings, at under 80.

I chose explicit hook calls at named locations over Django signals specifically so the change would not surprise the agency's team, who had never used a signal in that codebase before.

The integration leaves no trace if switched off. A feature flag defaults to off, a separate dry-run flag defaults to on (so the sync logs the payload it would send and exits before calling HubSpot), every write is an idempotent upsert keyed on a stable external application ID, and the sync is wrapped in a database transaction commit hook so a rolled-back save never produces a HubSpot write. Log lines carry only an application ID and an event name — no personal data.

It works. A real HubSpot company record created by the integration, with all three lifecycle stages (initial submission, NDA signed, final approval) landing correctly over a two-day window, was read back from HubSpot's own property-history interface.

Three audit rounds, permanent and dated

The shipped code carries three numbered rounds of adversarial audit findings — AUDIT 001, 002, and 003 — left as comments in the source files. Each finding is severity-coded: critical, high, medium, low. At least 3 findings are explicitly marked resolved with a date; 8 remained open as of the last commit I read.
Rather than fixing or hiding the open findings before handoff, I left them dated in the code itself, so a reviewer can see what was already known to be unresolved.

The field-coverage audit that caught the silent drop

A separate, dedicated field-coverage audit caught a data-integrity gap that no functional test had surfaced.

A form field submitted by the client's front end as web_site did not match the name (website) the receiving code expected. Because the field was optional, the mismatch was treated as simply absent rather than an error, and the value was never stored anywhere — not just never forwarded to HubSpot.

It was found two months after the initial merge, through the field-coverage audit rather than a bug report. The field had been silently dropped the entire time, and nobody noticed.

What this is

This is what adversarial audits look like when you leave them in the code: dated, severity-coded, some resolved and some not, all visible. The field-coverage audit is a separate pattern — a deliberate check that every field the front end sends is actually received and stored — and it found something no functional test caught.

The integration is live. The audits are still there.