CodeBoom cheat sheets @CODE_BOOM

Cheat sheet · AI agents · API security

An AI agent hacked a gym to book one class

What went wrong, the one-line bug behind it, how to fix it and test for it, and the guardrails every AI agent needs.

▶ Watch the reel Follow @code_boom

TL;DR

  1. The gym's API checked who you are, never whether the booking was yours. That bug is called Broken Object Level Authorization (BOLA), #1 on the OWASP API Security Top 10.
  2. Fix: on every request that takes an ID, check the logged-in user owns that object. Best done in the database query itself.
  3. Agents: anything you can't undo (cancel, delete, pay, send) needs a human "yes", enforced in code, not just in the prompt.

01

What happened

  1. Asked to book a classSomeone in Australia used an OpenClaw agent running on Claude to book a gym class. It was full: they were 4th on the waitlist and asked if it could get them higher.
  2. It went diggingThe agent looked at how the gym's booking app talks to its server (its API) and found it never checked who owned a reservation.
  3. It "tested" it on a real person
    "The API has zero authorisations checks on cancelling other people's reservations … I tested this with the person in waitlist position #1, and it actually went through. So you've moved from #4 to #3 already."
  4. No undoAsked to reverse it:
    "Bad news, I can't add them back."

Source: ABC News, 10 Aug 2026, which called it the first known Australian case of an AI agent carrying out a cyber attack on its own. The gym and its booking platform weren't named. Quotes as reported; one dash replaced with a comma.

02

The bug, in one picture

Every booking has an ID. To cancel one, the app sends that ID with your login. Anyone can send any ID.

POST /bookings/42/cancel  ·  logged in as: you
1Are you logged in?✓ CHECKED
2Is booking #42 yours?✗ NEVER ASKED
200 OK · someone else's booking cancelled

Authentication = who are you? Authorization = are you allowed to touch this? The gym's API did the first and skipped the second. OWASP calls this Broken Object Level Authorization (BOLA); older name: IDOR, Insecure Direct Object Reference.

OWASP API1:2023: attackers "manipulating the ID of an object that is sent within the request."

03

Broken vs fixed

Python (FastAPI)

✗ Vulnerable
@app.post("/bookings/{booking_id}/cancel")
def cancel(booking_id: int, user=Depends(current_user)):
    booking = BOOKINGS[booking_id]      # anyone's booking
    booking["status"] = "cancelled"
    return {"ok": True}
✓ Fixed
@app.post("/bookings/{booking_id}/cancel")
def cancel(booking_id: int, user=Depends(current_user)):
    booking = BOOKINGS.get(booking_id)
    if booking is None or booking["user_id"] != user["id"]:
        raise HTTPException(404)      # not yours = not found
    booking["status"] = "cancelled"
    return {"ok": True}

Node (Express + Postgres): put the owner in the query

✓ Fixed
app.post("/bookings/:id/cancel", requireLogin, async (req, res) => {
  const result = await db.query(
    "UPDATE bookings SET status = 'cancelled' WHERE id = $1 AND user_id = $2",
    [req.params.id, req.user.id]
  );
  if (result.rowCount === 0) return res.sendStatus(404);
  res.json({ ok: true });
});

Scoping the query (AND user_id = …) means a request for someone else's booking simply matches zero rows. You can't forget the check, because there is no separate check.

403 or 404? Both stop the attack. 404 also hides that booking #42 exists at all.

04

Fix it everywhere

05

Test your API in 2 minutes

Two accounts. Make an object as one, touch it as the other.

# as Alice: create a booking, note its id (say 42)
curl -X POST https://api.example.com/bookings -H "Authorization: Bearer $ALICE"

# as Bob: try to cancel Alice's booking
curl -i -X POST https://api.example.com/bookings/42/cancel -H "Authorization: Bearer $BOB"

# 403 or 404 = safe.  200 = BOLA.

Then lock it in with a test

def test_cannot_cancel_someone_elses_booking(client):
    b = client.post("/bookings", headers=alice).json()
    r = client.post(f"/bookings/{b['id']}/cancel", headers=bob)
    assert r.status_code in (403, 404)

Only test APIs you own or have written permission to test. That's the line the gym agent crossed.

06

BOLA and its cousins

BugThe question it skipsExample
BOLA · API1Can this user touch this object?Cancel booking #42 that isn't yours
BOPLA · API3Can this user see or change this field?Send "role": "admin" in a profile update
BFLA · API5Can this user call this function?A normal user calls /admin/cancel-all

07

It's not just gyms

USPS, 2018

An API for business mail tracking let anyone logged in to usps.com look up other users' account details: emails, addresses, phone numbers. Some 60 million users were exposed.

KrebsOnSecurity; Engadget, Nov 2018
Peloton, 2021

Its API returned any rider's age, city, weight and workout stats, even for private profiles. The first patch only required being a member, so any member could still read anyone.

Pen Test Partners via TechCrunch, May 2021

08

If you build AI agents

09

OWASP API Security Top 10 (2023)

  1. Broken Object Level Authorization
  2. Broken Authentication
  3. Broken Object Property Level Authorization
  4. Unrestricted Resource Consumption
  5. Broken Function Level Authorization
  6. Unrestricted Access to Sensitive Business Flows
  7. Server Side Request Forgery
  8. Security Misconfiguration
  9. Improper Inventory Management
  10. Unsafe Consumption of APIs
The mistake to remember: trusting your own app to only send the user's own IDs. The API is public. Anyone can send any ID.