Skip to content

Commit 091c0e2

Browse files
authored
feat: Reduce duckdb extensions and filesystem access (#179)
* feat: Lock down duckdb to limit filesystem access * chore(py): Make py-format
1 parent b2d8cc6 commit 091c0e2

File tree

4 files changed

+44
-2
lines changed

4 files changed

+44
-2
lines changed

pkg-py/docs/_examples/multiple-datasets.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,19 @@
1515
qc_penguins.ui()
1616

1717
with ui.nav_panel("Titanic"):
18+
1819
@render.data_frame
1920
def titanic_table():
2021
return qc_titanic.df()
2122

23+
2224
with ui.nav_panel("Penguins"):
25+
2326
@render.data_frame
2427
def penguins_table():
2528
return qc_penguins.df()
2629

30+
2731
ui.page_opts(
2832
id="navbar",
2933
title="Multiple Datasets with querychat",

pkg-py/docs/_examples/titanic-dashboard.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def fare():
3232
avg = qc.df()["fare"].mean()
3333
return f"${avg:.2f}"
3434

35+
3536
with ui.layout_columns():
3637
with ui.card():
3738
with ui.card_header():
@@ -59,6 +60,7 @@ def survival_by_class():
5960
labels={"pclass": "Class", "survived": "Survival Rate"},
6061
)
6162

63+
6264
with ui.layout_columns():
6365
with ui.card():
6466
ui.card_header("Age Distribution")
@@ -76,6 +78,7 @@ def fare_by_class():
7678
df = qc.df()
7779
return px.box(df, x="pclass", y="fare", color="survived")
7880

81+
7982
ui.page_opts(
8083
title="Titanic Survival Analysis",
8184
fillable=True,

pkg-py/src/querychat/_datasource.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,26 @@ def __init__(self, df: IntoFrame, table_name: str):
115115
Name of the table in SQL queries
116116
117117
"""
118-
self._conn = duckdb.connect(database=":memory:")
119118
self._df = nw.from_native(df)
120119
self.table_name = table_name
121-
# TODO(@gadenbuie): If the data frame is already SQL-backed, maybe we shouldn't be making a new copy here.
120+
121+
self._conn = duckdb.connect(database=":memory:")
122+
# TODO(@gadenbuie): What if the data frame is already SQL-backed?
122123
self._conn.register(table_name, self._df.lazy().collect().to_pandas())
124+
self._conn.execute("""
125+
-- extensions: lock down supply chain + auto behaviors
126+
SET allow_community_extensions = false;
127+
SET allow_unsigned_extensions = false;
128+
SET autoinstall_known_extensions = false;
129+
SET autoload_known_extensions = false;
130+
131+
-- external I/O: block file/database/network access from SQL
132+
SET enable_external_access = false;
133+
SET disabled_filesystems = 'LocalFileSystem';
134+
135+
-- freeze configuration so user SQL can't relax anything
136+
SET lock_configuration = true;
137+
""")
123138

124139
def get_db_type(self) -> str:
125140
"""

pkg-r/R/DataSource.R

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,33 @@ DataFrameSource <- R6::R6Class(
163163
# Create in-memory connection and register the data frame
164164
if (engine == "duckdb") {
165165
check_installed("duckdb")
166+
166167
private$conn <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:")
168+
167169
duckdb::duckdb_register(
168170
private$conn,
169171
table_name,
170172
df,
171173
experimental = FALSE
172174
)
175+
176+
DBI::dbExecute(
177+
private$conn,
178+
r"(
179+
-- extensions: lock down supply chain + auto behaviors
180+
SET allow_community_extensions = false;
181+
SET allow_unsigned_extensions = false;
182+
SET autoinstall_known_extensions = false;
183+
SET autoload_known_extensions = false;
184+
185+
-- external I/O: block file/database/network access from SQL
186+
SET enable_external_access = false;
187+
SET disabled_filesystems = 'LocalFileSystem';
188+
189+
-- freeze configuration so user SQL can't relax anything
190+
SET lock_configuration = true;
191+
)"
192+
)
173193
} else if (engine == "sqlite") {
174194
check_installed("RSQLite")
175195
private$conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")

0 commit comments

Comments
 (0)