Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Commit 1f71fcd

Browse files
committed
fix(security): ReportsController — drop _rbac/_multitenancy bypass
`loadDashboard()` was calling the mapper with `_rbac: false, _multitenancy: false`, then `render()` and `preview()` carry `@NoAdminRequired`. Combined effect: any authenticated user on any tenant could render any dashboard by enumerating identifiers, and the rendered output embeds resolved widget data → cross-tenant exfiltration. Drop the bypass flags so the standard RBAC + multitenancy filter applies on every load. Existing schema-level RBAC + tenant scoping is now the single source of truth for "can this caller see this dashboard?". Refs: #1419 review (blocker 1) — discussion_r3187494400
1 parent b7df198 commit 1f71fcd

1 file changed

Lines changed: 7 additions & 5 deletions

File tree

lib/Controller/ReportsController.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,13 @@ private function loadDashboard(string $identifier): ObjectEntity
174174
{
175175
$identifier = trim($identifier);
176176
$arg = ctype_digit($identifier) === true ? (int) $identifier : $identifier;
177-
return $this->objectMapper->find(
178-
$arg,
179-
_rbac: false,
180-
_multitenancy: false
181-
);
177+
178+
// SECURITY: standard RBAC + multitenancy filtering must apply on every
179+
// dashboard load — render/preview surface user-controlled HTML/XLSX/PDF
180+
// bytes that embed widget data resolved at render time. Bypassing the
181+
// mapper's filters lets any authenticated caller render any dashboard
182+
// in any tenant by guessing identifiers (cross-tenant exfiltration).
183+
return $this->objectMapper->find($arg);
182184

183185
}//end loadDashboard()
184186
}//end class

0 commit comments

Comments
 (0)