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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions