Skip to content

Commit ad418f9

Browse files
tianzhouclaude
andcommitted
fix: also reject dynamic SQL primitives in executeReadOnly() guard
EXEC('COMMIT') would bypass the COMMIT/ROLLBACK keyword guard because stripCommentsAndStrings removes the string content. Block EXEC, EXECUTE, sp_executesql, and xp_cmdshell at the connector level so dynamic SQL carrying hidden transaction control cannot escape the rollback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ad2e669 commit ad418f9

1 file changed

Lines changed: 10 additions & 3 deletions

File tree

src/connectors/sqlserver/index.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -709,9 +709,11 @@ export class SQLServerConnector implements Connector {
709709
* keyword classifier.
710710
*
711711
* Because the rollback guard is application-level (not engine-enforced),
712-
* we must reject transaction-control keywords (COMMIT, ROLLBACK) in the
713-
* SQL — an in-band COMMIT would end the outer transaction before the
714-
* finally-block ROLLBACK runs, letting writes persist.
712+
* we reject dangerous keywords in the stripped SQL before opening the
713+
* transaction:
714+
* - COMMIT/ROLLBACK: would end the outer transaction, letting writes persist
715+
* - EXEC/EXECUTE/sp_executesql/xp_cmdshell: dynamic SQL can carry hidden
716+
* COMMIT/ROLLBACK inside string literals that stripCommentsAndStrings removes
715717
*/
716718
private async executeReadOnly(
717719
processedSQL: string,
@@ -723,6 +725,11 @@ export class SQLServerConnector implements Connector {
723725
"Read-only mode: transaction control statements (COMMIT, ROLLBACK) are not allowed",
724726
);
725727
}
728+
if (/\b(?:exec|execute|sp_executesql|xp_cmdshell)\b/.test(cleaned)) {
729+
throw new Error(
730+
"Read-only mode: dynamic SQL execution (EXEC, EXECUTE, sp_executesql, xp_cmdshell) is not allowed",
731+
);
732+
}
726733

727734
const transaction = new sql.Transaction(this.connection!);
728735
await transaction.begin();

0 commit comments

Comments
 (0)