Skip to content

Commit e247db1

Browse files
authored
pyDoc2GitHub Adding documentation for CWE-197 01 as part of #531 to GitHub (#649)
* Adding documentation for CWE-197 01 as part of #531 to GitHub Signed-off-by: Helge Wehder <[email protected]> * Fixed formatting according to comments Signed-off-by: myteron <[email protected]> * Update README.md Adding link/reference to 8 rounding modes Signed-off-by: myteron <[email protected]> --------- Signed-off-by: myteron <[email protected]>
1 parent 6fc707f commit e247db1

File tree

6 files changed

+142
-7
lines changed

6 files changed

+142
-7
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# CWE-197: Control rounding when converting to less precise numbers
2+
3+
While defensive coding requires enforcing types, it is important to make conscious design decisions on how conversions are rounded.
4+
5+
The `example01.py` code demonstrates how `int()` behaves differently to `round()`.
6+
7+
[*example01.py:*](example01.py)
8+
9+
```py
10+
""" Code Example """
11+
12+
print(int(0.5)) # prints 0
13+
print(int(1.5)) # prints 1
14+
print(int(1.45)) # prints 1
15+
print(int(1.51)) # prints 1
16+
print(int(-1.5)) # prints -1
17+
18+
print(round(0.5)) # prints 0
19+
print(round(1.5)) # prints 2
20+
print(round(1.45)) # prints 1
21+
print(round(1.51)) # prints 2
22+
print(round(-1.5)) # prints -2
23+
24+
print(type(round(0.5))) # prints <class 'int'>
25+
26+
```
27+
28+
The build in `round()` does not allow to specify the type of rounding in use [[python round( ) 2024]](https://docs.python.org/3/library/functions.html#round). In Python 3 the `round()` function uses "bankers' rounding" (rounds to the nearest even number in case of ties). This is different to Python 2 which always rounds away from zero. Rounding provided by the `decimal` module allows a choice between 8 rounding modes [[python decimal 2024]](https://docs.python.org/3/library/decimal.html#rounding-modes). Rounding in mathematics and science is not discussed here as it requires a deeper knowledge of computer floating-point arithmetic's.
29+
30+
## Non-Compliant Code Example (float to int)
31+
32+
In `noncompliant01.py` there is no conscious choice of rounding mode.
33+
34+
[*noncompliant01.py:*](noncompliant01.py)
35+
36+
```py
37+
""" Non-compliant Code Example """
38+
39+
print(int(0.5)) # prints 0
40+
print(int(1.5)) # prints 1
41+
print(round(0.5)) # prints 0
42+
print(round(1.5)) # prints 2
43+
```
44+
45+
## Compliant Solution (float to int)
46+
47+
Using the `Decimal` class from the `decimal` module allows more control over rounding by choosing one of the `8` rounding modes [[python decimal 2024]](https://docs.python.org/3/library/decimal.html#rounding-modes).
48+
49+
[*compliant01.py:*](compliant01.py)
50+
51+
```py
52+
""" Compliant Code Example """
53+
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
54+
55+
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 1
56+
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 2
57+
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 0
58+
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 1
59+
```
60+
61+
The `.quantize(Decimal("1")`, determines the precision to be `integer` and `rounding=ROUND_HALF_UP` determines the type of rounding applied. Specifying numbers as strings avoids issues such as floating-point representations in binary.
62+
63+
That `Decimal` can have unexpected results when operated without `Decimal.quantize()` on floating point numbers is demonstrated in `example02.py`.
64+
65+
[*example02.py:*](example02.py)
66+
67+
```py
68+
# SPDX-FileCopyrightText: OpenSSF project contributors
69+
# SPDX-License-Identifier: MIT
70+
""" Code Example """
71+
from decimal import ROUND_HALF_UP, Decimal
72+
73+
print(Decimal("0.10")) # prints 0.10
74+
print(Decimal(0.10)) # prints 0.1000000000000000055511151231257827021181583404541015625
75+
print(Decimal("0.10").quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10
76+
print(Decimal(0.10).quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10
77+
```
78+
79+
Initializing `Decimal` with an actual `float`, such as `0.10`, and without rounding creates an unprecise number `0.1000000000000000055511151231257827021181583404541015625` in `Python 3.9.2`.
80+
81+
## Automated Detection
82+
83+
|Tool|Version|Checker|Description|
84+
|:---|:---|:---|:---|
85+
|Bandit|1.7.4 on Python 3.10.4|Not Available||
86+
|Flake8|8-4.0.1 on Python 3.10.4|Not Available||
87+
88+
## Related Guidelines
89+
90+
|||
91+
|:---|:---|
92+
|[MITRE CWE](http://cwe.mitre.org/)|Pillar [CWE-682, Incorrect Conversion between Numeric Types (mitre.org)](http://cwe.mitre.org/data/definitions/682.html)|
93+
|[MITRE CWE](http://cwe.mitre.org/)|Class [CWE-197, Numeric Truncation Error](https://cwe.mitre.org/data/definitions/197.html)|
94+
|[SEI CERT C Coding Standard](https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard)|[INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data](https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data)|
95+
|[SEI CERT C Coding Standard](https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard)|[FLP34-C. Ensure that floating-point conversions are within range of the new type](https://wiki.sei.cmu.edu/confluence/display/c/FLP34-C.+Ensure+that+floating-point+conversions+are+within+range+of+the+new+type)|
96+
|[ISO/IEC TR 24772:2019](https://www.iso.org/standard/71091.html)|Programming languages — Guidance to avoiding vulnerabilities in programming languages, available from [https://www.iso.org/standard/71091.html](https://www.iso.org/standard/71091.html)|
97+
|[SEI CERT Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java)|[NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data](https://wiki.sei.cmu.edu/confluence/display/java/NUM12-J.+Ensure+conversions+of+numeric+types+to+narrower+types+do+not+result+in+lost+or+misinterpreted+data)|
98+
99+
## Biblography
100+
101+
|||
102+
|:---|:---|
103+
|[python round( ) 2024](https://docs.python.org/3/library/functions.html#round)|python round( ), available from: [https://docs.python.org/3/library/functions.html#round](https://docs.python.org/3/library/functions.html#round), [Last accessed June 2024] |
104+
|[python decimal 2024](https://docs.python.org/3/library/decimal.html#rounding-modes)|Python decimal module, available from: [https://docs.python.org/3/library/decimal.html#rounding-modes](https://docs.python.org/3/library/decimal.html#rounding-modes)|
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# SPDX-FileCopyrightText: OpenSSF project contributors
22
# SPDX-License-Identifier: MIT
33
""" Compliant Code Example """
4-
foo = int(round(0.9))
5-
type(foo) # class int
6-
print(foo) # prints 1
4+
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
5+
6+
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 1
7+
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 2
8+
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 0
9+
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 1
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+
""" Code Example """
4+
5+
print(int(0.5)) # prints 0
6+
print(int(1.5)) # prints 1
7+
print(int(1.45)) # prints 1
8+
print(int(1.51)) # prints 1
9+
print(int(-1.5)) # prints -1
10+
11+
print(round(0.5)) # prints 0
12+
print(round(1.5)) # prints 2
13+
print(round(1.45)) # prints 1
14+
print(round(1.51)) # prints 2
15+
print(round(-1.5)) # prints -2
16+
17+
print(type(round(0.5))) # prints <class 'int'>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Code Example """
4+
from decimal import ROUND_HALF_UP, Decimal
5+
6+
print(Decimal("0.10")) # prints 0.10
7+
print(Decimal(0.10)) # prints 0.1000000000000000055511151231257827021181583404541015625
8+
print(Decimal("0.10").quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10
9+
print(Decimal(0.10).quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# SPDX-FileCopyrightText: OpenSSF project contributors
22
# SPDX-License-Identifier: MIT
33
""" Non-compliant Code Example """
4-
foo = int(0.9)
5-
type(foo) # class int
6-
print(foo) # prints 0
4+
5+
print(int(0.5)) # prints 0
6+
print(int(1.5)) # prints 1
7+
print(round(0.5)) # prints 0
8+
print(round(1.5)) # prints 2

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ It is **not production code** and requires code-style or python best practices t
4242
|:-----------------------------------------------------------------------------------------------------------------------------------------------|:----|
4343
|[CWE-134: Use of Externally-Controlled Format String](CWE-664/CWE-134/README.md)|[CVE-2022-27177](https://www.cvedetails.com/cve/CVE-2022-27177/),<br/>CVSSv3.1: **9.8**,<br/>EPSS: **00.37** (01.12.2023)|
4444
|[CWE-197: Numeric Truncation Error](CWE-664/CWE-197/README.md)||
45-
|[CWE-197: Control rounding when converting to less precise numbers](CWE-664/CWE-197/01/.)||
45+
|[CWE-197: Control rounding when converting to less precise numbers](CWE-664/CWE-197/01/README.md)||
4646
|[CWE-400: Uncontrolled Resource Consumption](CWE-664/CWE-400/README.md)||
4747
|[CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)](CWE-664/CWE-409/.)||
4848
|[CWE-410: Insufficient Resource Pool](CWE-664/CWE-410/README.md)||

0 commit comments

Comments
 (0)