@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
54 lines • 2.83 kB
TypeScript
import type { SqlParserLike } from './parser.js';
/** Configuration for {@link TenantScopeRewriter}. */
export interface TenantScopeConfig {
/** The column that carries the tenant key on every scoped table (e.g. `base_id`, `org_id`). */
tenantColumn: string;
/** Tables that must be constrained to the caller's tenant when referenced. */
scopedTables: string[];
}
/**
* Rewrites a SELECT so every reference to a scoped table is constrained to a
* single tenant: `<tenantColumn> = '<tenantRef>'` is AND-ed into the WHERE for
* each scoped table in the FROM. An existing predicate for a different tenant
* is rejected (no cross-tenant reads). `tenantRef === undefined` is the
* privileged path and passes the SQL through unchanged.
*
* CRITICAL: only a strictly `=== undefined` tenantRef is privileged. A `null`
* or empty-string tenantRef is NOT privileged — it flows into the rewrite and
* injects a predicate that matches nothing (fail-closed), so a missing tenant
* can never accidentally read every tenant's rows.
*
* Scoped mode rejects CTEs, UNION/INTERSECT/EXCEPT, and subqueries in FROM:
* those make it impossible to statically guarantee every tenant-bearing source
* is constrained, so we fail closed and ask the caller to rephrase.
*
* The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
* optional peer at module load.
*/
export declare class TenantScopeRewriter {
private readonly parser;
private readonly tenantColumn;
private readonly scopedTables;
constructor(config: TenantScopeConfig, parser: SqlParserLike);
/** Rewrite `sql` to constrain scoped tables to `tenantRef`. Strictly-`undefined` → pass through. */
rewrite(sql: string, tenantRef: string | undefined): string;
/**
* Every tenant-column `=` predicate anywhere in the tree (recurses into both `AND` and `OR`).
* Used ONLY for the mismatch check: a query naming a foreign tenant anywhere must be rejected,
* even where that predicate cannot be relied on to actually constrain the result set.
*/
private collectAllTenantPredicates;
/**
* Tenant-column `=` predicates on the top-level `AND` spine only. Descent stops at `OR` (and at
* any operator other than `AND`/`=`, e.g. `NOT`, which the parser represents as a `unary_expr`
* and so is never a `binary_expr` here) because only a conjunctively-combined predicate is
* guaranteed to hold for every row the query can return. Used for coverage: a table alias is
* "already scoped" only when one of these covers it.
*/
private collectConjunctiveTenantPredicates;
private extractTenantPredicate;
private buildTenantEquality;
private andCondition;
private forceParens;
}
//# sourceMappingURL=tenant-scope.d.ts.map