hasura / hasura/graphql-engine
Prevent running SQL with COMMIT statement via run_sql
- Dominant language
- TypeScript
- Stars
- 32.1k
- Forks
- 3k
- PR merge metrics
- PR metrics pending
Description
### Is your proposal related to a problem?
The `run_sql` api allows users to run arbitrary sql, and is, amongst other things, used to run migrations.
SQL executed via this api is wrapped like so:
```sql
BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY;
SET LOCAL "hasura.tracecontext" = '{"span_id":"8e8f1409160a4489","trace_id":"95deafe2b16486ac75b7f1185730d0aa"}';
SET LOCAL "hasura.user" = '{"x-hasura-role":"admin"}';
-- BEGIN USER SQL
-- END USER SQL
COMMIT;
```
This ensures all SQL executed via this api is run in a transaction.
The issue is users can execute a `COMMIT` statement of their own.
If they do so, projects break if they have event triggers.
Given the following user migration:
```sql
SELECT 1;
COMMIT;
INSERT INTO notifications(message) VALUES ('this is a message');
```
The SQL executed will look like this:
```sql
BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY;
SET LOCAL "hasura.tracecontext" = '{"span_id":"8e8f1409160a4489","trace_id":"95deafe2b16486ac75b7f1185730d0aa"}';
SET LOCAL "hasura.user" = '{"x-hasura-role":"admin"}';
SELECT 1;
COMMIT;
INSERT INTO notifications(message) VALUES ('this is a message');
COMMIT;
```
The issue is, the first `COMMIT` will result in the local settings `hasura.tracecontext` and `hasura.user` [going out of scope](https://www.postgresql.org/docs/current/sql-set.html):
> Specifies that the command takes effect for only the current transaction. After COMMIT or ROLLBACK, the session-level setting takes effect again. Issuing this outside of a transaction block emits a warning and otherwise has no effect.
This conflicts with our postgres function `hdb_catalog.insert_event_log` which is used by events.
The [function](https://github.com/hasura/graphql-engine/blob/525361d3d256f2432f6da2eefe8dbf17ab047e19/server/src-rsr/migrations/43_to_42.sql#L758-L759) expects the settings `hasura.user` and `hasura.tracecontext` to be valid JSON values, or be unset such that the value will be null.
But when a local variable goes out of scope, it is reset to the default value of an empty string, so `current_setting('hasura.user', 't')` will return an empty string instead of a valid json value or null.
### Describe the solution you'd like
For the reasons described above, and others, using `COMMIT` in `run_sql` can be problematic and should be prevented.
Furthermore, the exact behavior of `run_sql` should be documented.
Contributor guide
Research direction
Start from the run_sql API implementation and its PostgreSQL transaction handling, then inspect existing tests and documentation for run_sql behavior. Add coverage showing that user-supplied COMMIT statements are rejected or handled safely, and document the resulting transaction behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- postgresql, sql
- Domain
- api, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100