GHSA-qm33-p5p9-f8vg
Fix: forgekeep/nebula-mesh@8baaaceGHSA-qm33-p5p9-f8vg is a CWE-285 vulnerability in github.com/juev/nebula-mesh. O3 Security confirms whether GHSA-qm33-p5p9-f8vg is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
nebula-mesh: GET /api/v1/audit-log discloses all entries to any operator
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-qm33-p5p9-f8vg.
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.
Real-World Exposure
github.com/juev/nebula-meshReal-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
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
curl -H "Authorization: Bearer <any-operator-key>" \
https://server/api/v1/audit-log?limit=1000
Suggested fix
Two options, either acceptable:
if !actorIsAdmin(ctx) { 403 }— strictest; matches the "operator management is admin-only" stance.- Scope to actor: filter
store.ListAuditEntriesbyactor.Usernameplus 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 v2.12 all clean.
diff --git a/internal/api/audit.go b/internal/api/audit.go
index 3236631..57b57ce 100644
--- a/internal/api/audit.go
+++ b/internal/api/audit.go
@@ -10,6 +10,10 @@ import (
const defaultAuditLimit = 100
func (s *Server) handleGetAuditLog(w http.ResponseWriter, r *http.Request) {
+ if !actorIsAdmin(r.Context()) {
+ writeError(w, http.StatusForbidden, "audit log access requires the admin role")
+ return
+ }
filter := store.AuditFilter{
Action: r.URL.Query().Get("action"),
Limit: defaultAuditLimit,
diff --git a/internal/api/audit_admin_test.go b/internal/api/audit_admin_test.go
new file mode 100644
index 0000000..47e1ca4
--- /dev/null
+++ b/internal/api/audit_admin_test.go
@@ -0,0 +1,62 @@
+package api
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/hex"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/juev/nebula-mesh/internal/models"
+)
+
+// TestHandleGetAuditLog_NonAdminForbidden confirms a non-admin operator
+// API key cannot read the audit log. The legacy config-key path stays
+// admin and is covered by the happy-path test elsewhere.
+func TestHandleGetAuditLog_NonAdminForbidden(t *testing.T) {
+ srv, _ := newTestServer(t)
+
+ nonAdminKey := uuid.New().String()
+ keyHash := sha256.Sum256([]byte(nonAdminKey))
+ if err := srv.store.CreateOperator(context.Background(), &models.Operator{
+ ID: uuid.New().String(), Username: "non-admin", PasswordHash: "x",
+ Role: "user", Status: models.OperatorStatusActive,
+ }); err != nil {
+ t.Fatal(err)
+ }
+ op, err := srv.store.GetOperatorByUsername(context.Background(), "non-admin")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := srv.store.CreateOperatorAPIKey(context.Background(), &models.OperatorAPIKey{
+ ID: uuid.New().String(), OperatorID: op.ID, KeyHash: hex.EncodeToString(keyHash[:]),
+ }); err != nil {
+ t.Fatal(err)
+ }
+
+ req := httptest.NewRequest("GET", "/api/v1/audit-log", nil)
+ req.Header.Set("Authorization", "Bearer "+nonAdminKey)
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, req)
+
+ if rec.Code != http.StatusForbidden {
+ t.Errorf("non-admin audit-log status = %d, want 403", rec.Code)
+ }
+}
+
+// TestHandleGetAuditLog_LegacyKeyAllowed confirms the legacy config-key
+// path still reaches the handler (preserves backward compatibility).
+func TestHandleGetAuditLog_LegacyKeyAllowed(t *testing.T) {
+ srv, _ := newTestServer(t)
+
+ req := httptest.NewRequest("GET", "/api/v1/audit-log", nil)
+ req.Header.Set("Authorization", "Bearer "+testAPIKey)
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, req)
+
+ if rec.Code == http.StatusForbidden {
+ t.Errorf("legacy key rejected with 403; want pass-through")
+ }
+}
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/juev/nebula-mesh | all versions | 0.3.2 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/juev/nebula-mesh. 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/juev/nebula-mesh to 0.3.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-qm33-p5p9-f8vg 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-qm33-p5p9-f8vg 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-qm33-p5p9-f8vg. 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-qm33-p5p9-f8vg in your dependencies?
O3 detects GHSA-qm33-p5p9-f8vg across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.