Skip to content

feat(plugin-host): cosign-based signing + registry publishing pipeline - #457

Merged
tayebmokni merged 3 commits into
mainfrom
feat/plugin-signing
May 25, 2026
Merged

feat(plugin-host): cosign-based signing + registry publishing pipeline#457
tayebmokni merged 3 commits into
mainfrom
feat/plugin-signing

Conversation

@tayebmokni

Copy link
Copy Markdown
Contributor

Closes #130, #233, #274.

Plugin supply chain:

  • packages/go/plugins/sign: sigstore + air-gapped cosign verification with publisher.identity match + Rekor cache
  • gonext plugin sign CLI (keyless + keyed fallback)
  • .github/workflows/plugin-publish.yml: build -> scan -> capability-diff -> sign -> publish to OCI

Capability-diff prints what cap each new version added/removed -- operators see the surface they're accepting.

🤖 Generated with Claude Code

tib0o0o and others added 3 commits May 25, 2026 21:24
…233)

Adds packages/go/plugins/sign with:
- Identity parser/matcher for github.com/<org>, gitlab.com/<group>,
  mailto:<email>, sha256:<fp> publisher identity schemes.
- Verifier with keyless (Fulcio + Rekor) and keyed (long-lived cosign
  key) modes. Falls back to keyed when no Fulcio cert is present.
- Canonical bundle-digest computation that excludes signatures/.
- In-memory Rekor-result cache keyed by canonical digest.
- EnvKeySource (COSIGN_PUBLIC_KEY / COSIGN_PUBLIC_KEY_FILE) and a
  bundle-local PEM override for self-contained air-gapped bundles.
- Wire shape: signatures/cosign.sig, signatures/cosign.bundle,
  signatures/publisher.json. publisher.json lives in signatures/ (not
  manifest.json) because the gonext.io/v1 manifest schema is frozen
  with additionalProperties:false.

Tests cover keyless happy path, identity mismatch rejection, Rekor
unreachable, future-dated Rekor entries, keyed happy path, bundle-local
PEM override, no-key keyed mode, missing signature, cache hit, env key
source, and PEM fingerprint stability across CRLF/LF.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Claude (for tayebmokni) <tayeb.mokni@gmail.com>
Wraps cosign sign-blob to produce the bundle's canonical-digest
signature and writes signatures/cosign.sig, signatures/cosign.bundle,
and signatures/publisher.json into the bundle zip.

Keyless mode (default) requires --identity (the SAN cosign will bind);
keyed mode (--key cosign.key) is for air-gapped publishers and embeds
the public key into the bundle so verification is self-contained.

The CLI's canonical-digest computation routes through
plugins/sign.MarshalCanonicalBundleForSigning so the host verifier and
the signer agree on what bytes the signature covers (signatures/ is
stripped before hashing). Existing signatures are replaced when a
bundle is re-signed.

Tests cover dry-run digest+identity rendering, missing-identity
rejection, bad-identity rejection, and missing-bundle handling.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Claude (for tayebmokni) <tayeb.mokni@gmail.com>
Adds .github/workflows/plugin-publish.yml: triggered by `plugin/<slug>/v*`
tags, the workflow builds the bundle, runs `gonext plugin test`,
scans with trivy (HIGH+CRITICAL exit-on-find), computes a
capability-diff against the previous tag, signs with cosign keyless
(GitHub OIDC), and pushes to ghcr.io as an OCI artefact under
ghcr.io/<owner>/plugins/<slug>:<version>. cosign attaches the
signature to the OCI ref so operator-side install can verify against
the registry without unzipping.

OCI artefact contract documented inline in the workflow:
- layer mediaType:  application/vnd.gonext.plugin.bundle.v1+zip
- config mediaType: application/vnd.gonext.plugin.config.v1+json
- config body:      { slug, version, publisher }

Adds `gonext plugin diff` (cli/gonext/cmd/plugin/diff.go) that emits
the added/removed/unchanged capability set between two bundle
versions, in human or --json format. Accepts both v1-schema arrays
and legacy lifecycle map-shaped capabilities so the diff works
through the migration window. The pipeline writes the diff into the
GitHub-summary panel so operators see the surface they're accepting
before they activate.

Tests cover added/removed/unchanged splits, the legacy map shape,
the no-change message, and arg validation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Claude (for tayebmokni) <tayeb.mokni@gmail.com>
@tayebmokni
tayebmokni enabled auto-merge (squash) May 25, 2026 19:29
@github-actions

Copy link
Copy Markdown

Heads up — this PR touches strings that often signal a security disclosure (vulnerab, CVE-, exploit, bypass, or auth bypass).

If this PR fixes or describes a real vulnerability that has not yet been publicly disclosed, please stop and use the private path:

  1. Open a private security advisory, or
  2. Email security@gonext.io with subject [SECURITY] GoNext - <summary>.

See /SECURITY.md for the full disclosure flow and /docs/16-bug-bounty.md for bounty terms.

If this is a false positive (test fixture, doc update, release notes, etc.) please ignore this comment — the check is advisory only and does not block the PR.

Matched files:

  • .github/workflows/plugin-publish.yml

@tayebmokni
tayebmokni merged commit 182f8bc into main May 25, 2026
12 of 15 checks passed
@tayebmokni
tayebmokni deleted the feat/plugin-signing branch May 25, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin signing pipeline (cosign / Sigstore)

2 participants