Skip to content

Commit fc50de6

Browse files
committed
new branch to fix issues when rebaseing
Signed-off-by: ewlxdnx <[email protected]>
1 parent 05cf658 commit fc50de6

File tree

3 files changed

+172
-1
lines changed

3 files changed

+172
-1
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# CWE-230: Improper Handling of Missing Values
2+
3+
The `NaN` value should be stripped before as they can cause surprising or undefined behaviours in the statistics functions that sort or count occurrences [[2024 doc.python.org]](https://docs.python.org/3/library/statistics.html).
4+
In python, some datasets use `NaN` (not-a-number) to represent the missing data. This can be problematic as the `NaN` values are unordered. Any ordered comparison of a number to a not-a-number value are `False`. A counter-intuitive implication is that `not-a-number` values are not equal to themselves.
5+
6+
This behavior is compliant with IEEE 754[[2024 Wikipedia]](https://en.wikipedia.org/wiki/IEEE_754) a hardware induced compromise.
7+
The [example01.py](example01.py) code demonstrates various comparisons of `float('NaN')` all resulting in `False`.
8+
9+
```python
10+
# SPDX-FileCopyrightText: OpenSSF project contributors
11+
# SPDX-License-Identifier: MIT
12+
""" Code Example """
13+
14+
foo = float('NaN')
15+
print(f"foo={foo} type = {type(foo)}")
16+
17+
18+
print(foo == float("NaN") or
19+
foo is float("NaN") or
20+
foo < 3 or
21+
foo == foo or
22+
foo is None
23+
)
24+
25+
```
26+
27+
## Non-Compliant Code Example
28+
29+
This noncompliant code example [[2024 docs.python.org]](https://docs.python.org/3/reference/expressions.html#value-comparisons) attempts a direct comparison with `NaN` in `_value == float("NaN")`.
30+
*[noncompliant01.py](noncompliant01.py):*
31+
32+
```python
33+
# SPDX-FileCopyrightText: OpenSSF project contributors
34+
# SPDX-License-Identifier: MIT
35+
""" Non-compliant Code Example """
36+
# SPDX-FileCopyrightText: OpenSSF project contributors
37+
# SPDX-License-Identifier: MIT
38+
""" Code Example """
39+
40+
foo = float('NaN')
41+
print(f"foo={foo} type = {type(foo)}")
42+
43+
44+
print(foo == float("NaN") or
45+
foo is float("NaN") or
46+
foo < 3 or
47+
foo == foo or
48+
foo is None
49+
)
50+
51+
```
52+
53+
## Compliant Solution
54+
55+
In the `compliant01.py` code example, the method `Decimal.quantize` is used to gain control over known rounding errors in floating point values.
56+
57+
The decision by the `balance_is_positive` method is to `ROUND_DOWN` instead of the default `ROUND_HALF_EVEN`.
58+
59+
60+
*[compliant01.py](compliant01.py):*
61+
62+
```python
63+
# SPDX-FileCopyrightText: OpenSSF project contributors
64+
# SPDX-License-Identifier: MIT
65+
""" Compliant Code Example """
66+
67+
from decimal import ROUND_DOWN, Decimal
68+
69+
70+
def balance_is_positive(value: str) -> bool:
71+
"""Returns True if there is still enough value for a transaction"""
72+
# TODO: additional input sanitation for expected type
73+
_value = Decimal(value)
74+
# TODO: exception handling
75+
return _value.quantize(Decimal(".01"), rounding=ROUND_DOWN) > Decimal("0.00")
76+
77+
78+
#####################
79+
# attempting to exploit above code example
80+
#####################
81+
print(balance_is_positive("0.01"))
82+
print(balance_is_positive("0.001"))
83+
print(balance_is_positive("NaN"))
84+
85+
```
86+
87+
`Decimal` throws a `decimal.InvalidOperation` for `NaN` values, the controlled rounding causes only `"0.01"` to return `True`.
88+
89+
In `compliant02.py` we use the `math.isnan` to verify if the value passed is a valid `float` value.
90+
91+
92+
*[compliant02.py](compliant02.py):*
93+
94+
```python
95+
# SPDX-FileCopyrightText: OpenSSF project contributors
96+
# SPDX-License-Identifier: MIT
97+
""" Compliant Code Example """
98+
99+
import math
100+
101+
102+
def balance_is_positive(value: str) -> bool:
103+
"""Returns True if there is still enough value for a transaction"""
104+
_value = float(value)
105+
if math.isnan(_value) or _value is None:
106+
raise ValueError("Expected a float")
107+
if _value < 0.01:
108+
return False
109+
else:
110+
return True
111+
112+
113+
#####################
114+
# attempting to exploit above code example
115+
#####################
116+
print(balance_is_positive("0.01"))
117+
print(balance_is_positive("0.001"))
118+
print(balance_is_positive("NaN"))
119+
120+
```
121+
122+
The `balance_is_poitive` method will raise an `ValueError` for `NaN` values.
123+
124+
## Automated Detection
125+
126+
|Tool|Version|Checker|Description|
127+
|:----|:----|:----|:----|
128+
|Bandit|1.7.4 on Python 3.10.4|Not Available||
129+
|flake8|flake8-4.0.1 on python 3.10.4||Not Available|
130+
131+
## Related Guidelines
132+
133+
|||
134+
|:---|:---|
135+
|[SEI CERT Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java)|[NUM07-J. Do not attempt comparisons with NaN](https://wiki.sei.cmu.edu/confluence/display/java/NUM07-J.+Do+not+attempt+comparisons+with+NaN)|
136+
|[ISO/IEC TR 24772:2013](https://wiki.sei.cmu.edu/confluence/display/java/Rule+AA.+References#RuleAA.References-ISO/IECTR24772-2013)|Injection RST|
137+
|[MITRE CWE Pillar](http://cwe.mitre.org/)|[CWE-703: Improper Check or Handling of Exceptional Conditions (mitre.org)](https://cwe.mitre.org/data/definitions/703.html)|
138+
|[MITRE CWE Pillar](http://cwe.mitre.org/)|[CWE-230: Improper Handling of Missing Values](https://cwe.mitre.org/data/definitions/230.html)|
139+
140+
## Bibliography
141+
142+
|||
143+
|:---|:---|
144+
|[[Python 3.10.4 docs]](https://docs.python.org/3/library/string.html#formatstrings)|Format String Syntax. Available from: <https://docs.python.org/3/library/string.html#formatstrings> \[Accessed 22 July 2025]|
145+
|[Python docs](https://docs.python.org/3/)|<https://docs.python.org/3/library/math.html#math.nan> \[Accessed 22 July 2025]|
146+
|[Python docs](https://docs.python.org/3/)|Python Value comparisons<https://docs.python.org/3/reference/expressions.html#value-comparisons> \[Accessed 22 July 2025]|
147+
|[[Wikipedia 2024]](https://realpython.com/python-string-formatting/)|IEEE 754: <https://en.wikipedia.org/wiki/IEEE_754> \[Accessed 22 July 2025]|

docs/Secure-Coding-Guide-for-Python/CWE-703/CWE-230/compliant01.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-FileCopyrightText: OpenSSF project contributors
22
# SPDX-License-Identifier: MIT
3-
""" Non-compliant Code Example """
3+
""" Compliant Code Example """
44

55
from decimal import ROUND_DOWN, Decimal
66

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Compliant Code Example """
4+
5+
import math
6+
7+
8+
def balance_is_positive(value: str) -> bool:
9+
"""Returns True if there is still enough value for a transaction"""
10+
_value = float(value)
11+
if math.isnan(_value) or _value is None:
12+
raise ValueError("Expected a float")
13+
if _value < 0.01:
14+
return False
15+
else:
16+
return True
17+
18+
19+
#####################
20+
# attempting to exploit above code example
21+
#####################
22+
print(balance_is_positive("0.01"))
23+
print(balance_is_positive("0.001"))
24+
print(balance_is_positive("NaN"))

0 commit comments

Comments
 (0)