Skip to content

fix: four safe audit findings — entitlement, attribution, storage - #66

Open
keithfawcett wants to merge 1 commit into
mainfrom
fix/audit-safe-fixes
Open

fix: four safe audit findings — entitlement, attribution, storage#66
keithfawcett wants to merge 1 commit into
mainfrom
fix/audit-safe-fixes

Conversation

@keithfawcett

Copy link
Copy Markdown
Contributor

Four mechanical, low-regression-risk findings from the audit, each Codex-verified as safe-to-fix. Bundled since they're small; each has a test.

1. Enterprise self-assignment (entitlement) — HIGH

POST /billing/plan accepted plan='enterprise' from the full BILLING_PLANS enum. Enterprise counts as active with no Stripe subscription and can enable white-label directly — so any tenant could grant itself permanent paid entitlement for free. Signup already excludes it; this restricts the authenticated in-place setter to flex/revshare.

2. Delinquent subscription keeps white-label (entitlement) — MEDIUM

isWhiteLabelEntitled keyed only on a non-null subscription id, ignoring the mirrored status — so unpaid/paused/canceled kept white-label branding + the custom domain live for free. Now routes through hasActivePlan (the canonical gate), which rejects delinquent statuses. past_due stays entitled (Stripe is still dunning), matching hasActivePlan.

3. Future-click attribution (money correctness) — MEDIUM

The window check rejected only age > window, not negative ages, so during backlog/backdated attribution a click that happened after the event could take (or share) that earlier conversion. Now rejects ageMs < 0.

4. Filesystem upload ENOENT (self-host) — MEDIUM

The fs storage backend created only the storage root before writing, but keys nest (tenants/<id>/logos/...), so the first upload on a fresh self-host install ENOENTed. Now mkdirs the full parent path.

Tests

Delinquent-status white-label cases (unpaid/paused/canceled false, past_due true), a nested-key storage write + overwrite, the enterprise-rejection route check, and a negative-age attribution case. Full typecheck + lint (0 errors) clean.

🤖 Generated with Claude Code

Mechanical, low-risk fixes from the audit (Codex-verified as safe-to-fix):

- Enterprise self-assign (billing.ts): POST /billing/plan accepted
  plan='enterprise', which counts as active + enables white-label with no
  Stripe subscription. Restrict the setter schema to flex/revshare (signup
  already excludes enterprise).
- Delinquent keeps white-label (white-label.ts): isWhiteLabelEntitled keyed
  only on a non-null subscription id, so an unpaid/paused/canceled sub kept
  white-label + custom domain live for free. Route it through hasActivePlan,
  which already rejects delinquent statuses (past_due still entitled — Stripe
  is dunning).
- Future-click attribution (attribution.ts): the window check only rejected
  ages > window, so a click that happened AFTER the event (negative age)
  could attribute an earlier conversion during backlog/backdated processing.
  Reject ageMs < 0 too.
- Upload ENOENT (storage.ts): the fs backend created only the storage root,
  so a nested key (tenants/<id>/logos/...) failed on a fresh self-host
  install. mkdir the full parent path.

Tests: delinquent-status white-label cases, a nested-key storage write, the
enterprise-rejection route check, and a negative-age attribution case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant