Skip to content

Commit df144f3

Browse files
authored
Merge pull request github#14406 from amammad/amammad-python-FileSystemAccess
Python: New FileSystem Access
2 parents 14268f3 + e349891 commit df144f3

33 files changed

+355
-1
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
55
// If you add modeling of a new framework/library, remember to add it to the docs in
66
// `docs/codeql/reusables/supported-frameworks.rst`
77
private import semmle.python.frameworks.Aioch
8+
private import semmle.python.frameworks.Aiofile
9+
private import semmle.python.frameworks.Aiofiles
810
private import semmle.python.frameworks.Aiohttp
911
private import semmle.python.frameworks.Aiomysql
1012
private import semmle.python.frameworks.Aiopg
1113
private import semmle.python.frameworks.Aiosqlite
14+
private import semmle.python.frameworks.Anyio
1215
private import semmle.python.frameworks.Asyncpg
16+
private import semmle.python.frameworks.Baize
1317
private import semmle.python.frameworks.BSon
1418
private import semmle.python.frameworks.CassandraDriver
19+
private import semmle.python.frameworks.Cherrypy
1520
private import semmle.python.frameworks.ClickhouseDriver
1621
private import semmle.python.frameworks.Cryptodome
1722
private import semmle.python.frameworks.Cryptography
@@ -54,6 +59,7 @@ private import semmle.python.frameworks.Requests
5459
private import semmle.python.frameworks.RestFramework
5560
private import semmle.python.frameworks.Rsa
5661
private import semmle.python.frameworks.RuamelYaml
62+
private import semmle.python.frameworks.Sanic
5763
private import semmle.python.frameworks.ServerLess
5864
private import semmle.python.frameworks.Setuptools
5965
private import semmle.python.frameworks.Simplejson
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 `aiofile` PyPI package.
3+
*
4+
* See https://pypi.org/project/aiofile.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.dataflow.new.RemoteFlowSources
10+
private import semmle.python.dataflow.new.TaintTracking
11+
private import semmle.python.Concepts
12+
private import semmle.python.ApiGraphs
13+
14+
/**
15+
* Provides models for the `aiofile` PyPI package.
16+
*
17+
* See https://pypi.org/project/aiofile.
18+
*/
19+
private module Aiofile {
20+
/**
21+
* A call to the `async_open` function or `AIOFile` constructor from `aiofile` as a sink for Filesystem access.
22+
*/
23+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
24+
string methodName;
25+
26+
FileResponseCall() {
27+
this = API::moduleImport("aiofile").getMember("async_open").getACall() and
28+
methodName = "async_open"
29+
or
30+
this = API::moduleImport("aiofile").getMember("AIOFile").getACall() and
31+
methodName = "AIOFile"
32+
}
33+
34+
override DataFlow::Node getAPathArgument() {
35+
result = this.getParameter(0, "file_specifier").asSink() and
36+
methodName = "async_open"
37+
or
38+
result = this.getParameter(0, "filename").asSink() and
39+
methodName = "AIOFile"
40+
}
41+
}
42+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `aiofiles` PyPI package.
3+
*
4+
* See https://pypi.org/project/aiofiles.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.dataflow.new.RemoteFlowSources
10+
private import semmle.python.dataflow.new.TaintTracking
11+
private import semmle.python.Concepts
12+
private import semmle.python.ApiGraphs
13+
14+
/**
15+
* Provides models for the `aiofiles` PyPI package.
16+
*
17+
* See https://pypi.org/project/aiofiles.
18+
*/
19+
private module Aiofiles {
20+
/**
21+
* A call to the `open` function from `aiofiles` as a sink for Filesystem access.
22+
*/
23+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
24+
FileResponseCall() { this = API::moduleImport("aiofiles").getMember("open").getACall() }
25+
26+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
27+
}
28+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `anyio` PyPI package.
3+
*
4+
* See https://pypi.org/project/anyio.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.dataflow.new.RemoteFlowSources
10+
private import semmle.python.dataflow.new.TaintTracking
11+
private import semmle.python.Concepts
12+
private import semmle.python.ApiGraphs
13+
14+
/**
15+
* Provides models for the `anyio` PyPI package.
16+
*
17+
* See https://pypi.org/project/anyio.
18+
*/
19+
private module Anyio {
20+
/**
21+
* A call to the `from_path` function from `FileReadStream` or `FileWriteStream` constructors of `anyio.streams.file` as a sink for Filesystem access.
22+
*/
23+
class FileStreamCall extends FileSystemAccess::Range, API::CallNode {
24+
FileStreamCall() {
25+
this =
26+
API::moduleImport("anyio")
27+
.getMember("streams")
28+
.getMember("file")
29+
.getMember(["FileReadStream", "FileWriteStream"])
30+
.getMember("from_path")
31+
.getACall()
32+
}
33+
34+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
35+
}
36+
37+
/**
38+
* A call to the `Path` constructor from `anyio` as a sink for Filesystem access.
39+
*/
40+
class PathCall extends FileSystemAccess::Range, API::CallNode {
41+
PathCall() { this = API::moduleImport("anyio").getMember("Path").getACall() }
42+
43+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0).asSink() }
44+
}
45+
46+
/**
47+
* A call to the `open_file` function from `anyio` as a sink for Filesystem access.
48+
*/
49+
class OpenFileCall extends FileSystemAccess::Range, API::CallNode {
50+
OpenFileCall() { this = API::moduleImport("anyio").getMember("open_file").getACall() }
51+
52+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
53+
}
54+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `baize` PyPI package.
3+
*
4+
* See https://pypi.org/project/baize.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.dataflow.new.TaintTracking
10+
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
12+
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
13+
private import semmle.python.frameworks.Stdlib
14+
15+
/**
16+
* Provides models for `baize` PyPI package.
17+
*
18+
* See https://pypi.org/project/baize.
19+
*/
20+
module Baize {
21+
/**
22+
* A call to the `baize.asgi.FileResponse` constructor as a sink for Filesystem access.
23+
*
24+
* it is not contained to Starlette source code but it is mentioned in documents as an alternative to Starlette FileResponse
25+
*/
26+
class BaizeFileResponseCall extends FileSystemAccess::Range, API::CallNode {
27+
BaizeFileResponseCall() {
28+
this = API::moduleImport("baize").getMember("asgi").getMember("FileResponse").getACall()
29+
}
30+
31+
override DataFlow::Node getAPathArgument() {
32+
result = this.getParameter(0, "filepath").asSink()
33+
}
34+
}
35+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `cherrypy` PyPI package.
3+
*/
4+
5+
private import python
6+
private import semmle.python.dataflow.new.DataFlow
7+
private import semmle.python.dataflow.new.RemoteFlowSources
8+
private import semmle.python.dataflow.new.TaintTracking
9+
private import semmle.python.Concepts
10+
private import semmle.python.ApiGraphs
11+
12+
/**
13+
* Provides models for the `cherrypy` PyPI package.
14+
* See https://cherrypy.dev/.
15+
*/
16+
private module Cherrypy {
17+
/**
18+
* Holds for an instance of `cherrypy.lib.static`
19+
*/
20+
API::Node libStatic() {
21+
result = API::moduleImport("cherrypy").getMember("lib").getMember("static")
22+
}
23+
24+
/**
25+
* A call to the `serve_file` or `serve_download`or `staticfile` functions of `cherrypy.lib.static` as a sink for Filesystem access.
26+
*/
27+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
28+
string funcName;
29+
30+
FileResponseCall() {
31+
this = libStatic().getMember("staticfile").getACall() and
32+
funcName = "staticfile"
33+
or
34+
this = libStatic().getMember("serve_file").getACall() and
35+
funcName = "serve_file"
36+
or
37+
this = libStatic().getMember("serve_download").getACall() and
38+
funcName = "serve_download"
39+
}
40+
41+
override DataFlow::Node getAPathArgument() {
42+
result = this.getParameter(0, "path").asSink() and funcName = ["serve_download", "serve_file"]
43+
or
44+
result = this.getParameter(0, "filename").asSink() and
45+
funcName = "staticfile"
46+
}
47+
}
48+
}
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 `sanic` PyPI package.
3+
* See https://sanic.dev/.
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.dataflow.new.TaintTracking
10+
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
12+
13+
/**
14+
* Provides models for the `sanic` PyPI package.
15+
* See https://sanic.dev/.
16+
*/
17+
private module Sanic {
18+
/**
19+
* Provides models for Sanic applications (an instance of `sanic.Sanic`).
20+
*/
21+
module App {
22+
/** Gets a reference to a Sanic application (an instance of `sanic.Sanic`). */
23+
API::Node instance() { result = API::moduleImport("sanic").getMember("Sanic").getReturn() }
24+
}
25+
26+
/**
27+
* A call to the `file` or `file_stream` functions of `sanic.response` as a sink for Filesystem access.
28+
*/
29+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
30+
FileResponseCall() {
31+
this =
32+
API::moduleImport("sanic")
33+
.getMember("response")
34+
.getMember(["file", "file_stream"])
35+
.getACall()
36+
}
37+
38+
override DataFlow::Node getAPathArgument() {
39+
result = this.getParameter(0, "location").asSink()
40+
}
41+
}
42+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,16 @@ module Starlette {
163163

164164
/** DEPRECATED: Alias for Url */
165165
deprecated module URL = Url;
166+
167+
/**
168+
* A call to the `starlette.responses.FileResponse` constructor as a sink for Filesystem access.
169+
*/
170+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
171+
FileResponseCall() {
172+
this =
173+
API::moduleImport("starlette").getMember("responses").getMember("FileResponse").getACall()
174+
}
175+
176+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
177+
}
166178
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,26 @@ private module StdlibPrivate {
14791479
}
14801480
}
14811481

1482+
/**
1483+
* A call to the `io.FileIO` constructor.
1484+
* See https://docs.python.org/3/library/io.html#io.FileIO
1485+
*/
1486+
private class FileIOCall extends FileSystemAccess::Range, API::CallNode {
1487+
FileIOCall() { this = API::moduleImport("io").getMember("FileIO").getACall() }
1488+
1489+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
1490+
}
1491+
1492+
/**
1493+
* A call to the `io.open_code` function.
1494+
* See https://docs.python.org/3.11/library/io.html#io.open_code
1495+
*/
1496+
private class OpenCodeCall extends FileSystemAccess::Range, API::CallNode {
1497+
OpenCodeCall() { this = API::moduleImport("io").getMember("open_code").getACall() }
1498+
1499+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
1500+
}
1501+
14821502
/** Gets a reference to an open file. */
14831503
private DataFlow::TypeTrackingNode openFile(DataFlow::TypeTracker t, FileSystemAccess openCall) {
14841504
t.start() and
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added modeling of more `FileSystemAccess` in packages `cherrypy`, `aiofile`, `aiofiles`, `anyio`, `sanic`, `starlette`, `baize`, and `io`. This will mainly affect the _Uncontrolled data used in path expression_ (`py/path-injection`) query.

0 commit comments

Comments
 (0)