Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aaa0040

Browse files
committed
Seperate the dataflow config from the query
1 parent 9464940 commit aaa0040

4 files changed

Lines changed: 140 additions & 136 deletions

File tree

python/ql/src/experimental/Security/CWE-022bis/UnsafeUnpack.ql

Lines changed: 2 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -10,141 +10,13 @@
1010
* @security-severity 7.5
1111
* @precision high
1212
* @tags security
13+
* experimental
1314
* external/cwe/cwe-022
1415
*/
1516

1617
import python
17-
import semmle.python.Concepts
18-
import semmle.python.dataflow.new.internal.DataFlowPublic
19-
import semmle.python.ApiGraphs
18+
import UnsafeUnpackQuery
2019
import DataFlow::PathGraph
21-
import semmle.python.dataflow.new.TaintTracking
22-
import semmle.python.frameworks.Stdlib
23-
24-
class UnsafeUnpackingConfig extends TaintTracking::Configuration {
25-
UnsafeUnpackingConfig() { this = "UnsafeUnpackingConfig" }
26-
27-
override predicate isSource(DataFlow::Node source) {
28-
// A source coming from a remote location
29-
exists(Http::Client::Request request | source = request)
30-
or
31-
// A source coming from a CLI argparse module
32-
// see argparse: https://docs.python.org/3/library/argparse.html
33-
exists(MethodCallNode args |
34-
args = source.(AttrRead).getObject().getALocalSource() and
35-
args =
36-
API::moduleImport("argparse")
37-
.getMember("ArgumentParser")
38-
.getACall()
39-
.getReturn()
40-
.getMember("parse_args")
41-
.getACall()
42-
)
43-
or
44-
// A source catching an S3 filename download
45-
// see boto3: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.download_file
46-
exists(MethodCallNode mcn, Node s3, Node bc |
47-
bc = API::moduleImport("boto3").getMember("client").getACall() and
48-
bc = s3.getALocalSource() and
49-
mcn.calls(s3, "download_file") and
50-
source = mcn.getArg(2)
51-
)
52-
or
53-
// A source download a file using wget
54-
// see wget: https://pypi.org/project/wget/
55-
exists(API::CallNode mcn |
56-
mcn = API::moduleImport("wget").getMember("download").getACall() and
57-
(
58-
source = mcn.getArg(1)
59-
or
60-
source = mcn.getReturn().asSource() and not exists(Node arg | arg = mcn.getArg(1))
61-
)
62-
)
63-
or
64-
// catch the uploaded files as a source
65-
exists(Subscript s, Attribute at |
66-
at = s.getObject() and at.getAttr() = "FILES" and source.asExpr() = s
67-
)
68-
or
69-
exists(Node obj, AttrRead ar |
70-
ar.getAMethodCall("get").flowsTo(source) and
71-
ar.accesses(obj, "FILES")
72-
)
73-
or
74-
exists(Node obj, AttrRead ar |
75-
ar.getAMethodCall("getlist").flowsTo(source) and
76-
ar.accesses(obj, "FILES")
77-
)
78-
}
79-
80-
override predicate isSink(DataFlow::Node sink) {
81-
// A sink capturing method calls to `unpack_archive`.
82-
sink = API::moduleImport("shutil").getMember("unpack_archive").getACall().getArg(0)
83-
}
84-
85-
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
86-
// Writing the response data to the archive
87-
exists(Stdlib::FileLikeObject::InstanceSource is, Node f, MethodCallNode mc |
88-
is.flowsTo(f) and
89-
mc.calls(f, "write") and
90-
nodeFrom = mc.getArg(0) and
91-
nodeTo = is.(CallCfgNode).getArg(0)
92-
)
93-
or
94-
// Copying the response data to the archive
95-
exists(Stdlib::FileLikeObject::InstanceSource is, Node f, MethodCallNode mc |
96-
is.flowsTo(f) and
97-
mc = API::moduleImport("shutil").getMember("copyfileobj").getACall() and
98-
f = mc.getArg(1) and
99-
nodeFrom = mc.getArg(0) and
100-
nodeTo = is.(CallCfgNode).getArg(0)
101-
)
102-
or
103-
// Reading the response
104-
exists(MethodCallNode mc |
105-
nodeFrom = mc.getObject() and
106-
mc.getMethodName() = "read" and
107-
mc.flowsTo(nodeTo)
108-
)
109-
or
110-
// Accessing the name or raw content
111-
exists(AttrRead ar | ar.accesses(nodeFrom, ["name", "raw"]) and ar.flowsTo(nodeTo))
112-
or
113-
//Use of join of filename
114-
exists(API::CallNode mcn |
115-
mcn = API::moduleImport("os").getMember("path").getMember("join").getACall() and
116-
nodeFrom = mcn.getArg(1) and
117-
mcn.flowsTo(nodeTo)
118-
)
119-
or
120-
// Read by chunks
121-
exists(MethodCallNode mc |
122-
nodeFrom = mc.getObject() and mc.getMethodName() = "chunks" and mc.flowsTo(nodeTo)
123-
)
124-
or
125-
// Considering the use of closing()
126-
exists(API::CallNode closing |
127-
closing = API::moduleImport("contextlib").getMember("closing").getACall() and
128-
closing.flowsTo(nodeTo) and
129-
nodeFrom = closing.getArg(0)
130-
)
131-
or
132-
// Considering the use of "fs"
133-
exists(API::CallNode fs, MethodCallNode mcn |
134-
fs =
135-
API::moduleImport("django")
136-
.getMember("core")
137-
.getMember("files")
138-
.getMember("storage")
139-
.getMember("FileSystemStorage")
140-
.getACall() and
141-
fs.flowsTo(mcn.getObject()) and
142-
mcn.getMethodName() = ["save", "path"] and
143-
nodeFrom = mcn.getArg(0) and
144-
nodeTo = mcn
145-
)
146-
}
147-
}
14820

14921
from UnsafeUnpackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
15022
where config.hasFlowPath(source, sink)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
*
3+
* Provides a taint-tracking configuration for detecting "UnsafeUnpacking" vulnerabilities.
4+
*
5+
*/
6+
7+
import python
8+
import semmle.python.Concepts
9+
import semmle.python.dataflow.new.internal.DataFlowPublic
10+
import semmle.python.ApiGraphs
11+
import semmle.python.dataflow.new.TaintTracking
12+
import semmle.python.frameworks.Stdlib
13+
14+
class UnsafeUnpackingConfig extends TaintTracking::Configuration {
15+
UnsafeUnpackingConfig() { this = "UnsafeUnpackingConfig" }
16+
17+
override predicate isSource(DataFlow::Node source) {
18+
// A source coming from a remote location
19+
exists(Http::Client::Request request | source = request)
20+
or
21+
// A source coming from a CLI argparse module
22+
// see argparse: https://docs.python.org/3/library/argparse.html
23+
exists(MethodCallNode args |
24+
args = source.(AttrRead).getObject().getALocalSource() and
25+
args =
26+
API::moduleImport("argparse")
27+
.getMember("ArgumentParser")
28+
.getACall()
29+
.getReturn()
30+
.getMember("parse_args")
31+
.getACall()
32+
)
33+
or
34+
// A source catching an S3 filename download
35+
// see boto3: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.download_file
36+
exists(MethodCallNode mcn, Node s3, Node bc |
37+
bc = API::moduleImport("boto3").getMember("client").getACall() and
38+
bc = s3.getALocalSource() and
39+
mcn.calls(s3, "download_file") and
40+
source = mcn.getArg(2)
41+
)
42+
or
43+
// A source download a file using wget
44+
// see wget: https://pypi.org/project/wget/
45+
exists(API::CallNode mcn |
46+
mcn = API::moduleImport("wget").getMember("download").getACall() and
47+
(
48+
source = mcn.getArg(1)
49+
or
50+
source = mcn.getReturn().asSource() and not exists(Node arg | arg = mcn.getArg(1))
51+
)
52+
)
53+
or
54+
// catch the uploaded files as a source
55+
exists(Subscript s, Attribute at |
56+
at = s.getObject() and at.getAttr() = "FILES" and source.asExpr() = s
57+
)
58+
or
59+
exists(Node obj, AttrRead ar |
60+
ar.getAMethodCall("get").flowsTo(source) and
61+
ar.accesses(obj, "FILES")
62+
)
63+
or
64+
exists(Node obj, AttrRead ar |
65+
ar.getAMethodCall("getlist").flowsTo(source) and
66+
ar.accesses(obj, "FILES")
67+
)
68+
}
69+
70+
override predicate isSink(DataFlow::Node sink) {
71+
// A sink capturing method calls to `unpack_archive`.
72+
sink = API::moduleImport("shutil").getMember("unpack_archive").getACall().getArg(0)
73+
}
74+
75+
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
76+
// Writing the response data to the archive
77+
exists(Stdlib::FileLikeObject::InstanceSource is, Node f, MethodCallNode mc |
78+
is.flowsTo(f) and
79+
mc.calls(f, "write") and
80+
nodeFrom = mc.getArg(0) and
81+
nodeTo = is.(CallCfgNode).getArg(0)
82+
)
83+
or
84+
// Copying the response data to the archive
85+
exists(Stdlib::FileLikeObject::InstanceSource is, Node f, MethodCallNode mc |
86+
is.flowsTo(f) and
87+
mc = API::moduleImport("shutil").getMember("copyfileobj").getACall() and
88+
f = mc.getArg(1) and
89+
nodeFrom = mc.getArg(0) and
90+
nodeTo = is.(CallCfgNode).getArg(0)
91+
)
92+
or
93+
// Reading the response
94+
exists(MethodCallNode mc |
95+
nodeFrom = mc.getObject() and
96+
mc.getMethodName() = "read" and
97+
mc.flowsTo(nodeTo)
98+
)
99+
or
100+
// Accessing the name or raw content
101+
exists(AttrRead ar | ar.accesses(nodeFrom, ["name", "raw"]) and ar.flowsTo(nodeTo))
102+
or
103+
//Use of join of filename
104+
exists(API::CallNode mcn |
105+
mcn = API::moduleImport("os").getMember("path").getMember("join").getACall() and
106+
nodeFrom = mcn.getArg(1) and
107+
mcn.flowsTo(nodeTo)
108+
)
109+
or
110+
// Read by chunks
111+
exists(MethodCallNode mc |
112+
nodeFrom = mc.getObject() and mc.getMethodName() = "chunks" and mc.flowsTo(nodeTo)
113+
)
114+
or
115+
// Considering the use of closing()
116+
exists(API::CallNode closing |
117+
closing = API::moduleImport("contextlib").getMember("closing").getACall() and
118+
closing.flowsTo(nodeTo) and
119+
nodeFrom = closing.getArg(0)
120+
)
121+
or
122+
// Considering the use of "fs"
123+
exists(API::CallNode fs, MethodCallNode mcn |
124+
fs =
125+
API::moduleImport("django")
126+
.getMember("core")
127+
.getMember("files")
128+
.getMember("storage")
129+
.getMember("FileSystemStorage")
130+
.getACall() and
131+
fs.flowsTo(mcn.getObject()) and
132+
mcn.getMethodName() = ["save", "path"] and
133+
nodeFrom = mcn.getArg(0) and
134+
nodeTo = mcn
135+
)
136+
}
137+
}
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
11
missingAnnotationOnSink
22
failures
3-
| UnsafeUnpack.py:12:46:12:58 | Comment # $result=BAD | Missing result:result=BAD |
4-
| UnsafeUnpack.py:55:53:55:65 | Comment # $result=BAD | Missing result:result=BAD |
5-
| UnsafeUnpack.py:71:51:71:63 | Comment # $result=BAD | Missing result:result=BAD |
6-
| UnsafeUnpack.py:85:50:85:62 | Comment # $result=BAD | Missing result:result=BAD |
7-
| UnsafeUnpack.py:89:50:89:62 | Comment # $result=BAD | Missing result:result=BAD |
8-
| UnsafeUnpack.py:103:50:103:62 | Comment # $result=BAD | Missing result:result=BAD |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import python
22
import experimental.dataflow.TestUtil.DataflowQueryTest
3+
import UnsafeUnpackQuery

0 commit comments

Comments
 (0)