This release fixes five security issues, one of them critical. Please upgrade.
Huge thanks to @arpitjain099, @sondt99, @tonghuaroot, @Faze-up, @456789TZ, @kodareef5, and @thientd for reporting these.
Security
Privilege escalation to global admin (GHSA-952g-9vq9-782q, critical)
The permission check written for repository commands was also used to guard
the global user and settings commands. Because that check reads the
command's first argument as a repository name, any authenticated user could
create a repository named to match and let the check pass, reaching commands
meant for server administrators only.
Global commands now use a dedicated server-admin gate that never consults
repository access. Two related escalations found alongside it are fixed as
well: a read-write collaborator could grant admin access to any account, and
could remove or downgrade a collaborator ranked above them. Both are now
capped at the caller's own access level, with server admins exempt.
Vulnerable SSH library (GHSA-v57m-p5gf-9mf5, high)
The SSH server reached code paths in golang.org/x/crypto/ssh that were
fixed upstream in 0.52.0. Dependencies are updated, along with the Go
toolchain, clearing fifteen govulncheck findings in total.
Server-side request forgery via repo import and mirror sync (GHSA-vpj5-m56f-8h3f, medium)
Any authenticated user could import a repository from an arbitrary URL,
including loopback, private, and cloud metadata addresses. Scheduled mirror
syncs re-fetched that URL every ten minutes, so a single import kept the
access alive with no further interaction.
Remotes are now checked against private and internal ranges before any
network access, and re-checked before each sync. Because the fetch itself is
done by a git subprocess that makes its own network decisions, redirect
following is disabled for these operations and validated hostnames are
pinned to the address that was checked. Addresses in non-standard forms such
as 0177.0.0.1 are rejected rather than resolved, and git:// remotes are
now validated too.
One behavior change worth noting: moved repositories must be imported from
their current URL, since redirects are no longer followed.
SSRF guard bypass via IPv6 transition addresses (GHSA-3qq2-64gj-j5qf, medium)
The guard above reasoned about IPv4 and IPv6 as separate address spaces.
Several IPv6 forms carry an IPv4 address inside them, and those went
uninspected, so an internal host could be named in a form the check read as
ordinary public IPv6. The NAT64 encoding of the cloud metadata endpoint,
64:ff9b::a9fe:a9fe, passed.
6to4, both NAT64 prefixes, Teredo, the deprecated IPv4-compatible form, and
the 6to4 relay anycast range are now blocked in full rather than decoded and
re-checked. That removes the whole class of bypass instead of depending on
each decoding being implemented correctly. The consequence: such an encoding
of a genuinely public address is now rejected too.
Cross-repository disclosure of Git LFS lock metadata (GHSA-xqg9-4q65-m2wf, medium)
The Git LFS lock endpoints checked the caller's permissions against the
repository in the request URL, but looked locks up by their global ID alone.
Anyone with write access to any repository could walk lock IDs and read locks
belonging to repositories they cannot access, disclosing file paths, who held
the lock, and when it was taken. Lock file paths from a private repository
are themselves sensitive, since they reveal what a project is working on.
Locks are now looked up scoped to the repository being requested, matching
how every other lock query already behaved.
Also hardened
SSH sessions are now isolated from one another in the underlying wish
library, and webhook delivery reads are scoped to the named repository so
stored request and response bodies cannot be read across repositories.
Changelog
New!
- 2d0a636: feat(backend): Support README files in the second-depth directories (@5ouma)
- fdd9fd2: feat(commands): add mirror command (@taciturnaxolotl)
- 4ef0573: feat: add a taskfile (@taciturnaxolotl)
- 4f07b43: feat: anon and keyless access (@mdgreenwald)
- 66a1806: feat: default repo option (#915) (@mdgreenwald)
- e6d975b: feat: improve smart HTTP clone performance (#913) (@akhilles)
Fixed
- 6d2a663: fix(hooks): propagate custom hook failures (#910) (@Solaris-star)
- b2c97e0: fix(jobs): disable default timeout for mirror sync git commands (#904) (@syf2211)
- e7a94cb: fix(ui): prevent repo duplication when copying from filtered list (#902) (@syf2211)
- 1002e92: fix(ui): use path.Join for git tree paths on Windows (#903) (@syf2211)
- 4d7d02a: fix: banner alignment (@mdgreenwald)
- 375c494: fix: block outbound requests to IPv6 addresses that embed IPv4 (@taciturnaxolotl)
- b15ad59: fix: use correct error variable in stderr copy check (#892) (@toller892)
Docs
- 11f9f7f: docs: fix typo in inline diff comment (#833) (@Rohan5commit)
Other stuff
- e349146: Merge commit from fork (@taciturnaxolotl)
- 06fdf62: Merge commit from fork (@tonghuaroot)
- 07d4736: Merge commit from fork (@taciturnaxolotl)
- 35103b6: refactor: make anon-access config a typed tri-state override (@taciturnaxolotl)
- 1901e50: serve: don't drop the server start error on the way out (#889) (@c-tonneslan)
- ec7a676: v0.12.0 (@taciturnaxolotl)
Verifying the artifacts
First, download the checksums.txt file and the checksums.txt.sigstore.json file files, for example, with wget:
wget 'https://github.com/charmbracelet/soft-serve/releases/download/v0.12.0/checksums.txt'
wget 'https://github.com/charmbracelet/soft-serve/releases/download/v0.12.0/checksums.txt.sigstore.json'Then, verify it using cosign:
cosign verify-blob \
--certificate-identity 'https://github.com/charmbracelet/meta/.github/workflows/goreleaser.yml@refs/heads/main' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--bundle 'checksums.txt.sigstore.json' \
./checksums.txtIf the output is Verified OK, you can safely use it to verify the checksums of other artifacts you downloaded from the release using sha256sum:
sha256sum --ignore-missing -c checksums.txtDone! You artifacts are now verified!
Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.