diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml new file mode 100644 index 00000000..6571f5ad --- /dev/null +++ b/.github/workflows/govulncheck.yml @@ -0,0 +1,76 @@ +name: govulncheck + +# Reachability-aware vulnerability scan against every PR + push to main. +# Fails the build if our code calls into a known-vulnerable function in +# any direct or transitive Go dep. +# +# This is the enforcement half of the documented SCA policy in +# docs/DEPENDENCIES.md "Pre-release SCA gate". OpenSSF Baseline +# OSPS-VM-05.03 requires changes to be automatically evaluated against +# a documented policy and blocked on violation, except when declared +# non-exploitable (which we do via vex/openvex.json). +# +# Why govulncheck instead of osv-scanner / Trivy on Go modules: +# govulncheck is the reachability-aware scanner maintained by the Go +# security team. It runs the call-graph against the OSV advisory data +# and reports only vulnerabilities whose symbols are reachable from +# OUR code. Module-level scanners (osv-scanner, Trivy) flag any vuln +# in any transitive dep regardless of whether the vulnerable symbol +# is called — which produces false positives like GO-2022-0635/0646 +# (aws-sdk-go v1 s3crypto, transitively pulled in by Pulumi but never +# called). Those false positives are documented in vex/openvex.json +# but govulncheck filters them at the scan level without needing the +# VEX consult. +# +# CI behaviour: +# - PR: fails if govulncheck reports any reachable vulnerability. +# - main push: same gate; an unfixable transitive that suddenly +# becomes reachable would block the next release. +# - Weekly cron: catches newly-published advisories landing against +# existing reachable code paths. + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '37 6 * * 1' # Monday 06:37 UTC — early week, off-peak + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.head.sha || github.sha }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + govulncheck: + name: govulncheck (reachability-aware) + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + with: + go-version-file: go.mod + cache: true + + - name: Install govulncheck + run: | + set -euo pipefail + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck -version + + - name: Run govulncheck on source + run: | + set -euo pipefail + # -mode=source: scan the call graph from this module's main + # packages outward (reachability-aware). Exits non-zero if + # any reachable vulnerability is found. + govulncheck -mode=source ./... diff --git a/README.md b/README.md index 9885a7a2..18afdda5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # **Simple Container** [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/simple-container-com/api/badge)](https://scorecard.dev/viewer/?uri=github.com/simple-container-com/api) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12886/badge)](https://www.bestpractices.dev/projects/12886) [![OpenSSF Baseline](https://www.bestpractices.dev/projects/12886/baseline)](https://www.bestpractices.dev/projects/12886) [![CodeQL](https://github.com/simple-container-com/api/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/simple-container-com/api/actions/workflows/codeql.yml) [![Semgrep](https://github.com/simple-container-com/api/actions/workflows/semgrep.yml/badge.svg?branch=main)](https://github.com/simple-container-com/api/actions/workflows/semgrep.yml) @@ -69,7 +70,13 @@ Check out the [full documentation](https://docs.simple-container.com) for detail ## **Contributing** -We welcome contributions! Please see our [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines on how to get involved. +We welcome contributions! Please see our [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines on how to get involved, and our [CODE_OF_CONDUCT.md](docs/CODE_OF_CONDUCT.md) for the community standards every participant is expected to uphold. + +--- + +## **Roadmap** + +See [ROADMAP.md](docs/ROADMAP.md) for current themes, release cadence, and where planned work surfaces in the [Issues](https://github.com/simple-container-com/api/issues) tracker. --- diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 0b5128a2..35493bab 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -97,7 +97,8 @@ For deeper component-level designs, see: ## Related security documentation - [SECURITY.md](SECURITY.md) — threat model (STRIDE) + attack vectors V1–V5 + responsible-disclosure channels -- [`../HARDENING.md`](../HARDENING.md) — phase-by-phase hardening tracker (Phase 1 image hardening → Phase 8 OpenSSF visibility) -- [`../SECURITY-CONTROLS.md`](../SECURITY-CONTROLS.md) — control matrix mapping STRIDE categories to specific controls - [DEPENDENCIES.md](DEPENDENCIES.md) — dependency selection / obtaining / tracking +- [SECRETS-POLICY.md](SECRETS-POLICY.md) — secret categorisation, storage, rotation cadence - [MAINTAINERS.md](MAINTAINERS.md) — project members with access to sensitive resources +- [RELEASES.md](RELEASES.md) — release pipeline, signing chain, vuln-disclosure flow +- [ROADMAP.md](ROADMAP.md) — public roadmap state (Issues labels, themes, release cadence) diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..de135069 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,87 @@ +# Contributor Covenant Code of Conduct + +This is the code of conduct for the Simple Container project. +Violations should be reported to creed@simple-container.com, consistent with docs/SECURITY.md and docs/MAINTAINERS.md. +The maintainer group is responsible for enforcement according to the standard process below. + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at creed@simple-container.com. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 36c6dffb..9e77ae50 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -118,9 +118,10 @@ If your change touches: - Anything in the SLSA / cosign / sigstore chain … open the PR with a **threat-model note**: which entry in -[HARDENING.md](../HARDENING.md) does this change address or affect? -Maintainers will pull in additional reviewers (codex + gemini round + -human security review) for changes here. +[`SECURITY.md`](SECURITY.md)'s STRIDE table + attack vectors V1–V5 +does this change address or affect, and what's the reachability / +blast-radius story? Maintainers will pull in additional reviewers +(codex + gemini round + human security review) for changes here. **Never disable verification** as a fix for a verification bug. If sc.sh's cosign-verify path rejects a release, the bug is in the diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index b205aace..860a2d19 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -27,7 +27,7 @@ selects, obtains, and tracks its dependencies." | **Go** | `go.mod` + `go.sum` | `go.sum` hashes every direct + transitive dep | `govulncheck` (reachability-aware), `osv-scanner` (via Scorecard), `trivy fs` | | **Python (docs)** | `docs/requirements.in` (sources) + `docs/requirements.txt` (compiled with `--generate-hashes`) | `pip install --require-hashes` in `push.yaml` docs-build step | `pip-audit`, Scorecard pinned-deps check | | **npm (docs examples)** | example `package*.json` files | Lockfile-aware install (`npm ci` when lockfile present, falls back to `npm install`) | Scorecard pinned-deps check | -| **Docker base images** | `Dockerfile`s + `.Dockerfile`s at repo root + example dirs | SHA digest pin: `python@sha256:401f...`, `node:22-alpine@sha256:757e...` | `trivy image` per published image (see HARDENING.md Phase 1) | +| **Docker base images** | `Dockerfile`s + `.Dockerfile`s at repo root + example dirs | SHA digest pin: `python@sha256:401f...`, `node:22-alpine@sha256:757e...` | `trivy image` per published image | | **GitHub Actions** | `.github/workflows/*` + `.github/actions/*` | Commit SHA pin with `# vX.Y.Z` comment for human-readability | Scorecard pinned-deps check; Semgrep custom rules | | **End-user installer tools** | `sc.sh` (Pulumi installer) | Tarball + SHA256 checksum verification before extract | n/a (sc.sh is shipped, not built against) | @@ -91,8 +91,10 @@ The PR pipeline runs: - Scorecard runs daily; the badge surfaces the current score A subset of findings is intentionally accepted as documented false -positives — those live in PR descriptions and in -[HARDENING.md](../HARDENING.md), never in a suppression file. +positives — those live in PR descriptions and as OpenVEX +`not_affected` statements in [`vex/openvex.json`](../vex/openvex.json), +never in a scanner-suppression file (no `.trivyignore`, no +`# nosemgrep`, no `// nolint:` for vuln findings). ## Out-of-tree dependency surface @@ -106,3 +108,81 @@ These are not "deps" in the tree-sense, but the trust chain matters the same way: each ships with cosign keyless signature + SLSA L3 provenance + SHA256. Consumers verify via the patterns documented in [SECURITY.md](SECURITY.md). + +## Remediation thresholds (SCA policy) + +This section satisfies OpenSSF Baseline **OSPS-VM-05.01** +("the project documentation MUST include a policy that defines a +threshold for remediation of SCA findings related to vulnerabilities +and licenses") and is the contract the pre-release gate enforces. + +### Vulnerability severity → remediation SLA + +| Severity (CVSS or distro rating) | Remediation SLA | Behaviour while open | +|---|---|---| +| **CRITICAL** (9.0–10.0) | within **24 hours** of confirmation if there is a known patched version; within **7 days** if upstream fix pending | merge gate blocks all non-emergency PRs | +| **HIGH** (7.0–8.9) | within **30 days** | merge gate blocks; documented exceptions require VEX (see below) | +| **MEDIUM** (4.0–6.9) | within **90 days** | best-effort with batch bumps via Dependabot | +| **LOW** (0.1–3.9) | next scheduled SCA pass (~weekly) | informational | + +The SLA clock starts when the advisory is confirmed reachable in our +codebase by `govulncheck` (reachability-aware). If govulncheck reports +an advisory as *not* reachable, the finding does NOT block — it is +declared `not_affected` via `vex/openvex.json` with the govulncheck +output as evidence (per OSPS-VM-04.02). + +### License policy + +| License class | Allowed for direct + transitive deps | +|---|---| +| MIT / Apache-2.0 / BSD-3-Clause / BSD-2-Clause / ISC / MPL-2.0 | ✅ Allowed | +| LGPL (any version) | ⚠️ Flagged for maintainer review; case-by-case | +| GPL / AGPL (any version) | ❌ Rejected for runtime; allowed for build-only tooling iff the tool is not redistributed | +| Unknown / proprietary | ❌ Rejected | + +License compliance is checked at PR review time. License-incompatible +deps are blocked at merge by maintainer review (no automated license +scanner yet — tracked internally). + +## Pre-release SCA gate + +This section satisfies OpenSSF Baseline **OSPS-VM-05.02** ("a policy +to address SCA violations prior to any release") and **OSPS-VM-05.03** +("automatically evaluated against a documented policy ... then blocked +in the event of violations"). + +Production releases are cut automatically on every merge to `main` +(`.github/workflows/push.yaml`), so "pre-release" semantically equals +"pre-merge" — every release IS a merge to main. The SCA gate fires +on every PR + every push to main: + +| Tool | Coverage | Blocks on | +|---|---|---| +| **govulncheck** (`.github/workflows/govulncheck.yml`) | Reachable Go vulnerabilities (call-graph aware) | Any reachable advisory finding | +| **CodeQL** (`.github/workflows/codeql.yml`) | Common-vulnerability patterns (SQL injection, XSS, command injection, taint flow, SSRF) | ERROR-severity findings | +| **Semgrep** (`.github/workflows/semgrep.yml`) | Custom rules from the SC org (sigstore, gha-extras, pulumi-iac, go-canon) | ERROR-severity findings | +| **TruffleHog** (`.github/workflows/security-scan.yml`) | Leaked secrets in diff | Any verified secret | +| **Dependabot** | Daily advisory poll → auto-PR for known vulns | Open security alert (visibility, not blocking, but the auto-PR becomes a blocking item per the SLA above) | +| **Trivy / Grype** (in shared security-scan workflow) | SBOM-based scan of source-tree deps | Currently reports counts only; planned to graduate to blocking on HIGH/CRITICAL once VEX consumption is wired in | + +### Suppressing a finding (non-exploitable / false positive) + +The only sanctioned suppression channel is **VEX**: edit +`vex/openvex.json` and add a `not_affected` statement with: + +- `vulnerability.@id` — the OSV / CVE / GHSA ID +- `justification` — one of the OpenVEX standard values + (`vulnerable_code_not_in_execute_path`, + `inline_mitigations_already_exist`, etc.) +- `impact_statement` — free-form explanation citing evidence + (e.g., govulncheck output, code-path analysis, mitigation in place) + +`.trivyignore`, `# nosemgrep`, `// nolint:`, `# noqa` are NOT +sanctioned suppression channels. Any of these in a PR must point at +a documented false positive in the PR description; the project +prefers the formal VEX statement for tracking. + +Per OSPS-VM-05.03, the suppression flow is also "documented policy": +findings reachable in our code WITHOUT a VEX `not_affected` entry +block the merge. Findings declared `not_affected` in VEX bypass the +gate. diff --git a/docs/MAINTAINERS.md b/docs/MAINTAINERS.md index 66fe6951..72aba9c6 100644 --- a/docs/MAINTAINERS.md +++ b/docs/MAINTAINERS.md @@ -68,6 +68,31 @@ run inside grants them, scoped per-job by GitHub Actions permissions. that pulls in additional reviewers for `pkg/security/` / `push.yaml` / `sc.sh` changes. +### Decision-making + +This section satisfies the OpenSSF Best Practices `governance` +criterion (how decisions on technical direction are made). + +- **Default rule: consensus via PR review.** Any change ships only + after a maintainer's approving review on the PR and all required + status checks (CI, signed commits, DCO sign-off) pass. Branch + protection on `main` enforces this — no maintainer self-merges + unreviewed work. +- **Security-sensitive changes** (`pkg/security/`, `push.yaml`, + `sc.sh`, anything in the SLSA / cosign / Sigstore chain) require at + least one maintainer review on top of the automated codex + Gemini + review pass triggered by `/thorough-review`. The contribution rules + in [`CONTRIBUTING.md`](CONTRIBUTING.md) describe this protocol. +- **Contentious technical decisions** (architecture, breaking changes, + security-policy shifts) are resolved by maintainer discussion in the + PR thread or repo Discussions. If consensus is not reached, the + project lead (currently [@smecsia](https://github.com/smecsia)) has + tiebreaker authority. Material direction changes are surfaced in the + Discussions tab before they ship. +- **Roadmap shaping** follows the same pattern — see + [`ROADMAP.md`](ROADMAP.md) for how issues become shipped code, and + how maintainers triage them. + ## Sensitive-resource access map The following resources have access controlled by the maintainer @@ -92,7 +117,69 @@ per-maintainer ACL membership is tracked in the SC team's internal credential inventory (not published here to avoid leaking attack surface, but maintained per `SECURITY.md`'s threat model). -## Adding or removing a maintainer +## Promoting a contributor / Granting escalated permissions + +This section satisfies OpenSSF Baseline **OSPS-GV-04.01** — +"the project documentation MUST have a policy that code collaborators +are reviewed prior to granting escalated permissions to sensitive +resources." + +A contributor is considered for maintainer-role promotion only after +the following review-and-vetting gates are passed. Each gate is +recorded in the promotion PR description. + +### Track-record gate + +- **Minimum**: 5+ merged PRs over ≥3 months, spanning multiple files + / packages. +- Demonstrated review-and-iterate behaviour (responsive to reviewer + feedback, clean commit history, signed commits with DCO sign-off). +- No suppression-policy violations in merged PRs (`.trivyignore`, + `# nosemgrep`, `// nolint:` etc. used outside the sanctioned VEX + channel documented in [DEPENDENCIES.md](DEPENDENCIES.md)). + +### Sponsorship gate + +- An existing maintainer must **explicitly nominate** the contributor + in a PR amending this file, with rationale. +- A second maintainer (or, if only one maintainer exists, the project + lead) seconds the nomination. +- The contributor accepts in the PR thread. + +### Account-hardening gate (must pass BEFORE any resource ACL changes) + +The promoting maintainer verifies and records (in the promotion PR +description) that the candidate's accounts meet: + +- ✅ **2FA enabled** on GitHub (TOTP or hardware key — NOT SMS). +- ✅ **2FA enabled** on Docker Hub if they will receive `simplecontainer` + org access. +- ✅ **2FA enabled** on Cloudflare if they will receive zone admin. +- ✅ **SSH/GPG commit-signing key** is registered in GitHub and matches + the GitHub account identity (no impersonation surface). Signed + commits will be required on every PR they merge. + +### Least-privilege grant + +- Even after promotion, the new maintainer receives **only the + credentials needed for their work**. Per the SECURITY.md threat + model, not every maintainer holds every key. +- Specifically: Docker Hub publish access is granted only if the + maintainer regularly handles releases; Cloudflare admin only if + they own DNS/WAF; AWS Secrets Manager only if they own the + infrastructure that consumes those secrets. +- Specific per-credential ACL membership is recorded in the SC team's + internal credential inventory (not in this public file) per + [SECRETS-POLICY.md](SECRETS-POLICY.md). + +### Probationary period + +- For the first 30 days after promotion, the new maintainer's merges + are **co-reviewed** by an existing maintainer — they may approve, + but another maintainer must also approve before merge. After 30 + days with no concerns, full merge authority becomes effective. + +## Adding or removing a maintainer (mechanics) Changes to this list happen via a PR amending this file + a corresponding update to: @@ -106,11 +193,12 @@ Maintainer offboarding additionally: - Rotates any shared CI tokens / API keys the departing maintainer could access (Docker Hub publish token, Cloudflare API tokens, - GitHub PAT-equivalents) + GitHub PAT-equivalents) — per [SECRETS-POLICY.md](SECRETS-POLICY.md) + rotation schedule, accelerated to "immediate" on offboarding. - Revokes Sigstore / cosign signing identities if the maintainer's - GitHub workflow identity was wired into any signing path -- Audits org-level 2FA enforcement (per - [HARDENING.md](../HARDENING.md) Phase 8 admin-UI list) + GitHub workflow identity was wired into any signing path. +- Audits org-level 2FA enforcement against the maintainer-side admin + checklist. ## Security contact diff --git a/docs/RELEASES.md b/docs/RELEASES.md new file mode 100644 index 00000000..d76ec0f6 --- /dev/null +++ b/docs/RELEASES.md @@ -0,0 +1,138 @@ +# Releases + +This doc covers how Simple Container (`sc`) is released, where to read +release notes, how vulnerabilities are surfaced in notes, and how the +release ↔ disclosure flow ties together. + +It satisfies the OpenSSF Best Practices `release_notes`, +`release_notes_vulns`, and `report_archive` criteria. + +## Where to read release notes + +| What | Where | +|---|---| +| **Per-release human-readable notes** | | +| **Public vulnerability advisories** | | +| **Public issue / PR archive (reports + responses)** | + | + +## How release notes are produced + +Every push to `main` triggers `.github/workflows/push.yaml`. After +build + sign + publish to dist, the `docker-finalize` job invokes +[`scripts/create-github-release.sh`](../scripts/create-github-release.sh) +which calls: + +```bash +gh release create "${VERSION}" "${assets[@]}" \ + --repo "${GITHUB_REPOSITORY}" \ + --title "v${VERSION}" \ + --generate-notes \ + --verify-tag +``` + +The `--generate-notes` flag uses GitHub's auto-generation: it walks +back to the previous tag and produces a categorised summary of merged +PRs since then, **plus** a "New Contributors" callout. This is **not** +raw `git log` output — it groups by PR + author + label, with PR +titles and authors hyperlinked. + +Release notes are attached to each Release object alongside the signed +sidecars (`.cosign-bundle`, `.sigstore.json`, `.sha256`, `.sbom.cdx.json`). +Consumers can verify the integrity of the artifacts AND read the notes +in the same Release UI. + +## How vulnerabilities appear in release notes + +Security-relevant changes follow these conventions so they surface +clearly in the auto-generated notes: + +1. **Commit subject prefix**: security fixes use `fix(security):` / + `fix(deps):` (for CVE-closing dep bumps) / `hotfix(...)`. Examples + from history: + + - `fix(deps): bump docs python 3.9 → 3.12-slim + pin requests/urllib3 to patched versions` + (closes GHSA-gc5v-m9x4-r6x2, GHSA-mf9v-mfxr-j63j, GHSA-qccp-gfcp-xxvc) + - `fix(deps): migrate aws-sdk-go v1 → v2 (Vulnerabilities 8→10)` + (addresses GO-2022-0635, GO-2022-0646) + - `hotfix(sc.sh): drop invalid --yes flag on cosign verify-blob` + (production outage — installs broken since #264) + +2. **CVE / GHSA references**: the body of every security-fix commit + includes the upstream advisory IDs (`GHSA-...`, `CVE-...`, + `GO-YYYY-NNNN`) it closes. These show up in the release-notes + summary via the linked PR. + +3. **GitHub Security Advisories**: when a security report reaches + maintainers via the channels in [`SECURITY.md`](SECURITY.md), the + fix lands via a regular PR, and the corresponding GHSA is published + on . + The advisory cross-links the affected version + the patch PR + the + release tag, so a consumer reading the release notes can pivot to + the advisory page directly. + +4. **N/A is honest**: if a given release fixed no publicly-known + vulnerabilities, no vulnerability section is forced into the notes. + The `--generate-notes` output simply omits a security callout. + +## Reading a release for security implications + +For a given release `vYYYY.M.X`: + +1. Open + (note: tag has no `v` prefix in our scheme; the Release title shows + `v` for human readability). +2. The auto-generated body lists every merged PR. Filter mentally for + PRs whose title starts with `fix(security)`, `fix(deps)`, or + `hotfix(...)`. +3. Click through to any PR for the full commit body — that's where the + CVE / GHSA ID lives. +4. Cross-check against the advisory archive at + + for any advisory marked **Published** with a `Patched in` field + matching this release. +5. The dist-side tarballs at `https://dist.simple-container.com/sc-*-vYYYY.M.X.tar.gz` + ship the same release. Verify their integrity with `cosign verify-blob` + per [`SECURITY.md`](SECURITY.md) before upgrading. + +## Release cadence + +- **Production releases**: cut automatically on every merge to `main`. + Calver scheme `YYYY.M.X` driven by + `reecetech/version-increment@2023.10.2`. There is no manual release + process — every accepted change in `main` ships. +- **Preview releases**: produced by `.github/workflows/branch-preview.yaml` + on a `workflow_dispatch` trigger from a feature branch. Tagged + `vYYYY.M.X-pre.SHORTSHA-preview.SHORTSHA`. These do NOT create GitHub + Release objects (filtered by `scripts/create-github-release.sh`), + but their signed sidecars are still published to `dist.*`. +- **Hotfixes**: same flow as a regular fix. Merge to `main` → + automatic release. No separate hotfix branch model. See PR #268 for + the canonical hotfix example (cosign `--yes` flag bug). + +## Vulnerability report archive + +Public issue and PR threads — including security-fix PR discussions +once their corresponding advisory is published — are archived at: + +- — open + closed + issues +- + — closed PRs +- — + Published GitHub Security Advisories + +GitHub's full-text search across these surfaces makes the archive +searchable without additional infrastructure. + +## Cross-references + +- [`SECURITY.md`](SECURITY.md) — threat model, reporting channels, + disclosure cadence +- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution requirements +- [`MAINTAINERS.md`](MAINTAINERS.md) — who handles security responses +- [`ARCHITECTURE.md`](ARCHITECTURE.md) — system actors + trust + boundaries the release pipeline operates within +- [`DEPENDENCIES.md`](DEPENDENCIES.md) — dep selection + tracking that + drives most of the `fix(deps)` security PRs +- [`ROADMAP.md`](ROADMAP.md) — where planned work + open security + items surface in public diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 00000000..aaae865f --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,71 @@ +# Roadmap + +This document satisfies the OpenSSF Best Practices `documentation_roadmap` +criterion. It describes how Simple Container plans, tracks, and ships +future work in public. + +## Release cadence + +Simple Container does not gate features behind a future "version X" cut. +Production releases are cut **automatically on every merge to `main`**, +using calver tags `YYYY.M.X`. Detailed release mechanics, notes +generation, and security-fix labelling conventions are documented in +[`RELEASES.md`](RELEASES.md). + +This means there is no separate "next release date" the roadmap targets. +What ships next is what's open and approaching merge. + +## Where to read roadmap state + +| What | Where | +|---|---| +| **Feature requests + planned work** | [Issues with `feature` label](https://github.com/simple-container-com/api/issues?q=is%3Aissue+label%3Afeature) | +| **Open security work** | [Issues with `security` label](https://github.com/simple-container-com/api/issues?q=is%3Aissue+label%3Asecurity) | +| **Active work in flight** | [Open pull requests](https://github.com/simple-container-com/api/pulls) | +| **Recently shipped** | [Releases page](https://github.com/simple-container-com/api/releases) | +| **Architectural direction** | [`ARCHITECTURE.md`](ARCHITECTURE.md) + design records under [`design/`](design/) | +| **Threat model + security posture** | [`SECURITY.md`](SECURITY.md) | +| **Dependency + SCA policy** | [`DEPENDENCIES.md`](DEPENDENCIES.md) | + +## Themes for the current cycle + +The following are the broad themes the maintainers are working on. They +are not commitments — priorities shift as security reports, downstream +consumer needs, and contributor bandwidth change. For real-time state, +the Issues page above is canonical. + +1. **Supply-chain hardening.** Sigstore keyless signing, SLSA Build L3 + provenance, CycloneDX SBOM attachment, signed-release sidecars, + reachability-aware SCA gating. The pieces already shipped are + visible on the [Releases page](https://github.com/simple-container-com/api/releases); + open work surfaces under the `security` label. +2. **Cloud-integration breadth.** New cloud-provider stack templates + for AWS / GCP / Azure / Kubernetes deployment shapes. Tracked under + the `feature` label. +3. **Documentation depth.** Per-cloud guides, troubleshooting, + walkthrough examples published to + [`docs.simple-container.com`](https://docs.simple-container.com/). +4. **OpenSSF maturity.** Climbing Scorecard score + completing + bestpractices.dev attestation against the OpenSSF Baseline (project + 12886). State visible via the badges in the README. + +## How a roadmap item becomes shipped code + +1. **File an issue** with the appropriate label, or open a discussion + in the repo's Discussions tab for larger ideas. +2. **Maintainer triage** assigns priority based on user demand, security + impact, and alignment with the themes above. See + [`MAINTAINERS.md`](MAINTAINERS.md) for the decision-making + process maintainers use. +3. **Implementation** happens via PR per the contribution rules in + [`CONTRIBUTING.md`](CONTRIBUTING.md). +4. **Merge ships it** — the next push to `main` cuts a release. + +## Long-term direction + +Simple Container is a maintained product, not a hobby project. The +long-term direction is set by the maintainers (see +[`MAINTAINERS.md`](MAINTAINERS.md)) in consultation with the +downstream consumers who depend on it. Material direction changes are +discussed in the public Discussions tab before any breaking change +ships. diff --git a/docs/SECRETS-POLICY.md b/docs/SECRETS-POLICY.md new file mode 100644 index 00000000..f88e4b07 --- /dev/null +++ b/docs/SECRETS-POLICY.md @@ -0,0 +1,96 @@ +# Secrets & Credentials Policy + +This document satisfies OpenSSF Baseline **OSPS-BR-07.02** — +"the project MUST define a policy for managing secrets and credentials +used by the project. The policy should include guidelines for storing, +accessing, and rotating secrets and credentials." + +It complements [`SECURITY.md`](SECURITY.md) (threat model + disclosure) +and [`MAINTAINERS.md`](MAINTAINERS.md) (who holds what). + +## Categories of secret + +| Category | Examples | Where they live | +|---|---|---| +| **CI/CD secrets** | `SC_CONFIG` SC stack configuration, Docker Hub publish token, Telegram CI bot token | GitHub Actions encrypted secrets, repo-scoped | +| **Production runtime secrets** | DB credentials, Pulumi state encryption keys, customer-supplied cloud credentials | Consumer-side AWS Secrets Manager / GCP Secret Manager (NEVER in this repo) | +| **External-platform admin tokens** | Cloudflare API tokens, NameCheap API access, GitHub PATs for cross-repo access | Maintainer-personal vaults (1Password, similar); never committed | +| **Sigstore signing identity** | The build workflow's GitHub OIDC token used by cosign keyless sign | Ephemeral; minted per-build by GitHub Actions, valid ~10 min | +| **Test fixtures with placeholder credentials** | Example secrets YAML in `docs/docs/examples/secrets/`, `pkg/api/secrets/testdata/` | Repo, but in a format that TruffleHog excludes (per `.github/workflows/security-scan.yml` `secret-scan-extra-excludes`) | + +## Storing secrets + +- **Never commit** real secrets to the repo. TruffleHog runs on every + PR diff in `.github/workflows/security-scan.yml` and fails the build + on detected findings (`fail-on-secrets: true`). GitHub + secret-scanning push-protection is also enabled at the repo level. +- **Encrypted-at-rest only**: CI secrets in GitHub Actions; production + secrets in AWS Secrets Manager or GCP Secret Manager (consumer side). +- **No plain-text checked-in encrypted secrets**: even SOPS/Mozilla + encrypted-blobs in the repo are discouraged. Use the platform's + native secret store. +- **Example / test fixtures** must use clearly-non-credential + placeholders (``, `xxxxxxxxxxxxxxx`). Where the + fixture's format requires a real-shaped string (e.g., OpenSSH key + body in `pkg/api/secrets/testdata/`), the file is excluded via the + central TruffleHog excludes list in the shared security-scan + workflow. + +## Accessing secrets + +- **Principle of least privilege per CI job**: workflows set root + `permissions:` to `contents: read`; per-job `write` only where + required (e.g., `docker-finalize` for tag push + Release create). + Scorecard Token-Permissions = 10/10 after PR #263 enforces this. +- **No `pull_request_target`** on workflows that handle untrusted PR + code. The shared security scan uses `pull_request` so fork PRs run + with a read-only token and no access to org secrets. +- **No secrets in workflow logs**: GitHub Actions auto-redacts known + secret values; we add no debug echoes of env vars that might + contain secrets. +- **Job-scoped env**: secrets passed to steps via `env:` at the step + level (or `env:` on the smallest enclosing job), never globally. + +## Rotation cadence + +| Secret type | Cadence | Trigger | +|---|---|---| +| **CI publish tokens** (Docker Hub, Sigstore-key alternatives if any) | 90 days | Calendar reminder; immediate on suspected leak | +| **Cloudflare API tokens** (Maintainer-personal) | Quarterly | Calendar; immediate on maintainer offboarding | +| **GitHub Personal Access Tokens** (for cross-repo automation) | 90 days, scoped to specific repos only | Calendar | +| **Sigstore Fulcio certs** (per-build) | Per-build (~10 min lifetime) | Automatic | +| **Production runtime secrets** (DB creds, etc.) | Per consumer's policy | Owned by consumer | + +Rotation is recorded in the maintainer's credential inventory (kept +in a private SC team vault, not published here per +[`MAINTAINERS.md`](MAINTAINERS.md) "Sensitive-resource access map"). + +## On suspected leak + +1. **Rotate immediately** — don't wait for the next scheduled cadence. +2. **Audit the access logs** of the platform the secret was on + (GitHub Audit Log, Cloudflare Audit Log, Docker Hub access log, + AWS CloudTrail, etc.). Cloudflare audit-log retention is currently + 18 months on the SC `simple-container.com` account (Free-tier + default, satisfies OpenSSF ≥1-year requirement). +3. **Open a private security advisory** via the channel in + [`SECURITY.md`](SECURITY.md) if the leak affects a published artifact. +4. **Document the incident** in the post-mortem PR description (no + secret values, just the rotation reference + access-log summary). + +## Detection + +- **TruffleHog** on every PR diff (`.github/workflows/security-scan.yml` + via the shared `simple-container-com/actions/.github/workflows/security-scan.yml`). +- **GitHub secret-scanning** with **push-protection**: blocks pushes + that contain recognised credential formats. +- **Semgrep** custom rules (`simple-container-com/actions/semgrep-scan`) + flag patterns like hardcoded JWTs, AWS access keys in code, + embedded private keys. + +## Cross-references + +- [`SECURITY.md`](SECURITY.md) — threat model + disclosure +- [`MAINTAINERS.md`](MAINTAINERS.md) — who holds maintainer-personal + credentials +- [`DEPENDENCIES.md`](DEPENDENCIES.md) — dep policy diff --git a/docs/SECURITY.md b/docs/SECURITY.md index ce023c88..eeb121fb 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -7,19 +7,40 @@ all reports as high priority. ## Supported versions -Security fixes are issued for the **most recent calver release** (the -tag pushed by [`.github/workflows/push.yaml`][push] on every merge to -`main`) and back-ported to the latest `vYYYY.M.x` line only when the -fix is non-trivial. Older versions receive no patches; consumers should -pin to a recent release tag (or a SHA) and update via Dependabot / -[`integrail/devops/.github/actions/install-sc`][install-sc] (or -equivalent) on at least a monthly cadence. - -| Version | Supported | +### Support scope (OSPS-DO-04.01) + +`sc` ships **production calver releases on every merge to `main`** via +[`.github/workflows/push.yaml`][push]. Each release is supported for +security fixes **until the next calver release ships**. There is no +LTS branch model; the supported version moves continuously. + +| Version | Supported for security fixes | |---|---| -| `vYYYY.M.x` latest | ✅ | -| Previous calver release on the same month line | ✅ (best-effort back-port) | -| Anything older | ❌ | +| Latest `vYYYY.M.x` published at | ✅ Active support | +| Previous `vYYYY.M.x` on the same month line | ✅ Best-effort back-port for HIGH/CRIT only; SLA per [`DEPENDENCIES.md`](DEPENDENCIES.md) "Remediation thresholds" | +| Older releases | ❌ No patches; see EOL policy below | + +### End-of-life policy (OSPS-DO-05.01) + +A release becomes **EOL the moment a newer `vYYYY.M.x` ships**. The +practical implication is: + +- Older releases receive **no security updates** going forward — + including HIGH/CRITICAL fixes. +- The only supported upgrade path is to the latest calver release. +- Consumers MUST pin to a specific release tag (or a SHA) and update + at least monthly via Dependabot or the equivalent in + [`integrail/devops/.github/actions/install-sc`][install-sc]. +- This policy is intentional: SC has fewer than five active + maintainers, and an LTS branch model would dilute attention on the + active head. Continuous-release calver with reproducible verified + artifacts (Phase 2 cosign + SLSA L3) is the supported posture. + +If a consumer has a regulatory or contractual constraint that +prevents a monthly update cadence, contact the maintainers via the +channels in this document before pinning to an older release; we may +be able to coordinate a privately-supported branch on a case-by-case +basis (not covered by this public policy). ## Reporting a vulnerability diff --git a/vex/openvex.json b/vex/openvex.json new file mode 100644 index 00000000..8f59a3a8 --- /dev/null +++ b/vex/openvex.json @@ -0,0 +1,48 @@ +{ + "@context": "https://openvex.dev/ns/v0.2.0", + "@id": "https://simple-container.com/.well-known/openvex/api.json", + "author": "Simple Container maintainers ", + "timestamp": "2026-05-18T19:00:00Z", + "version": 1, + "tooling": "Hand-authored. Reachability evidence from `govulncheck -mode=source ./...`.", + "statements": [ + { + "vulnerability": { + "@id": "https://pkg.go.dev/vuln/GO-2022-0635", + "name": "GO-2022-0635", + "aliases": ["CVE-2020-8911"], + "description": "In-band key negotiation issue in AWS S3 Crypto SDK for golang. Affected symbols: github.com/aws/aws-sdk-go/service/s3/s3crypto NewDecryptionClient, NewEncryptionClient." + }, + "products": [ + { + "@id": "pkg:golang/github.com/simple-container-com/api", + "subcomponents": [ + { "@id": "pkg:golang/github.com/aws/aws-sdk-go" } + ] + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_in_execute_path", + "impact_statement": "github.com/aws/aws-sdk-go is required transitively by github.com/pulumi/pulumi/pkg/v3/operations. Neither this codebase nor Pulumi's operations package imports github.com/aws/aws-sdk-go/service/s3/s3crypto. govulncheck -mode=source ./... reports 0 reachable vulnerabilities (and explicitly identifies these two advisories as 'vulnerabilities in modules you require, but your code doesn't appear to call these vulnerabilities')." + }, + { + "vulnerability": { + "@id": "https://pkg.go.dev/vuln/GO-2022-0646", + "name": "GO-2022-0646", + "aliases": ["CVE-2020-8912"], + "description": "CBC padding oracle issue in AWS S3 Crypto SDK for golang. Affected symbols: github.com/aws/aws-sdk-go/service/s3/s3crypto NewDecryptionClient, NewEncryptionClient." + }, + "products": [ + { + "@id": "pkg:golang/github.com/simple-container-com/api", + "subcomponents": [ + { "@id": "pkg:golang/github.com/aws/aws-sdk-go" } + ] + } + ], + "status": "not_affected", + "justification": "vulnerable_code_not_in_execute_path", + "impact_statement": "Same as GO-2022-0635 — s3crypto subpackage is not imported by this codebase or by any code path Pulumi's operations package reaches. govulncheck reachability scan confirms zero reachable calls to the affected symbols." + } + ] +}