Book a check

Case study · my own app

I ran the 36 points on my own app first

Before selling this check to anyone else, I pointed it at an AI receptionist and booking SaaS I built. Here is honestly what came back — including the six findings I had to delete, because they turned out to be wrong.

What I did

I audited an AI receptionist and booking SaaS I built myself, against the same 36-point checklist I sell. The method was static source review plus active verification against an isolated instance seeded with two synthetic tenants and fake data — no production database, no real records, at any point. Then I ran an adversarial pass over my own findings and deleted six of them, because they did not survive a second look.

2High
7Medium
8Low
5Attacked and held

Nothing critical. Total time to find all of it: one focused day.

What held up

This goes first on purpose. A report that only lists problems tells you nothing about what is already solid, and the solid half is the part people quietly want to know. Each of these was actively tested against a running instance, not merely read in the source.

  • Cross-tenant isolation. I attacked it directly, as one tenant against another tenant's records. Every foreign id returned 404 on read, cancel, update and delete. A canary string planted in one tenant never appeared in any response to the other.
  • Session invalidation after logout. A cookie captured before logging out, replayed afterwards. Dead.
  • Webhook signatures. All four integrations — WhatsApp, Telegram, Stripe, Monobank — verify against the raw body, which is exactly the part most implementations get wrong. Unsigned requests were rejected on every one.
  • Payment amounts. Resolved server-side from the stored record, never read out of the request.
  • Prompt-injection scoping. Every AI tool scopes to the business id derived from the session, not to an argument the model supplies. That server-side scoping is the containment that actually matters.

What I found

Two high, seven medium, eight low. I have described both highs the way you would want them described if you were checking your own app, which means no file paths and no reproduction steps.

High · reproduced

A confirmed booking on a contact nobody verified

The booking assistant would create a confirmed booking against an email address or phone number that had never been verified. The one-time-code requirement did exist. It existed as English prose, inside a prompt. No code path anywhere checked that verification had actually succeeded before the booking was written.

That is worse than calendar clutter, because the confirmation then goes out from the business's own sender to somebody who never asked for it.

Check in yours: take every rule you wrote into a prompt and ask where the code enforces it. If the answer is "the model has been told to", it is not enforced. It is a suggestion with good manners.

High · five-minute fix

One environment string, and the signature checks stop mattering

A single environment string, left at the value it ships with, made the webhook verifiers fail open. Unsigned messages accepted. Every signature check above it, all four of them correct, rendered decorative by one line of configuration.

I proved it by inversion: the same request is rejected under production-like settings and accepted under the shipped default. Five minutes to fix.

Check in yours: search for any verification that skips itself in development or test mode, then go and look at what your deployed environment is genuinely set to. Those two facts live in different places, which is the whole reason this survives.

Then the part that has nothing to do with security

The product's headline promise is that the AI never invents a slot — every time it offers is checked against the live calendar first. So I tested the promise. I sent the booking assistant 22 adversarial scheduling messages.

It handled 12 of the 22 categories correctly: already-booked slots, weekends, before opening and after closing, an appointment that would run past closing time, dates in the past, impossible dates like 30 February, and three contradictory reschedules inside one message. In three languages.

Same-day requests are where it fell over. Across those it offered roughly 20 individual times that were not bookable — already in the past, or inside the configured one-hour lead window. Asked at 16:21 for the earliest appointment, it answered "09:00 today" and put a confirm button underneath. Zero future-dated requests failed. Every single failure was same-day.

Two root causes, and they are unrelated to each other:

The past-time filter ran at day granularity instead of time-of-day. The engine knew what day it was — it correctly refused a request for last Monday — and then offered this morning.

Separately, a cap on the first page of generated slots meant that an almost-empty Friday came back as "completely full".

The second one is a revenue bug, and it is the one I would raise with whoever owns the P&L. A customer told you are full does not complain. They book somewhere else, and nothing in your analytics will ever show it happened.

Two of the medium findings — data retention and erasing a single customer record — are cheap to build before real customer data exists and expensive afterwards. That gap only widens.

The lesson

The tenant boundary — the thing everybody worries about, the thing every "is my AI-built app secure" thread is about — was solid under direct attack. What broke was the layer above it: business logic the AI tools skip precisely because the app still works without it.

Nothing errors. Nothing returns a 500. The thing just quietly does the wrong thing, politely, with a confirm button under it. That is the gap this kind of check exists for, and it is why "it works" and "it is ready" are two different sentences.

Run the same 36 points

The checklist I used on my own app is free, and it is the same one I use on other people's. If you would rather I ran it, that is the $199 check.

— Slavik. I ran it on my own app before I ran it on anyone else's.
Questions: hello@itworksbut.com