Every endpoint, manager method, or database query that reads or mutates tenant data must explicitly verify and scope access to the authenticated user’s organization — never rely solely on a client-supplied ID (e.g., a record’s id) to locate a resource. Add organization_id/organizationId filters to every SELECT, UPDATE, and DELETE, and pass identifiers as bound parameters rather than interpolating them into raw SQL strings, to prevent both cross-tenant data leaks and injection attacks.
Example:
// Bad: no tenant scoping, vulnerable to cross-org access
await dbExecute(
"UPDATE saved_queries SET name = $1, sql = $2 WHERE id = $3",
[name, sql, id]
);
// Good: scoped to the authenticated org, using bound parameters
await dbExecute(
"UPDATE saved_queries SET name = $1, sql = $2 WHERE id = $3 AND organization_id = $4",
[name, sql, id, authParams.organizationId]
);
Additionally:
Treat every new query, controller, or manager method as a potential authorization gap by default, and require an explicit organization/tenant check and parameterized inputs before merging.</description>