September 30, 2026
Two Researchers Found the Same Bug. One Got Paid. Here Is What Differed.
Reproduce it cold, lead with the consequence, pre-answer the closure, and run the loop after the bounty lands.

By Nitin yadav
9 min read
Hello, I am Nitin.
Day thirty. The last one. And I want to finish with the part of this work that nobody makes videos about, because it is the part that decides whether the previous twenty-nine days were worth anything.
So let me tell you about two reports.
Same target. Same week. Two researchers, working independently, found the identical bug โ a stored XSS in a field on a shared record, rendering for anyone who opened it.
The first report arrived as a title that said "XSS found", a paragraph explaining that the researcher had discovered a critical vulnerability, a screenshot of a dialog box on a page, and a request to be paid quickly. There were no steps. The screenshot showed the researcher's own browser with a proxy interception banner across the top and half a dozen extensions in the toolbar. The field name was not mentioned anywhere.
It was closed in two days: unable to reproduce.
The second report opened with one sentence saying what happens to a victim: any user who opens a shared record created by another user executes script in their session, allowing the record's creator to perform authenticated actions as the viewer. Then five numbered steps, written so that steps one to three were things the attacker does and steps four and five were things the victim does โ and the victim's only action was to open the record. Then the exact request, copied as text. Then a thirty-second video recorded in a clean browser profile. Then a short impact paragraph that named which users could be reached and why that included the support team. Then two sentences of remediation. Then a line noting that the payload had been removed from both test accounts after recording.
It was triaged in four hours and paid at high severity.
It was the same bug. Every difference in outcome happened after the discovery, in the part of the work that most people treat as paperwork.
So today is that part. And then the pipeline, because a bug is an event and a pipeline is an income.
Part one: the proof
The triager's first question is never "is this serious". It is "does this reproduce", because a finding they cannot reproduce is one they cannot escalate, and a finding they cannot escalate is one they must close.
So build the proof for that question first.
Reproduce it cold before you write a word. New browser profile. No extensions. No proxy. Fresh account made minutes ago. If it does not fire under those conditions, you do not yet know what your bug depends on โ and finding out is part of the work, not an inconvenience. Half of all "unable to reproduce" closures are findings that quietly depended on something in the researcher's own setup.
Make the proof minimal. Not impressive โ minimal. The smallest thing that demonstrates execution in the relevant context. A visible, harmless marker is ideal. Resist every temptation to show off the full chain; a triager reading a weaponised script has to decide whether it is safe to run, and hesitation costs you days.
Make it non-destructive and say so. Nothing that deletes, emails, charges, notifies, or changes another account. Write one sentence confirming this explicitly โ it turns your report from something that must be reviewed cautiously into something that can be run immediately.
Give the exact request as text. Not a screenshot of it. Text they can copy. Screenshots of requests are the single most common reason a triager cannot reproduce something โ one invisible character, one wrong encoding, and it dies.
State the preconditions honestly. Which role, which plan tier, which feature flag, which browser, which state the account must be in. A hidden precondition that the triager discovers themselves reads as carelessness. The same precondition disclosed by you reads as rigour, and costs you nothing.
Record thirty seconds of video. Clean profile, visible address bar, the whole flow start to finish, no cuts. Video is not evidence โ the steps are the evidence โ but it answers "is this person confused" before the question is asked.
Part two: how to write it
A good report has one job: make the reader understand the consequence before they understand the mechanism. Everything below serves that.
The title states the outcome, not the technique. "Stored XSS in shared record description allows authenticated actions as any viewer, including support staff" beats "XSS in description field" by an enormous margin, because the first one has already made the severity argument in the only line guaranteed to be read.
The first line is what happens to a victim. One sentence. Who is affected, what they do, what happens to them. No preamble, no "while testing your application I discovered".
Steps are numbered, and the victim's actions are visibly separated from yours. This is the single highest-leverage formatting choice in the whole report. A reader needs to see at a glance how much the victim has to do, because that is what they are mentally computing while they read. If the victim's only contribution is opening a page, make that impossible to miss.
Impact is concrete and bounded. Name the capability and who it reaches. Do not inflate โ "complete compromise of all users" for a bug that needs a specific role will lose you the argument and some credibility with it. Do not deflate either. If the reader of the injected content is likely to be privileged, say so plainly, because the triager may not know their own product's usage patterns as well as you think.
Pre-answer the closure you expect. You usually know which one is coming: self-XSS, requires interaction, requires an unusual role, already mitigated by the content policy. Put one sentence in the report that addresses it directly, with evidence. This single habit has changed more of my outcomes than any technique in this month.
Suggest remediation in two sentences, and be right. Name the encoding or the sink change, not a generic instruction to sanitise. A developer who reads a correct, specific fix suggestion treats the rest of your report as credible. One who reads "please add input validation" does not.
Say what you cleaned up. Payload removed, accounts affected, anything installed and now removed. This sentence costs you nothing and is read by the security team as a signal about every future report you will send them.
Part three: arguing severity without becoming a problem
You will sometimes be rated lower than you believe is right. Here is how to handle it in a way that works.
Understand what actually moves a rating. Not payload sophistication. Three things: reach (how many people, how much interaction), the privilege of whoever reads the injected content (day twenty-five's entire argument), and persistence (whether the compromise survives remediation, from day twenty-eight). If you want a higher rating, produce evidence on one of those three axes. Nothing else moves the number.
Push back once, with new information, politely. One message. New evidence, not a restatement of your feelings about the first rating. "Here is a second account, non-privileged, reaching the same render path" is new information. "I believe this is more serious than medium" is not.
Then accept the outcome. Programmes have context you do not have โ compensating controls, planned deprecations, internal architecture. Arguing twice marks you, and being marked costs more over a year than any single rating.
Do not weaponise the threat of disclosure. It ends relationships and, depending on where you are, it can end more than that. The entire value of this career is being someone teams are glad to hear from.
Part four: the loop nobody runs
This is where the money actually is, and almost nobody does it, which is exactly why it works.
Retest the fix. When it lands, test it properly. Fixes are written under time pressure by someone who has read your report and not much else. A surprising share are incomplete โ one context handled and not another, one encoding function applied on one path.
Hunt the variant. The fix is new code. New code is unreviewed code. Go at it with the context work from day one: the fix probably handles the context it was shown, and may not handle the others.
Hunt the sibling. A developer who made this mistake here made it elsewhere. Find the pattern โ the helper function, the component, the template idiom โ and search the whole application for it. One finding routinely becomes four this way, and the fourth takes ten minutes.
Hunt the class. If the root cause is a library, a framework idiom, or a design pattern rather than one developer's slip, the same bug exists on other targets. This is how a single good afternoon turns into a quarter of findings.
Write it down while it is warm. Not for the blog โ for yourself. What you tried, what failed, which signal told you to keep going. Your notes from six months ago are the closest thing to a superpower this field offers.
Part five: the pipeline
A repeatable pass, built from this month. This is the order I actually work in.
Map before you test. Enumerate hosts and certificate transparency records. Pull the front-end bundles and read them. Catalogue sinks, framework escape hatches, renderers, message listeners, and anything that looks like a sanitiser. You are building a list of places where untrusted data meets markup, before you have sent a single payload. Days nine, ten, eleven and twelve.
Probe with markers, not payloads. Push a harmless distinctive string through every input and find where it lands. Read the context of each landing โ body, attribute, script, template, JSON, URL. Contexts decide everything; payloads are a consequence. Days one and two.
Work the contexts you found. Attribute breakouts, script blocks, URL handling, DOM sources and sinks, listeners, clobbering, pollution. Days three through eight, and thirteen and fourteen.
Characterise the defences before fighting them. Fingerprint the sanitiser. Read the content policy and check the directives everyone forgets. Isolate what the filter actually objects to instead of throwing lists at it. Days fifteen through twenty-one.
Cover the surfaces people skip. Uploads and the document formats they accept. Blind paths into admin and support tooling. Model-generated output rendered as markup. Days twenty-two through twenty-six.
Escalate whatever you found. Delivery routes for self-XSS. The token-lifting primitive for takeover. Amplifiers for reach. Days twenty-seven through twenty-nine.
Then write it the way today describes, and run the loop.
On automation, briefly. Automate discovery โ host enumeration, bundle fetching, marker injection, change monitoring. Never automate the decision about whether something is a finding, and never fire payloads broadly at a live application. The judgement is the job; the crawling is just typing.
The month, in eight lines
If you remember nothing else from these thirty days:
Context decides everything. Find where your input lands before you decide what to send.
Markers before payloads. A harmless string tells you more than a hundred payloads.
Read the code. The bundle is sitting right there, and almost nobody opens it.
Sanitisers are parsers, and parsers disagree. That disagreement is the bug.
A blocked payload is information. Isolate what was objected to instead of guessing louder.
Who reads the content decides the severity โ not how clever the injection was.
Self-XSS is a delivery problem, and the http-only flag is not a wall.
Severity is reach. When the rating disappoints you, go find the multiplier.
Impact ladder โ for your reports, not your bugs
- Closed as unable to reproduce. No cold test, screenshot instead of text, hidden precondition. Entirely preventable, and painfully common.
- Triaged slowly, rated conservatively. Mechanism explained before consequence; reader has to work out the impact themselves.
- Triaged quickly at the obvious rating. Clean steps, clean proof, honest impact. This is a good report and most people never get here.
- Rated above the obvious. You supplied evidence on reach, reader privilege, or persistence, and pre-answered the closure the triager was reaching for.
- You become someone they trust. Fast triage by default, benefit of the doubt on ambiguous findings, invitations to private programmes. Built from clean-up sentences, correct fix suggestions, and never arguing twice. This is the actual asset.
Conclusion โ steal this checklist
- Two identical bugs. One closed as unable to reproduce, one paid at high. Every difference happened after the discovery.
- The triager's first question is not "is this serious" โ it is "does this reproduce". Build the proof for that question.
- Reproduce it cold first: new profile, no extensions, no proxy, fresh account. Half of all non-reproducible closures are findings that depended on the researcher's own setup.
- Minimal beats impressive. A harmless marker in the right context. Nobody wants to run your weaponised chain.
- Give the request as copyable text, never a screenshot. One invisible character kills a report.
- State preconditions yourself. Disclosed by you it reads as rigour; discovered by them it reads as carelessness.
- The title states the outcome, not the technique. It is the only line guaranteed to be read.
- First line: what happens to a victim. Numbered steps, with the victim's actions visibly separated from yours.
- Pre-answer the closure you expect. One sentence with evidence. This habit changes more outcomes than any payload.
- Suggest the fix in two sentences and be right about it. A correct fix makes the whole report credible.
- Say what you cleaned up. It costs nothing and it is read as a signal about every report you will ever send.
- Only three things move a rating: reach, the privilege of whoever reads the content, and persistence. Bring evidence on one of those or accept the number.
- Push back once, with new information, then stop. Never threaten disclosure.
- Run the loop nobody runs: retest the fix, hunt the variant, hunt the sibling, hunt the class. One finding routinely becomes four.
- Automate discovery. Never automate the judgement โ the judgement is the job.
- And the month in one line: context decides everything, markers before payloads, and severity is reach.
That is thirty days. Start at day one on a target you are allowed to test, and work the pipeline. See you in the next batch.
If you Love reading my blogs. Check my Youtube Channel too.