Skip to content

fix(properties): keep NewReferenceList's digest for a tag and digest - #1450

Merged
TerryHowe merged 2 commits into
oras-project:mainfrom
Rohilalala:fix/reference-list-keeps-digest
Sep 24, 2026
Merged

TerryHowe merged 2 commits into
oras-project:mainfrom
Rohilalala:fix/reference-list-keeps-digest

Conversation

@Rohilalala

@Rohilalala Rohilalala commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

NewReferenceList dropped a digest that followed the tags:

  • NewReferenceList("localhost:5000/repo:v1@sha256:â€Ķ") returned repo:v1 with no digest, while NewReference on the same string returns the tag and the digest.
  • NewReferenceList("localhost:5000/repo:v1,v2@sha256:â€Ķ") returned two plain tags, although the doc says every reference in a list is of one type.

A single tag with a digest now returns one reference with both, its tag trimmed and validated like the tags in a list, and a list of tags combined with a digest returns ErrInvalidReference.

Not changed: repo:@d1,d2 (an empty tag followed by a digest list) is still read as a digest list.

NewReferenceList dropped a digest that followed the tags. A single
"repo:v1@sha256:..." came back as plain "repo:v1", disagreeing with
NewReference on the same input, and "repo:v1,v2@sha256:..." returned two
plain tags although the function documents that all references in a list
are of one type.

Return a single tag with a digest as the one reference NewReference
parses, and reject a list of tags combined with a digest with
ErrInvalidReference.

Signed-off-by: Aditya <205600203+Rohilalala@users.noreply.github.com>
Comment thread registry/remote/properties/reference.go
NewReference skips the tag check when a digest is present, so
"repo: v1 @sha256:..." kept the spaces in its tag and "repo:bad tag@..."
was accepted. Trim and validate the tag as the comma-separated path does.

Signed-off-by: Aditya <205600203+Rohilalala@users.noreply.github.com>

@TerryHowe TerryHowe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 6597011. Fetched the branch, built it, ran go vet and go test ./registry/... — all pass, gofmt clean.

The trimming/validation asymmetry is fixed and covered by tests. Verified behavior:

"localhost:5000/repo: v1 @sha256:â€Ķ"     -> [Tag "v1", Digest sha256:â€Ķ]              (was Tag " v1 ")
"localhost:5000/repo:bad tag@sha256:â€Ķ"  -> invalid reference: invalid tag "bad tag"  (was accepted)
"localhost:5000/repo:v1,v2@sha256:â€Ķ"    -> invalid reference: a list of tags cannot be combined with a digest
"oci://LOCALHOST:5000/nested/repo:v1@â€Ķ" -> localhost:5000/nested/repo:v1@â€Ķ           (scheme strip, lower-casing, nested path intact)
"localhost:5000/repo:@d1,d2"            -> two digest refs                           (unchanged, as the PR body states)

LGTM. Nothing below blocks the merge.

Follow-up worth filing separately

NewReferenceList is now stricter than NewReference for the same input, because NewReference skips ValidateTag whenever Digest != "" (reference.go:126-134):

NewReference("localhost:5000/repo:bad tag@sha256:â€Ķ")     // Tag "bad tag", no error
NewReferenceList("localhost:5000/repo:bad tag@sha256:â€Ķ") // invalid reference: invalid tag "bad tag"

The doc comment no longer claims parity with NewReference, so this PR is self-consistent — but NewReference accepting a regex-invalid tag in Form B looks like a real bug, and fixing it there would make the explicit ValidateTag call here redundant. Suggest an issue rather than expanding this PR.

Optional polish

  • tag@digest1,digest2 is rejected with invalid digest "sha256:â€Ķ,sha256:â€Ķ": invalid checksum digest length, while its mirror tag1,tag2@digest gets a clear mixed-type message. A strings.Contains(digestRef, ",") check would make the two symmetric.
  • The doc comment covers trimming and validation now, but not that tag@digest1,digest2 is rejected, nor that repo:@d1,d2 parses as a digest list with the empty tag dropped. The latter is the least obvious behavior here and is documented only in the PR description.
  • No test pins repo:@d1,d2, the case the PR body calls out as an intentional non-change.
  • In the table loop, len(refs) != tt.wantCount reports with t.Errorf but doesn't return, and the loop then indexes tt.wantTags[i] / tt.wantDigests[i]. A return there would make a future regression fail cleanly instead of panicking.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.01%. Comparing base (d43d96a) to head (6597011).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1450      +/-   ##
==========================================
+ Coverage   91.00%   91.01%   +0.01%     
==========================================
  Files          97       97              
  Lines        7358     7368      +10     
==========================================
+ Hits         6696     6706      +10     
  Misses        651      651              
  Partials       11       11              

☔ View full report in Codecov by Harness.
ðŸ“Ē Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄ïļ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TerryHowe
TerryHowe merged commit 7bbd154 into oras-project:main Sep 24, 2026
10 checks passed
@TerryHowe TerryHowe mentioned this pull request Oct 14, 2026
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.

2 participants