Vulnerability management
How trstctl receives, triages, fixes, and discloses security vulnerabilities. This is the process an enterprise vendor-risk review expects to find; it complements the reporting policy in SECURITY.md and the architectural threat model.
Coordinated disclosure
We practise coordinated disclosure. Reports arrive privately (GitHub private vulnerability reporting, or email to the maintainer — see SECURITY.md), we agree a remediation and disclosure timeline with the reporter, and we publish an advisory once a fix is available. We credit reporters who want to be credited and ask for a reasonable embargo while we remediate. We never penalise good-faith research conducted within the documented scope.
Triage
Every report is triaged on intake and assigned a severity that drives the SLA below.
- Acknowledge the report to the reporter and open a private tracking issue (a GitHub Security Advisory draft — GHSA).
- Reproduce against the latest
mainand the reported version/commit; capture a minimal proof of concept. - Assess severity using CVSS v3.1 as the baseline, adjusted for trstctl's trust boundaries — anything that crosses a tenant boundary (AN-1), extracts or misuses key material (AN-3/AN-4/AN-8), forges or breaks the audit chain, or bypasses authentication/authorization is treated as at least High, and a remote, unauthenticated instance of any of those as Critical.
- Decide scope: confirm whether served subsystems are affected, identify the fix, and request a CVE ID (via GitHub) for anything Medium or above.
- Track the advisory through fix, release, and publication.
Patch SLA (by severity)
Targets are measured from triage confirmation. "Fix" means a patched main and, once
releases are tagged, a patched release; "disclose" means publishing the advisory.
| Severity | Acknowledge | Triage & assess | Fix target | Public disclosure |
|---|---|---|---|---|
| Critical | 1 business day | 2 business days | 7 days | with the fix (coordinated) |
| High | 2 business days | 3 business days | 30 days | with the fix |
| Medium | 3 business days | 5 business days | 90 days | with the fix or next release |
| Low | 5 business days | 10 business days | best effort / next release | next release notes |
Critical issues may ship an out-of-band security release; lower severities are rolled into the normal release cadence. If a target will slip, we tell the reporter and agree a revised date rather than letting the embargo lapse silently.
Security releases & advisories
- Fixes land on
mainfirst. A security release is tagged for Critical/High issues (and bundled for Medium/Low), with the cosign-signed, reproducible image and SBOM the normal pipeline produces. - We publish a GitHub Security Advisory (GHSA) with the CVE, affected versions/commits, severity, impact, remediation, and credit. The release notes link the GHSA.
- Downstream forks and plugin authors can watch advisories; the conformance suites let them self-validate after pulling a fix.
Known dependency advisories (assessed not-reachable)
govulncheck runs in CI (reachability-aware) and fails the build on any advisory the
code can actually reach. A small number of advisories exist in the dependency graph but
are not reachable from trstctl's call graph; we record them here so a vendor-risk
review sees a deliberate assessment, not an oversight. Each is re-checked on every
dependency bump, and the upgrade that clears it is scheduled (not silently deferred).
| Advisory | Dependency | Status | Why not reachable | Plan |
|---|---|---|---|---|
| GO-2025-3660 | github.com/open-policy-agent/opa v0.69.0 |
Not reachable (govulncheck: not called) | The advisory is a path-injection in OPA's server Data API. trstctl embeds OPA only as a library evaluator — it imports github.com/open-policy-agent/opa/rego and never .../server, so it does not run or expose the OPA HTTP server/Data API at all (internal/policy/policy.go). |
Upgrade to the OPA 1.x line (module path .../opa/v1) behind the policy-engine tests; tracked under SUPPLY-007. The 1.x bump is a major-version/module-path change, scheduled as its own dependency change rather than a hygiene-sweep edit. |
If a future change ever wires OPA's server/Data API (it should not — trstctl evaluates Rego in-process), this advisory becomes reachable and govulncheck will fail CI until OPA is upgraded.
Triage dry-run (worked example)
A dry-run of the process end to end against a sample advisory, to prove the workflow holds before a real report arrives. (Illustrative — not a real vulnerability.)
Sample report: "An authenticated tenant A user can read tenant B's issued certificates by passing a crafted
tenant_idtoGET /api/v1/certificates."
- Acknowledge (day 0): reply to the reporter; open a private GHSA draft
GHSA-xxxx-sample. - Reproduce (day 0–1): stand up a two-tenant instance, issue a cert under tenant B, and confirm whether tenant A can read it. In this sample, suppose it reproduces.
- Assess: a cross-tenant read bypasses AN-1 row-level security — Critical (remote, authenticated, tenant-boundary crossing). CVSS ≈ 8.1. Request a CVE.
- Scope the fix: the offending handler trusted a request-supplied
tenant_idinstead of the authenticated context; the fix forces the tenant from the session and relies on the RLS policy. Add a regression: aprojectionsintegration test asserting tenant A receives404/empty for tenant B's cert, plus atrstctllintcheck that the query path is tenant-filtered (AN-1). - Fix (within the 7-day Critical target): patch
main, tests green (make lint test), tag a security release. - Disclose: publish
GHSA-xxxx-samplewith the CVE, affected commits, impact, the fixed version, and reporter credit; link it from the release notes. - Retrospective: confirm the architecture linter or a new rule would have caught the class of bug, and file follow-ups if not.
This dry-run is re-run whenever the triage process or the served surface changes, so the process stays exercised rather than aspirational.