diff --git a/datasette/utils/__init__.py b/datasette/utils/__init__.py index 490b71c8..f1c24041 100644 --- a/datasette/utils/__init__.py +++ b/datasette/utils/__init__.py @@ -171,7 +171,26 @@ allowed_sql_res = [ re.compile(r"^explain with\b"), re.compile(r"^explain query plan with\b"), ] -disallawed_sql_res = [(re.compile("pragma"), "Statement may not contain PRAGMA")] +allowed_pragmas = ( + "database_list", + "foreign_key_list", + "function_list", + "index_info", + "index_list", + "index_xinfo", + "page_count", + "max_page_count", + "page_size", + "schema_version", + "table_info", + "table_xinfo", +) +disallawed_sql_res = [ + ( + re.compile("pragma(?!_({}))".format("|".join(allowed_pragmas))), + "Statement may not contain PRAGMA", + ) +] def validate_sql_select(sql): diff --git a/tests/test_utils.py b/tests/test_utils.py index fe5d9a26..7e4f1a8e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -140,7 +140,8 @@ def test_custom_json_encoder(obj, expected): "update blah;", "-- sql comment to skip\nupdate blah;", "update blah set some_column='# Hello there\n\n* This is a list\n* of items\n--\n[And a link](https://github.com/simonw/datasette-render-markdown).'\nas demo_markdown", - "PRAGMA case_sensitive_like = true" "SELECT * FROM pragma_index_info('idx52')", + "PRAGMA case_sensitive_like = true", + "SELECT * FROM pragma_not_on_allow_list('idx52')", ], ) def test_validate_sql_select_bad(bad_sql): @@ -162,6 +163,8 @@ def test_validate_sql_select_bad(bad_sql): "WITH RECURSIVE cnt(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 10) SELECT x FROM cnt;", "explain WITH RECURSIVE cnt(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 10) SELECT x FROM cnt;", "explain query plan WITH RECURSIVE cnt(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 10) SELECT x FROM cnt;", + "SELECT * FROM pragma_index_info('idx52')", + "select * from pragma_table_xinfo('table')", ], ) def test_validate_sql_select_good(good_sql):