GHSA-FGXM-5HVV-X4VH: GHSA-fgxm-5hvv-x4vh: Private Repository Enumeration via Go Module Discovery and HTTP Response…
GHSA-fgxm-5hvv-x4vh: Private Repository Enumeration via Go Module Discovery and HTTP Response Discrepancies in Soft Serve
> Vulnerability ID: GHSA-FGXM-5HVV-X4VH
> CVSS Score: 5.3
> Published: 2026-10-30
Charmbracelet Soft Serve versions 0.5.0 through 0.12.2 contain an authorization bypass and observable response discrepancy vulnerability in the HTTP Git server. Remote attackers can determine the existence, exact names, and clone URLs of private repositories by issuing crafted Go remote import path discovery requests (?go-get=1) or by observing differential HTTP status codes across Git Smart HTTP, Dumb HTTP, and Git LFS endpoints.
TL;DR
Soft Serve versions 0.5.0 to 0.12.2 disclose private repository names and clone URLs over HTTP. An authorization bypass in the ?go-get=1 handler allows unauthenticated or low-privileged users to resolve private repository metadata via sub-path queries. Additionally, inconsistent HTTP status codes (401/403 vs. 404) across Git and LFS endpoints act as an oracle for repository existence. Upgrading to v0.12.3 resolves these issues.
---
Technical Details
- Vulnerability ID: GHSA-fgxm-5hvv-x4vh
- Primary CWE: CWE-200 / CWE-203 / CWE-285
- CVSS v3.1 Score: 5.3 Medium (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
- Attack Vector: Network (HTTP Git / LFS / Go Import Handlers)
- Impact: Private Repository Name & Clone URL Disclosure
- Affected Versions: >= 0.5.0, < 0.12.3
- Patched Version: v0.12.3
- Exploit Status: No Public Exploits / Trivial via HTTP Client
Affected Systems
- github.com/charmbracelet/soft-serve
- Soft Serve (github.com/charmbracelet/soft-serve): >= 0.5.0, < 0.12.3 (Fixed in: 0.12.3)
Code Analysis
Commit: 74e4154
fix: stop go-get requests from revealing private repository names
// pkg/web/git.go
- r.Handle(basePrefix, withParams(withAccess(access.ReadOnlyAccess, http.HandlerFunc(GoGetHandler)))).Methods(http.MethodGet)
+ r.Handle(basePrefix, withParams(withCaller(http.HandlerFunc(GoGetHandler)))).Methods(http.MethodGet)
// pkg/web/goget.go
+ user := proto.UserFromContext(ctx)
for {
- if _, err := be.Repository(ctx, repo); err == nil {
+ if _, err := be.Repository(ctx, repo); err == nil && be.AccessLevelForUser(ctx, repo, user) >= access.ReadOnlyAccess {
break
}
repo = path.Dir(repo)
Commit: dc351aa
fix: answer the same for private and missing repositories over HTTP
// pkg/web/git.go
+func denyRead(w http.ResponseWriter, r *http.Request, err error, lfsJSON bool) {
+ switch {
+ case badCredentials(err) && lfsJSON:
+ renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{Message: "bad credentials"})
+ case badCredentials(err):
+ renderForbidden(w, r)
+ case proto.UserFromContext(r.Context()) == nil && lfsJSON:
+ askCredentials(w, r)
+ renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{Message: "credentials needed"})
+ case proto.UserFromContext(r.Context()) == nil:
+ askCredentials(w, r)
+ renderUnauthorized(w, r)
+ case lfsJSON:
+ renderJSON(w, http.StatusNotFound, lfs.ErrorResponse{Message: "repository not found"})
+ default:
+ renderNotFound(w, r)
+ }
+}
Mitigation Strategies
- Upgrade Soft Serve to version v0.12.3 or later to enforce authorization checks during go-get resolution and unify HTTP error responses.
- Disable the Soft Serve HTTP listener if Git operations can be restricted exclusively to SSH.
- Set anonymous access (anon-access) to no-access to prevent unauthenticated ?go-get=1 sub-path traversal.
- Block requests containing the ?go-get=1 query parameter at the reverse proxy or WAF layer if Go module discovery is not required.
Remediation Steps:
1. Download the Soft Serve v0.12.3 binary from the official GitHub release page (github.com/charmbracelet/soft-serve/releases/tag/…) or build via go install github.com/charmbracelet/soft-serve/cmd/soft@v0.12.3.
2. Stop the running Soft Serve service, replace the binary with v0.12.3, and restart the service.
3. Verify the fix by issuing an unauthenticated HTTP GET request to a non-existent repository path (GET /nonexistent-repo.git/info/refs?service=git-upload-pack) and confirming the server returns 401 Unauthorized rather than 404 Not Found.
References
- GitHub Security Advisory: GHSA-fgxm-5hvv-x4vh
- Charmbracelet Soft Serve Release v0.12.3
- Fix Commit: stop go-get requests from revealing private repository names
- Fix Commit: answer the same for private and missing repositories over HTTP
- Charmbracelet Soft Serve Source Repository
---
Read the full report for GHSA-FGXM-5HVV-X4VH on our website for more details including interactive diagrams and full exploit analysis.
Mentions
GHSA-FGXM-5HVV-X4VH: GHSA-fgxm-5hvv-x4vh: Private Repository Enumeration via Go Module Discovery and HTTP Response… GHSA-fgxm-5hvv-x4vh: Private Repository Enumeration via Go Module Discovery and HTTP Response Discrepancies in Soft Serve > Vulnerability ID: GHSA-FGXM-5HVV-X4VH > CVSS Score: 5.3 > Published: 2026-10-30 Charmbracelet Soft Serve versions 0.5.0 through 0.12.2 contain an authorization bypass and observable response discrepancy vulnerability in the HTTP Git server. Remote attackers can determine the existence, exact names, and clone URLs of private repositories by issuing crafted Go remote import path discovery requests (?go-get=1) or by observing differential HTTP status codes across Git Smart HTTP, Dumb HTTP, and Git LFS endpoints. TL;DR Soft Serve versions 0.5.0 to 0.12.2 disclose private repository names and clone URLs over HTTP. An authorization bypass in the ?go-get=1 handler allows unauthenticated or low-privileged users to resolve private repository metadata via sub-path queries. Additionally, inconsistent HTTP status codes (401/403 vs. 404) across Git and LFS endpoints act as an oracle for repository existence. Upgrading to v0.12.3 resolves these issues. --- Technical Details - Vulnerability ID: GHSA-fgxm-5hvv-x4vh - Primary CWE: CWE-200 / CWE-203 / CWE-285 - CVSS v3.1 Score: 5.3 Medium (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) - Attack Vector: Network (HTTP Git / LFS / Go Import Handlers) - Impact: Private Repository Name & Clone URL Disclosure - Affected Versions: >= 0.5.0, < 0.12.3 - Patched Version: v0.12.3 - Exploit Status: No Public Exploits / Trivial via HTTP Client Affected Systems - github.com/charmbracelet/soft-serve - Soft Serve (github.com/charmbracelet/soft-serve): >= 0.5.0, < 0.12.3 (Fixed in: 0.12.3) Code Analysis Commit: 74e4154 fix: stop go-get requests from revealing private repository names // pkg/web/git.go - r.Handle(basePrefix, withParams(withAccess(access.ReadOnlyAccess, http.HandlerFunc(GoGetHandler)))).Methods(http.MethodGet) + r.Handle(basePrefix, withParams(withCaller(http.HandlerFunc(GoGetHandler)))).Methods(http.MethodGet) // pkg/web/goget.go + user := proto.UserFromContext(ctx) for { - if _, err := be.Repository(ctx, repo); err == nil { + if _, err := be.Repository(ctx, repo); err == nil && be.AccessLevelForUser(ctx, repo, user) >= access.ReadOnlyAccess { break } repo = path.Dir(repo) Commit: dc351aa fix: answer the same for private and missing repositories over HTTP // pkg/web/git.go +func denyRead(w http.ResponseWriter, r *http.Request, err error, lfsJSON bool) { + switch { + case badCredentials(err) && lfsJSON: + renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{Message: "bad credentials"}) + case badCredentials(err): + renderForbidden(w, r) + case proto.UserFromContext(r.Context()) == nil && lfsJSON: + askCredentials(w, r) + renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{Message: "credentials needed"}) + case proto.UserFromContext(r.Context()) == nil: + askCredentials(w, r) + renderUnauthorized(w, r) + case lfsJSON: + renderJSON(w, http.StatusNotFound, lfs.ErrorResponse{Message: "repository not found"}) + default: + renderNotFound(w, r) + } +} Mitigation Strategies - Upgrade Soft Serve to version v0.12.3 or later to enforce authorization checks during go-get resolution and unify HTTP error responses. - Disable the Soft Serve HTTP listener if Git operations can be restricted exclusively to SSH. - Set anonymous access (anon-access) to no-access to prevent unauthenticated ?go-get=1 sub-path traversal. - Block requests containing the ?go-get=1 query parameter at the reverse proxy or WAF layer if Go module discovery is not required. Remediation Steps: 1. Download the Soft Serve v0.12.3 binary from the official GitHub release page (github.com/charmbracelet/soft-serve/releases/tag/…) or build via go install github.com/charmbracelet/soft-serve/cmd/soft@v0.12.3. 2. Stop the running Soft Serve service, replace the binary with v0.12.3, and restart the service. 3. Verify the fix by issuing an unauthenticated HTTP GET request to a non-existent repository path (GET /nonexistent-repo.git/info/refs?service=git-upload-pack) and confirming the server returns 401 Unauthorized rather than 404 Not Found. References - GitHub Security Advisory: GHSA-fgxm-5hvv-x4vh - Charmbracelet Soft Serve Release v0.12.3 - Fix Commit: stop go-get requests from revealing private repository names - Fix Commit: answer the same for private and missing repositories over HTTP - Charmbracelet Soft Serve Source Repository --- Read the full report for GHSA-FGXM-5HVV-X4VH on our website for more details including interactive diagrams and full exploit analysis.
GHSA-F3FJ-9625-RV3M: GHSA-f3fj-9625-rv3m: Race Condition in Soft Serve Repository Cache Invalidation GHSA-f3fj-9625-rv3m: Race Condition in Soft Serve Repository Cache Invalidation > Vulnerability ID: GHSA-F3FJ-9625-RV3M > CVSS Score: 5.9 > Published: 2026-10-30 A Time-of-Check to Time-of-Use (TOCTOU) race condition in Charmbracelet Soft Serve versions 0.6.0 through 0.12.2 allows unauthenticated users to retain read access to repositories after their visibility is changed from public to private. The flaw stems from premature cache invalidation in the SetPrivate method prior to committing the visibility update to the underlying database. Concurrent read requests arriving during the database transaction window repopulate the non-expiring in-memory LRU cache with stale public metadata, bypassing access controls across HTTP, SSH, and Git protocols until the server process is restarted. TL;DR Soft Serve versions >= 0.6.0 and < 0.12.3 contain a cache invalidation race condition in the SetPrivate repository method. Evicting the in-memory LRU cache before committing the database transaction allows concurrent unauthenticated requests to re-cache the stale public state, leaving private repositories perpetually readable by anonymous users. Upgrade to version 0.12.3, which removes the in-memory repository cache entirely. --- ⚠️ Exploit Status: POC Technical Details - Advisory ID: GHSA-f3fj-9625-rv3m - Vulnerability Class: CWE-362 / CWE-367 (TOCTOU Race Condition) - CVSS v3.1 Score: 5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N) - Affected Versions: github.com/charmbracelet/soft-serve >= 0.6.0, < 0.12.3 - Patched Version: 0.12.3 - Attack Vector: Network (HTTP, SSH, and git:// protocols) - Security Impact: Unauthenticated read access to private Git repositories - Exploit Status: Proof-of-Concept in unit tests (No KEV listing) Affected Systems - github.com/charmbracelet/soft-serve - soft-serve: >= 0.6.0, < 0.12.3 (Fixed in: 0.12.3) Code Analysis Commit: 5b307b4 fix: keep a repository private after its visibility changes (GHSA-f3fj-9625-rv3m) Commit: 0a4e6f0 fix: send the visibility webhook only when visibility changes Commit: e398b4d refactor: implement database module and abstract backend (Introduced vulnerability) Mitigation Strategies - Upgrade Soft Serve to version 0.12.3 or later to eliminate the vulnerable in-memory repository cache. - Restart the Soft Serve server process immediately after changing any repository's visibility from public to private to flush stale in-memory cache entries. - Create confidential repositories with the private flag (-p) enabled at initialization rather than toggling visibility after creation. - Disable anonymous read access globally by setting anon_access to no-access if unauthenticated public access is not required. Remediation Steps: 1. Update the Soft Serve binary or Go module to v0.12.3 using 'go get -u github.com/charmbracelet/soft-serve@v0.12.3' or via system package managers. 2. Restart all running Soft Serve service instances ('systemctl restart soft-serve') to ensure the updated binary is active and any previously cached in-memory state is cleared. 3. Audit server access logs for any unauthenticated Git read operations (such as GET requests to /<repo>.git/info/refs) that occurred after a repository was marked private. References - Soft Serve Security Advisory GHSA-f3fj-9625-rv3m - GitHub Advisory Database: GHSA-f3fj-9625-rv3m - Fix Commit: Remove in-memory repository cache (5b307b4da4db285f797cc028b381ba4d3d853019) - Fix Commit: Send visibility webhook only when visibility changes (0a4e6f0b7e063927c7128756e1c1b3db871edabf) - Prior Commit: Implement database module and abstract backend (e398b4dde723437f637b06710e0938d349b360e8) - Soft Serve Release v0.12.3 - Reporter Profile: sondt99 --- Read the full report for GHSA-F3FJ-9625-RV3M on our website for more details including interactive diagrams and full exploit analysis.
@thdxr Its kind of weird no one mentioned soft serve charmbracelet/soft-serve