Skip to content

Commit b5aad25

Browse files
committed
Fixes ossf#519: code examples use Jekyll
Signed-off-by: emcdtho <thomas.mcdermott@ericsson.com>
1 parent 355128a commit b5aad25

File tree

6 files changed

+51
-939
lines changed

6 files changed

+51
-939
lines changed

docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-400/README.md

+4-73
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,8 @@ Tasks can be submitted to the ThreadPoolExecutor by calling `submit()`. Submitte
88

99
[*noncompliant01.py:*](noncompliant01.py)
1010

11-
```py
12-
""" Non-compliant Code Example """
13-
import time
14-
from concurrent.futures import ThreadPoolExecutor
15-
16-
17-
def take_time(x):
18-
print(f"Started Task: {x}")
19-
# Simulate work
20-
for i in range(10):
21-
time.sleep(1)
22-
print(f"Completed Task: {x}")
23-
24-
25-
def run_thread(_executor, var):
26-
future = _executor.submit(take_time, var)
27-
return future
28-
29-
30-
def interrupt(future):
31-
print(future.cancel())
32-
print(f"Interrupted: {future}")
33-
34-
35-
#####################
36-
# Exploiting above code example
37-
#####################
38-
39-
40-
with ThreadPoolExecutor() as executor:
41-
task = run_thread(executor, "A")
42-
interrupt(task)
43-
11+
```python
12+
{% include_relative noncompliant01.py %}
4413
```
4514

4615
## Compliant Solution
@@ -49,46 +18,8 @@ Tasks submitted to the ThreadPoolExecutor can be interrupted by setting a thread
4918

5019
[*compliant01.py:*](compliant01.py)
5120

52-
```py
53-
""" Compliant Code Example """
54-
import time
55-
from concurrent.futures import ThreadPoolExecutor
56-
from threading import Event
57-
58-
59-
def take_time(x, _event):
60-
print(f"Started Task: {x}")
61-
# Simulate work
62-
for _ in range(10):
63-
if _event.is_set():
64-
print(f"Interrupted Task: {x}")
65-
# Save partial results
66-
return
67-
time.sleep(1)
68-
print(f"Completed Task: {x}")
69-
70-
71-
def run_thread(_executor, var):
72-
e = Event()
73-
future = _executor.submit(take_time, var, e)
74-
return future, e
75-
76-
77-
def interrupt(future, e):
78-
"""Cancel the task, just in case it is not yet running, and set the Event flag"""
79-
future.cancel()
80-
e.set()
81-
82-
83-
#####################
84-
# Exploiting above code example
85-
#####################
86-
87-
88-
with ThreadPoolExecutor() as executor:
89-
task, event = run_thread(executor, "A")
90-
interrupt(task, event)
91-
21+
```python
22+
{% include_relative compliant01.py %}
9223
```
9324

9425
## Related Guidelines

docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-410/README.md

+8-235
Original file line numberDiff line numberDiff line change
@@ -14,55 +14,8 @@ The `noncompliant01.py` code example demonstrates the Thread-Per-Message design
1414

1515
*[noncompliant01.py](noncompliant01.py):*
1616

17-
```py
18-
""" Non-compliant Code Example """
19-
import logging
20-
import threading
21-
import time
22-
23-
logging.basicConfig(level=logging.INFO)
24-
25-
26-
def process_message(message: str, processed_messages: list):
27-
""" Method simulating mediation layer i/o heavy work"""
28-
logging.debug("process_message: started message %s working %is", message, int(message) / 10)
29-
for _ in range(int(message)):
30-
time.sleep(0.01)
31-
logging.debug("process_message: completed message %s", message)
32-
processed_messages.append(f"processed {message}")
33-
34-
35-
class MessageAPI(object):
36-
"""Class simulating the front end facing API"""
37-
38-
def add_messages(self, messages: list) -> list:
39-
""" Receives a list of messages to work on """
40-
logging.info("add_messages: got %i messages to process", len(messages))
41-
processed_messages = []
42-
threads = []
43-
for message in messages:
44-
threads.append(
45-
threading.Thread(target=process_message, args=[message, processed_messages]))
46-
threads[-1].start()
47-
logging.debug("add_messages: submitted %i messages", len(messages))
48-
for thread in threads:
49-
thread.join()
50-
logging.info("add_messages: messages_done=%i", len(processed_messages))
51-
return processed_messages
52-
53-
54-
#####################
55-
# exploiting above code example
56-
#####################
57-
mapi = MessageAPI()
58-
attacker_messages = [str(msg) for msg in range(1000)]
59-
print("ATTACKER: start sending messages")
60-
result_list = mapi.add_messages(attacker_messages)
61-
print(
62-
f"ATTACKER: done sending {len(attacker_messages)} messages, got {len(result_list)} messages "
63-
f"back")
64-
print(f"ATTACKER: result_list = {result_list}")
65-
17+
```python
18+
{% include_relative noncompliant01.py %}
6619
```
6720

6821
The `noncompliant01.py` code creates a risk of having a single component exhaust all available hardware resources even when used by a single user for a single use-case. When running the code, the Unix `top` command can show a significant increase in CPU usage (%CPU exceeds 100% because multiple cores are being used):
@@ -80,64 +33,8 @@ The attacker could still flood the server by creating multiple MessageAPI, each
8033

8134
*[compliant01.py](compliant01.py)*
8235

83-
```py
84-
""" Compliant Code Example """
85-
import logging
86-
import time
87-
from concurrent.futures import ThreadPoolExecutor
88-
from concurrent.futures import wait
89-
90-
logging.basicConfig(level=logging.INFO)
91-
92-
93-
def process_message(message: str):
94-
""" Method simulating mediation layer i/o heavy work"""
95-
logging.debug("process_message: started message %s working %is", message, int(message) / 10)
96-
for _ in range(int(message)):
97-
time.sleep(0.01)
98-
logging.debug("process_message: completed message %s", message)
99-
return f"processed {message}"
100-
101-
102-
class MessageAPI(object):
103-
"""Class simulating the front end facing API"""
104-
# TODO: Prevent the attacker from creating multiple MessageAPI objects
105-
106-
def __init__(self):
107-
# TODO: set or handle timeout as it is provided by the mediation layer
108-
self.timeout = 1
109-
self.executor = ThreadPoolExecutor()
110-
111-
def add_messages(self, messages: list) -> list:
112-
""" Receives a list of messages to work on """
113-
# TODO: limit on max messages from the mediation layer.
114-
# TODO: input sanitation.
115-
futures = []
116-
# with self.executor:
117-
for message in messages:
118-
futures.append(self.executor.submit(process_message, message))
119-
logging.debug("add_messages: submitted %i messages, waiting for %is to complete.", len(messages), self.timeout)
120-
messages_done, messages_not_done = wait(futures, timeout=self.timeout)
121-
for future in messages_not_done:
122-
future.cancel()
123-
124-
logging.info("add_messages: messages_done=%i messages_not_done=%i", len(messages_done), len(messages_not_done))
125-
process_messages = []
126-
for future in messages_done:
127-
process_messages.append(future.result())
128-
return process_messages
129-
130-
131-
#####################
132-
# exploiting above code example
133-
#####################
134-
mapi = MessageAPI()
135-
result_list = []
136-
attacker_messages = [str(msg) for msg in range(100)]
137-
print("ATTACKER: start sending messages")
138-
result_list = mapi.add_messages(attacker_messages)
139-
print(f"ATTACKER: done sending {len(attacker_messages)} messages, got {len(result_list)} messages back")
140-
print(f"ATTACKER: result_list = {result_list}")
36+
```python
37+
{% include_relative compliant01.py %}
14138
```
14239

14340
Now, after the timeout is reached, `MessageAPI` drops unprocessed messages and returns partial results:
@@ -155,63 +52,8 @@ The `executor.shutdown()` method has a `cancel_futures` parameter, which by def
15552

15653
*[noncompliant02.py](noncompliant02.py):*
15754

158-
```py
159-
""" Non-compliant Code Example """
160-
import logging
161-
import time
162-
from concurrent.futures import ThreadPoolExecutor
163-
from concurrent.futures import wait
164-
165-
logging.basicConfig(level=logging.INFO)
166-
167-
168-
def process_message(message: str):
169-
""" Method simulating mediation layer i/o heavy work"""
170-
logging.debug("process_message: started message %s working %is", message, int(message) / 10)
171-
for _ in range(int(message)):
172-
time.sleep(0.01)
173-
logging.debug("process_message: completed message %s", message)
174-
return f"processed {message}"
175-
176-
177-
class MessageAPI(object):
178-
"""Class simulating the front end facing API"""
179-
180-
def __init__(self):
181-
self.executor = ThreadPoolExecutor()
182-
self.timeout = 1
183-
184-
def add_messages(self, messages: list) -> list:
185-
""" Receives a list of messages to work on """
186-
futures = []
187-
for message in messages:
188-
futures.append(self.executor.submit(process_message, message))
189-
190-
logging.debug("add_messages: submitted %i messages, waiting for %is to complete.",
191-
len(messages), self.timeout)
192-
messages_done, messages_not_done = wait(futures, timeout=self.timeout)
193-
194-
logging.info("add_messages: messages_done=%i messages_not_done=%i", len(messages_done),
195-
len(messages_not_done))
196-
197-
process_messages = []
198-
for future in messages_done:
199-
process_messages.append(future.result())
200-
return process_messages
201-
202-
203-
#####################
204-
# exploiting above code example
205-
#####################
206-
mapi = MessageAPI()
207-
result_list = []
208-
attacker_messages = [str(msg) for msg in range(1000)]
209-
print("ATTACKER: start sending messages")
210-
result_list = mapi.add_messages(attacker_messages)
211-
print(
212-
f"ATTACKER: done sending {len(attacker_messages)} messages, got {len(result_list)} messages "
213-
f"back")
214-
print(f"ATTACKER: result_list = {result_list}")
55+
```python
56+
{% include_relative noncompliant02.py %}
21557
```
21658

21759
## Compliant Solution (Thread Pool with grace period)
@@ -220,77 +62,8 @@ The `compliant01.py` can be expanded by adding a grace period. Before dropping t
22062

22163
*[compliant02.py](compliant02.py):*
22264

223-
```py
224-
""" Compliant Code Example """
225-
import logging
226-
import time
227-
from concurrent.futures import ThreadPoolExecutor
228-
from concurrent.futures import wait
229-
230-
logging.basicConfig(level=logging.INFO)
231-
232-
233-
def process_message(message: str):
234-
""" Method simulating mediation layer i/o heavy work"""
235-
logging.debug("process_message: started message %s working %is", message, int(message) / 10)
236-
for _ in range(int(message)):
237-
time.sleep(0.01)
238-
logging.debug("process_message: completed message %s", message)
239-
return f"processed {message}"
240-
241-
242-
class MessageAPI(object):
243-
"""Class simulating the front end facing API"""
244-
# TODO: Prevent the attacker from creating multiple MessageAPI objects
245-
246-
def __init__(self):
247-
# TODO: set or handle timeout as it is provided by the mediation layer
248-
self.timeout = 1
249-
self.executor = ThreadPoolExecutor()
250-
251-
def add_messages(self, messages: list) -> list:
252-
""" Receives a list of messages to work on """
253-
# TODO: limit on max messages from the mediation layer.
254-
# TODO: input sanitation.
255-
futures = []
256-
# with self.executor:
257-
for message in messages:
258-
futures.append(self.executor.submit(process_message, message))
259-
logging.debug("add_messages: submitted %i messages, waiting for %is to complete.",
260-
len(messages), self.timeout)
261-
messages_done, messages_not_done = wait(futures, timeout=self.timeout)
262-
logging.info("add_messages: messages_done=%i messages_not_done=%i", len(messages_done),
263-
len(messages_not_done))
264-
if len(messages_not_done) > 0:
265-
# TODO: be graceful, warn a trusted client
266-
logging.warning("add_messages: %i messages taking longer than %is, %i more to process",
267-
len(messages), self.timeout, len(messages_not_done))
268-
messages_done, messages_not_done = wait(futures, timeout=self.timeout)
269-
logging.info("add_messages: messages_done=%i messages_not_done=%i", len(messages_done),
270-
len(messages_not_done))
271-
for future in messages_not_done:
272-
future.cancel()
273-
274-
logging.info("add_messages: messages_done=%i messages_not_done=%i", len(messages_done),
275-
len(messages_not_done))
276-
process_messages = []
277-
for future in messages_done:
278-
process_messages.append(future.result())
279-
return process_messages
280-
281-
282-
#####################
283-
# exploiting above code example
284-
#####################
285-
mapi = MessageAPI()
286-
result_list = []
287-
attacker_messages = [str(msg) for msg in range(100)]
288-
print("ATTACKER: start sending messages")
289-
result_list = mapi.add_messages(attacker_messages)
290-
print(
291-
f"ATTACKER: done sending {len(attacker_messages)} messages, got {len(result_list)} messages "
292-
f"back")
293-
print(f"ATTACKER: result_list = {result_list}")
65+
```python
66+
{% include_relative compliant02.py %}
29467
```
29568

29669
## Automated Detection

0 commit comments

Comments
 (0)