Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions proposals/github-private-vulnerability-reporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Proposal: Switch Security Reporting to GitHub Private Vulnerability Reporting

Author: [Vadim Bauer](https://github.com/Vad1mo)

Discussion: [Harbor community meeting 8 July 2026](https://hackmd.io/CyQk5FdVQwWObMLVNqxW1w?both#July-8-2026)

## Abstract

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.

## Background

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:

- **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.
- **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.
- **Email is a hurdle for reporters and maintainers.**

## Proposal

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.

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.

### Advantages over the mailing list

- **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)).
- **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)).
- **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)).
- **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)).
- **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)).
- **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)).
- **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)).
- **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.
- **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)).
- **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)).

### High-level process changes

- Enable private reporting org-wide via a security configuration, defaulted for new repos.
- Rewrite `SECURITY.md` to point at the **Security** tab / **Report a vulnerability** form; remove or redirect the mailing-list instructions.
- 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.
- 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.
- Standardize CVSS scoring with the built-in calculator.
- Make the temporary private fork the mandatory embargoed-fix workflow; assign fork/merge admins.
- Scope collaborators per advisory (reporter + assigned maintainers only).
- Request CVEs in-band via GitHub as CNA; document handling when another CNA already covers a component.
- Keep distributor coordination: notify `cncf-harbor-distributors-announce` under embargo from the draft advisory before publish.
- Publish the advisory after the fork merges and the negotiated date, keeping the ~14-business-day critical target.
- Decommission and redirect the mailing list; drain in-flight reports through the new workflow.

## Non-Goals

- The distributor embargo list (`cncf-harbor-distributors-announce`) stays as the pre-disclosure channel.
- Disclosure timing is unchanged; this changes tooling, not the timeline.
- Non-security bugs continue through normal GitHub issues.
- Maintainers still decide CVSS/CWE values; only the recording moves onto structured fields.
- GitHub is used as CNA only where eligible; no switch away from a component already covered by another CNA.

## Rationale

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.

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.

Trade-offs:

- **GitHub account required** to file (reporter becomes a collaborator); a documented fallback contact may be warranted.
- **GitHub dependency** on availability and CNA review turnaround (~72h).
- **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.
- **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.
- **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.

## Compatibility

- **SECURITY.md** is updated to the new intake; documentation change only.
- **Distributor list**: the draft advisory becomes the source feeding the embargo notification.
- **In-flight reports** at cutover are handled to completion through the new workflow; the list stays read-only until they close.
- **Historical CVEs and existing advisories** remain valid; only new-report handling changes.

## Implementation

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.
2. **Enable private reporting** (org owners / security managers): org-level security configuration across public repos, defaulted for new repos.
3. **Define the workflow** (security team): triage ownership, SLA, labels, per-advisory scoping, CVSS usage, private-fork process, in-band CVE request.
4. **Update docs** (maintainers): rewrite `SECURITY.md`; note the GitHub-account requirement and any fallback.
5. **Pilot** (security team): run one or two reports end-to-end (report → draft → fork → CVE → publish) before cutover.
6. **Cutover** (security team / list admins): announce the new channel, set the list read-only with a redirect, route all new reports through GitHub.
7. **Drain and decommission** (list admins): close in-flight list reports, then archive `cncf-harbor-security@lists.cncf.io`.

## Open issues

- **Fallback for reporters without a GitHub account** — keep a minimal contact, and if so what.
- **CNA eligibility overlap** — who files the CVE when another CNA already covers a component.
- **OCI/container downstream notification gap** — how consumers of Harbor images (not Go modules) are notified beyond the distributor list and release notes.
- **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.
- **Mailing-list archive retention** — whether and how long to keep the groups.io archive.
- **Reporter state-change visibility** — whether maintainers must manually keep reporters informed at triage/draft/publish.