Skip to content

Keep Go modules that differ only in case apart in the cache - #505

Closed
pinguinfuss wants to merge 2 commits into
git-pkgs:mainfrom
pinguinfuss:issue-466-go-module-case
Closed

pinguinfuss wants to merge 2 commits into
git-pkgs:mainfrom
pinguinfuss:issue-466-go-module-case

Conversation

@pinguinfuss

Copy link
Copy Markdown
Contributor

Fixes #466.

The PURL lowercases Go module paths, so github.com/Sirupsen/logrus and github.com/sirupsen/logrus shared one cache entry and whichever came first was served for both. The go command then failed with a checksum mismatch.

  1. A module path with capitals now keeps them in the cached zip's filename, in the escaped form: github.com_!sirupsen_logrus@v1.0.6.zip. Lowercase paths keep logrus@v1.0.6.zip, so existing entries stay valid. The PURL stays lowercased, so retention, cooldown and denylist rules match as before.
  2. An entry from before this change can still hold the wrong module's zip. The storage path names the module that was fetched, so the handler checks it on a hit and refetches when it names the other spelling.

Tested by hand against proxy.golang.org with go mod download: both logrus spellings come back with the right checksums, and a cache filled by main is repaired on the first request.

Left open: during a rolling upgrade where old and new instances share one database, an old instance can write a mixed-up entry again. A new instance repairs it on the next request.

…o github.com/Sirupsen/logrus and github.com/sirupsen/logrus no longer share one cache entry. Lowercase paths keep their filename.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please bound the case-sensitive cache filename and cover long module paths with the file storage backend.

Comment thread internal/handler/go.go
if module == strings.ToLower(module) {
return fmt.Sprintf("%s@%s.zip", lastComponent(module), version)
}
return fmt.Sprintf("%s@%s.zip", strings.ReplaceAll(encodeGoModule(module), "/", "_"), version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Flattening the entire module path into one filename can exceed the filesystem's component-length limit, even when every component of the original path is short. I reproduced this through GoHandler.Routes with real file storage and a path accepted by module.CheckPath: example.com/Org/ + 24 repetitions of subpackage/ + leaf (284 characters). GET of its v1.0.0 zip returns 502; storage reports file name too long when creating the temporary file. The same test on main returns 200 with the expected bytes. Use a bounded case-sensitive filename, for example a digest of the module identity, allowing room for the storage temporary-file suffix, and add a handler regression test using file storage.

@andrew

andrew commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

#540 fixed #466 the same way, with a hashed filename that stays bounded for long module paths, so I'm closing this as superseded. Thanks for the investigation and the by-hand checks against proxy.golang.org.

@andrew andrew closed this Oct 22, 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.

Go: modules that differ only in case are served each other’s zip

2 participants