Skip to content

Allow single-character repository paths - #2407

Merged
Subserial merged 1 commit into
google:mainfrom
semx:allow-single-char-repository
Aug 18, 2026
Merged

Subserial merged 1 commit into
google:mainfrom
semx:allow-single-char-repository

Conversation

@semx

@semx semx commented Aug 14, 2026

Copy link
Copy Markdown

checkRepository requires at least two characters, so a repository whose path is a single character cannot be parsed — even though the spec allows it and registries serve it.

The reference is valid, and everything else accepts it

The distribution spec's grammar for a repository name is

[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*

[a-z0-9]+ is one or more, so a is a valid path component. distribution/reference parses example.com/a into domain example.com, path a.

Against a local registry:2 with an image pushed by docker as 127.0.0.1:5555/a:v1:

$ curl -s http://127.0.0.1:5555/v2/_catalog
{"repositories":["a"]}
$ curl -s http://127.0.0.1:5555/v2/a/tags/list
{"name":"a","tags":["v1"]}

$ crane ls 127.0.0.1:5555/a
Error: parsing repo "127.0.0.1:5555/a": repository must be between 2 and 255 characters in length: a
$ crane digest 127.0.0.1:5555/a:v1
Error: parsing reference "127.0.0.1:5555/a:v1": could not parse reference: 127.0.0.1:5555/a:v1

docker push and docker pull both round-trip that name; only this library refuses it, and it refuses at parse time, so every consumer — crane, ko, kaniko, cosign, skaffold — is cut off from the image.

With this change:

$ crane ls 127.0.0.1:5555/a
v1
$ crane digest 127.0.0.1:5555/a:v1
sha256:45e09956dc667c5eff3583c9d94830261fb1ca0be10a0a7db36266edf5de9e1d

which is the digest docker push reported for the same image.

Why the minimum isn't load-bearing

It reads like a guard against degenerate names, but it isn't one — the current release accepts all of these:

--        ..        __        //        -.
example.com/--      example.com/..      example.com///

So two-character garbage passes today; the floor only excluded the single-character subset of exactly the same class, and took every valid single-character repository with it. Character-level validation is unchanged by this PR, and the existing TODO(dekkagaijin): use the docker/distribution regexes for validation still describes the way to tighten the whole class properly. checkTag already uses a minimum of 1; the repository was the only element with a 2.

What changes, exactly

I ran 10,133 generated references — hosts (none, docker.io, localhost, ports, IPv6, uppercase), paths (1…256 chars, separators, mixed case), and suffixes (tags, digests, both) — through ParseReference before and after:

inputs 10,133
results that change 225
direction every one of them error → parses; nothing that parsed before stops parsing
single-character alphanumeric repositories (the point of the change) 216
single-character -, ., _, / 9

The 9 are the degenerate class quoted above, now consistent with their two-character forms rather than singled out by length. Nothing else moves.

Tests

example.com/a, example.com/a/b, a and 0 are added to the existing fixtures. They are not vacuous — with the 2 restored, the suite fails:

--- FAIL: TestNewRepositoryStrictValidation
    repository_test.go:56: `example.com/a` should be a valid Repository name,
        got error: repository must be between 2 and 255 characters in length: a
--- FAIL: TestNewRepository
    repository_test.go:74: `example.com/a` should be a valid repository name, ...

go test ./... is green across all 37 packages.

@google-cla

google-cla Bot commented Aug 14, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@semx

semx commented Aug 14, 2026

Copy link
Copy Markdown
Author

@googlebot I signed it!

The distribution spec's grammar for a repository name is

  [a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*

so a path component may be a single character. checkRepository required
at least two, so a reference a registry serves happily could not be
parsed at all:

  $ crane ls 127.0.0.1:5555/a
  Error: parsing repo "127.0.0.1:5555/a": repository must be between 2
  and 255 characters in length: a

while docker push, docker pull and the registry's own API accept the
same name. The minimum for a tag is already 1; only the repository
carried a 2.

The floor was never a guard against degenerate names: --, .., // and
example.com/.. all parse today. It only excluded their single-character
subset, along with every valid single-character repository.
@semx
semx force-pushed the allow-single-char-repository branch from e929917 to 5127c3e Compare August 14, 2026 08:01
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.42%. Comparing base (d3bca10) to head (5127c3e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2407   +/-   ##
=======================================
  Coverage   57.42%   57.42%           
=======================================
  Files         166      166           
  Lines       11558    11558           
=======================================
  Hits         6637     6637           
  Misses       4142     4142           
  Partials      779      779           

☔ 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.

@Subserial
Subserial merged commit 4cb3583 into google:main Aug 18, 2026
19 checks passed
Subserial pushed a commit to Subserial/go-containerregistry that referenced this pull request Aug 18, 2026
The distribution spec's grammar for a repository name is

  [a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*

so a path component may be a single character. checkRepository required
at least two, so a reference a registry serves happily could not be
parsed at all:

  $ crane ls 127.0.0.1:5555/a
  Error: parsing repo "127.0.0.1:5555/a": repository must be between 2
  and 255 characters in length: a

while docker push, docker pull and the registry's own API accept the
same name. The minimum for a tag is already 1; only the repository
carried a 2.

The floor was never a guard against degenerate names: --, .., // and
example.com/.. all parse today. It only excluded their single-character
subset, along with every valid single-character repository.
Subserial pushed a commit to Subserial/go-containerregistry that referenced this pull request Aug 18, 2026
The distribution spec's grammar for a repository name is

  [a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*

so a path component may be a single character. checkRepository required
at least two, so a reference a registry serves happily could not be
parsed at all:

  $ crane ls 127.0.0.1:5555/a
  Error: parsing repo "127.0.0.1:5555/a": repository must be between 2
  and 255 characters in length: a

while docker push, docker pull and the registry's own API accept the
same name. The minimum for a tag is already 1; only the repository
carried a 2.

The floor was never a guard against degenerate names: --, .., // and
example.com/.. all parse today. It only excluded their single-character
subset, along with every valid single-character repository.
@semx
semx deleted the allow-single-char-repository branch September 13, 2026 10:34
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
crane 0.22.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>## What's Changed
* mutate: let Time and Canonical take tarball.LayerOption by @mzihlmann in google/go-containerregistry#2403
* build: add multi-architecture Cloud Build configurations for crane, gcrane, and krane by @tprussak in google/go-containerregistry#2412
* remote: resolve push-check credentials against the repository by @mzihlmann in google/go-containerregistry#2411
* Allow single-character repository paths by @semx in google/go-containerregistry#2407
* fix: add missing substitutions and workspace cleanup to new build files by @tprussak in google/go-containerregistry#2413
* remote: retry failed Puller and Pusher initialization by @iahsanGill in google/go-containerregistry#2406
* build(deps): bump the actions group across 1 directory with 8 updates by @dependabot[bot] in google/go-containerregistry#2405
* build(deps): bump the go-deps group across 1 directory with 3 updates by @dependabot[bot] in google/go-containerregistry#2415
* go.mod: bump Go version + add toolchain directive to replace .go-version file by @Subserial in google/go-containerregistry#2416
* fix: Fix new build options and provenance by @tprussak in google/go-containerregistry#2417
* fix(build): unify new build flow into cloudbuild_v2.yaml by @tprussak in google/go-containerregistry#2419

## New Contributors
* @mzihlmann made their first contribution in google/go-containerregistry#2403
* @tprussak made their first contribution in google/go-containerregistry#2412
* @semx made their first contribution in google/go-containerregistry#2407

**Full Changelog**: https://github.com/google/go-containerregistry/compare/v0.21.9...v0.21.10</pre>
  <p>View the full release notes at <a href="https://github.com/google/go-containerregistry/releases/tag/v0.22.0">https://github.com/google/go-containerregistry/releases/tag/v0.22.0</a>.</p>
</details>
<hr>

See merge request: Harmonybrew/homebrew-core!17674
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.

3 participants