diff --git a/common/pkg/libartifact/store.go b/common/pkg/libartifact/store.go index 8ad7a145d6..4c88fed878 100644 --- a/common/pkg/libartifact/store.go +++ b/common/pkg/libartifact/store.go @@ -886,7 +886,16 @@ func (as *ArtifactStore) getArtifacts(ctx context.Context, _ *libartTypes.GetArt rawManifest: b, } if val, ok := l.ManifestDescriptor.Annotations[specV1.AnnotationRefName]; ok { - artifact.SetName(val) + // OCI layouts created by external tools (e.g. skopeo) may + // store only the tag portion in the annotation (e.g. "1.14.4" + // instead of "docker.io/coredns/coredns:1.14.4"). Only set + // the name when it looks like a qualified reference. + if strings.Contains(val, "/") { + artifact.SetName(val) + } else { + logrus.Warnf("Ignoring bare tag %q in OCI layout annotation %s: use a fully qualified reference instead", + val, specV1.AnnotationRefName) + } } al = append(al, &artifact) diff --git a/common/pkg/libartifact/store_test.go b/common/pkg/libartifact/store_test.go index 8487edbad1..0db0319af3 100644 --- a/common/pkg/libartifact/store_test.go +++ b/common/pkg/libartifact/store_test.go @@ -5,6 +5,7 @@ import ( "context" "crypto/rand" "crypto/sha256" + "encoding/json" "errors" "io" "os" @@ -690,6 +691,46 @@ func TestArtifactStore_List_Multiple(t *testing.T) { } } +func TestArtifactStore_List_BareTagAnnotation(t *testing.T) { + bareTags := []string{"v1", "latest", "1.14.4"} + + for _, bareTag := range bareTags { + t.Run(bareTag, func(t *testing.T) { + as, ctx := setupTestStore(t) + + // Add an artifact normally first, so the store has valid blobs + fileNames := map[string]int{"test.txt": 64} + refName := "quay.io/test/artifact:v1" + helperAddArtifact(t, as, refName, fileNames, nil) + + // Rewrite the index.json to simulate an externally created OCI + // layout (like skopeo) that only stores the tag in the annotation + indexPath := filepath.Join(as.storePath, "index.json") + indexData, err := os.ReadFile(indexPath) + require.NoError(t, err) + + var index specV1.Index + require.NoError(t, json.Unmarshal(indexData, &index)) + + for i := range index.Manifests { + if _, ok := index.Manifests[i].Annotations[specV1.AnnotationRefName]; ok { + index.Manifests[i].Annotations[specV1.AnnotationRefName] = bareTag + } + } + + updatedIndex, err := json.Marshal(index) + require.NoError(t, err) + require.NoError(t, os.WriteFile(indexPath, updatedIndex, 0o644)) + + artifacts, err := as.List(ctx) + require.NoError(t, err) + require.Len(t, artifacts, 1) + assert.Empty(t, artifacts[0].Name, + "bare tag %q should not be used as artifact name", bareTag) + }) + } +} + func TestDetermineBlobMIMEType(t *testing.T) { tests := []struct { name string