A triager may read forty reports in a day. Yours competes for attention with all of them. The reports that get paid quickly are the ones where reproduction takes under five minutes and the impact is obvious by the third paragraph.
Lead with a one-line summary
The title and first line decide how your report is queued. Write it as <bug class> in <component> allows <who> to <do what>.
- Weak: "XSS found on your site"
- Strong: "Stored XSS in the support ticket subject field allows any user to run script in an agent's session"
Name the exact asset
Give the full URL, the HTTP method, the parameter, and the account role needed. If the bug is in a mobile app, give the package name and build number. If it is in an API, give the version prefix. "The dashboard" is not an asset.
Write steps a non-expert can follow
Number them. Assume the reader has a fresh browser and your attached files, and nothing else. State which of your test accounts is acting at each step. Include the exact payload as inline code, not as a screenshot of code, so it can be copied.
A step is too vague if it contains "then intercept the request and modify it as needed". Say which field, to what value.
State impact, not a CVSS string
The triager will compute the score themselves. What they cannot compute is what the affected system holds. Answer three questions in plain sentences: who can do this, what do they get, and what stops them. "Any authenticated free-tier user can read invoices belonging to any other tenant, including billing addresses, by changing one numeric ID" is worth more than a vector string. See severity and rewards for how that gets scored.
Evidence: requests beat screenshots
Attach the raw HTTP request and response, trimmed of your session cookie. A screenshot proves you saw something. A request proves the server did something, and it can be replayed.
Video helps for timing-dependent or multi-window bugs, but never as the only evidence. Never include another person's data: redact it, and prove the bug with your own test accounts instead. That boundary is part of safe harbor.
Suggest a fix
One or two sentences. Name the layer where the fix belongs, not just the symptom: "authorize on the server by comparing the record's tenant_id to the session's tenant, rather than hiding the control in the UI". You are not expected to be right, and a wrong suggestion does not hurt your report. A plausible one speeds up the handoff to engineering.
Report template
## Summary
<Bug class> in <component> allows <attacker role> to <impact> .
## Asset
- URL/endpoint: <https://target.example/api/v2/...>
- Method and parameter: <POST, body field `invoice_id`>
- Account required: <none | free tier | admin>
- Tested on: <date, build/commit if known>
## Steps to reproduce
1. Log in as test account A (<a+test@researcher.example>).
2. Navigate to <...>.
3. Send the following request:
POST /api/v2/... HTTP/1.1
Host: target.example
X-Sentinel-Researcher: <handle>
{"invoice_id": "<B's id>"}
4. Observe: <the exact observable result>.
## Impact
<Who can do this, what they get, what currently stops them.>
## Evidence
- `request-response.txt` (raw, cookie redacted)
- `poc.html` (self-contained, no external calls)
- Optional: `demo.mp4`
## Suggested fix
<One or two sentences, at the right layer.>
## Cleanup
<Test data created and removed, records accessed, all deleted on YYYY-MM-DD.>
Why reports get closed
| Closure | Common reason | How to avoid it |
|---|---|---|
N/A |
Asset not in the program's scope list | Check scope on the program page in the directory before testing |
N/A |
Finding type is on the exclusion list | Read the exclusions, not just the scope |
N/A |
Not reproducible from the steps given | Reproduce from a clean session before submitting |
Informative |
Missing header or config with no demonstrated attack | Show the attack it enables, or do not file it |
Informative |
Self-XSS, or requires the victim to attack themselves | Find a delivery path first |
Informative |
Theoretical impact only, scanner output pasted in | Prove the consequence with your own accounts |
Duplicate |
Same root cause already reported | Nothing to do, timestamps decide |
Informative |
Rate limiting or enumeration with no shown consequence | Demonstrate account discovery or credential stuffing impact |
The first three are the ones fully inside your control, and together they account for most closures. The fix is boring: read the policy, reproduce twice, attach raw traffic. See getting started for the order of operations, and disclosure for what happens after resolution.