Skip to content

PolicyPlugin rejects raw SQL methods ($queryRaw, $executeRaw) #2495

@pkudinov

Description

@pkudinov

Description

$queryRawUnsafe, $executeRawUnsafe, $queryRaw, and $executeRaw fail with "non-CRUD queries are not allowed" when PolicyPlugin is active.

Reproduction

const client = new ZenStackClient(schema, {
  dialect: new PostgresDialect({ pool }),
  plugins: [new PolicyPlugin()],
});

// This throws: "non-CRUD queries are not allowed"
await client.$queryRawUnsafe('SELECT 1');

Root cause

In client-impl.ts, all four raw methods execute through this.kysely, which routes queries through ZenStackQueryExecutor and the plugin pipeline. PolicyPlugin's handle() (policy-handler.ts:78-84) rejects any node that isn't Select/Insert/Update/Delete:

if (!this.isCrudQueryNode(node)) {
    throw createRejectedByPolicyError(
        undefined,
        RejectedByPolicyReason.OTHER,
        'non-CRUD queries are not allowed',
    );
}

Raw SQL produces a RawNode, not a CRUD node, so it's always rejected.

Why this should be fixed

Raw SQL has no model-level semantics — there's no table/row context for PolicyPlugin to enforce access control on. The caller is fully responsible for the SQL they write. Blocking raw SQL forces users to either:

All three workarounds are fragile and error-prone.

Suggested fix

Route raw SQL methods through kyselyRaw instead of this.kysely:

$queryRawUnsafe<T = unknown>(query: string, ...values: any[]) {
    return createZenStackPromise(async () => {
        const compiledQuery = this.createRawCompiledQuery(query, values);
        const result = await this.kyselyRaw.executeQuery(compiledQuery);
        return result.rows as T;
    });
}

Note: kyselyRaw would also need to be updated inside interactiveTransaction to preserve transaction isolation (same pattern as this.kysely = tx).

Alternatively, PolicyPlugin could pass through non-CRUD nodes instead of rejecting them.

Version

@zenstackhq/orm 3.4.6, @zenstackhq/plugin-policy 3.4.6

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions