Write PoCs a Triager Can Run in Five Minutes
A researcher's guide to proof-of-concept that reproduces first try: curl over screenshots, marked placeholders, stated assumptions, and the PoC failures that bounce valid bugs.
The bug in your head is worth nothing. The bug the triager reproduces on the first try is worth full bounty on the fast timeline. Everything between those two states is your proof of concept, and it is the part of the report researchers most consistently underinvest in. A triager reads your PoC not as documentation of what you did but as a set of instructions they are about to execute against a live target, and the quality of that execution decides how fast you get paid and whether you get paid at all. Treat the PoC as the deliverable, because to the person on the other side of the queue, it is the only part that matters.
The PoC is the product
Everything else in a report is context for the PoC. The title, the summary, the impact statement, all of it exists to frame the thing the triager is going to run. When the PoC reproduces cleanly, the rest of the report is confirmation. When it does not, the rest of the report is a list of claims, and claims do not get paid on the fast timeline. The reports that get paid are the ones where a stranger can go from reading to reproducing without stopping to ask you anything.
So the standard for a PoC is not "does this document my finding." It is "can someone who has never seen my target reproduce this from the text alone, in one pass, with no access to my head." Every gap between those two is a place the triager lands in a different state than you did, fails to reproduce, and bounces the report back for clarification. Each round trip adds days and starts the severity conversation with everyone mildly annoyed.
Curl over screenshots
A screenshot of Burp proves you saw something. A curl command lets the triager see it too. That difference is the whole reason text beats images for anything involving a request:
- The triager can copy the request and run it, instead of transcribing it from a screenshot by pausing on the right frame.
- They can diff it against the API's real behavior, change one parameter, and confirm the boundary of the bug.
- They can hand the exact request to the program's engineers, who will fix what they can reproduce and deprioritize what they cannot.
Give the raw request and the raw response as text, trimmed to what matters. Full request so nothing is ambiguous, response cut to the lines that prove the point with ... for the rest. Nobody wants four hundred lines of JSON, and nobody trusts a screenshot of a status code.
POST /api/v3/transfers HTTP/1.1
Host: app.example.com
Authorization: Bearer <ATTACKER_TOKEN>
Content-Type: application/json
{"from":"<ATTACKER_ACCOUNT>","to":"<ATTACKER_ACCOUNT>","amount":-500}
HTTP/1.1 200 OK
Content-Type: application/json
{"status":"completed","new_balance":50500, ...}
That is a bug a triager can run in thirty seconds. The equivalent screenshot is a bug they have to rebuild by hand before they can even start.
Mark your placeholders clearly
The fastest way to break an otherwise perfect PoC is to leave the triager guessing which parts are literal and which they need to substitute. Pick one obvious convention and hold it throughout. Angle brackets and caps work: <ATTACKER_TOKEN>, <VICTIM_ID>, <YOUR_SUBDOMAIN>. Then, critically, tell them how to get each value.
# Replace these before running:
# <ATTACKER_TOKEN> - Bearer token from any free-tier account (DevTools > Network > any XHR > Authorization header)
# <VICTIM_INVOICE> - any invoice ID other than your own; IDs are sequential, so <your_id> + 1 works
curl -s "https://api.example.com/v2/invoices/<VICTIM_INVOICE>" \
-H "Authorization: Bearer <ATTACKER_TOKEN>"
The comment block is the difference between a PoC that runs and one that generates a clarification request. A placeholder with no instructions for filling it is a dead end the triager hits five minutes in, and now they are asking you where to get a token instead of confirming your bug.
State your environment assumptions up front
Most reproduction failures are not wrong steps, they are unstated preconditions. The steps work perfectly in the state you were in and fail in the state the triager is in, because you never told them what state to be in. Put the assumptions at the top, before the steps, where they cannot be missed:
- Account state. What tier, what role, what setup? "Attacker: any free account. Victim: any paid account with at least one saved invoice." If the bug needs a specific state (2FA disabled, onboarding incomplete, a pending invite), that is a prerequisite, not a detail.
- Feature flags and rollout. If the vulnerable feature is behind a flag, in beta, or only live for some cohort, say so. A triager on an account without the flag will fail to reproduce and reasonably conclude the bug is not real.
- Region and tenant. Some behavior differs by data region, currency, or tenant configuration. If you found it on the EU cluster, name the EU cluster.
- Timing and account age. Rate limits, trial windows, and fraud checks behave differently on a fresh account than an aged one. If the bug needs an account older than 30 days, that is load-bearing.
Stating a prerequisite reads as honesty and speeds validation. Hiding one, even by accident, reads as inflation once the triager discovers it, and they will discover it. Finding out a precondition from you costs a sentence. Finding it out despite you costs you a week and your credibility on the rating.
Self-contained scripts beat tool-dependent chains
A PoC that runs with tools every triager already has (curl, a browser, standard shell) reproduces on the first machine it touches. A PoC that assumes your exact Burp setup, a specific extension, a custom proxy configuration, or a niche CLI the triager has to install first, does not. Every dependency you add is a machine your PoC might not run on and a reason it gets set aside for the report that just worked.
When the bug genuinely needs tooling, ship it self-contained. A single script the triager can save and run beats a prose description of clicks to perform across three tools:
#!/usr/bin/env bash
# Race condition: single-use coupon redeems multiple times if fired concurrently.
# Prereqs: two free accounts. Fill in tokens below.
TOKEN="<ATTACKER_TOKEN>"
COUPON="SAVE50"
# Fire 20 redemptions concurrently; single-use coupon should allow exactly 1.
for i in $(seq 1 20); do
curl -s "https://app.example.com/api/coupons/redeem" \
-H "Authorization: Bearer $TOKEN" \
-d "{\"code\":\"$COUPON\"}" &
done
wait
echo "Now check the account's credit balance; expected 1x, actual observed 20x."
The triager saves that, sets one variable, runs it, and reads the balance. No tool to install, no clicks to reproduce, no ambiguity about timing. That is a race condition report that gets validated instead of one that gets a "cannot reproduce."
Video: when it helps and when it hides the bug
Video earns its place when timing, UI state, or a visual result is the point, a race you need to see happen, a rendered XSS payload, a UI that reveals data it should not. As a supplement to the text, it is useful. As the only PoC, it is a liability.
A video-only PoC cannot be copied, cannot be diffed against the API, cannot be handed to engineering, and forces the triager to scrub through minutes of cursor movement to transcribe a request by pausing frames. Worse, video can hide the bug: a smooth recording of a working exploit conceals the three retries off-screen, the pre-seeded state, the token pasted in from somewhere the video never shows. When the requests are in text, the triager sees exactly what was sent. When they are only in the video, they see what you chose to record. Lead with the text, use the video for the part that genuinely needs to be seen, and make sure every request in the video also appears as copyable text in the report.
The PoC failures that bounce valid bugs
Real bugs get closed as "cannot reproduce" all the time, and it is almost always the PoC, not the bug. The recurring culprits:
- Expired hardcoded tokens. You pasted your live session token straight into the PoC. By the time the triager runs it, the token is dead, the request
401s, and your critical looks like a broken report. Use marked placeholders with instructions for getting fresh values, never a token that expires. - Unlabeled race dependencies. The bug needs concurrent requests, but your steps read as a sequential list. The triager runs them one at a time, cannot reproduce the race, and bounces it. If a step is timing-dependent, label it in bold and ship a script that handles the concurrency, the same standard we apply when triaging any report where reproduction is subtle.
- Prod-only behavior in a staging PoC, or the reverse. The bug reproduces on production but you wrote the PoC against a staging host the triager cannot reach, or vice versa. Name the exact environment and confirm the PoC targets one the triager can actually hit.
- Unstated preconditions baked into your account. The step that "just works" for you relies on a setting you changed months ago and forgot. Reproduce your own PoC from a fresh account before you submit. If it fails, you found the missing prerequisite before the triager did.
- One report per variant instead of one strong report. The same root cause across five endpoints is one PoC with five demonstrated instances, which proves the flaw is systemic. Five separate reports get deduplicated into one payout minus the goodwill.
The self-check that catches most of these takes two minutes: open a fresh account or an incognito window, follow your own PoC to the letter without using anything in your head, and see if it reproduces. The gaps you find are the exact gaps the triager would have found, except now you fix them instead of losing a week to them.
The programs TRIAGERS™ triages for pay fast, and the single biggest reason is that clean, runnable PoCs get validated the same day they land; if you want to know which programs read reports this way, get in touch.