Extend verifySiteAccess to check that when req.userOrgId is already set
by a prior middleware (e.g. verifyResourceAccess/verifyTargetAccess), the
site from req.body.siteId belongs to the same organization. This prevents
the cross-organization tunnel boundary bypass where an attacker with
resource access in one org binds that resource's target to a site in
another org.
Add verifySiteAccess to both target route stacks:
- PUT /resource/:resourceId/target (after verifyResourceAccess)
- POST /target/:targetId (after verifyTargetAccess)
The org-match check runs before req.userOrg is overwritten, so the
resource's organization context is preserved for comparison.
Signed-off-by: Marc Schäfer <git@marcschaeferger.de>
Route the read paths in queryRequestAuditLog.ts and
queryRequestAnalytics.ts through `logsDb` instead of
`primaryLogsDb`, matching the existing private audit log routes
(queryActionAuditLog, queryAccessAuditLog, queryConnectionAuditLog
all already use `logsDb`). In PostgreSQL deployments configured
with a read replica via `withReplicas` (see server/db/pg/logsDriver.ts),
this keeps high-volume audit log reads off the primary. No-op
in OSS-SQLite where `logsDb === primaryDb`.
Investigated rewriting `queryUniqueFilterAttributes` per the
in-line TODO ("SOMEONE PLEASE OPTIMIZE THIS!!!!!"). A candidate
rewrite using UNION ALL with six GROUP BY...LIMIT 500 arms
benchmarked 48-61% slower than the current SELECT DISTINCT
LIMIT 501 approach on SQLite (100k/300k/1M rows, 20 runs each):
each grouped arm materializes a temp B-tree before applying LIMIT,
while DISTINCT short-circuits via hash dedup with early exit.
A materialized facets table is likely the right long-term fix,
not a query-shape rewrite.