这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@nicuveo
Copy link
Contributor

@nicuveo nicuveo commented Mar 31, 2020

Description

This fixes a bug where the server could perform a metadata check while in read-only mode, causing a postgres error. (For a full explanation of the bug, see this comment.)

This PR also slightly improves the regex used to identify whether a given request might need a metadata check. While it is now ok for it to aggressively check, this will reduce the number of false positives.

Changelog

  • CHANGELOG.md is updated with user-facing content relevant to this PR.

Affected components

  • Server

@nicuveo nicuveo requested a review from a team as a code owner March 31, 2020 02:02
@lexi-lambda
Copy link
Contributor

I am not sure how to force a query to be in read-only mode, since run-sql is by definition an admin-only command. I am not sure how to test this, as a result.

Could you write a test that runs your example select attisdropped from pg_attribute query via run_sql and ensures it doesn’t fail? I’m not sure I quite understand why that wouldn’t work.

@hasura-bot
Copy link
Contributor

Review app for commit 5124945 deployed to Heroku: https://hge-ci-pull-4250.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4250-5124945e

@nicuveo
Copy link
Contributor Author

nicuveo commented Mar 31, 2020

I’m not sure I quite understand why that wouldn’t work.

To be completely honest, I don't quite understand yet either. But it's the reason the tests in #4204 kept failing. Upon investigating, I found this error message: a read-only query that matches "drop". This causes the code to treat it as as an alteration, to run the metadata check... and then it fails with an internal postgres error.

The only thing I don't know is why / when the console marks a query as being read only. But while writing that comment, I checked: it is simply a valid argument for run_sql, which means I know now how to write a test. ^^

@lexi-lambda
Copy link
Contributor

Reading the code, AFAICT, the regex is only used to determine whether it can skip rebuilding the schema cache—executing the query in READ ONLY mode is determined elsewhere. So while this PR does appear to be an improvement—it makes the check a little less stupid—false positives should be okay. The check matching is the case where we take the slow path, so matching too much is alright; it’s just matching too little that’s bad.

@hasura-bot
Copy link
Contributor

Review app for commit f227328 deployed to Heroku: https://hge-ci-pull-4250.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4250-f227328c

@nicuveo
Copy link
Contributor Author

nicuveo commented Mar 31, 2020

Here's a short write-up of the investigation.


replicating the bug

Imagine the two following run_sql commands are sent to the server:

- type: run_sql
  args:
    sql: "create view v as select 1;"
- type: run_sql
  args:
    read_only: True
    sql: "select attisdropped from pg_attribute;"

The first one only creates a trivial view. What is interesting is what happens with the second one:

  • as it is read only, the code (correctly) identifies it as being a request that does not modify the schema cache
  • the code therefore opens the connection to postgres in read_only mode
  • due to the erroneous regex this patch fixes, the query is marked as requiring a metadata check
  • the metadata check starts by clearing all hdb views
  • which, for each of them, issues a DROP; since we have one view, one DROP query is sent to postgres
  • we're in read-only mode => internal postgres error, the query fails

tl;dr: when hdb_views is not empty (1), a read_only (2) run_sql query that match words such as "drop" or "alter" (3) will erroneously trigger a metadata check and issue a drop while in read-only mode.


fixing the bug

This PR offers a very simple solution to the problem: make the regex check for word boundaries. This is a quick-fix and it is not enough. The problem would still arise if one of the "bad words" were to be included in a string in the query, such as: select * from q where f = ' drop '.

There are three ways we could choose to tackle this; note that they are not mutually exclusive:

  1. make the check more robust: use a full sql parser to properly identify whether the query requires a metadata check or not
  2. use the read_only information to override that check: we only perform metadata checks if we're not in read only mode AND the query seems to match a drop / alter
  3. we change metadata checks so that they don't alter the schema cache

This PR goes towards (1), making the check more robust. I am not sure if / how the two other points are reasonable.

@lexi-lambda
Copy link
Contributor

There are three ways we could choose to tackle this; note that they are not mutually exclusive:

  1. make the check more robust: use a full sql parser to properly identify whether the query requires a metadata check or not
  2. use the read_only information to override that check: we only perform metadata checks if we're not in read only mode AND the query seems to match a drop / alter
  3. we change metadata checks so that they don't alter the schema cache

This PR goes towards (1), making the check more robust. I am not sure if / how the two other points are reasonable.

I think that doing (1) is a Good Thing, since it’s clearly an improvement over the old code. However, I think that doing it alone is a bit sketchy, since it doesn’t actually fix the bug—it just sweeps it under the rug.

The imprecision of the regex is a red herring, since the code was written with the intent that the regex would enable a conservative optimization, so any regex at all ought to do! The code ought to work if the regex were .*. Anything else is a bug.

So I think we have to do (2) as well. We’ve discovered the bug, and the fix is easy, so we should fix it now! All we need to do is guard the use of withMetadataCheck with an additional check to see if we’re in ReadOnly mode or not. We can include the improvement to the regex as well, of course, but then we can claim we’re actually fixing the bug.

@nicuveo nicuveo force-pushed the fix-runsql-alter-drop-check branch from f227328 to 7259821 Compare April 1, 2020 07:37
@nicuveo nicuveo changed the title server: fix isAltrDropReplace regex server: prevent metadata checks in read-only mode Apr 1, 2020
@nicuveo
Copy link
Contributor Author

nicuveo commented Apr 1, 2020

I've added the check for read-only mode, added more tests, and rewrote the PR description. I am unsure whether this needs a changelog entry; it is only a minor bug, in the grand scheme of things... What do you think?

@hasura-bot
Copy link
Contributor

Review app for commit 7259821 deployed to Heroku: https://hge-ci-pull-4250.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4250-72598214

@nicuveo nicuveo mentioned this pull request Apr 1, 2020
1 task

isAltrDropReplace :: QErrM m => T.Text -> m Bool
isAltrDropReplace = either throwErr return . matchRegex regex False
containsMutationKeyword :: QErrM m => T.Text -> m Bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something along the lines of containsDDLKeyword might be better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and I’ve made this change.

@netlify
Copy link

netlify bot commented Apr 1, 2020

Deploy preview for hasura-docs ready!

Built with commit 2b5073c

https://deploy-preview-4250--hasura-docs.netlify.com

Copy link
Contributor

@lexi-lambda lexi-lambda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve pushed three changes to this PR:

  1. I’ve incorporated @0x777’s suggestion.

  2. I’ve refactored the code around the regex to perform the parsing of the pattern at compile-time instead of at runtime, so containsDDLKeyword can be total.

  3. I’ve added a Note that incorporates the discussion from slack.

Antoine Leblanc and others added 2 commits April 1, 2020 16:50
* improve the isAltrDropReplace regex
* do not perform the metadata check in read-only mode
@lexi-lambda lexi-lambda force-pushed the fix-runsql-alter-drop-check branch from 2b5073c to 288bd6b Compare April 1, 2020 21:51
@hasura-bot
Copy link
Contributor

Review app for commit 288bd6b deployed to Heroku: https://hge-ci-pull-4250.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4250-288bd6b2

@lexi-lambda lexi-lambda merged commit 5b74b2e into hasura:master Apr 1, 2020
@hasura-bot
Copy link
Contributor

Review app https://hge-ci-pull-4250.herokuapp.com is deleted

@nicuveo nicuveo deleted the fix-runsql-alter-drop-check branch April 3, 2020 11:47
codingkarthik pushed a commit to lexi-lambda/graphql-engine that referenced this pull request Jul 23, 2020
* do not perform the metadata check in read-only mode
* improve the isAltrDropReplace regex
* quote the regex at compile-time to handle syntax errors statically

Co-authored-by: Alexis King <lexi.lambda@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants