This file documents two things: how the pipeline that builds this software protects it, and how anyone can verify that what they downloaded is what the pipeline actually built.
If you bought GoVueKit, this file is a template. Replace OWNER/REPO with your
own repository, adjust the reporting section, and it describes your product.
Report privately, never in a public issue. Use GitHub's Security → Report a vulnerability on this repository, or email the maintainer. Please include a description, affected version, and reproduction steps. Expect an acknowledgement within a few working days.
Every pull request must pass, in this order (.github/workflows/ci.yml):
| Gate | What it catches |
|---|---|
| zizmor | Vulnerable GitHub Actions workflow patterns |
| actionlint | Workflow syntax, invalid permission scopes, shell bugs |
| poutine | CI/CD exploitation chains |
go mod verify |
A module whose content no longer matches go.sum |
| govulncheck | Advisories on Go code the binary actually reaches |
| Trivy (filesystem) | Known CVEs in go.sum and package-lock.json |
| golangci-lint | Includes gosec — see .golangci.yml |
go test -race |
Data races, on SQLite and PostgreSQL |
| eslint / vue-tsc / vitest | Frontend correctness |
npm audit |
Known CVEs in the npm tree |
| Trivy (image) | Known CVEs in the built container image |
.github/workflows/codeql.yml adds interprocedural data-flow analysis for Go
and TypeScript, on every push and weekly.
Hardening applied to every workflow: the default token holds no permissions and
each job opts into the scopes it needs, every action is pinned to a full commit
SHA, actions/checkout never persists credentials, runners are pinned to a
numbered image, jobs are time-boxed, and dynamic values reach shell steps
through env: rather than ${{ }} interpolation.
Those SHA pins do not update themselves, and this kit ships without
Dependabot: raising them is a manual step you own. A pin nobody updates is a
stale dependency wearing a security costume, so treat it as a recurring
chore, not a one-off. .github/dependabot.yml was removed in favour of doing
it by hand — restore it from the git history if you would rather automate it.
Publishing a GitHub Release runs .github/workflows/release.yml, which emits:
| Asset | What it is |
|---|---|
<name>_<version>_linux_amd64.tar.gz |
Binary, LICENSE, README |
<name>_<version>_linux_arm64.tar.gz |
Same, arm64 |
checksums.txt |
SHA-256 of both archives |
checksums.txt.cosign.bundle |
Cosign signature over checksums.txt |
provenance.intoto.jsonl |
SLSA build provenance for the archives |
sbom.cdx.json |
CycloneDX SBOM of the image |
ghcr.io/OWNER/REPO:<version> |
Multi-arch image (amd64 + arm64) |
Signing is keyless: there is no private key to steal. The signing identity
is a short-lived certificate issued over OIDC that binds the signature to this
workflow file, in this repository, at this tag. That is what the
--certificate-identity-regexp below checks — verifying "it is signed" without
checking who signed it proves nothing.
Replace OWNER/REPO and 1.0.0 throughout.
sha256sum -c checksums.txt --ignore-missingThis is the floor. Everything below proves checksums.txt itself is genuine.
Needs cosign v3+.
cosign verify-blob checksums.txt \
--bundle checksums.txt.cosign.bundle \
--certificate-identity-regexp '^https://github.com/OWNER/REPO/\.github/workflows/release\.yml@' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comNeeds the GitHub CLI.
gh attestation verify govuekit_1.0.0_linux_amd64.tar.gz --repo OWNER/REPO# Signature
cosign verify ghcr.io/owner/repo:1.0.0 \
--certificate-identity-regexp '^https://github.com/OWNER/REPO/\.github/workflows/release\.yml@' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# Build provenance and SBOM
gh attestation verify oci://ghcr.io/owner/repo:1.0.0 --repo OWNER/REPORegistry paths are lowercase even when the GitHub account is not.
Signing is skipped automatically when the repository is private, and the release workflow says so in its log. Two reasons:
- Keyless signing records the repository name, the workflow path and the artifact digests in Rekor, the public transparency log. For a private product, that is a disclosure its owner should choose deliberately.
- GitHub artifact attestations are gated separately: on a private repository the API requires Enterprise Cloud (a Team plan is refused), and a release that goes red out of the box is a bad default.
The archives, the checksums and the image are still published — only the signatures are missing.
To sign anyway (any plan, accepting the Rekor disclosure), set a repository variable — this yields the Cosign bundle over the checksums, the signed image and the SBOM:
gh variable set SIGNING --body onFor the GitHub attestation store on top (Enterprise Cloud only on a private repository):
gh variable set ATTESTATIONS --body onApplication-level controls — argon2id password hashing, server-side sessions, CSRF, rate limiting, row-level multi-tenancy, RBAC, the anti-SSRF outbound client, security headers — are documented in README.md and DECISIONS.md.