Feedback Records gateway accepts organization permissions across tenants
Organization-scoped API-key flags were evaluated without binding the target directory's organization
CVSS 3.1 base score
HighVector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L
Summary
Source
resolveTenantId()
apps/web/modules/hub/feedback-records-gateway.ts:219
Sink
authorizeFeedbackRecordsGatewayRequest()
apps/web/modules/hub/feedback-records-gateway.ts:299
A holder of an API key for organization A could select an active feedback directory belonging to organization B and list, search, create, update, or delete its feedback records according to A's organization-level permissions.
Root cause. The gateway resolved the target directory's authoritative organizationId, but the API-key branch called hasApiKeyImplicitFeedbackDirectoryAccess() without passing or comparing it. A's organizationAccess.accessControl flags were therefore treated as global.
Remediation evidence. Formbricks accepted the disclosure and opened the exact issue-tracked remediation after it. PR #8648 merged on 30 July 2026 and requires organization equality plus matching workspace permissions before the privileged Hub proxy path is allowed.
Severity
- Attack vectorAV
- Network
- Attack complexityAC
- Low
- Privileges requiredPR
- Low
- User interactionUI
- None
- ScopeS
- Unchanged
- ConfidentialityC
- High
- IntegrityI
- High
- AvailabilityA
- Low
Metric values as published in the disclosure vector. Meters show how far each value raises exposure.
Source-to-sink trace
- Source · attacker-controlledapps/
web/ modules/ hub/ feedback-records-gateway.ts:219 resolveTenantId()
- Step 01apps/
web/ modules/ hub/ feedback-records-gateway.ts:219 The authenticated caller selects the target feedback directory through
tenant_idor the record path.typescript const tenantId = parseTenantId( originalUrl.searchParams.get("tenant_id") ); - Step 02apps/
web/ modules/ ee/ feedback-directory/ lib/ feedback-directory.ts:115 The server loads the selected directory and learns its real organization and workspace assignments.
typescript prisma.feedbackDirectory.findUnique({ where: { id: directoryId }, select: { organizationId: true, workspaces: true }, }); - Step 03apps/
web/ modules/ hub/ feedback-records-gateway.ts:192 The vulnerable helper accepted the attacker's own organization-level flag without comparing it to the target owner.
typescript const acl = authentication.organizationAccess?.accessControl; if (acl?.write) return true; if (acl?.read && requiredPermission === "read") return true; - Sinkapps/
web/ modules/ hub/ feedback-records-gateway.ts:299 authorizeFeedbackRecordsGatewayRequest()
Impact
Reported impact
The attacker can disclose substantial cross-tenant feedback datasets and, with write access, create, alter, or bulk-delete victim records. Feedback may contain survey text, user identifiers, and business metadata; availability impact is limited to the selected data.
Attack surface
Formbricks Feedback Records gateway routes (/api/v3/feedbackRecords and /v1/feedback-records) when feedback directories and the Hub proxy are enabled.
Preconditions
The attacker controls a normal organization and one of its API keys with organization-level read or write access. They need a victim directory or record identifier, but no victim membership or platform-admin credential.
Attack path
- 1
Create or obtain an organization-A API key with feedback read/write authority.
- 2
Send a gateway request selecting an active organization-B directory.
- 3
The gateway authenticates the key as organization A and resolves the target as organization B.
- 4
The vulnerable helper accepts A's organization permission without checking A equals B.
- 5
The proxy forwards the operation to Hub with its privileged service credential.
Proof of concept
- 01
Environment setup
Use an isolated affected Formbricks deployment with Feedback Directories, the gateway, and two organizations enabled.
- 02
Target configuration
Give organization A an API key with organization-level read/write access and create an active feedback directory owned by organization B.
- 03
Exploit delivery
Send
GET /api/v3/feedbackRecords?tenant_id=<org-B-directory>with the organization-A API key, then repeat with a harmless test create/update/delete against seeded records. - 04
Expected response
The vulnerable gateway returns or mutates organization-B feedback instead of returning
403. - 05
Outcome
PR #8648 compares the authenticated and target organization IDs before evaluating permission weights, so every cross-organization request is denied.
Remediation
Guidance
Upgrade to a Formbricks release containing PR #8648. Require authenticated organization equality before evaluating organization or workspace permission flags at every directory and record route.
return hasApiKeyImplicitFeedbackDirectoryAccess(authentication, workspaceIds, requiredPermission);if (authentication.organizationId !== targetOrganizationId) return false;return hasApiKeyImplicitFeedbackDirectoryAccess( authentication, targetOrganizationId, workspaceIds, requiredPermission);
Check the upstream record for the project's current remediation status.
Investigate the paths that matter in your codebase.
Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.
