Skip to content

Commit 7cafd08

Browse files
committed
adding 789 as part of #531
Signed-off-by: Helge Wehder <[email protected]>
1 parent a868b9d commit 7cafd08

File tree

6 files changed

+230
-0
lines changed

6 files changed

+230
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# CWE-783: Operator Precedence Logic Error
2+
3+
Failing to understand the order of precedence in expressions that read and write to the same object can lead to unintended side effects.
4+
5+
Python has distinct different concepts for:
6+
7+
<table>
8+
<tr>
9+
<th>
10+
type
11+
</th>
12+
<th>examples</th>
13+
<th>Typical direction</th>
14+
<tr>
15+
<td>Assignments</td><td>to store a value such as x = 1</td><td>right-to-left.</td>
16+
</tr>
17+
<tr>
18+
<td>Expressions</td><td>3+4 or x*2</td><td>left-to-right</td>
19+
</tr>
20+
<tr>
21+
<td>Augmented assignments</td><td>a += 1</td><td>left-to-right <a href="https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statements">[Python docs 2025 - simple statements]</a></td>
22+
</tr>
23+
</table>
24+
25+
Expressions such as `2 ** 3`, or two to the power of three, are evaluated from right to left [[python power 2025](https://docs.python.org/3/reference/expressions.html#index-59)] as demonstrated in `example01.py`
26+
27+
_[example01.py:](example01.py)_
28+
29+
```py
30+
"""Code Example"""
31+
32+
print(2**3**2) # prints 512
33+
print((2**3) ** 2) # prints 64
34+
print(2**9)
35+
36+
```
37+
38+
The first expression would print `64` if Python would resolve from left-to-right but prints `512` as it calculates `3**2` before using its result with `2**9`.
39+
The `example02.py` behaves 'normal' for a programmer but makes no sense as a mathematical formular.
40+
41+
_[example02.py:](example02.py)_
42+
43+
```py
44+
z = 2
45+
z *= 2 + 1
46+
print(f"z *= 2 + 1 ={z}")
47+
```
48+
49+
If a method changes an object’s state (has side effects) and is called multiple times within one expression, the result can be surprising and incorrect. For further info on python's order of precedence refer to The Python Language Specification , §6.16, "Evaluation Order" [[PLR 2022](https://docs.python.org/3/reference/expressions.html#evaluation-order)].
50+
51+
## Non-Compliant Code Example
52+
53+
This `noncompliant01.py` code is expected to provide labels for numbers obfuscates the evaluation and logic.
54+
55+
_[noncompliant01.py](noncompliant01.py):_
56+
57+
```python
58+
"""Non-Compliant Code Example"""
59+
60+
61+
def label(number: int) -> str:
62+
a = int(number < 0) # negative flag
63+
b = (number & 1) ^ 1 # even flag (1 for even, 0 for odd)
64+
c = int(number < 5) # small flag
65+
66+
key = (a << 2) | (b << 1) | c # pack flags into a single key
67+
68+
parts = ("big", "small", "even", "even", "neg", "neg", "neg", "neg")
69+
70+
permuted = tuple(parts[(i * 5) & 7] for i in range(8))
71+
72+
idx = (key * 5) & 7
73+
return permuted[idx]
74+
75+
76+
for number in range(-3, 3):
77+
print(f"{number} = {label(number)}")
78+
79+
```
80+
81+
_Example output of `noncompliant01.py`:_
82+
83+
```bash
84+
-3 = neg
85+
-2 = neg
86+
-1 = neg
87+
0 = even
88+
1 = small
89+
2 = even
90+
```
91+
92+
Attempting to add a label for `zero` will be challenging.
93+
94+
## Compliant Solution
95+
96+
This compliant solution, uses equivalent logic and performs at most one write operation per expression, which makes the code easier to understand and maintain.
97+
98+
_[compliant01.py](compliant01.py):_
99+
100+
```python
101+
"""Compliant Code Example"""
102+
103+
104+
def label(number: int):
105+
if number < 0:
106+
return "neg"
107+
if number % 2 == 0:
108+
return "even"
109+
if number < 5:
110+
return "small"
111+
return "big"
112+
113+
114+
for number in range(-3, 3):
115+
print(f"{number} = {label(number)}")
116+
117+
```
118+
119+
## Automated Detection
120+
121+
<table>
122+
<hr>
123+
<td>Tool</td>
124+
<td>Version</td>
125+
<td>Checker</td>
126+
<td>Description</td>
127+
</hr>
128+
<tr>
129+
<td>Bandit</td>
130+
<td>1.7.4 on Python 3.10.4</td>
131+
<td>Not Available</td>
132+
<td></td>
133+
</tr>
134+
</table>
135+
136+
## Related Guidelines
137+
138+
<table>
139+
<tr>
140+
<td><a href="http://cwe.mitre.org/">MITRE CWE</a></td>
141+
<td>Pillar: <a href="https://cwe.mitre.org/data/definitions/691.html"> [CWE-691: Insufficient Control Flow Management]</a></td>
142+
</tr>
143+
<tr>
144+
<td><a href="http://cwe.mitre.org/">MITRE CWE</a></td>
145+
<td>Base <a href="https://cwe.mitre.org/data/definitions/783.html">CWE-783: Operator Precedence Logic Error</a></td>
146+
</tr>
147+
<tr>
148+
<td><a href="https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java">SEI CERT Oracle Coding Standard for Java</a></td>
149+
<td><a href="https://wiki.sei.cmu.edu/confluence/display/java/EXP05-J.+Do+not+follow+a+write+by+a+subsequent+write+or+read+of+the+same+object+within+an+expression"></a>EXP05-J. Do not follow a write by a subsequent write or read of the same object within an expression</td>
150+
</tr>
151+
<tr>
152+
<td><a href="https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard">CERT C Coding Standard</a></td>
153+
<td><a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP30-C.+Do+not+depend+on+the+order+of+evaluation+for+side+effects">EXP30-C. Do not depend on the order of evaluation for side effects</a></td>
154+
</tr>
155+
<tr>
156+
<td><a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046682">SEI CERT C++ Coding Standard</a></td>
157+
<td><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP50-CPP.+Do+not+depend+on+the+order+of+evaluation+for+side+effects">EXP50-CPP. Do not depend on the order of evaluation for side effects</a></td>
158+
</tr>
159+
</table>
160+
161+
## Bibliography
162+
163+
<table>
164+
<tr>
165+
<td>[Python docs 2025 - simple statements]</td>
166+
<td>7.2.1. Augmented assignment statements [online]. Available from: <a href="https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statements">https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statement</a>, [Accessed 19 September 2025]</td>
167+
</tr>
168+
<tr>
169+
<td>[python power 2025]</td>
170+
<td>7.2.1. Augmented assignment statements [online]. Available from: <a href="https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statements">https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statement</a>, [Accessed 19 September 2025]</td>
171+
</tr>
172+
<tr>
173+
<td>[PLR 2022]</td>
174+
<td>6.16. Evaluation order [online]. Available from: <a href="https://docs.python.org/3/reference/expressions.html#evaluation-orde">https://docs.python.org/3/reference/expressions.html#evaluation-orde</a>, [Accessed 19 September 2025]</td>
175+
</tr>
176+
</table>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
"""Compliant Code Example"""
4+
5+
6+
def label(number: int):
7+
if number < 0:
8+
return "neg"
9+
if number % 2 == 0:
10+
return "even"
11+
if number < 5:
12+
return "small"
13+
return "big"
14+
15+
16+
for number in range(-3, 3):
17+
print(f"{number} = {label(number)}")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
"""Code Example"""
4+
5+
print(2**3**2) # prints 512
6+
print((2**3) ** 2) # prints 64
7+
print(2**9)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
"""Code Example"""
4+
5+
z = 2
6+
z *= 2 + 1
7+
print(f"z *= 2 + 1 ={z}")
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
"""Non-Compliant Code Example"""
4+
5+
6+
def label(number: int) -> str:
7+
a = int(number < 0) # negative flag
8+
b = (number & 1) ^ 1 # even flag (1 for even, 0 for odd)
9+
c = int(number < 5) # small flag
10+
11+
key = (a << 2) | (b << 1) | c # pack flags into a single key
12+
13+
parts = ("big", "small", "even", "even", "neg", "neg", "neg", "neg")
14+
15+
permuted = tuple(parts[(i * 5) & 7] for i in range(8))
16+
17+
idx = (key * 5) & 7
18+
return permuted[idx]
19+
20+
21+
for number in range(-3, 3):
22+
print(f"{number} = {label(number)}")

docs/Secure-Coding-Guide-for-Python/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ It is __not production code__ and requires code-style or python best practices t
8181
|[CWE-366: Race Condition within a Thread](CWE-691/CWE-366/README.md)||
8282
|[CWE-362: Concurrent Execution Using Shared Resource with Improper Synchronization ("Race Condition")](CWE-691/CWE-362/README.md)||
8383
|[CWE-617: Reachable Assertion](CWE-691/CWE-617/README.md)||
84+
|[CWE-783: Operator Precedence Logic Error](CWE-691/CWE-783/README.md)||
8485

8586
|[CWE-693: Protection Mechanism Failure](https://cwe.mitre.org/data/definitions/693.html)|Prominent CVE|
8687
|:---------------------------------------------------------------------------------------------------------------|:----|

0 commit comments

Comments
 (0)