| Version | Supported |
|---|---|
| latest | Yes |
| < latest | No |
Please do not report security vulnerabilities through public GitHub issues.
Instead, please report them via GitHub Security Advisories.
- A description of the vulnerability
- Steps to reproduce the issue
- Potential impact
- Suggested fix (if any)
- Acknowledgment: Within 48 hours of the report
- Initial assessment: Within 1 week
- Fix and disclosure: Coordinated with the reporter, typically within 90 days
We follow responsible disclosure practices:
- The reporter privately notifies us of the vulnerability.
- We work together to understand and fix the issue.
- We release a patched version.
- The vulnerability is publicly disclosed after users have had time to update.
This policy applies to the go-htmx module and its published artifacts. Third-party dependencies are managed via Go modules and audited through the CI pipeline.
This project employs several security practices:
- govulncheck: Audits dependencies and the module's own code paths for known Go vulnerabilities
- Dependabot: Automated dependency updates for
go.sumand GitHub Actions, with a 7-day cooldown on newly published versions - SHA-pinned actions: Every GitHub Actions
uses:reference is pinned to a full commit SHA, enforced by apin-checkCI gate - SARIF-normalized scanning: SAST (CodeQL), SCA (OSV-Scanner), and IaC/license (Trivy) findings all land in the repository's Security tab
Disposition against the org's 12-gate map (see the docs site's
github-native-attested-quality-gates spec), so every gap is stated
explicitly rather than silently absent:
| Gate | Status | Where |
|---|---|---|
| SAST | Covered | CodeQL, quality-gates.yml's sast job (required check) + re-run and seam-signed at release |
| SCA / dependency | Covered | OSV-Scanner + dependency review, quality-gates.yml's sca job (required check) + re-run and seam-signed at release |
| Secret detection | Covered | GitHub secret scanning + push protection, enabled (repo setting, confirmed via API) |
| Container / image scan | Covered | Trivy against the built image, release.yml's gate-image job + seam-signed |
| IaC / misconfiguration | Covered | Trivy filesystem scan, quality-gates.yml's trivy job (required check) + re-run and seam-signed at release |
| License compliance | Covered | Same Trivy job as IaC (scan-iac: true covers both) |
| SBOM | Covered | Binaries: anchore/sbom-action + actions/attest-sbom at release. Image: sign-and-attest.yml's own SBOM attestation |
| Vuln disposition (VEX) | Gap, documented | Not wired. The org's reusable-vex.yml is opt-in; revisit if/when a real finding needs a disposition record, not before |
| Build provenance (SLSA) | Covered | Binaries: actions/attest-build-provenance (L3). Image: sign-and-attest.yml (L3, separate signer identity) |
| Supply-chain posture | Covered | OpenSSF Scorecard, quality-gates.yml's posture job (push/schedule only, per Scorecard's own default-branch requirement) |
| Peer review | Gap, documented, org-wide | requiredApprovingReviewCount: 0 on main — confirmed via get_branch_protection. This matches the org's current auto-merge pattern across its public repos, not a go-htmx-specific gap; revisit at the org level if that pattern changes |
| Load / performance | Not applicable, documented | Needs a running app to test against; no deployed instance of this template exists to point k6 at. A real deployment's own repo should wire reusable-k6.yml against itself |
| DAST | Not applicable, documented | Same reasoning as load/performance — reusable-zap.yml needs a running app. Documented opt-in for a real deployment, not this template repo |
Every tagged release (.github/workflows/release.yml) ships five
platform binaries (linux-amd64, linux-arm64, macos-amd64,
macos-arm64, windows-amd64.exe) plus a go-htmx-<version>-checksums.txt
manifest, named go-htmx-<version>-<platform> per the org's release
naming standard (see docs/reference/release-artifacts.md). This section
covers that static-artifact path specifically — a plain Go binary, whose
provenance and SBOM are produced by GitHub's own attestation actions
running in this repo's release.yml, not the org's central image signer.
The release also ships a container image; see "Verifying the Container
Image" below for that separate, image-only attested path.
Verification is independent and keyless: no shared secret, just the gh
CLI with read access to this repo.
gh release download <tag> --repo attested-delivery/go-htmx
shasum -a 256 -c go-htmx-<version>-checksums.txtSLSA provenance (per binary):
gh attestation verify go-htmx-<version>-<platform> \
--repo attested-delivery/go-htmx \
--signer-workflow attested-delivery/go-htmx/.github/workflows/release.yml \
--predicate-type https://slsa.dev/provenance/v1CycloneDX SBOM binding (per binary):
gh attestation verify go-htmx-<version>-<platform> \
--repo attested-delivery/go-htmx \
--signer-workflow attested-delivery/go-htmx/.github/workflows/release.yml \
--predicate-type https://cyclonedx.org/bomSAST (CodeQL), SCA (OSV-Scanner), and IaC/license (Trivy) are re-run at
release time against the exact tagged commit and seam-signed by the org's
central reusable-attest-scan.yml, bound to the sha256 digest of the
release's own go-htmx-<version>-checksums.txt — the one artifact whose
content (a hash of every shipped binary) identifies this exact release.
Under SLSA Build L3 the signer identity is the central signer workflow,
not this repo, so verification pins --owner + --signer-workflow
together (one signer per command) rather than --repo:
SEAM=attested-delivery/.github/.github/workflows/reusable-attest-scan.yml
gh attestation verify go-htmx-<version>-checksums.txt --owner attested-delivery \
--signer-workflow "$SEAM" \
--predicate-type https://attested-delivery.github.io/attestations/sast/v1
gh attestation verify go-htmx-<version>-checksums.txt --owner attested-delivery \
--signer-workflow "$SEAM" \
--predicate-type https://attested-delivery.github.io/attestations/sca/v1
gh attestation verify go-htmx-<version>-checksums.txt --owner attested-delivery \
--signer-workflow "$SEAM" \
--predicate-type https://attested-delivery.github.io/attestations/iac-license/v1Signed is not the same as passed: each attestation proves the named gate
ran and recorded a verdict against this exact release, not that the
verdict was clean — inspect the predicate body (e.g. via
gh attestation verify --format json) to read the actual result.
Every tagged release also publishes a distroless container image to
ghcr.io/attested-delivery/go-htmx, built from the Dockerfile at the
repo root (FROM pinned by digest, not tag — see that file). Unlike the
binaries above, this is the image-only attested path: signed by the
org's central sign-and-attest.yml (a different signer identity from
the static-artifact path), keyed to the pushed image's digest, not its
tag:
IMAGE="ghcr.io/attested-delivery/go-htmx@<digest>" # resolve the digest
# from the release,
# never trust a
# mutable tag alone
SIGNER="attested-delivery/.github/.github/workflows/sign-and-attest.yml"
cosign verify "$IMAGE" \
--certificate-identity-regexp "^https://github.com/${SIGNER}@.*$" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify "oci://$IMAGE" \
--repo attested-delivery/go-htmx \
--signer-workflow "$SIGNER" \
--predicate-type https://slsa.dev/provenance/v1
cosign verify-attestation "$IMAGE" \
--type cyclonedx \
--certificate-identity-regexp "^https://github.com/${SIGNER}@.*$" \
--certificate-oidc-issuer https://token.actions.githubusercontent.comThe image's own Trivy scan verdict is seam-signed and bound to the same digest, same "signed != passed" caveat as above:
gh attestation verify "oci://$IMAGE" --owner attested-delivery \
--signer-workflow attested-delivery/.github/.github/workflows/reusable-attest-scan.yml \
--predicate-type https://attested-delivery.github.io/attestations/container-scan/v1