Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
11 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
internal/api/pop/nonce.go:25,40,86 + internal/api/server.go:38 — the signed-poll nonce cache is an in-process LRU sized at 65,536 entries. internal/api/updates.go:31 sets pollClockSkew = 5 time.Minute as the replay window. Affected All released versions through v0.3.0 that have shipped the ADR 0004 signed-poll path. (If this is gated behind a feature flag, on a side branch, or not yet on a release tag, please flag — this advisory may not apply to the released artifact yet.) Threat model A captured signed-poll request can be replayed: 1. After any process restart — the in-memory LRU is wiped, so the original nonce becomes "unseen" again. Replay succeeds if the original timestamp is still within the 5-minute skew. 2. After forced eviction — an attacker with control of any single host can flood 65,536 nonces under their own hostid, driving the global LRU to evict the victim's recorded nonce. Replay then succeeds. Impact is bounded: a replayed poll fetches the /api/v1/agent/updates body.…
Recommended action: Upgrade to 0.3.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/store/sqlite.go:1177,1192,1221,1245 — the enrollmenttokens.token column holds the raw UUID token. ConsumeToken does WHERE token = ? against the raw string. Compare with operatorapikeys.keyhash, which is SHA-256 hex (constructed in internal/api/middleware.go:51-53). Affected All released versions up to v0.3.0. Threat model Read access to nebula-mgmt.db: backup, snapshot, file-system access, future SQL-injection sink. The principle of defense-in-depth: API keys are hashed at rest; enrollment tokens — which grant the same lifecycle authority over a host's identity — are not. An attacker who reads the DB before a legitimate agent enrolls can consume the single-use token first, mint a cert against their own keypair, and take the agent's intended Nebula identity. Suggested fix 1. Schema migration: rename enrollmenttokens.token → tokenhash (or add the new column and drop the old after backfill of pending rows). 2. Store SHA-256 of token on create: 3. ConsumeToken accepts the raw…
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/api/mobilebundle.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: 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.
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/web/operators.go:251 — after handleOperatorCreateAPIKey mints a fresh 32-byte bearer token, the redirect points the operator's browser at: /ui/operators/<id?newkey=<raw-token&keyname=<name The raw API key ends up: - in the browser's URL history - in the Referer header on every cross-origin asset the detail page loads (any third-party SVG/CSS/JS resource the layout pulls in) - in any reverse-proxy or load-balancer access log on the path (nginx default combined log captures the query string) - in any structured log sink the operator's local browser-history backup tool ships out Authorization: Bearer <token headers go through the same hops without these problems because access logs typically don't capture request headers and the browser doesn't replay headers cross-origin. Same handler also appends name (r.FormValue("name")) to the query string without url.QueryEscape, so an & in the operator-supplied key name corrupts query parsing and a \r\n in older proxies could split…
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/web/session.go and internal/web/oidc.go set HttpOnly and SameSite=Lax on every cookie but never Secure. A single plaintext request to the origin (operator on a LAN, mistyped URL, HTTP→HTTPS not strictly enforced, reverse proxy misconfiguration) discloses the session. Affected All released versions up to v0.3.1. Impact An attacker who can observe one HTTP request to the origin recovers the session cookie and impersonates the operator for the remainder of its 24h TTL. The OIDC state cookie has a narrower 10-minute window but enables CSRF on the OIDC callback during that window. Cookie sites - internal/web/session.go — Login, StartAuthenticatedSession, CompleteTwoFactor, Logout - internal/web/oidc.go — HandleLogin (state set), HandleCallback (state clear) Suggested fix Driven by an explicit cookiesecure config option, inferred true when tlscert+tlskey are configured and false otherwise. ratelimit.trustproxyheader is deliberately not used as a signal — that flag controls XFF…
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/pki/resolver.go:36-64 constructs a CAManager with the plaintext ed25519.PrivateKey after unwrapping via the master key; internal/pki/ca.go:13-16 stores it. Callers at internal/api/enroll.go:116, internal/api/updates.go:297, and internal/api/mobilebundle.go:40 use the manager for one Sign() and drop the reference on function return — but the underlying slice contents are not wiped before release. The keystore package's contract (internal/keystore/keystore.go doc: "Callers MUST zeroise the returned plaintext DEK as soon as it is no longer needed") is not met by the CAManager consumer. Decrypted CA private keys persist in process heap until Go's GC scavenges the underlying slice — minutes to hours under load, indefinitely on idle servers. Affected All released versions up to v0.3.6. Threat model Memory-read access: core dump, ptrace, kernel swap to disk, container/VM snapshot, OOM-debug bundle, side-channel via shared cache lines. Not a remote-network vulnerability, but defeats…
Recommended action: Upgrade to 0.3.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/api/audit.go:12 — handleGetAuditLog does no admin check. The route is bearer-auth gated only; any operator API key returns the full audit log via store.ListAuditEntries (up to limit=1000). This includes cross-tenant actor names, host/CA/operator IDs, action timestamps, and masked-IP entries from rate-limit refusals — enough surface for a tenant to enumerate the server's activity, infer staffing patterns, or identify high-value targets. Affected All released versions up to v0.3.1. Reproducer Suggested fix Two options, either acceptable: 1. if !actorIsAdmin(ctx) { 403 } — strictest; matches the "operator management is admin-only" stance. 2. Scope to actor: filter store.ListAuditEntries by actor.Username plus a subquery of CA IDs the actor owns. Operators see their own audit entries plus entries against their CA's resources. Recommend option 1 unless the UI needs per-operator audit views. Suggested patch Verified locally: go vet, go test -race -count=1 ./..., golangci-lint…
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Every /ui/ POST / PUT / PATCH / DELETE route processes the request as soon as the session cookie validates. SameSite=Lax on the session cookie prevents most cross-site form submits but does not protect: - top-level form-submit navigations from third-party pages (some browsers still send Lax cookies on top-level POSTs) - same-registrable-domain attackers (sibling-subdomain XSS, subdomain takeover) - the GET /ui/logout route, which a third-party <img src=".../ui/logout" can force-trigger The admin UI signs CA certificates, mints API keys, rotates / retires / deletes CAs, disables operators, and changes server settings. CSRF here is a real privilege escalation, not just annoyance. Affected All released versions up to v0.3.2. Suggested fix Double-submit cookie: a 32-byte crypto/rand token in a non-HttpOnly csrf cookie, echoed in either X-CSRF-Token (htmx) or a csrf form field (HTML forms). Compared in constant time. Rotated on every privilege transition (Login, OIDC…
Recommended action: Upgrade to 0.3.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The /api/v1/ route surface trusts the bearer token alone for authorisation on most endpoints. The codebase itself admits this at internal/api/hosts.go:384: "API trusts the bearer token for authorisation; per-CA ownership is enforced only in the Web layer." The Web UI gates state-changing routes through loadAccessibleCA (internal/web/cas.go); CA-management endpoints in internal/api/cas.go ALSO have proper canAccessCA gates. The gap is on the host, network, firewall, mobile-bundle, and most operator endpoints. Combined with the per-operator CA model from ADR 0002, this gives any non-admin operator API key broad cross-tenant access — instant privilege escalation in the worst case. Affected All released versions prior to v0.3.4. Exploit chain A) Mint admin API key from any operator key (instant privilege escalation) internal/api/operators.go:118 — handleCreateOperatorAPIKey does no admin check and no actor/target-operator ownership check. Any operator key can call it for any operator…
Recommended action: Upgrade to 0.3.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
None of the response paths in internal/web/ or internal/api/ set the standard browser-security headers. grep for Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, X-Content-Type-Options, Referrer-Policy returns zero matches across the codebase. Impact The admin UI signs CA certificates, mints API keys (returned inline once per page), displays TOTP QR codes, and exposes operator-management forms. Missing X-Frame-Options: DENY / frame-ancestors 'none' is a real clickjacking lever against an admin browsing /ui/operators/ or /ui/cas/. Missing X-Content-Type-Options: nosniff allows MIME confusion on any user-supplied content surface. Missing HSTS on TLS deployments leaves a downgrade window. Affected All released versions up to v0.3.0. Suggested fix A single response-header middleware mounted at the chi router root in both /ui/ and /api/ paths: The inline <script in layout.html for CSRF wiring (added in the CSRF advisory) will need either a nonce, a hash in CSP, or…
Recommended action: Upgrade to 0.3.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
internal/configgen/generator.go:86,108,119 interpolates the operator-supplied ListenHost and TunDevice fields raw into a text/template that produces the agent's config.yml. internal/web/advanced.go:20-35 accepts both with only strings.TrimSpace — no character or shape validation. Exploit An operator (or attacker with any operator key, given the cross-tenant CRUD advisory) sets advtundevice to: The agent fetches the rendered config on its next signed poll. On config reload, it loads the injected YAML keys: the host self-promotes to lighthouse, attracts mesh traffic, or sets amrelay: true to be selected as a relay. The ListenHost field has the same shape. Affected All released versions prior to v0.3.2. Threat model - Today: operator can compromise their own host's config (trivially allowed if they own the host, but they can also set lighthouse/relay flags that the operator-create form does NOT expose — privilege uplift within their own tenant). - Combined with the critical /api/v1…
Recommended action: Upgrade to 0.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.