carlos@cesaints: ~/projects/business-logic-review.md — zsh

Command: cat projects/business-logic-review.md

projects/business-logic-review.md · 1.3 KB

A security review of the business logic of my own system

I reviewed the business logic of my own system, with payments, credits, rankings and paid content, and fixed thirteen classes of flaws, each with a regression test. The process was an adversarial review that never touched production: every hypothesis reproduced in a failing test and fixed at the narrowest boundary. The costliest flaws were the ones scanners and generic checklists miss.

Role
Author of the process and owner of the decisions
Period
Aug 2026 – Sep 2026
What I did

Me: Threat model, deciding what is critical, the review process design, trade-off decisions and final approval.

Private project, described without identifying the client, product or company.

Diagram

From hypothesis to regression test, without touching production

The whole review runs in a local environment with synthetic data; production is out of scope. A security review and a business-logic review raise hypotheses, and a validator tries to refute each one: a refuted one is dropped, and a confirmed one becomes a failing test on real database connections. The fix goes in at the narrowest boundary, behind a review gate, and the test stays as a regression test, with a CI guard.

Context

Between August and September 2026 I reviewed a system of mine with checkout, credits, rankings, timed exams, 2FA and a personal-data export. The reviews ran against local code and Postgres with synthetic data. The system wasn’t open for sales yet.

This is a security review of my own code. It is not a professional pentest, and it does not claim the system is free of flaws.

What weighed the most

  • False positives. The validator knocked down several suggestions from automated reviewers that had no real attack path. Only confirmed findings moved on to a fix.
  • Partial external reviews. Reviews that timed out or brought nothing useful were logged as unavailable, not as approval.
  • Honest counting. Overlapping test runs were not added up, and a red run caused by disk contention was not reported as green.
  • The pattern that kept coming back. Granting a right without the revocation path in the same diff broke five times before it became a mandatory checklist item.

What I learned

The costliest flaws weren’t on any list of known vulnerabilities. They lived in the gap between what a business rule promises and what two simultaneous requests can do. That builder’s way of thinking is what I bring to security testing: knowing a system well enough to know where it gives.

Constraints

  • Never attack the live service; only code, a local environment and synthetic data.
  • Every hypothesis must be falsifiable and reproduced in a test that fails before the fix.
  • Opinions do not count as proof: only a test that reproduces the flaw.
  • Mocks don't prove database isolation; races need real connections.
  • No lowering coverage or loosening assertions to get a green run.

Security report

Scope and authorization

My own system, reviewed locally with synthetic data. No testing against production.

Threat model

  • Assets: paid content, money and credits, rankings, admin accounts, personal data.
  • Actors: a curious user, a malicious user with several accounts, bots, a competitor scraping, an insider.
  • Surfaces: Server Actions treated as public endpoints, webhooks, cron jobs, signed links and forms.

Method

  1. Investigation in two roles: security (authorization, 2FA, injection, secrets, headers) and business logic (concurrency, value, skipped steps, state machines).
  2. Refutation: a validator tries to kill every finding. Does the code really lead to the effect? Can the attacker reach it with the privileges they have? Is the impact inflated?
  3. Fix at the narrowest boundary or in the shared function, with a mandatory review gate that can block any change to money and access rules.
  4. Purple: the reproduction becomes a regression test; the root cause becomes a CI guard when the pattern could reappear elsewhere.

Flaw classes found and fixed

Flaw classes found and fixed
CWEFlaw classes found and fixedReproduced effect
CWE-362Race on a single-use credentialThe same 2FA backup code was accepted by two concurrent logins.
CWE-613Second-factor boundaryA session opened before 2FA was enabled later gained admin access.
CWE-362Token reuseA password-reset token could be consumed twice in parallel.
CWE-367TOCTOU on a balanceTwo concurrent redemptions debited credits twice.
CWE-362Double countingTwo simultaneous exams awarded two ranking credits.
CWE-362Webhook idempotencyTwo simultaneous deliveries of the same event ran the handler twice.
CWE-269Excessive SQL privilegeAn anonymous role could TRUNCATE an RLS-protected table, because RLS does not cover TRUNCATE.
CWE-359Personal data exposureThe data-subject export included third parties' IPs and anti-fraud signals.
CWE-200Payload leakThe URL of a paid file was serialized to the client.
CWE-636Failing open on missing configurationWithout one configuration variable a private file was treated as external.
CWE-841Incomplete revocationA chargeback kept access; a refund did not cancel the remote subscription.
CWE-840Selling without deliveringCheckout sold an item with no published content.
CWE-20Input validationA price typed in the admin panel was multiplied by 100.

Fixes

  • Atomic conditional consumption (compare-and-set) for single-use codes and tokens.
  • Proof of the second factor in the session, and a new login after enabling 2FA.
  • A per-resource transactional lock for ranking credits; a re-read inside the transaction for balances.
  • An atomic claim for webhook events.
  • A REVOKE migration for anonymous roles, on top of RLS.
  • An allow-list projection for the data-subject export.
  • A signed link per request, with no public URL in the payload.
  • Failing closed when configuration is missing.
  • Revocation and remote cancellation in the same refund and chargeback flow.
  • A single deliverable-offer predicate, and a price parser without floating point.

Validation

  • Races reproduced with two real Postgres connections and a barrier before the insert.
  • Every fix shipped with a test that failed before it.
  • Database privileges checked with tests that switch roles (SET ROLE).
  • CI guards that fail hand-written access filters outside the canonical helper.

Stack and why

PostgreSQL
Advisory locks, RLS, grants and SKIP LOCKED as concurrency and isolation primitives.
Vitest with isolated Postgres schemas
Concurrency and privilege tests against a real database.
Playwright
CSP and HTTP flow checks.
GitHub Actions
Dependency audit and regression guards derived from the review.

Results

  • Thirteen classes of flaws reproduced and fixed, each with a regression test.

    audited private repositoryProject review reports(Project documentation (not re-run in this audit), Sep 2026)
  • About 16 test files create an isolated schema in a real Postgres and about 31 deal with concurrency.

    audited private repositoryApproximate count from a repository search(Audit of the private repository, Sep 2026)
  • A dependency-audit job and a regression-guard job in CI.

    audited private repositoryWorkflow structure(Audit of the private repository, Sep 2026)

Evidence

  • audited private repositoryAudited private repository(Audit of the private repository, Sep 2026)