Skip to content

Commit f796efe

Browse files
Add Streamlit SQLAlchemy models
1 parent 9741ddb commit f796efe

File tree

1 file changed

+59
-15
lines changed

1 file changed

+59
-15
lines changed

python/ql/lib/semmle/python/frameworks/Streamlit.qll

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import semmle.python.dataflow.new.RemoteFlowSources
88
import semmle.python.dataflow.new.TaintTracking
99
import semmle.python.ApiGraphs
1010
import semmle.python.Concepts
11+
import semmle.python.frameworks.SqlAlchemy
1112

1213
/**
1314
* Provides models for the `gradio` PyPI package.
@@ -27,31 +28,74 @@ module Streamlit {
2728

2829
override string getSourceType() { result = "Streamlit user input" }
2930
}
31+
/**
32+
* The Streamlit SQLConnection class, which is used to create a connection to a SQL Database.
33+
* Streamlit wraps around SQL Alchemy for most database functionality, and adds some on top of it, such as the `query` method.
34+
* Streamlit can also connect to Snowflake and Snowpark databases, but the modeling is not the same, so we need to limit the scope to SQL databases.
35+
* https://docs.streamlit.io/develop/api-reference/connections/st.connections.sqlconnection#:~:text=to%20data.-,st.connections.SQLConnection,-Streamlit%20Version
36+
* We can connect to SQL databases for example with `import streamlit as st; conn = st.connection('pets_db', type='sql')`
37+
*/
38+
private class StreamlitSQLConnection extends API::CallNode {
39+
StreamlitSQLConnection() {
40+
exists(StringLiteral str, API::CallNode n |
41+
str.getText().matches("sql")
42+
and
43+
n = API::moduleImport("streamlit").getMember("connection").getACall()
44+
and
45+
DataFlow::exprNode(str).(DataFlow::LocalSourceNode)
46+
.flowsTo([n.getArg(1), n.getArgByName("type")])
47+
and this = n
48+
)
3049

50+
}
51+
}
3152
/**
32-
* The `query` call that can execute raw queries on a connection to a SQL/Sonwflake/Snowpark database.
53+
* The `query` call that can execute raw queries on a connection to a SQL database.
3354
* https://docs.streamlit.io/develop/api-reference/connections/st.connection
3455
*/
3556
private class QueryMethodCall extends DataFlow::CallCfgNode, SqlExecution::Range {
57+
3658
QueryMethodCall() {
37-
this =
38-
API::moduleImport("streamlit")
39-
.getMember("connection")
40-
.getReturn()
41-
.getMember("query")
42-
.getACall()
59+
exists(StreamlitSQLConnection s |
60+
this = s.getReturn().getMember("query").getACall())
4361
}
4462

4563
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
4664
}
47-
private class StreamlitConnection extends SqlAlchemy::Connection::InstanceSource {
48-
StreamlitConnection() {
49-
this =
50-
API::moduleImport("streamlit")
51-
.getMember("connection")
52-
.getReturn()
53-
.getMember("connect")
54-
.getACall()
65+
66+
67+
/**
68+
* The Streamlit SQLConnection.connect() call, which returns a a new sqlalchemy.engine.Connection object.
69+
* Streamlit creates a connection to a SQL database basing off SQL Alchemy, so we can reuse the models that we already have.
70+
*/
71+
private class StreamlitSQLAlchemyConnection extends SqlAlchemy::Connection::InstanceSource {
72+
StreamlitSQLAlchemyConnection() {
73+
exists(StreamlitSQLConnection s |
74+
this = s.getReturn().getMember("connect").getACall())
75+
}
76+
}
77+
78+
/**
79+
* The underlying SQLAlchemy Engine, accessed via `st.connection().engine`.
80+
* Streamlit creates an engine to a SQL database basing off SQL Alchemy, so we can reuse the models that we already have.
81+
*/
82+
private class StreamlitSQLAlchemyEngine extends SqlAlchemy::Engine::InstanceSource {
83+
StreamlitSQLAlchemyEngine() {
84+
exists(StreamlitSQLConnection s |
85+
this = s.getReturn().getMember("engine").asSource())
86+
}
87+
}
88+
89+
/**
90+
* The SQLAlchemy Session, accessed via `st.connection().session`.
91+
* Streamlit can create a session to a SQL database basing off SQL Alchemy, so we can reuse the models that we already have.
92+
* For example, the modeling for `session` includes an `execute` method, which is used to execute raw SQL queries.
93+
* https://docs.streamlit.io/develop/api-reference/connections/st.connections.sqlconnection#:~:text=SQLConnection.engine-,SQLConnection.session,-Streamlit%20Version
94+
*/
95+
private class StreamlitSession extends SqlAlchemy::Session::InstanceSource {
96+
StreamlitSession() {
97+
exists(StreamlitSQLConnection s |
98+
this = s.getReturn().getMember("session").asSource())
5599
}
56100
}
57101
}

0 commit comments

Comments
 (0)