Skip to content

Security: kOlapsis/govuekit

Security

SECURITY.md

Security

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.

Reporting a vulnerability

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.

What the pipeline enforces

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.

What a release publishes

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.

Verifying what you downloaded

Replace OWNER/REPO and 1.0.0 throughout.

1. The archives — no tooling required

sha256sum -c checksums.txt --ignore-missing

This is the floor. Everything below proves checksums.txt itself is genuine.

2. The checksums are signed by the release workflow

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.com

3. The archives carry SLSA build provenance

Needs the GitHub CLI.

gh attestation verify govuekit_1.0.0_linux_amd64.tar.gz --repo OWNER/REPO

4. The image is signed and attested

# 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/REPO

Registry paths are lowercase even when the GitHub account is not.

Signing on a private repository

Signing is skipped automatically when the repository is private, and the release workflow says so in its log. Two reasons:

  1. 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.
  2. 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 on

For the GitHub attestation store on top (Enterprise Cloud only on a private repository):

gh variable set ATTESTATIONS --body on

Runtime security

Application-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.

There aren't any published security advisories