A SECURITY.md vulnerability-disclosure policy with a real reporting contact is committed to the repo
Why it matters
This is an evidence-pointer check: it verifies committed evidence of the control, which is what an auditor will ask for. SOC 2 CC2.3 requires the entity to communicate with external parties about matters affecting internal control, and a vulnerability-disclosure policy is the cheapest CC2.3 evidence a repository can carry: a SECURITY.md naming a working reporting contact plus instructions for what to send. AI coding tools scaffold READMEs, licenses, and CI configs but essentially never generate a disclosure policy, and template repos that do include one often ship it with security@example.com placeholder text, so vibe-coded apps routinely reach production with no intake channel at all. The consequence is concrete: a researcher who finds a hole and cannot locate a contact either gives up or discloses publicly, and when the SOC 2 auditor asks how external parties report security issues, there is nothing to point at.
Severity rationale
Low because a missing disclosure policy is an absent evidence artifact and intake channel, not itself an exploitable weakness: the mechanism of harm is slower researcher-to-vendor communication and a missing auditor deliverable, not direct compromise.
Remediation
Commit a SECURITY.md at the repo root (or .github/SECURITY.md) with a real contact and reporting instructions, for example:
# Security Policy
## Reporting a Vulnerability
Email security@yourdomain.com, or use GitHub private vulnerability
reporting on this repository ("Report a vulnerability" under the
Security tab). Include the affected endpoint or file, reproduction
steps, and expected impact. We acknowledge reports within 5 business days.
If the app is deployed, additionally publishing /.well-known/security.txt (see https://securitytxt.org) gives researchers a machine-findable contact; the SECURITY.md can link to it as the canonical channel.
Detection
- ID:
security-md-disclosure-policy - Severity:
low - What to look for: A vulnerability-disclosure policy file, case-insensitive, at any of:
SECURITY.md(repo root),.github/SECURITY.md,docs/SECURITY.md(also accept.txt/.rstextensions and a monorepo root-level copy). Same locations apply regardless of language: JS/TS, Python, and Go repos all use these GitHub-convention paths. Inside the file, look for (a) a reporting contact: an email address, anhttps://URL to a security.txt page, bug-bounty program (HackerOne, Bugcrowd, Intigriti), or advisory/reporting page, or an explicit statement that GitHub private vulnerability reporting is enabled for the repo ("Report a vulnerability" / "Security Advisories"); and (b) reporting instructions: at least one sentence telling the reporter what to include or what happens next. A committedpublic/.well-known/security.txt(orstatic/.well-known/security.txt) with aContact:line counts as the contact channel if SECURITY.md references it. - Pass criteria: A SECURITY.md (or accepted variant path) exists AND contains a concrete contact per (a) that is not placeholder text AND contains reporting instructions per (b). GitHub private vulnerability reporting named as the channel passes; the file itself is the committed evidence.
- Fail criteria: No SECURITY.md at any accepted location. OR the file is a stub: heading only, empty sections, or under two sentences of content with no contact. OR the contact is a placeholder (
security@example.com,yourname@,TODO,INSERT EMAIL, lorem text, a dead template link likehttps://example.com/security). OR the file discusses supported versions only (the default GitHub template table) with the reporting section left as template text. Instructions without any contact, or a contact with zero instructions, both fail: half-wired policies are not an intake channel. - Skip (N/A) when: The project is a genuinely internal-only tool with no external users or distribution:
package.jsonhas"private": true(or no publishable package manifest), there is no deployment config (novercel.json,netlify.toml,Dockerfile,fly.toml, or hosting workflow in.github/workflows/), and the README describes an internal/personal tool. Quote:"Internal-only tool: private package.json, no deployment or distribution config found; no external parties to communicate with". - Before evaluating, quote: The SECURITY.md path plus the exact line(s) containing the contact and one line of the reporting instructions. If no file exists, quote the locations checked (root,
.github/,docs/) and the evidence of external users/distribution that makes the check applicable (deploy config path or public package manifest). - Report even on pass:
"SECURITY.md at <path>; contact: <email | URL | GitHub private vulnerability reporting>; instructions present". - Detail on fail:
"No SECURITY.md at repo root, .github/, or docs/; vercel.json present so the app is deployed with no vulnerability-disclosure channel; commit evidence of your reporting policy"or".github/SECURITY.md is the unedited GitHub template: supported-versions table plus 'Tell them where to go' placeholder text, no real contact". - Remediation: Commit
SECURITY.mdat the repo root with a real reporting email or URL (GitHub private vulnerability reporting is fine as the named channel) plus what to include in a report and an acknowledgment expectation. Optionally add a committedpublic/.well-known/security.txtper https://securitytxt.org and link it from SECURITY.md. Thepre-launchaudit covers the broader launch-communication surface this belongs to.
External references
- soc2:2017 · CC2.3 — Communication with external parties
- iso-27001:2022 · A.8.8 — Management of technical vulnerabilities
- nist:rev5 · RA-5(11) — Vulnerability Monitoring and Scanning | Public Disclosure Program
Taxons
History
- 2026-07-03·v1.0.0·Initial soc2-readiness v1 authoring, committed SECURITY.md disclosure policy as the repo-checkable CC2.3 evidence pointer.·by soc2-readiness-v1-build