Skip to content

Commit 2d947a4

Browse files
authored
Merge pull request #13781 from maikypedia/maikypedia/python-unsafe-deserialization
Python: Add unsafe deserialization sinks (CWE-502)
2 parents 542d5a2 + f3acc89 commit 2d947a4

File tree

16 files changed

+153
-2
lines changed

16 files changed

+153
-2
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Improved modeling of decoding through pickle related functions (which can lead to code execution), resulting in additional sinks for the _Deserializing untrusted input_ query (`py/unsafe-deserialization`). Added support for `pandas.read_pickle`, `numpy.load` and `joblib.load`.

python/ql/lib/semmle/python/Frameworks.qll

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
private import semmle.python.frameworks.Aioch
88
private import semmle.python.frameworks.Aiohttp
99
private import semmle.python.frameworks.Aiomysql
10-
private import semmle.python.frameworks.Aiosqlite
1110
private import semmle.python.frameworks.Aiopg
11+
private import semmle.python.frameworks.Aiosqlite
1212
private import semmle.python.frameworks.Asyncpg
1313
private import semmle.python.frameworks.BSon
1414
private import semmle.python.frameworks.CassandraDriver
@@ -28,6 +28,7 @@ private import semmle.python.frameworks.Httpx
2828
private import semmle.python.frameworks.Idna
2929
private import semmle.python.frameworks.Invoke
3030
private import semmle.python.frameworks.Jmespath
31+
private import semmle.python.frameworks.Joblib
3132
private import semmle.python.frameworks.Ldap
3233
private import semmle.python.frameworks.Ldap3
3334
private import semmle.python.frameworks.Libtaxii
@@ -37,7 +38,9 @@ private import semmle.python.frameworks.MarkupSafe
3738
private import semmle.python.frameworks.Multidict
3839
private import semmle.python.frameworks.Mysql
3940
private import semmle.python.frameworks.MySQLdb
41+
private import semmle.python.frameworks.Numpy
4042
private import semmle.python.frameworks.Oracledb
43+
private import semmle.python.frameworks.Pandas
4144
private import semmle.python.frameworks.Peewee
4245
private import semmle.python.frameworks.Phoenixdb
4346
private import semmle.python.frameworks.Psycopg2
@@ -52,11 +55,11 @@ private import semmle.python.frameworks.RestFramework
5255
private import semmle.python.frameworks.Rsa
5356
private import semmle.python.frameworks.RuamelYaml
5457
private import semmle.python.frameworks.ServerLess
58+
private import semmle.python.frameworks.Setuptools
5559
private import semmle.python.frameworks.Simplejson
5660
private import semmle.python.frameworks.SqlAlchemy
5761
private import semmle.python.frameworks.Starlette
5862
private import semmle.python.frameworks.Stdlib
59-
private import semmle.python.frameworks.Setuptools
6063
private import semmle.python.frameworks.Toml
6164
private import semmle.python.frameworks.Tornado
6265
private import semmle.python.frameworks.Twisted
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `joblib` PyPI package.
3+
* See https://pypi.org/project/joblib/.
4+
*/
5+
6+
private import python
7+
private import semmle.python.dataflow.new.DataFlow
8+
private import semmle.python.Concepts
9+
private import semmle.python.ApiGraphs
10+
11+
/**
12+
* Provides models for the `joblib` PyPI package.
13+
* See https://pypi.org/project/joblib/.
14+
*/
15+
private module Joblib {
16+
/**
17+
* A call to `joblib.load`
18+
* See https://pypi.org/project/joblib/
19+
*/
20+
private class JoblibLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
21+
JoblibLoadCall() { this = API::moduleImport("joblib").getMember("load").getACall() }
22+
23+
override predicate mayExecuteInput() { any() }
24+
25+
override DataFlow::Node getAnInput() {
26+
result in [this.getArg(0), this.getArgByName("filename")]
27+
}
28+
29+
override DataFlow::Node getOutput() { result = this }
30+
31+
override string getFormat() { result = "joblib" }
32+
}
33+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `numpy` PyPI package.
3+
* See https://pypi.org/project/numpy/.
4+
*/
5+
6+
private import python
7+
private import semmle.python.dataflow.new.DataFlow
8+
private import semmle.python.dataflow.new.RemoteFlowSources
9+
private import semmle.python.Concepts
10+
private import semmle.python.ApiGraphs
11+
12+
/**
13+
* Provides models for the `numpy` PyPI package.
14+
* See https://pypi.org/project/numpy/.
15+
*/
16+
private module Numpy {
17+
/**
18+
* A call to `numpy.load`
19+
* See https://numpy.org/doc/stable/reference/generated/numpy.load.html
20+
*/
21+
private class NumpyLoadCall extends Decoding::Range, API::CallNode {
22+
NumpyLoadCall() { this = API::moduleImport("numpy").getMember("load").getACall() }
23+
24+
override predicate mayExecuteInput() {
25+
this.getParameter(2, "allow_pickle")
26+
.getAValueReachingSink()
27+
.asExpr()
28+
.(ImmutableLiteral)
29+
.booleanValue() = true
30+
}
31+
32+
override DataFlow::Node getAnInput() { result = this.getParameter(0, "filename").asSink() }
33+
34+
override DataFlow::Node getOutput() { result = this }
35+
36+
override string getFormat() {
37+
result = "numpy"
38+
or
39+
this.mayExecuteInput() and result = "pickle"
40+
}
41+
}
42+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `pandas` PyPI package.
3+
* See https://pypi.org/project/pandas/.
4+
*/
5+
6+
private import python
7+
private import semmle.python.dataflow.new.DataFlow
8+
private import semmle.python.dataflow.new.RemoteFlowSources
9+
private import semmle.python.Concepts
10+
private import semmle.python.ApiGraphs
11+
12+
/**
13+
* Provides models for the `pandas` PyPI package.
14+
* See https://pypi.org/project/pandas/.
15+
*/
16+
private module Pandas {
17+
/**
18+
* A call to `pandas.read_pickle`
19+
* See https://pypi.org/project/pandas/
20+
* See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_pickle.html
21+
*/
22+
private class PandasReadPickleCall extends Decoding::Range, DataFlow::CallCfgNode {
23+
PandasReadPickleCall() {
24+
this = API::moduleImport("pandas").getMember("read_pickle").getACall()
25+
}
26+
27+
override predicate mayExecuteInput() { any() }
28+
29+
override DataFlow::Node getAnInput() {
30+
result in [this.getArg(0), this.getArgByName("filepath_or_buffer")]
31+
}
32+
33+
override DataFlow::Node getOutput() { result = this }
34+
35+
override string getFormat() { result = "pickle" }
36+
}
37+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
failures
2+
testFailures
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import python
2+
import experimental.meta.ConceptsTest
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import joblib
2+
3+
joblib.load(file_) # $ decodeInput=file_ decodeOutput=joblib.load(..) decodeFormat=joblib decodeMayExecuteInput
4+
joblib.load(filename=file_) # $ decodeInput=file_ decodeOutput=joblib.load(..) decodeFormat=joblib decodeMayExecuteInput
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
failures
2+
testFailures
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import python
2+
import experimental.meta.ConceptsTest

0 commit comments

Comments
 (0)