|
| 1 | +# Proposal: Switch Security Reporting to GitHub Private Vulnerability Reporting |
| 2 | + |
| 3 | +Author: [Vadim Bauer](https://github.com/Vad1mo), [Orlin Vasilev](https://github.com/OrlinVasilev), He Yi, Prasanth Baskar/[bupd](https://github.com/bupd) |
| 4 | + |
| 5 | +Discussion: [Harbor community meeting 8 July 2026](https://hackmd.io/CyQk5FdVQwWObMLVNqxW1w?both#July-8-2026) |
| 6 | + |
| 7 | +## Abstract |
| 8 | + |
| 9 | +Replace Harbor's email-based security intake (the CNCF groups.io list `cncf-harbor-security@lists.cncf.io`) with GitHub's private vulnerability reporting and repository security advisories, consolidating intake, triage, embargoed fix development, CVE assignment, and disclosure into one advisory workflow. Distributor coordination and disclosure timing are unchanged. |
| 10 | + |
| 11 | +## Background |
| 12 | + |
| 13 | +Today a reporter emails the security team with name, affiliation, repro, and PoC. The team replies within three business days, scores CVSS by hand, may file a manual MITRE CVE request, notifies `cncf-harbor-distributors-announce` under embargo, negotiates a disclosure date (~14 business days for critical issues), and finally publishes a GitHub advisory plus patches. Maintainer pain points: |
| 14 | + |
| 15 | +- **Manual, fragmented workflow.** Triage, scoring, CVE tracking (a separate out-of-band MITRE request), and disclosure are stitched across email, MITRE, and GitHub, with the report re-transcribed into an advisory only at the end. |
| 16 | +- **Patches leak in the public repo.** The list has no code-hosting surface, so fixes get developed in the public repo (or ad-hoc private branches with no access barrier) where commits and diffs expose the bug before embargo lifts. |
| 17 | +- **Email is a hurdle for reporters and maintainers.** |
| 18 | + |
| 19 | +## Proposal |
| 20 | + |
| 21 | +Harbor adopts **GitHub private vulnerability reporting** as the sole intake channel and **repository security advisories** as the triage/fix/CVE/disclosure workflow across the org's public repositories, replacing the mailing list. |
| 22 | + |
| 23 | +Flow: a reporter opens the repo **Security** tab and clicks **Report a vulnerability**; a maintainer accepts it as a draft advisory; the fix is co-developed with the reporter in a temporary private fork; a CVE is requested from GitHub in-band; the maintainer publishes the advisory. |
| 24 | + |
| 25 | +### Advantages over the mailing list |
| 26 | + |
| 27 | +- **In-repo intake, no address to find.** With private reporting enabled, a reporter files from the repo **Security** tab (only title and description mandatory; GitHub account required) ([report privately](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-privately)). |
| 28 | +- **Structured form, not a blank email.** The form prompts for affected products (ecosystem, versions, vulnerable functions), CWE, description, and a severity dropdown with a built-in CVSS calculator. Caveat: fields are optional and there is no PoC field, so omissions are still possible ([report privately](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-privately)). |
| 29 | +- **Report converts into the advisory, no re-transcription.** A submitted report creates a PROPOSED advisory; **Accept and open as draft** turns that same object into the draft worked on in-repo, accruing CVE, CVSS, and version data. Intake and final advisory are one artifact ([manage vulnerability reports](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/manage-vulnerability-reports)). |
| 30 | +- **Reporter auto-added as a scoped collaborator.** Discussion, fix, and credit live on one advisory; comments are visible only to the reporter and collaborators, giving them a private thread to follow. Caveat: docs do not confirm per-state-change notifications ([report privately](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-privately)). |
| 31 | +- **Patch built in a temporary private fork CI cannot see.** From the draft, a maintainer creates a fork (`repo-ghsa-xxxx-xxxx-xxxx`) where collaborators commit the fix and review PRs; integrations and CI cannot access it, so the patch stays private until disclosure. Caveat: fork creation, merge, and publish are distinct manual steps, not one "merge = ship" event ([temporary private fork](https://docs.github.com/en/code-security/security-advisories/working-with-repository-security-advisories/collaborating-in-a-temporary-private-fork-to-resolve-a-repository-security-vulnerability)). |
| 32 | +- **Per-advisory access with auto-cleanup.** An advisory admin adds named users/teams to a single advisory only (no main-repo or cross-advisory access); collaborators are auto-removed when they leave the org. Caveat: write-only, no read-only tier ([adding a collaborator](https://docs.github.com/en/code-security/security-advisories/working-with-repository-security-advisories/adding-a-collaborator-to-a-repository-security-advisory)). |
| 33 | +- **In-band CVE via GitHub as CNA.** A maintainer requests a CVE inside the advisory instead of filing a MITRE form; GitHub reviews (usually within 72h) and reserves a CVE bound to the record, published to MITRE only when the advisory publishes. Caveat: eligibility-gated, not one-click (GitHub cannot assign if another CNA covers the component) ([about repository security advisories](https://docs.github.com/en/code-security/security-advisories/working-with-repository-security-advisories/about-repository-security-advisories)). |
| 34 | +- **Built-in CVSS + single publish gate.** The calculator (v3.1/v4.0) stores a structured severity field; the advisory stays a private draft until published, and publishing feeds the GitHub Advisory Database and Dependabot via structured version metadata ([create repository advisory](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/create-repository-advisory)). Caveat: Advisory Database inclusion is reviewed (up to 72h) and Dependabot reaches only consumers of affected Harbor **Go modules**, not container/OCI distribution. |
| 35 | +- **Formal credit + queryable audit trail.** Structured Credits (nine roles) with reporter accept/decline, carried into the published GHSA; the org audit log records `repository_advisory`/`advisory_credit` actions, timestamped, actor-attributed, exportable ([audit log events](https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/audit-log-events-for-your-organization)). |
| 36 | +- **Org-level rollout.** Org owners/security managers can enable private reporting across all public repos and default it for new ones ([configuring PVR for an organization](https://docs.github.com/en/code-security/security-advisories/working-with-repository-security-advisories/configuring-private-vulnerability-reporting-for-an-organization)). |
| 37 | + |
| 38 | +### High-level process changes |
| 39 | + |
| 40 | +- Enable private reporting org-wide via a security configuration, defaulted for new repos. |
| 41 | +- Rewrite `SECURITY.md` to point at the **Security** tab / **Report a vulnerability** form; remove or redirect the mailing-list instructions. |
| 42 | +- Define triage: who accepts a PROPOSED report into a draft, first-response SLA, advisory labels, and a close/decline policy for spam and invalid reports. |
| 43 | +- Map the security team onto GitHub roles: private reports are visible to repo admins and security managers, so the team roster becomes a GitHub team with the **security manager** role, replacing list membership. |
| 44 | +- Standardize CVSS scoring with the built-in calculator. |
| 45 | +- Make the temporary private fork the mandatory embargoed-fix workflow; assign fork/merge admins. |
| 46 | +- Scope collaborators per advisory (reporter + assigned maintainers only). |
| 47 | +- Request CVEs in-band via GitHub as CNA; document handling when another CNA already covers a component. |
| 48 | +- Keep distributor coordination: notify `cncf-harbor-distributors-announce` under embargo from the draft advisory before publish. |
| 49 | +- Publish the advisory after the fork merges and the negotiated date, keeping the ~14-business-day critical target. |
| 50 | +- Decommission and redirect the mailing list; drain in-flight reports through the new workflow. |
| 51 | + |
| 52 | +## Non-Goals |
| 53 | + |
| 54 | +- The distributor embargo list (`cncf-harbor-distributors-announce`) stays as the pre-disclosure channel. |
| 55 | +- Disclosure timing is unchanged; this changes tooling, not the timeline. |
| 56 | +- Non-security bugs continue through normal GitHub issues. |
| 57 | +- Maintainers still decide CVSS/CWE values; only the recording moves onto structured fields. |
| 58 | +- GitHub is used as CNA only where eligible; no switch away from a component already covered by another CNA. |
| 59 | + |
| 60 | +## Rationale |
| 61 | + |
| 62 | +GitHub-centric wins because intake, embargoed discussion, fix, CVE, score, and published advisory become one artifact instead of manual handoffs across inbox, MITRE, and GitHub. |
| 63 | + |
| 64 | +Nothing forces Harbor to stay on the list. CNCF treats a `cncf.io` security list as optional infrastructure ("CNCF could help create a mailing address ... should projects need one") and leaves the reporting medium to the project — "Email, Web form etc." — naming GitHub's coordinated disclosure as an acceptable alternative ([CNCF security guidelines](https://contribute.cncf.io/maintainers/security/security-guidelines/)). CNCF projects already run intake through GitHub's **Report a vulnerability** form instead of a list — **Argo CD** ([advisories/new](https://github.com/argoproj/argo-cd/security/advisories/new)), **in-toto** ([advisories/new](https://github.com/in-toto/in-toto/security/advisories/new)), and **Backstage** — so the switch breaks no CNCF obligation and follows established precedent. |
| 65 | + |
| 66 | +Trade-offs: |
| 67 | + |
| 68 | +- **GitHub account required** to file (reporter becomes a collaborator); a documented fallback contact may be warranted. |
| 69 | +- **GitHub dependency** on availability and CNA review turnaround (~72h). |
| 70 | +- **Open intake invites noise.** Anyone with a GitHub account can file, so expect spam and low-quality reports the moderated list filtered by friction; triage needs an explicit close/decline policy. |
| 71 | +- **No CI on the private fork cuts both ways:** the patch cannot leak, but it also cannot be tested by automation before merge; fixes need manual verification. |
| 72 | +- **No read-only collaborator tier**, and **Dependabot does not cover OCI/container consumers**, so downstream notification of most Harbor users still relies on the distributor list and release notes. |
| 73 | + |
| 74 | +## Compatibility |
| 75 | + |
| 76 | +- **SECURITY.md** is updated to the new intake; documentation change only. |
| 77 | +- **Distributor list**: the draft advisory becomes the source feeding the embargo notification. |
| 78 | +- **In-flight reports** at cutover are handled to completion through the new workflow; the list stays read-only until they close. |
| 79 | +- **Historical CVEs and existing advisories** remain valid; only new-report handling changes. |
| 80 | + |
| 81 | +## Implementation |
| 82 | + |
| 83 | +1. **Governance sign-off** (maintainers / CNCF liaison): agree to the switch and to decommissioning the list; name the org owners / security managers who own the config. |
| 84 | +2. **Enable private reporting** (org owners / security managers): org-level security configuration across public repos, defaulted for new repos. |
| 85 | +3. **Define the workflow** (security team): triage ownership, SLA, labels, per-advisory scoping, CVSS usage, private-fork process, in-band CVE request. |
| 86 | +4. **Update docs** (maintainers): rewrite `SECURITY.md`; note the GitHub-account requirement and any fallback. |
| 87 | +5. **Pilot** (security team): run one or two reports end-to-end (report → draft → fork → CVE → publish) before cutover. |
| 88 | +6. **Cutover** (security team / list admins): announce the new channel, set the list read-only with a redirect, route all new reports through GitHub. |
| 89 | +7. **Drain and decommission** (list admins): close in-flight list reports, then archive `cncf-harbor-security@lists.cncf.io`. |
| 90 | + |
| 91 | +## Open issues |
| 92 | + |
| 93 | +- **Fallback for reporters without a GitHub account** — keep a minimal contact, and if so what. |
| 94 | +- **CNA eligibility overlap** — who files the CVE when another CNA already covers a component. |
| 95 | +- **OCI/container downstream notification gap** — how consumers of Harbor images (not Go modules) are notified beyond the distributor list and release notes. |
| 96 | +- **Cross-repo routing** — Harbor spans multiple repos (core, helm chart, operator, satellite); how reports filed against the wrong repo are transferred, and which repos get private reporting monitored. |
| 97 | +- **Mailing-list archive retention** — whether and how long to keep the groups.io archive. |
| 98 | +- **Reporter state-change visibility** — whether maintainers must manually keep reporters informed at triage/draft/publish. |
0 commit comments