GHSA-5fqm-cc34-fcf5 is a medium-severity (CVSS 5.3) Improper Authentication vulnerability in github.com/azukaar/cosmos-server. O3 Security confirms whether GHSA-5fqm-cc34-fcf5 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
Cosmos-Server's constellation public-devices endpoint accepts arbitrary bearer tokens
Exploitation Status
No confirmed exploitation observed yet
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-5fqm-cc34-fcf5.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-5fqm-cc34-fcf5 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 372,324 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
github.com/azukaar/cosmos-serverReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
GET /cosmos/api/constellation/public-devices discloses Constellation device metadata to a requester that supplies any non-empty Authorization header. The handler strips the string Bearer from the header but never validates the resulting token and never uses it in the database query.
This was confirmed locally by routing a request through the real tokenMiddleware with Authorization: Bearer not-a-real-token. The request returned public Constellation device metadata from a disposable fixture. A missing-header negative control returned 401 Unauthorized, proving the bypass is specifically the acceptance of arbitrary bearer values.
Details
Source-to-sink path:
src/httpServer.go:690registers/api/constellation/public-deviceson the authenticated admin API router.src/httpServer.go:815-817appliesSecureAPI(..., public=false, ...), which runstokenMiddleware.src/httpServer.go:231-237only treatsAuthorization: Bearer cosmos_...as a Cosmos API token for validation. Other bearer strings are not validated by the middleware and fall through to the handler.src/constellation/api_devices_public.go:42-47checks only that theAuthorizationheader is present.src/constellation/api_devices_public.go:49-50stripsBearerbut does not verify the token or compare it with a device/API key.src/constellation/api_devices_public.go:63-67queries all non-blocked and non-invisible devices without including the stripped auth value in the filter.src/constellation/api_devices_public.go:84-99returns device names, user nicknames, cleaned VPN/internal IPs, role flags, public hostname, and port.
The handler does not call utils.CheckPermissions, utils.CheckPermissionsOrSelf, the Cosmos API-token permission check, or any Constellation-token validation before returning the data.
Default/common exposure evidence:
- The route is registered in the standard server setup (
src/httpServer.go:690). - Constellation is a documented product feature described as the VPN used to securely access applications remotely (
readme.md:49). - The default config ships Constellation disabled (
src/utils/utils.go:104-105), so impact requires a deployment that enables the Constellation/VPN feature and has at least one non-blocked, non-invisible device. - The root package identifies the product as
cosmos-serverversion0.22.18(package.json:1-2). - The Go module is
github.com/azukaar/cosmos-server(go.mod:0).
False-positive screen:
- The PoC wraps the handler in the same
tokenMiddlewareused bySecureAPI, so it tests the deployed middleware behavior rather than directly calling the handler alone. - A request with no
Authorizationheader returns401 Unauthorized. - A request with
Authorization: Bearer not-a-real-tokenreturns200 OKand device metadata. - The fixture data is stored in a disposable embedded database under
t.TempDir()and no external services are contacted. - The route is not protected by handler-level
CheckPermissions, and the arbitrary bearer value is not used by the database query.
Candidate score: 13/18. Reachability 1, attacker control 2, privilege required 2, sink impact 1, mitigation weakness 2, default exposure 1, safe PoC feasibility 2, static certainty 2, false-positive resistance 2. The lower score reflects that Constellation is disabled in the shipped default config, but it is a documented/common product feature.
Exploitability gate: confirmed for deployments with Constellation enabled and populated with devices. The reachable source, arbitrary-token bypass, data-disclosure impact, safe local reproduction, and affected-version evidence are present. Default exposure is feature-dependent rather than enabled in a fresh default config.
PoC
Clean-checkout maintainer recipe:
- Check out commit
88de73dcca50172393a75de0e4dc3ab93622825cor version0.22.18. - Create
src/zz_security_poc_test.gowith the following test. - Run
go test ./src -run TestAuditPublicDevicesAllowsArbitraryBearer -count=1 -v. - Delete
src/zz_security_poc_test.goafter confirming.
package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/azukaar/cosmos-server/src/constellation"
"github.com/azukaar/cosmos-server/src/utils"
)
func TestAuditPublicDevicesAllowsArbitraryBearer(t *testing.T) {
oldConfig := utils.MainConfig
oldBaseConfig := utils.BaseMainConfig
oldPush := utils.PushShieldMetrics
oldConfigFolder := utils.CONFIGFOLDER
defer func() {
utils.MainConfig = oldConfig
utils.BaseMainConfig = oldBaseConfig
utils.PushShieldMetrics = oldPush
utils.CONFIGFOLDER = oldConfigFolder
utils.CloseEmbeddedDB()
}()
utils.PushShieldMetrics = func(string) {}
utils.MainConfig = utils.DefaultConfig
utils.MainConfig.NewInstall = false
utils.MainConfig.HTTPConfig.Hostname = "example.test"
utils.BaseMainConfig = utils.MainConfig
tmp := t.TempDir()
utils.CONFIGFOLDER = tmp + "/"
utils.CloseEmbeddedDB()
c, closeDb, err := utils.GetEmbeddedCollection(utils.GetRootAppId(), "devices")
defer closeDb()
if err != nil {
t.Fatalf("embedded collection: %v", err)
}
_, err = c.InsertOne(nil, utils.ConstellationDevice{
Nickname: "victim-user",
DeviceName: "private-node",
IP: "10.8.0.42/24",
IsLighthouse: true,
PublicHostname: "vpn.example.test",
Port: "4242",
Blocked: false,
Invisible: false,
})
if err != nil {
t.Fatalf("insert device fixture: %v", err)
}
handler := tokenMiddleware(http.HandlerFunc(constellation.DevicePublicList))
noAuthReq := httptest.NewRequest(http.MethodGet, "/cosmos/api/constellation/public-devices", nil)
noAuthRec := httptest.NewRecorder()
handler.ServeHTTP(noAuthRec, noAuthReq)
if noAuthRec.Code != http.StatusUnauthorized {
t.Fatalf("missing Authorization status = %d body = %s", noAuthRec.Code, noAuthRec.Body.String())
}
req := httptest.NewRequest(http.MethodGet, "/cosmos/api/constellation/public-devices", nil)
req.Header.Set("Authorization", "Bearer not-a-real-token")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("DevicePublicList status = %d body = %s", rec.Code, rec.Body.String())
}
var body struct {
Status string `json:"status"`
Data []struct {
Name string `json:"name"`
User string `json:"user"`
IP string `json:"ip"`
PublicHostname string `json:"publicHostname"`
} `json:"data"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
t.Fatalf("response JSON: %v", err)
}
if body.Status != "OK" || len(body.Data) != 1 {
t.Fatalf("unexpected response body: %s", rec.Body.String())
}
if body.Data[0].Name != "private-node" || body.Data[0].User != "victim-user" || body.Data[0].IP != "10.8.0.42" || body.Data[0].PublicHostname != "vpn.example.test" {
t.Fatalf("unexpected device disclosure: %+v", body.Data[0])
}
}
Observed output in this environment:
=== RUN TestAuditPublicDevicesAllowsArbitraryBearer
2026/05/24 15:18:09 NOTICE: [INFO] DevicePublicList: Fetching devices with API key
--- PASS: TestAuditPublicDevicesAllowsArbitraryBearer (0.00s)
PASS
ok github.com/azukaar/cosmos-server/src 0.057s
Control/negative case: the same test first sends the request without Authorization and expects 401 Unauthorized. The subsequent Authorization: Bearer not-a-real-token request succeeds and returns the fixture, proving the bypass is not an intentionally unauthenticated endpoint but an unvalidated-header check.
Impact
An unauthenticated network attacker can enumerate Constellation device metadata from deployments that enable the Constellation/VPN feature and have visible devices. The response exposes device names, user nicknames, internal/VPN IP addresses, node roles such as lighthouse/relay/exit-node flags, public hostnames, and ports. This information can reveal private network topology and user/device inventory and can support targeted follow-on attacks against the VPN or exposed nodes.
The issue does not require a valid Cosmos session, valid Cosmos API token, valid Constellation token, or user interaction; any arbitrary non-empty bearer string is accepted.
Suggested remediation
Replace the header-presence check with real authorization. Depending on the intended trust model, the handler should require one of:
- a valid Cosmos user/API token with an appropriate permission such as
PERM_RESOURCES_READorPERM_CONFIGURATION_READ, or - a dedicated Constellation device/API token that is cryptographically verified and used to scope the query to devices the caller is allowed to see.
Also add regression tests for:
- missing
Authorizationheader returns 401, - malformed/arbitrary bearer token returns 401,
- invalid
cosmos_API token returns 401 through middleware, - valid but underprivileged token is rejected,
- valid authorized token returns only permitted devices.
Credits
- Thai Son Dinh from VinSOC Labs (R&D)
- Nguyen Huy Vu Dung from VinSOC Labs (AppSec)
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/azukaar/cosmos-server | ≥ 0.22.18&&< 0.22.19 | 0.22.19 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/azukaar/cosmos-server. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update github.com/azukaar/cosmos-server to 0.22.19 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-5fqm-cc34-fcf5 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-5fqm-cc34-fcf5 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-5fqm-cc34-fcf5. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-5fqm-cc34-fcf5 in your dependencies?
O3 detects GHSA-5fqm-cc34-fcf5 across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.