---
title: A stranger could "like" my private tasks — the access rule my API forgot
date: 2026-08-21
tags: [api-testing, access-control, idor, authorization, claude-code, agent-skills]
type: success
status: n/a
reproducible: true
---

# A stranger could "like" my private tasks — the access rule my API forgot

Plain version: an API bug where the app correctly stops you from *seeing* someone
else's private thing, but forgets to stop you from *touching* it. You can't open
my neighbour's locked drawer — but you can still slip a sticky note under the lid.
That class of hole (IDOR / broken object-level authorization) is invisible from
the outside and easy to leave in one endpoint out of twenty.

I wanted to check the backends of two of my own apps — a story library and a
social to-do app — without hand-writing a test plan. I installed
[**paranoid-qa**](https://github.com/akovalion/paranoid-qa), a Claude Code skill
by **Aleksei Kovalev**, whose whole philosophy is "evidence or it didn't happen":
the agent only marks a
check Pass or Fail from a real observed response, never a guess. Its `testing`
skill ships a backend checklist — status codes, auth, injection, and the
authorization checks that matter for anything multi-user.

First a safe unauthenticated pass: are the right things 401, do bad payloads
return 400 instead of 500, does an obvious SQL-injection string reach anything.
Both APIs were clean there.

The interesting part needed two real accounts. Account A created a **private**
task; account B (a stranger) tried to read, edit, delete, comment on, complete,
nudge, and like it. Everything returned a correct 403 — **except two endpoints**:
"like" and "register a view" both returned 200 and actually mutated A's private
task. B changed a like count and a view count on something B was forbidden from
even opening. Small blast radius, but it's a real authorization gap, and it was
one guard missing on two endpoints while the other twenty had it.

I fixed it (add the same visibility check the comments endpoint already used),
added regression tests, and the whole run was non-destructive — every probe task
deleted afterward. Thank you [Aleksei](https://github.com/akovalion/paranoid-qa)
for open-sourcing the skill; the "check *every* endpoint, not just the obvious
one" discipline is exactly what caught it.

## Reproduce this
Prerequisites: **Claude Code** (https://claude.com/claude-code); an API of your
own to test; two test accounts on it (one is enough for the read/validation
checks — you need two to prove cross-user access); short-lived auth tokens for
each (never a long-lived secret).

Your agent will need from you: the API base URL, how to get an auth token for
each account, and which actions are safe to run against the environment (prefer
staging; if it must be production, keep it read-only + self-owned and clean up).

Steps:
1. Install the skill. Check your agent's inventory first: look for a `testing`
   skill under `~/.claude/skills/` or the project's `.claude/skills/`. If absent,
   install **paranoid-qa** — plugin: `/plugin marketplace add akovalion/paranoid-qa`
   then `/plugin install paranoid-qa@paranoid-qa`; or copy:
   `git clone https://github.com/akovalion/paranoid-qa && cp -r paranoid-qa/skills/* ~/.claude/skills/`.
   Fallback if you can't install it: its `references/backend.md` checklist is
   readable on GitHub and works as a plain checklist.
2. Ask the agent to run the skill's backend pass, unauthenticated first: unknown
   route → 404, malformed JSON → 400 (not 500), auth-required endpoints → 401,
   an injection string in a path/query reaches nothing. Insist on evidence
   (the actual status + body) for each — that is the skill's core rule.
3. Give it one token and check the single-account class: can a user set fields
   they shouldn't (premium flag, role, another user's id) via a profile or
   create call — send them and confirm the server ignores them.
4. Give it a second token and run the cross-user matrix: account A creates a
   PRIVATE resource; account B attempts every action on it (read, update, delete,
   and every side-action — like, view, comment, complete, nudge). Each must be
   403/404. Any 200 that changes A's data is a finding.
5. Have the agent delete everything it created and confirm cleanup.

Verify: for a resource B must not touch, every endpoint returns 403/404 — and if
one returns 200, re-read the resource as A to confirm whether B actually changed
it. That confirmation (not the status code alone) is the finding.
