A vulnerability report is not a bug ticket
SECURITY.md is a front door, not a disclosure process. If your project cannot explain what happens in the first 24 hours after a real vulnerability report, you do not have a process — you have an inbox.
It is 16:47 on a Friday.
The report does not arrive in the security inbox. It arrives in a public GitHub issue, or worse, in the community Slack, or a DM to whoever was last active on the repo. It is written by someone who clearly knows what they are doing. There is a payload. There is a reproduction. There is a version number, and it is the version you shipped to production last week.
And it is public.
By 17:05 you have three problems, not one. There is a vulnerability. There is a disclosure control failure. And there is a small group of people trying to decide, in real time, who is allowed to read the thread, who owns the fix, and whether the person who found it is going to publish on Monday regardless of what you say.
That last part is not a security problem. It is an operations problem.
SECURITY.md is the front door, not the process
Most projects that “have disclosure” have a SECURITY.md with an email address and a sentence about responsible disclosure. Maybe a PGP key. Maybe a 90-day quote lifted from a blog post.
That is a front door. A door is not a building.
CNCF published a handling vulnerability reports recipe card in September 2026 aimed exactly at small and medium projects without a security team. It is good guidance, and it is honest about scope: it covers the policy artifacts. The hard part is not the policy text. The hard part is that a policy document does not decide who gets woken up, who has write access to the private fix branch, or who signs off on the advisory wording.
GitHub gives you private vulnerability reporting and repository security advisories with private forks, private discussion, CVE requests, and coordinated publication. OpenSSF’s vulnerability disclosures working group publishes templates and runbooks. CISA has a disclosure policy template. FIRST has an entire PSIRT services framework.
The tooling exists. The templates exist. And teams still improvise the moment a real report lands, because none of that tells you who owns triage at 16:47 on a Friday.
My take:
a vulnerability report is not a bug ticket. It is an operational event with a clock attached.
The clock is the entire point. A bug can sit in a backlog for a quarter. A vulnerability report starts an embargo, and an embargo is a countdown that a stranger is holding.
the first 24 hours decide everything
The first 24 hours determine whether this becomes engineering work or organizational panic. Panic looks like: a dozen people pulled into a call, three half-fixes started in parallel, a draft advisory circulating in a doc that is not access-controlled, and public speculation while nobody has confirmed impact.
The output of the first 24 hours should be small and boring:
- Acknowledged. The reporter knows a human received it, and knows approximately when they will hear more.
- Contained. The report is in a private channel, not a public issue, and comments are frozen in the public place if it leaked there.
- Owned. One named person owns the report until it closes. Not a team. A person.
- Classified. Initial severity, affected versions, and exploitability — provisional is fine, unstated is not.
- Scheduled. A tentative disclosure date, communicated to the reporter.
If you cannot produce those five things within a day, your SECURITY.md is marketing.
the intake contract
Intake is where most processes quietly fail, because the reporting path is whatever channel the reporter happens to find. You cannot fix that by asking nicely. You fix it by making the intended path obviously the best one.
- One canonical intake. Private vulnerability reporting enabled on the repo, plus a monitored security address for people who do not want to use GitHub. Both routed to the same place.
- Say what happens next on the page. “We acknowledge within 3 business days, we triage within 5, we will tell you our severity assessment and target disclosure window.” A reporter who knows the timeline argues less about the timeline.
- Require evidence, not drama. Affected version or commit, component, reproduction steps or PoC, observed impact, and whether the reporter has shared it anywhere else. That last question matters more than people expect.
- Restrict the audience from the start. A security alias that forwards to a 40-person engineering mailing list is not a private channel. It is a leak with extra steps.
- Assume reports will land in public anyway. Someone will file the issue publicly. Have a canned response that moves the conversation without confirming exploitability, and a moderator who can lock the thread fast.
The intake contract is not about gatekeeping researchers. It is about making sure the first person who reads the report is the right person.
the triage contract
Triage is where “vulnerability” and “bug” get separated, and it is the step AI is currently making harder.
A working triage decision answers five questions:
- Is this a vulnerability or a robustness bug? A crash on malformed input is not automatically a security issue. An auth bypass is, even if the user must already be authenticated.
- What is the impact if exploited? Confidentiality, integrity, availability, or privilege boundary crossed. Be specific about which boundary.
- How exploitable is it really? Does it require a local attacker, an authenticated tenant, a non-default configuration, or a specific race window? Severity without exploitability context is a number nobody can act on.
- Which versions are affected, and which are supported? Fixing an unsupported version is a gift, not an obligation. Say so early.
- What does the reporter hear, and when? A triaged report always gets a reply with the assessment, even when the assessment is “not a vulnerability, here is why.”
That last point is the one teams skip, and it is the cause of most hostile disclosures. Researchers escalate when they are ignored, not when they are told no.
the embargo contract
An embargo is not secrecy. It is a coordinated agreement with a deadline, and it should have the smallest useful group of people inside it.
- Smallest useful group. Everyone who must act, nobody who is merely curious. Product managers who are not shipping a mitigation do not need the PoC.
- Write the timeline down. Report date, triage date, fix target, disclosure date. Everyone in the thread can see the same clock.
- Know what confidentiality means in practice. Private advisory or private fork instead of a public PR. No screenshots of the exploit in a shared doc. No vague “we have an issue” in the incident channel that turns into a game of twenty questions.
- Decide the default window before you are under pressure. 90 days is a convention, not a rule. Some projects use 7 days for actively exploited issues and 30 for everything else. What matters is that the number is written down and defensible.
- Handle disagreement explicitly. If the reporter wants to publish early, or you need an extension, that is a negotiation with a stated reason — not silence and not a legal threat.
The embargo ends when the fix is available, or when the deadline arrives and it is not. Both are allowed. Pretending the deadline does not exist is how you end up reading about your own bug on a security news site with no patch ready.
the fix and release contract
This is where disclosure becomes ordinary engineering, with two differences: the branch is private, and the communication is public.
- Private fix path. Private fork or advisory branch, minimal diff, and reviewers who were explicitly added. No drive-by refactors while the clock runs.
- Focused review. Security fixes get a second pair of eyes from someone who can evaluate the boundary being crossed, not just the code style. Reviewers outside the embargo circle do not get the context, and that is fine.
- Test evidence. A regression test that fails before the fix and passes after. If you cannot write it, say why in the advisory.
- Fixed version plus mitigation. Not every team can upgrade immediately. Backports, config workarounds, feature flags, and WAF rules are all legitimate. An advisory that says “upgrade” and nothing else gets ignored by everyone who cannot.
- Advisory, CVE, and user notification. GHSA draft, CVE requested when the ecosystem needs it, severity with the exploitability caveats from triage, affected versions, credits to the reporter, and a link to the fixed release.
- Tell your users where they actually look. Release notes, a mailing list, a security page, an RSS feed, a Slack or Discord announcement. Publishing the advisory is not notifying users.
The measure of a good fix-and-release contract is the person running your software in production who finds out what to do without reading a research write-up.
the AI wrinkle
The AI-driven shift in vulnerability discovery is real, and it cuts both ways.
More reports are now generated by models pointed at codebases, and some of them are genuinely good: real bugs, real reproduction steps, reachable paths that a human reviewer missed. Others are confident, well-formatted, plausible-sounding findings with no reachable impact — a stack trace, an invented threat model, and a severity score chosen for drama.
Two things change:
Triage volume goes up without triage quality going up with it. A process that assumed a few thoughtful reports a month now gets a stream of synthetically detailed ones. That is fine as long as triage is evidence-based: does the repro actually work against a supported version? Is the code path reachable in a default deployment? Is the described impact real, or does it assume an attacker who already has the access the finding claims to escalate?
False positives get expensive because they look like real work. Every convincing-but-wrong report costs an engineer a day. Track that cost. If a share of your inbound reports never reproduce, that is a process input, not an annoyance — it tells you where your intake needs a reproduction requirement enforced harder.
The upside is equally real. AI-assisted review is genuinely good at finding the class of bug that humans skim past: missing authorization checks, unsafe deserialization, off-by-one in parsing, weak validation at boundaries. The maintainers who benefit are the ones whose triage process can absorb a higher volume without lowering the bar.
Which is the whole point of an operations contract. It is the thing that lets you say “this is credible” or “this is not” quickly, and move on either way.
a practical checklist for maintainers
SECURITY.mdpoints to one canonical private intake channel and states response timelines.- Private vulnerability reporting is enabled on every actively maintained repo.
- One named owner for triage, with a documented backup.
- A severity assessment template: impact, exploitability, affected versions, default-config reachability.
- A written disclosure window with defaults and an escalation path for active exploitation.
- An embargo list discipline: smallest group, audited access, no public forks or PRs.
- A private fix procedure: advisory branch, focused reviewers, regression test required.
- A release procedure that ships the fix, the advisory, the CVE, the mitigation, and the notification.
- A public-leak playbook: canned response, thread lock, move to private, no confirmation of exploitability.
- Credit and thank reporters, including the ones who file a false positive in good faith.
- A quarterly game day: walk a synthetic report through the whole path and time it.
None of this requires a security team. It requires someone to write down the decision that would otherwise be made by five people in a panic under time pressure.
A bug ticket asks “what should we build next.” A vulnerability report asks “what do we do in the next 24 hours, and who decides.” Different queues, different clocks, different consequences.
Write the contract before the reporter finds the door.
References
- CNCF/TAG Security: Handling vulnerability reports — recipe card
- CNCF: The AI-driven shift in vulnerability discovery
- OpenSSF: Vulnerability Disclosures Working Group repo
- OpenSSF: Vulnerability Disclosures group overview
- GitHub Docs: Repository security advisories
- GitHub Docs: About privately reporting a security vulnerability
- CISA: Vulnerability disclosure policy template
- FIRST: PSIRT Services Framework v1.1
To test my projects, I use Railway. If you want $20 USD to get started, use this link.