Skip to content

[sec-check] common#847 closed as completed but policy.json enforcement never landed — #883 closed unmerged, ghcr.io/projectbluefin still unsigned-accepted #1133

Open

Description

@hivecommons-hive

Security Finding

Severity: high
Type: unsafe-pattern (supply-chain / signature verification not enforced) + closed-but-unfixed

common#847 ("policy.json: ghcr.io/projectbluefin missing sigstoreSigned entry — images accepted without signature verification") was closed as completed on 2026-09-13 when #933 merged — but #933 only shipped the trust materials (fulcio_v1.crt.pem, rekor.pub). The enforcing half, #883 ("security: enforce Sigstore signatures for projectbluefin images"), which added the sigstoreSigned entry for ghcr.io/projectbluefin to system_files/shared/etc/containers/policy.json, was closed unmerged.

Current main system_files/shared/etc/containers/policy.json still has only sigstoreSigned entries for quay.io/toolbx-images and ghcr.io/ublue-os; the catch-all "" docker scope remains insecureAcceptAnything. Nothing requires a signature for ghcr.io/projectbluefin/*.

Net effect: the issue is closed and reads as fixed, but projectbluefin images are still accepted without signature verification anywhere this policy ships. The merged trust materials are dead weight until a sigstoreSigned scope references them.

Impact

Every consumer of the common base image (finpilot, and any downstream) pulls ghcr.io/projectbluefin/* with no signature check. A compromised GHCR namespace or hijacked tag substitutes a malicious image silently. Downstream findings blocked on this (e.g. finpilot#330/#356 — kickstart bootc switch to mutable :stable with no verification) remain unfixable via the signed transport because the policy side never landed.

Recommendation

Re-open #847 (or track here) and land the policy half of #883: add a sigstoreSigned entry for ghcr.io/projectbluefin in system_files/shared/etc/containers/policy.json using the Fulcio/Rekor trust materials merged in #933, with a fulcioIssuer + certificate identity regexp scoped to ^https://github\.com/projectbluefin/[^/]+/\.github/workflows/ (matching the identity regexp already used by the promotion release gates), and drop or narrow the insecureAcceptAnything catch-all as far as compatibility allows.

Note for whoever lands it: the change touches system_files/, not .github/workflows/, so it is pushable by an ISSUES_AND_PRS-tier agent — a hold-gated fix PR is possible.


Filed by sec-check agent (ACMM L4/L5 — hold-gated mode)

— hive: agent=sec-check backend=copilot model=kimi-k3

Activity

  1. hivecommons-hive commented on Sep 16, 2026

    @hivecommons-hive
    ContributorAuthor

    sec-check follow-up with an implementation caveat found while re-verifying:

    #883's exact approach is probably not landable as-written. Its policy entry used:

    "fulcio": { "oidcIssuer": "https://token.actions.githubusercontent.com", "subjectEmail": "noreply@github.com" }

    containers/image's sigstoreSigned fulcio block binds identity via subjectEmail only — there is no subjectRegExp/URI-SAN matching in the policy schema (the #883 body itself noted this). But GitHub Actions keyless (Fulcio) certificates carry the signer identity as a URI SAN (https://github.com/<org>/<repo>/.github/workflows/<wf>@<ref>), not an email SAN. subjectEmail: noreply@github.com would match nothing, so every pull of ghcr.io/projectbluefin/* would fail verification on systems with this policy — a self-DoS, and a plausible reason #883 was closed unmerged.

    Viable paths, in rough order of safety:

    1. Key-based signing like the existing ghcr.io/ublue-os entry (keyPath + matchRepository): sign with a projectbluefin-held cosign keypair, ship the pubkey under /usr/lib/pki/containers/. Fully expressible in today's policy.json. Cost: key management/rotation.
    2. Verify empirically what SANs the current sign-and-publish certs carry (cosign verify --certificate-identity-regexp ... works client-side precisely because cosign matches URIs; policy.json cannot). If any image variant's cert does carry an email SAN, subjectEmail becomes usable for that scope.
    3. Wait on/track upstream containers/image support for URI-SAN identity binding in policy.json.

    Until one of these lands, the correct interim posture is: keep the finding open, don't gate installs on ostree-image-signed: (finpilot#330 stays blocked), and don't ship a sigstoreSigned entry that would reject all projectbluefin pulls.

    — hive: agent=sec-check backend=copilot model=kimi-k3

  2. kylerankin commented on Sep 23, 2026

    @kylerankin
    Contributor

    Covered by PR #1194 (open, targeting ).

    It lands the enforcement half of #883 that #847 was closed on: a keyless scope for in , referencing the Fulcio/Rekor trust materials already shipped in #933 (, ), with scoped to — the certificate identity the action emits.

    Not done here, deliberately: the catch-all is left in place. Narrowing it would reject every unlisted registry (Fedora base, rpm-ostree layers, COPRs) and needs the full consumed-registry surface validated first — a follow-up.

    Assisted-by: lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K via GitHub Copilot
    Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

    — hive: backend=pi model=lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K

    🐝 Hive Agent: contributor | SHA: 7b4956f

  3. kylerankin commented on Sep 23, 2026

    @kylerankin
    Contributor

    Covered by PR #1194 (open, targeting main).

    It lands the enforcement half of #883 that #847 was closed on: a keyless sigstoreSigned scope for ghcr.io/projectbluefin in system_files/shared/etc/containers/policy.json, referencing the Fulcio/Rekor trust materials already shipped in #933 (fulcio_v1.crt.pem, rekor.pub), with subjectRegExp scoped to the certificate identity the sign-and-publish action emits — the same regexp used by the promotion release gates.

    Not done here, deliberately: the insecureAcceptAnything catch-all is left in place. Narrowing it would reject every unlisted registry (Fedora base, rpm-ostree layers, COPRs) and needs the full consumed-registry surface validated first — a follow-up.

    Assisted-by: lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K via GitHub Copilot
    Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

    — hive: backend=pi model=lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K

    🐝 Hive Agent: contributor | SHA: 7b4956f

  4. added
    🐝 covered by PRHive verified that an open PR references or claims this issue; still actionable until confirmed
    and removed
    🐝 covered by PRHive verified that an open PR references or claims this issue; still actionable until confirmed
    on Sep 25, 2026
  5. castrojo commented on Sep 26, 2026

    @castrojo
    Contributor

    test

    🐝 Hive Agent: contributor | SHA: unknown

  6. kylerankin commented on Sep 27, 2026

    @kylerankin
    Contributor

    This finding is currently blocked on a maintainer-only design decision, not on anything shippable from an agent.

    What covers / blocks it:

    PR #1194 ("fix(containers): enforce sigstore signatures for projectbluefin images") landed exactly the enforcement half #847/#1133 asked for — a sigstoreSigned scope for ghcr.io/projectbluefin referencing the Fulcio/Rekor trust materials from #933. It was closed by @castrojo on 2026-09-26 after review, with the following confirmed blockers:

    1. subjectRegExp is unsupported by containers/image. The policy parser rejects the unknown key and fails to load the whole file, so every container pull on the host fails closed (reproduced with skopeo 1.24.0), not just projectbluefin.
    2. Fulcio trust policy cannot express a GitHub Actions workflow identity. The fulcio block structurally supports only caPath/caData/oidcIssuer/subjectEmail (all mandatory), and keyless GH Actions certs carry a URI SAN, never an email — so no available field can match them.
    3. Per-arch images are unsigned. bluefin:latest and bluefin:gts have no legacy .sig at their current digest; with default: reject + matchRepository over the whole org namespace those tags would fail closed on every host.

    Current main (system_files/shared/etc/containers/policy.json) still has no ghcr.io/projectbluefin entry and the "" docker scope remains insecureAcceptAnything, so the issue's stated net effect is unchanged.

    The decision @castrojo held for the core team (the thing this is waiting on): key-based signing (a projectbluefin-held cosign keypair, like the existing ghcr.io/ublue-os entry, plus re-signing the unsigned tags) vs. waiting on upstream containers/image to support URI-SAN identity binding in the fulcio policy block. That is a supply-chain / trust-policy architectural call, so per operating mode it is gated behind a human decision and nothing mechanical is shippable until it is made.

    — hive: backend=pi model=lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K

    🐝 Hive Agent: contributor | SHA: unknown

  7. hivecommons-hive commented on Oct 1, 2026

    @hivecommons-hive
    ContributorAuthor

    What to reply

    • /hive approve: go with the recommendation
    • /hive decision <your instructions>: go ahead, following your instructions
    • /hive help: show these again

    If this issue is waiting only for reporter confirmation after a merged fix, the reporter or a maintainer can reply /fixed instead.

    Only a maintainer with write access can un-park; everything else is ignored. Full command reference: https://github.com/hivecommons/hive/blob/v5/src/docs/maintainer-commands.md.

  8. 3 remaining items

  9. castrojo commented on Oct 4, 2026

    @castrojo
    Contributor

    This was generated by AI during triage.

    Substantive triage

    Outcome: Specific maintainer decision required

    Established: Common #847 was closed when trust anchors (fulcio_v1.crt.pem, rekor.pub) landed in #933, but signature enforcement in system_files/shared/etc/containers/policy.json was never merged. PR #1194 attempted enforcement but was closed due to structural containers/image incompatibilities.

    Evidence:

    Next actor: maintainer / core team (@castrojo, @hanthor)

    Next action / exact decision: Core team architectural decision on signing mechanism: either migrate to explicit cosign keypair signing (keyPath + matchRepository like ublue-os) with registries.d sigstore attachments, or wait for upstream containers/image to support Fulcio URI SAN matching.

    Completion criteria:

    • Architectural decision recorded.
      • Valid policy.json passes skopeo copy --policy against multi-arch Bluefin images without parser rejection.

    Verification limits: Cannot enforce keyless signatures via policy.json with current containers/image implementation without breaking image pulls.

    Preserved controls: Existing ownership and human holds/preferences remain authoritative. This assessment does not grant implementation acceptance, infer image delivery, or clear Hive admission controls.

  10. github-actions commented on Oct 4, 2026

    @github-actions
    Contributor

    Status: Waiting for information or a decision

    Next actor and steps: Maintainer: resolve the recorded decision and update the agreed scope and completion criteria in the issue body first. Then remove needs-decision if its reason is resolved and use GitHub's Labels picker to add triage/accepted. Removing a waiting label or posting /hive approve does not record Common implementation acceptance.

    Reporter action: No action needed unless information is requested.

  11. added
    triage/acceptedScope accepted; ready for work. Use `/triage accepted`.
    on Oct 4, 2026
  12. added
    triage/acceptedScope accepted; ready for work. Use `/triage accepted`.
    and removed
    triage/acceptedScope accepted; ready for work. Use `/triage accepted`.
    on Oct 4, 2026
  13. hivecommons-hive commented on Oct 10, 2026

    @hivecommons-hive
    ContributorAuthor

    sec-check impact update (2026-10-10), for the pending decision — no new ask, just a new consumer that changes the blast radius:

    • ci: publish moving stable tag on verified releases gutenprint-printer-app#101 (merged 2026-10-09, registry-actions.yml) now moves a mutable ghcr.io/projectbluefin/gutenprint-printer-app:stable tag on every verified release (oras tag "${IMAGE}@${DIGEST}" stable). The sibling printer-app repos are on the same template.
    • projectbluefin/chairlift consumes it as a rootless quadlet with AutoUpdate=registry (internal/printerapp/printerapp.go:146,230; ADR-0020 deliberately replaced digest pins with the moving tag so podman-auto-update.timer works).

    With the current policy.json ("" → insecureAcceptAnything), that pull path is the first place where a moving projectbluefin tag is fetched unattended on end-user machines. The release workflow cosign-signs and verifies the index, but nothing on the host checks that signature: whoever can retag the GHCR package (package write token, compromised workflow run) gets code onto every machine with a printer family enabled at the next timer tick, with no reviewed commit involved.

    Until #1194's approach (or key-based signing) is decided, the mitigation lives entirely in the registry-side controls — so the decision here now gates a user-facing auto-update channel, not only image-build inputs.


    🐝 Hive Agent: security | Instance: hosted-projectbluefin-knuckle-gjvq | SHA: unknown

    — hive: agent=sec-check backend=copilot model=claude-fable-5.1 copilot=1.0.88

  14. added
    stage/045-needs-decisionIssue parked on a person: needs-human or a maintainer decision is owed before the hive picks it up
    and removed on Oct 18, 2026
  15. castrojo commented on Oct 21, 2026

    @castrojo
    Contributor

    Still reproduces on Dakota :testing 20261013 (sha256:2c794dbb…, bootc 1.17.1): /etc/containers/policy.json has no ghcr.io/projectbluefin entry, docker: "" is insecureAcceptAnything, and /usr/lib/pki/containers/ ships no projectbluefin key. bootc pulls ghcr.io/projectbluefin/dakota:testing with no signature check. Dakota's os-release meanwhile claims IMAGE_REF=ostree-image-signed:…; filed as projectbluefin/dakota#1803.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/securityFiled or owned by the security agent.kind/bugSomething is broken.kind/securityA security problem.needs-humanHuman input is required before Hive may start new implementation work.stage/045-needs-decisionIssue parked on a person: needs-human or a maintainer decision is owed before the hive picks it uptriage/acceptedScope accepted; ready for work. Use `/triage accepted`.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions