Do not open a public issue for a suspected vulnerability. Use GitHub's private
vulnerability reporting on the affected Spice repository. If repository
routing is unclear, report it privately through
spice-framework/spice.
Include affected versions or commits, reproduction steps, expected impact, and any known mitigations. Maintainers will acknowledge the report, establish a private remediation plan, coordinate affected repositories, and publish an advisory when users can safely update.
Spice is pre-1.0 and does not yet have a generally supported release line. Security fixes target the latest published preview and the current main branch. Each repository's release notes will state support changes explicitly.
Security-sensitive areas include annotation and generated-code injection, module/tool supply chains, secret handling, request binding, authentication and authorization defaults, module boundary bypasses, external-service clients, and editor-applied source edits. Security features require negative tests and documented secure defaults.
Each publishing repository must use a maintainer-generated Ed25519 key whose
private half remains user-owned. Commit only the reviewed public PEM at
security/release/ed25519-public.pem (or pass its repository-relative path as
the reusable workflow input). Store the private material as the repository
Actions secret SPICE_LIBRARY_RELEASE_SIGNING_KEY and map only that named
secret into the reusable workflow. GitHub does not make a caller environment
secret available to a cross-repository reusable workflow job, so the secret
must not be stored only on the environment. Never use secrets: inherit.
GitHub secret storage removes the PEM's terminal newline; the protected signing
job normalizes CRLF to LF and restores exactly one terminal newline before the
strict canonical parser reads it.
Require trusted-reviewer approval for the release-signing environment that
gates the called signing job.
Create a separate protected release-publish environment with trusted-reviewer
approval and no secrets. Validation runs before either environment, signing has
only contents:read, independent verification receives only signed artifacts
and the public anchor, and publication alone receives contents:write. Never
put the private key in an organization secret, expose it to validation,
verification, or publication, or generate a production key in CI. The explicit
one-secret workflow_call mapping is the only supported transfer boundary.
The release workflow must not execute a signer or verifier selected by the
tagged candidate. Candidate-owned checks run in a separate uncredentialed job
whose filesystem and outputs are not trusted as release authority. Planning and
signing build spice-dev from immutable commit
4c308d1b9fda11cb2b045f2e0d9e1616d32d007d; verification builds the independent
verifier from immutable commit
71211498297c9ab77cc05c4844db5e64e0170896. Both builds use the trusted
repositories' vendor trees, network-disabled Go settings, isolated build/module
caches, and -trimpath. Fresh candidate checkouts remain inert inputs after the
uncredentialed validation phase.
The uncredentialed candidate-validation job may bootstrap only the exact
candidate-owned pinned public tool graph through Go's proxy and checksum
database. It has no secrets or release authority, uses isolated caches, and
must leave the checkout clean. The following candidate-owned verify-release
step reuses those caches with GOPROXY=off, GOSUMDB=off, and private-module
exceptions cleared. Trusted renderer, signer, and verifier builds remain
vendor-only and network-disabled.
The workflow admits only tagged commits that descend from fetched origin/main.
Its public anchor path must be clean and repository-relative, resolve without a
symlink in any component, identify an exact 100644 Git blob in the tagged
commit, and match the checked-out bytes. These workflow checks supplement,
rather than replace, protected environment reviewers and server-side tag rules.
Publication also resolves both lightweight and annotated remote tag forms
immediately before and after release creation and requires the peeled target to
remain the exact workflow commit and the direct tag object to remain unchanged.
The publishing CLI is explicitly scoped to the caller repository.
Generic Go modules and binary distributions use separate GitHub artifact
attestation workflows backed by Sigstore rather than a repository or
organization private key. Each reusable workflow accepts
only the canonical module input and no secrets. The caller's permission ceiling
must include contents:write, id-token:write, attestations:write, and
artifact-metadata:write, but the called workflow narrows these capabilities
per job:
- candidate validation, rendering, and independent verification receive only
contents:readand cannot mint an OIDC identity; release-attestationreceivesid-token:write,attestations:write, andartifact-metadata:write, has no content-write authority, and consumes only independently verified bytes;- bundle verification receives only
contents:read; and release-publishalone receivescontents:write, cannot mint OIDC identity, and consumes only the authenticated artifact set.
Rendering and verification are built with Go 1.26.5, -mod=vendor, -trimpath,
network-disabled module settings, isolated caches, and distinct immutable
organization repository commits. Within this later central trust boundary,
the independently built verifier alone may use the public Go proxy and checksum
database in fresh caches to authenticate the selected graph and reproduce
vendor; it builds the archive-materialized
Git tree offline and never the caller worktree. It then re-lists untrusted
renderer input and copies only its profile's closed artifact set into a new
verifier-owned directory: four files for modules or nine files for the current
six-target distribution policy. Only those copied bytes cross into
attestation. Candidate-owned
tools run only in the earlier uncredentialed gate. The keyless verifier requires
the public GitHub OIDC issuer, exact caller repository, exact source commit and
tag ref, organization
reusable-workflow path and commit, and a GitHub-hosted runner. The caller must
repeat its immutable uses revision through the required workflow_commit
input; the signed certificate, not that input by itself, proves the match.
The verifier uses the fully qualified workflow as its signer selector and does
not combine it with --signer-repo: current gh versions treat those selectors
as mutually exclusive, while the workflow value already fixes the
spice-framework/.github repository and workflow path.
Publication resolves both annotated and lightweight remote tags before and
after release creation.
The portable Sigstore bundle is published beside the source archive, SBOM,
release metadata, and checksums so consumers are not limited to a live GitHub
API lookup. Consumers should verify every downloaded artifact with gh attestation verify, the bundle, the expected spice-framework repository, the
organization reusable-workflow path, and the immutable workflow commit they
reviewed. Offline consumers must separately acquire a current trusted-root
bundle through GitHub's documented TUF-backed process.
The generic workflow pins separately reviewed renderer and independent-verifier
commits and the semantic regression check requires those exact object IDs.
Changing either pin requires successful local trust-boundary checks, a newly
pinned caller workflow, protected secret-free release-attestation and
release-publish environments, repository tag immutability, and a public
end-to-end rehearsal. The module and distribution workflows reject each
other's profile, and both reject starters; no profile may bypass its dedicated
contract.
The trust model follows GitHub's documentation for artifact attestations, OIDC in reusable workflows, and offline attestation verification.