Skip to content

[Security] All-tenant metrics readable by any self-service signup (org-scoped role treated as platform authority) #337

Description

Audit finding: BUG-R3-S2-A8-H1 | CWE-284 | CVSS 7.1 (PR:L, S:C, C:H) | PoC: pending (two-request chain, verified in code)

Summary

require_metrics_access (api/main.py:204-222) admits anyone passing _has_metrics_admin_role (api/main.py:199-201):

return user is not None and user.is_active and user.role in {"owner", "admin"}

That is a per-organization role check with no organization_id comparison, and there is no platform-operator concept anywhere in the data model. Because open self-service POST /auth/signup hardcodes role="owner" for every new account (auth/routes.py:62) — and it is the only user-creation path — the gate degenerates to "possess any account", which is one anonymous request away.

GET /metrics then returns metrics_collector.get_all_tenant_metrics() (api/main.py:1068): for every organization — request volumes, success/failure/blocked counts, success rates, latencies, LLM provider usage, last-activity timestamps; GET /metrics/prometheus exposes the per-tenant labeled series. Sequential org PKs give complete tenant enumeration. The API-key branch is equally open, and ApiKey has no expiry field, so the cross-tenant read persists indefinitely.

This contradicts the codebase's own org-scoped sibling (/metrics/{organization_id} returns 403 for other orgs) and the audit routes' org filters.

Attack

POST /auth/signup (anonymous) -> GET /metrics with the returned JWT. Persistence variant: mint an API key. Works in every deployment shape that serves the API.

Suggested fix

Treat all-tenant metrics as a platform-operator capability: explicit env-configured allowlist (e.g. QWED_METRICS_OPERATOR_USER_IDS), fail-closed when unset. Never treat an org role as platform-wide authority. See audit patch sketch.

Related: #222 (RBAC middleware non-functional — same authorization-scope confusion class).


Source: OpenVuln external audit of QWED-AI/qwed-verification (snapshot v7.0.0, re-verified against current main v7.1.0). All code anchors below were spot-checked against current main before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High prioritybugSomething isn't workingexternal-auditFindings from external security auditsqwed securityqwed securitysecuritySecurity finding from adversarial audit

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions