GHSA-6vgg-xhvh-38ff json
nebula-mesh: POST /api/v1/hosts/{id}/mobile-bundle response lacks Cache-Control: no-store
published 5mo ago modified 4mo ago also GO-2026-5191
- cvss
- —
- not scored
- cisa kev
- Not listed
- not in CISA's catalog
- epss
- —
- no score, EPSS covers CVEs only
- public exploits
- 0
- none known
Summary
`internal/api/mobile_bundle.go:62-66` sets only `Content-Type: application/yaml`. The Web-UI sibling at `internal/web/handlers.go:1316-1321` sets `Cache-Control: no-store`, `Pragma: no-cache`, `Expires: 0`, `X-Content-Type-Options: nosniff` — and has a test asserting it. The API path was missed.
## Affected All released versions up to v0.3.0.
## Threat model The endpoint returns a freshly minted X25519 private key inline. Without `no-store`, any intermediary proxy or CDN that caches `200 OK` YAML responses retains the private key for its cache TTL. Same applies to browser disk cache for direct API hits. Combined with the cross-tenant authz advisory (critical), even a corrected authz layer would still leak via cache after fix.
## Suggested fix Copy the four headers from the Web sibling:
```go w.Header().Set("Content-Type", "application/yaml; charset=utf-8") w.Header().Set("Cache-Control", "no-store") w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") w.Header().Set("X-Content-Type-Options", "nosniff") ```
Mirrors `internal/web/handlers.go:1316-1321`. Add a parallel test to the existing web-side coverage.
## Suggested patch
Verified locally: `go vet`, `go test -race -count=1 ./...`, `golangci-lint v2.12` all clean.
```diff diff --git a/internal/api/mobile_bundle.go b/internal/api/mobile_bundle.go index fc09da0..73152eb 100644 --- a/internal/api/mobile_bundle.go +++ b/internal/api/mobile_bundle.go @@ -58,8 +58,15 @@ func (s *Server) handleMobileBundle(w http.ResponseWriter, r *http.Request) { return }
- // Return YAML bundle with proper content-type + // Return YAML bundle with proper content-type. The bundle inlines a + // freshly-minted X25519 private key, so suppress every layer of cache + // between server and operator (intermediate proxies/CDNs, browser disk + // cache). Mirrors the Web-UI sibling at internal/web/handlers.go. w.Header().Set("Content-Type", "application/yaml; charset=utf-8") + w.Header().Set("Cache-Control", "no-store") + w.Header().Set("Pragma", "no-cache") + w.Header().Set("Expires", "0") + w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(http.StatusOK) if _, err := w.Write(bundle); err != nil { s.logger.Error("write mobile bundle response", "error", err) diff --git a/internal/api/mobile_bundle_test.go b/internal/api/mobile_bundle_test.go index dcb8cd9..da08b01 100644 --- a/internal/api/mobile_bundle_test.go +++ b/internal/api/mobile_bundle_test.go @@ -52,6 +52,19 @@ func TestHandleMobileBundle_Success(t *testing.T) { t.Errorf("Content-Type = %q, want 'application/yaml; charset=utf-8'", ct) }
+ // Bundle inlines a private key — every cache between server and operator + // must drop the response. Mirrors the Web-UI sibling's headers. + for header, want := range map[string]string{ + "Cache-Control": "no-store", + "Pragma": "no-cache", + "Expires": "0", + "X-Content-Type-Options": "nosniff", + } { + if got := w.Header().Get(header); got != want { + t.Errorf("%s = %q, want %q", header, got, want) + } + } + // Verify body is valid YAML with expected keys var yamlData map[string]interface{} if err := yaml.Unmarshal(w.Body.Bytes(), &yamlData); err != nil { ```
Patch / fix
Is GHSA-6vgg-xhvh-38ff in your dependencies? Fencer shows which of your repositories are affected as soon as an advisory lands, and opens the fix as a pull request.
Check with FencerAffected packages
| ecosystem | package | affected | fixed in |
|---|---|---|---|
| go | github.com/juev/nebula-mesh | <0.3.2 |
0.3.2 |
Classification
- Weakness
-
- CWE-525 Use of Web Browser Cache Containing Sensitive Information
References
Sources
- GitHub Advisory GHSA-6vgg-xhvh-38ff
- OSV GO-2026-5191
Fencer Intel keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.