-
Notifications
You must be signed in to change notification settings - Fork 184
pyDoc2GitHub Adding documentation for CWE-197 01 as part of #531 to GitHub #649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# CWE-197: Control rounding when converting to less precise numbers | ||
|
||
While defensive coding requires enforcing types, it is important to make conscious design decisions on how conversions are rounded. | ||
|
||
The `example01.py` code demonstrates how `int()` behaves differently to `round()`. | ||
|
||
[*example01.py:*](example01.py) | ||
|
||
```py | ||
""" Code Example """ | ||
|
||
print(int(0.5)) # prints 0 | ||
print(int(1.5)) # prints 1 | ||
print(int(1.45)) # prints 1 | ||
print(int(1.51)) # prints 1 | ||
print(int(-1.5)) # prints -1 | ||
|
||
print(round(0.5)) # prints 0 | ||
print(round(1.5)) # prints 2 | ||
print(round(1.45)) # prints 1 | ||
print(round(1.51)) # prints 2 | ||
print(round(-1.5)) # prints -2 | ||
|
||
print(type(round(0.5))) # prints <class 'int'> | ||
|
||
``` | ||
|
||
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. | ||
|
||
## Non-Compliant Code Example (float to int) | ||
|
||
In `noncompliant01.py` there is no conscious choice of rounding mode. | ||
|
||
[*noncompliant01.py:*](noncompliant01.py) | ||
|
||
```py | ||
""" Non-compliant Code Example """ | ||
|
||
print(int(0.5)) # prints 0 | ||
print(int(1.5)) # prints 1 | ||
print(round(0.5)) # prints 0 | ||
print(round(1.5)) # prints 2 | ||
``` | ||
|
||
## Compliant Solution (float to int) | ||
|
||
Using the `Decimal` module allows more control over rounding by choosing one of the 8 rounding modes in the decimal module. | ||
|
||
|
||
[*compliant01.py:*](compliant01.py) | ||
|
||
```py | ||
""" Compliant Code Example """ | ||
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN | ||
|
||
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 1 | ||
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 2 | ||
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 0 | ||
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 1 | ||
``` | ||
|
||
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. | ||
|
||
|
||
That `Decimal` can have unexpected results when operated without `Decimal.quantize()` on floating point numbers is demonstrated in `example02.py`. | ||
|
||
[*example02.py:*](example02.py) | ||
|
||
```py | ||
""" Code Example """ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cosmetics: license comments in the Markdown code |
||
""" Code Example """ | ||
from decimal import ROUND_HALF_UP, Decimal | ||
|
||
print(Decimal("0.10")) # prints 0.10 | ||
print(Decimal(0.10)) # prints 0.1000000000000000055511151231257827021181583404541015625 | ||
print(Decimal("0.10").quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10 | ||
print(Decimal(0.10).quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10 | ||
``` | ||
|
||
Initializing `Decimal` with an actual `float`, such as `0.10`, and without rounding creates an unprecise number `0.1000000000000000055511151231257827021181583404541015625` in `Python 3.9.2`. | ||
|
||
## Automated Detection | ||
|
||
|Tool|Version|Checker|Description| | ||
|:---|:---|:---|:---| | ||
|Bandit|1.7.4 on Python 3.10.4|Not Available|| | ||
|Flake8|8-4.0.1 on Python 3.10.4|Not Available|| | ||
|
||
## Related Guidelines | ||
|
||
||| | ||
|:---|:---| | ||
|[MITRE CWE](http://cwe.mitre.org/)|Pillar [CWE-682, Incorrect Conversion between Numeric Types (mitre.org)](http://cwe.mitre.org/data/definitions/682.html)| | ||
|[MITRE CWE](http://cwe.mitre.org/)|Class [CWE-197, Numeric Truncation Error](https://cwe.mitre.org/data/definitions/197.html)| | ||
|[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)| | ||
|[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)| | ||
|[ISO/IEC TR 24772:2019]|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)| | ||
|
||
|[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)| | ||
|
||
## Biblography | ||
|
||
||| | ||
|:---|:---| | ||
|[python round( ) 2024]|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] | | ||
|[python decimal 2024]|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)| | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Compliant Code Example """ | ||
foo = int(round(0.9)) | ||
type(foo) # class int | ||
print(foo) # prints 1 | ||
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN | ||
|
||
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 1 | ||
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP)) # prints 2 | ||
print(Decimal("0.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 0 | ||
print(Decimal("1.5").quantize(Decimal("1"), rounding=ROUND_HALF_DOWN)) # prints 1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Code Example """ | ||
|
||
print(int(0.5)) # prints 0 | ||
print(int(1.5)) # prints 1 | ||
print(int(1.45)) # prints 1 | ||
print(int(1.51)) # prints 1 | ||
print(int(-1.5)) # prints -1 | ||
|
||
print(round(0.5)) # prints 0 | ||
print(round(1.5)) # prints 2 | ||
print(round(1.45)) # prints 1 | ||
print(round(1.51)) # prints 2 | ||
print(round(-1.5)) # prints -2 | ||
|
||
print(type(round(0.5))) # prints <class 'int'> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Code Example """ | ||
from decimal import ROUND_HALF_UP, Decimal | ||
|
||
print(Decimal("0.10")) # prints 0.10 | ||
print(Decimal(0.10)) # prints 0.1000000000000000055511151231257827021181583404541015625 | ||
print(Decimal("0.10").quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10 | ||
print(Decimal(0.10).quantize(Decimal("0.10"), rounding=ROUND_HALF_UP)) # prints 0.10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Non-compliant Code Example """ | ||
foo = int(0.9) | ||
type(foo) # class int | ||
print(foo) # prints 0 | ||
|
||
print(int(0.5)) # prints 0 | ||
print(int(1.5)) # prints 1 | ||
print(round(0.5)) # prints 0 | ||
print(round(1.5)) # prints 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cosmetics:
decimal
module-- [python round( ) 2024]
-- [python decimal 2024]