mirror of
https://github.com/simonw/datasette.git
synced 2026-09-09 01:54:15 +02:00
Fix for SQL injection issue in table filters, refs #2868
This commit is contained in:
parent
1d68d86e04
commit
c43d89382f
5 changed files with 119 additions and 23 deletions
|
|
@ -78,6 +78,35 @@ def test_build_where(args, expected_where, expected_params):
|
|||
assert {f"p{i}": param for i, param in enumerate(expected_params)} == actual_params
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key,expected_where",
|
||||
(
|
||||
(
|
||||
'has"quote__exact',
|
||||
'[has"quote] = :p0',
|
||||
),
|
||||
(
|
||||
'has"quote__isnull',
|
||||
'[has"quote] is null',
|
||||
),
|
||||
(
|
||||
"has]bracket__arraycontains",
|
||||
':p0 in (select value from json_each([table]."has]bracket"))',
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_build_where_escapes_column_names(key, expected_where):
|
||||
filters = Filters(((key, "value"),))
|
||||
sql_bits, _ = filters.build_where_clauses("table")
|
||||
assert sql_bits == [expected_where]
|
||||
|
||||
|
||||
def test_build_where_escapes_table_name():
|
||||
filters = Filters((("tags__arraycontains", "value"),))
|
||||
sql_bits, _ = filters.build_where_clauses("items]bracket")
|
||||
assert sql_bits == [':p0 in (select value from json_each("items]bracket"."tags"))']
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_through_filters_from_request(app_client):
|
||||
request = Request.fake(
|
||||
|
|
|
|||
|
|
@ -521,6 +521,35 @@ def test_table_filter_queries_multiple_of_same_type(app_client):
|
|||
] == response.json["rows"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not detect_json1(), reason="Requires the SQLite json1 module")
|
||||
def test_table_filters_quote_identifiers():
|
||||
with make_app_client(
|
||||
extra_databases={
|
||||
"demo.db": """
|
||||
create table items (
|
||||
id integer primary key,
|
||||
"name""quote" text,
|
||||
"tags]bracket" text
|
||||
);
|
||||
insert into items values (1, 'Alice', '["red"]');
|
||||
"""
|
||||
},
|
||||
) as client:
|
||||
exact_query = urllib.parse.urlencode(
|
||||
{'name"quote__exact': "Alice", "_shape": "arrays"}
|
||||
)
|
||||
exact_response = client.get(f"/demo/items.json?{exact_query}")
|
||||
assert exact_response.status == 200
|
||||
assert exact_response.json["rows"] == [[1, "Alice", '["red"]']]
|
||||
|
||||
array_query = urllib.parse.urlencode(
|
||||
{"tags]bracket__arraycontains": "red", "_shape": "arrays"}
|
||||
)
|
||||
array_response = client.get(f"/demo/items.json?{array_query}")
|
||||
assert array_response.status == 200
|
||||
assert array_response.json["rows"] == [[1, "Alice", '["red"]']]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not detect_json1(), reason="Requires the SQLite json1 module")
|
||||
def test_table_filter_json_arraycontains(app_client):
|
||||
response = app_client.get("/fixtures/facetable.json?tags__arraycontains=tag1")
|
||||
|
|
|
|||
|
|
@ -212,6 +212,30 @@ def test_detect_fts(open_quote, close_quote):
|
|||
assert "Street_Tree_List_fts" == utils.detect_fts(conn, "Street_Tree_List")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"identifier,expected",
|
||||
(
|
||||
("plain", "plain"),
|
||||
("select", "[select]"),
|
||||
("has space", "[has space]"),
|
||||
("has]bracket", '"has]bracket"'),
|
||||
('has"quote]', '"has""quote]"'),
|
||||
),
|
||||
)
|
||||
def test_escape_sqlite(identifier, expected):
|
||||
assert utils.escape_sqlite(identifier) == expected
|
||||
|
||||
|
||||
def test_escape_sqlite_closing_bracket_works_in_query():
|
||||
conn = utils.sqlite3.connect(":memory:")
|
||||
table = "has]bracket"
|
||||
escaped_table = utils.escape_sqlite(table)
|
||||
conn.execute(f"create table {escaped_table} (id integer)")
|
||||
conn.execute(f"insert into {escaped_table} values (1)")
|
||||
assert conn.execute(f"select id from {escaped_table}").fetchall() == [(1,)]
|
||||
conn.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("table", ("regular", "has'single quote"))
|
||||
def test_detect_fts_different_table_names(table):
|
||||
sql = """
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue