Skip to content

Cosign signed status not shown for child artifacts in OCI index depth view #17

@Vad1mo

Description

@Vad1mo

Summary

When a multi-arch (OCI index / manifest list) image is signed with cosign, the parent manifest list correctly shows the green checkmark in the "Signed" column, but all child platform images show a red X (unsigned) when viewed through the OCI index depth view.

This affects both scenarios:

  • Signing the manifest list by tag (signature attached to the index)
  • Signing a child image directly by digest

Steps to Reproduce

  1. Push a multi-arch image to Harbor:

    crane copy docker.io/library/alpine:latest localhost:8080/cosign-test/signed-image:alpine --insecure
  2. Sign the manifest list with cosign:

    cosign sign --key cosign.key --allow-insecure-registry --allow-http-registry \
      localhost:8080/cosign-test/signed-image:alpine
  3. Verify the signature:

    cosign verify --key cosign.pub --allow-insecure-registry --allow-http-registry \
      localhost:8080/cosign-test/signed-image:alpine
    # Verification succeeds
  4. Open the Harbor UI → Projects → cosign-test → signed-image → Artifacts tab

    • The manifest list shows green checkmark in the Signed column ✅
  5. Click the OCI index icon to drill into child platform images

    • All child images show red X (unsigned) ❌

Expected Behavior

Child artifacts within a signed OCI index should either:

  • Inherit the signed status from their parent manifest list, or
  • At minimum, display an indicator that the parent index is signed

Actual Behavior

  • Parent manifest list: Green checkmark (correct)
  • Child images (linux/amd64, linux/arm64, etc.): Red X (misleading)

Parent manifest list — green checkmark

Parent shows signed

Child images in depth view — all red X

Children show unsigned

API Confirmation

The API confirms the signature accessory is only attached to the parent, not propagated to children:

# Parent — has cosign accessory
curl ".../artifacts/sha256:25109184...?with_accessory=true"
# → accessories: [{"type": "signature.cosign", ...}]

# Child — no accessories
curl ".../artifacts/sha256:1529d135...?with_accessory=true"
# → accessories: null

This is technically correct (cosign signs exactly the digest it targets), but the UX is misleading since users expect the signed status to be visible on child images when the parent index is signed.

Customer Report Context

A customer on v2.13.2 reported this exact scenario: they signed an image, cosign verify succeeds, but the Harbor UI shows red X. Their cosign verify output confirmed the signature was on a specific child digest within a manifest list.

Related upstream issues (different root cause — cosign v3 bundle format):

Environment

  • Harbor: latest main branch (post-v2.14.1)
  • cosign: v3.0.4 (also reproduced with v2.2.4)
  • Image: multi-arch alpine:latest (all platforms)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions