Skip to content

Commit d4a0728

Browse files
Add more tests
1 parent eeef062 commit d4a0728

File tree

4 files changed

+135
-1
lines changed

4 files changed

+135
-1
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ module Werkzeug {
201201
DataFlow::MethodCallNode
202202
{
203203
HeaderWriteCall() {
204-
this.calls(instance(), ["add", "add_header", "set", "set_default", "__setitem__"])
204+
this.calls(instance(), ["add", "add_header", "set", "setdefault", "__setitem__"])
205205
}
206206

207207
override DataFlow::Node getNameArg() { result = this.getArg(0) }

python/ql/test/query-tests/Security/CWE-113-HeaderInjection/HeaderInjection.expected

+80
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ edges
1010
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:54:41:54:47 | ControlFlowNode for request | provenance | |
1111
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:59:18:59:24 | ControlFlowNode for request | provenance | |
1212
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:60:36:60:42 | ControlFlowNode for request | provenance | |
13+
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:71:18:71:24 | ControlFlowNode for request | provenance | |
1314
| flask_tests.py:9:5:9:14 | ControlFlowNode for rfs_header | flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | provenance | |
1415
| flask_tests.py:9:18:9:24 | ControlFlowNode for request | flask_tests.py:9:5:9:14 | ControlFlowNode for rfs_header | provenance | |
1516
| flask_tests.py:20:5:20:14 | ControlFlowNode for rfs_header | flask_tests.py:23:22:23:31 | ControlFlowNode for rfs_header | provenance | |
@@ -24,6 +25,33 @@ edges
2425
| flask_tests.py:54:41:54:47 | ControlFlowNode for request | flask_tests.py:54:41:54:66 | ControlFlowNode for Subscript | provenance | |
2526
| flask_tests.py:59:18:59:24 | ControlFlowNode for request | flask_tests.py:60:36:60:61 | ControlFlowNode for Subscript | provenance | |
2627
| flask_tests.py:60:36:60:42 | ControlFlowNode for request | flask_tests.py:60:36:60:61 | ControlFlowNode for Subscript | provenance | |
28+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:74:17:74:26 | ControlFlowNode for rfs_header | provenance | |
29+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:75:24:75:33 | ControlFlowNode for rfs_header | provenance | |
30+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:76:17:76:26 | ControlFlowNode for rfs_header | provenance | |
31+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:77:24:77:33 | ControlFlowNode for rfs_header | provenance | |
32+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:78:25:78:34 | ControlFlowNode for rfs_header | provenance | |
33+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:79:13:79:22 | ControlFlowNode for rfs_header | provenance | |
34+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:80:11:80:20 | ControlFlowNode for rfs_header | provenance | |
35+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:82:12:82:21 | ControlFlowNode for rfs_header | provenance | |
36+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:85:11:85:20 | ControlFlowNode for rfs_header | provenance | |
37+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | flask_tests.py:86:12:86:21 | ControlFlowNode for rfs_header | provenance | |
38+
| flask_tests.py:71:18:71:24 | ControlFlowNode for request | flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | provenance | |
39+
| wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | wsgiref_tests.py:6:5:6:10 | ControlFlowNode for h_name | provenance | |
40+
| wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | wsgiref_tests.py:7:5:7:9 | ControlFlowNode for h_val | provenance | |
41+
| wsgiref_tests.py:6:5:6:10 | ControlFlowNode for h_name | wsgiref_tests.py:8:17:8:22 | ControlFlowNode for h_name | provenance | |
42+
| wsgiref_tests.py:7:5:7:9 | ControlFlowNode for h_val | wsgiref_tests.py:8:42:8:46 | ControlFlowNode for h_val | provenance | |
43+
| wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | provenance | |
44+
| wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | provenance | |
45+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | wsgiref_tests.py:16:25:16:30 | ControlFlowNode for h_name | provenance | |
46+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | wsgiref_tests.py:17:24:17:29 | ControlFlowNode for h_name | provenance | |
47+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | wsgiref_tests.py:18:24:18:29 | ControlFlowNode for h_name | provenance | |
48+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | wsgiref_tests.py:19:25:19:30 | ControlFlowNode for h_name | provenance | |
49+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | wsgiref_tests.py:20:13:20:18 | ControlFlowNode for h_name | provenance | |
50+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | wsgiref_tests.py:16:50:16:54 | ControlFlowNode for h_val | provenance | |
51+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | wsgiref_tests.py:17:32:17:36 | ControlFlowNode for h_val | provenance | |
52+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | wsgiref_tests.py:18:32:18:36 | ControlFlowNode for h_val | provenance | |
53+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | wsgiref_tests.py:19:33:19:37 | ControlFlowNode for h_val | provenance | |
54+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | wsgiref_tests.py:20:23:20:27 | ControlFlowNode for h_val | provenance | |
2755
nodes
2856
| flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
2957
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
@@ -48,6 +76,36 @@ nodes
4876
| flask_tests.py:59:18:59:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
4977
| flask_tests.py:60:36:60:42 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
5078
| flask_tests.py:60:36:60:61 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
79+
| flask_tests.py:71:5:71:14 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
80+
| flask_tests.py:71:18:71:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
81+
| flask_tests.py:74:17:74:26 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
82+
| flask_tests.py:75:24:75:33 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
83+
| flask_tests.py:76:17:76:26 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
84+
| flask_tests.py:77:24:77:33 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
85+
| flask_tests.py:78:25:78:34 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
86+
| flask_tests.py:79:13:79:22 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
87+
| flask_tests.py:80:11:80:20 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
88+
| flask_tests.py:82:12:82:21 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
89+
| flask_tests.py:85:11:85:20 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
90+
| flask_tests.py:86:12:86:21 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
91+
| wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | semmle.label | ControlFlowNode for environ |
92+
| wsgiref_tests.py:6:5:6:10 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
93+
| wsgiref_tests.py:7:5:7:9 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
94+
| wsgiref_tests.py:8:17:8:22 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
95+
| wsgiref_tests.py:8:42:8:46 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
96+
| wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | semmle.label | ControlFlowNode for environ |
97+
| wsgiref_tests.py:14:5:14:10 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
98+
| wsgiref_tests.py:15:5:15:9 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
99+
| wsgiref_tests.py:16:25:16:30 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
100+
| wsgiref_tests.py:16:50:16:54 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
101+
| wsgiref_tests.py:17:24:17:29 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
102+
| wsgiref_tests.py:17:32:17:36 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
103+
| wsgiref_tests.py:18:24:18:29 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
104+
| wsgiref_tests.py:18:32:18:36 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
105+
| wsgiref_tests.py:19:25:19:30 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
106+
| wsgiref_tests.py:19:33:19:37 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
107+
| wsgiref_tests.py:20:13:20:18 | ControlFlowNode for h_name | semmle.label | ControlFlowNode for h_name |
108+
| wsgiref_tests.py:20:23:20:27 | ControlFlowNode for h_val | semmle.label | ControlFlowNode for h_val |
51109
subpaths
52110
#select
53111
| flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
@@ -57,3 +115,25 @@ subpaths
57115
| flask_tests.py:49:72:49:97 | ControlFlowNode for Subscript | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:49:72:49:97 | ControlFlowNode for Subscript | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
58116
| flask_tests.py:54:41:54:66 | ControlFlowNode for Subscript | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:54:41:54:66 | ControlFlowNode for Subscript | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
59117
| flask_tests.py:60:36:60:61 | ControlFlowNode for Subscript | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:60:36:60:61 | ControlFlowNode for Subscript | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
118+
| flask_tests.py:74:17:74:26 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:74:17:74:26 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
119+
| flask_tests.py:75:24:75:33 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:75:24:75:33 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
120+
| flask_tests.py:76:17:76:26 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:76:17:76:26 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
121+
| flask_tests.py:77:24:77:33 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:77:24:77:33 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
122+
| flask_tests.py:78:25:78:34 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:78:25:78:34 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
123+
| flask_tests.py:79:13:79:22 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:79:13:79:22 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
124+
| flask_tests.py:80:11:80:20 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:80:11:80:20 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
125+
| flask_tests.py:82:12:82:21 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:82:12:82:21 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
126+
| flask_tests.py:85:11:85:20 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:85:11:85:20 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
127+
| flask_tests.py:86:12:86:21 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:86:12:86:21 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
128+
| wsgiref_tests.py:8:17:8:22 | ControlFlowNode for h_name | wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | wsgiref_tests.py:8:17:8:22 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | user-provided value |
129+
| wsgiref_tests.py:8:42:8:46 | ControlFlowNode for h_val | wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | wsgiref_tests.py:8:42:8:46 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:4:14:4:20 | ControlFlowNode for environ | user-provided value |
130+
| wsgiref_tests.py:16:25:16:30 | ControlFlowNode for h_name | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:16:25:16:30 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
131+
| wsgiref_tests.py:16:50:16:54 | ControlFlowNode for h_val | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:16:50:16:54 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
132+
| wsgiref_tests.py:17:24:17:29 | ControlFlowNode for h_name | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:17:24:17:29 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
133+
| wsgiref_tests.py:17:32:17:36 | ControlFlowNode for h_val | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:17:32:17:36 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
134+
| wsgiref_tests.py:18:24:18:29 | ControlFlowNode for h_name | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:18:24:18:29 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
135+
| wsgiref_tests.py:18:32:18:36 | ControlFlowNode for h_val | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:18:32:18:36 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
136+
| wsgiref_tests.py:19:25:19:30 | ControlFlowNode for h_name | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:19:25:19:30 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
137+
| wsgiref_tests.py:19:33:19:37 | ControlFlowNode for h_val | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:19:33:19:37 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
138+
| wsgiref_tests.py:20:13:20:18 | ControlFlowNode for h_name | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:20:13:20:18 | ControlFlowNode for h_name | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |
139+
| wsgiref_tests.py:20:23:20:27 | ControlFlowNode for h_val | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | wsgiref_tests.py:20:23:20:27 | ControlFlowNode for h_val | This HTTP header is constructed from a $@. | wsgiref_tests.py:12:15:12:21 | ControlFlowNode for environ | user-provided value |

python/ql/test/query-tests/Security/CWE-113-HeaderInjection/flask_tests.py

+22
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,27 @@ def flask_escaped():
6666
resp = make_response("hello", {rfs_header.replace("\n", ""): "HeaderValue"}) # GOOD - Newlines are removed from the input.
6767
return resp
6868

69+
@app.route("/werkzeug_methods")
70+
def werkzeug_methods():
71+
rfs_header = request.args["rfs_header"]
72+
response = Response()
73+
headers = Headers()
74+
headers.add(rfs_header, "HeaderValue") # BAD
75+
headers.add_header(rfs_header, "HeaderValue") # BAD
76+
headers.set(rfs_header, "HeaderValue") # BAD
77+
headers.setdefault(rfs_header, "HeaderValue") # BAD
78+
headers.__setitem__(rfs_header, "HeaderValue") # BAD
79+
headers[rfs_header] = "HeaderValue" # BAD
80+
h1 = {rfs_header: "HeaderValue"}
81+
headers.extend(h1) # BAD
82+
h2 = [(rfs_header, "HeaderValue")]
83+
headers.extend(h2) # BAD
84+
response.headers = headers
85+
h3 = {rfs_header: "HeaderValue"}
86+
h4 = [(rfs_header, "HeaderValue")]
87+
resp2 = make_response("hi", h3) # BAD
88+
resp3 = make_response("hi", h4) # BAD
89+
return response
90+
6991
# if __name__ == "__main__":
7092
# app.run(debug=True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from wsgiref.simple_server import make_server
2+
from wsgiref.headers import Headers
3+
4+
def test_app(environ, start_response):
5+
status = "200 OK"
6+
h_name = environ["source_n"]
7+
h_val = environ["source_v"]
8+
headers = [(h_name, "val"), ("name", h_val)]
9+
start_response(status, headers) # BAD
10+
return [b"Hello"]
11+
12+
def test_app2(environ, start_response):
13+
status = "200 OK"
14+
h_name = environ["source_n"]
15+
h_val = environ["source_v"]
16+
headers = Headers([(h_name, "val"), ("name", h_val)]) # BAD
17+
headers.add_header(h_name, h_val) # BAD
18+
headers.setdefault(h_name, h_val) # BAD
19+
headers.__setitem__(h_name, h_val) # BAD
20+
headers[h_name] = h_val # BAD
21+
start_response(status, headers)
22+
return [b"Hello"]
23+
24+
def main1():
25+
with make_server('', 8000, test_app) as httpd:
26+
print("Serving on port 8000...")
27+
httpd.serve_forever()
28+
29+
def main2():
30+
with make_server('', 8000, test_app2) as httpd:
31+
print("Serving on port 8000...")
32+
httpd.serve_forever()

0 commit comments

Comments
 (0)