-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New rule to check nntplib for use without a timeout
PY042 Using nntplib classes NNTP and NNTP_SSL without setting a timeout value will result in using the global default with defaults to None (block forever). Therefore its important to pass a timeout value with initializing the class. Signed-off-by: Eric Brown <[email protected]>
- Loading branch information
Showing
19 changed files
with
264 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
id: PY042 | ||
title: nntplib — no timeout | ||
hide_title: true | ||
pagination_prev: null | ||
pagination_next: null | ||
slug: /rules/PY042 | ||
--- | ||
|
||
::: precli.rules.python.stdlib.nntplib_no_timeout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Copyright 2024 Secure Sauce LLC | ||
r""" | ||
# Synchronous Access of `NNTP` without Timeout | ||
The `nntplib.NNTP` and `nntplib.NNTP_SSL` classes are used to connect to | ||
Network News Transfer Protocol (NNTP) servers for accessing Usenet articles. | ||
These classes establish network connections with NNTP servers, and by | ||
default, they do not enforce a timeout on these connections. Without a | ||
timeout, the application may block indefinitely if the NNTP server is slow | ||
or unresponsive, leading to resource exhaustion, Denial of Service (DoS), or | ||
reduced application responsiveness. | ||
This rule ensures that a timeout parameter is provided when creating | ||
instances of `nntplib.NNTP` or `nntplib.NNTP_SSL` to prevent the risk of | ||
indefinite blocking. | ||
Failing to specify a timeout in these classes may cause the application to | ||
block indefinitely while waiting for a response from the mail server. This can | ||
lead to Denial of Service (DoS) vulnerabilities or cause the application to | ||
become unresponsive. | ||
# Example | ||
```python linenums="1" hl_lines="5" title="nntplib_nntp_no_timeout.py" | ||
import nntplib | ||
import ssl | ||
nntp = nntplib.NNTP("nntp.example.com") | ||
nntp.starttls(ssl.create_default_context()) | ||
``` | ||
??? example "Example Output" | ||
``` | ||
> precli tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_no_timeout.py | ||
⚠️ Warning on line 10 in tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_no_timeout.py | ||
PY041: Synchronous Access of Remote Resource without Timeout | ||
The class 'nntplib.NNTP' is used without a timeout, which may cause the application to block indefinitely if the remote server does not respond. | ||
``` | ||
# Remediation | ||
Always provide a timeout parameter when using `nntplib.NNTP` or | ||
`nntplib.NNTP_SSL`. This ensures that if the mail server is unreachable or | ||
unresponsive, the connection attempt will fail after a set period, preventing | ||
indefinite blocking and resource exhaustion. | ||
```python linenums="1" hl_lines="5" title="nntplib_nntp_no_timeout.py" | ||
import nntplib | ||
import ssl | ||
nntp = nntplib.NNTP("nntp.example.com", timeout=5) | ||
nntp.starttls(ssl.create_default_context()) | ||
``` | ||
# See also | ||
!!! info | ||
- [nntplib.NNTP — nntplib — IMAP4 protocol client](https://docs.python.org/3/library/nntplib.html#nntplib.NNTP) | ||
- [nntplib.NNTP_SSL — nntplib — IMAP4 protocol client](https://docs.python.org/3/library/nntplib.html#nntplib.NNTP_SSL) | ||
- [CWE-1088: Synchronous Access of Remote Resource without Timeout](https://cwe.mitre.org/data/definitions/1088.html) | ||
_New in version 0.6.7_ | ||
""" # noqa: E501 | ||
from precli.core.call import Call | ||
from precli.core.location import Location | ||
from precli.core.result import Result | ||
from precli.rules import Rule | ||
|
||
|
||
class NntplibNoTimeout(Rule): | ||
def __init__(self, id: str): | ||
super().__init__( | ||
id=id, | ||
name="no_timeout", | ||
description=__doc__, | ||
cwe_id=1088, | ||
message="The class '{0}' is used without a timeout, which may " | ||
"cause the application to block indefinitely if the remote server " | ||
"does not respond.", | ||
) | ||
|
||
def analyze_call(self, context: dict, call: Call) -> Result | None: | ||
if call.name_qualified not in ( | ||
"nntplib.NNTP", | ||
"nntplib.NNTP_SSL", | ||
): | ||
return | ||
|
||
if call.name_qualified == "nntplib.NNTP": | ||
# NNTP( | ||
# host, | ||
# port=119, | ||
# user=None, | ||
# password=None, | ||
# readermode=None, | ||
# usenetrc=False, | ||
# timeout=GLOBAL_TIMEOUT | ||
# ) | ||
argument = call.get_argument(position=6, name="timeout") | ||
elif call.name_qualified == "nntplib.NNTP_SSL": | ||
# NNTP_SSL( | ||
# host, | ||
# port=563, | ||
# user=None, | ||
# password=None, | ||
# ssl_context=None, | ||
# readermode=None, | ||
# usenetrc=False, | ||
# timeout=GLOBAL_TIMEOUT | ||
# ) | ||
argument = call.get_argument(position=7, name="timeout") | ||
|
||
timeout = argument.value | ||
|
||
if argument.node is None: | ||
arg_list_node = call.arg_list_node | ||
fix_node = arg_list_node | ||
args = [child.string for child in arg_list_node.named_children] | ||
args.append("timeout=5") | ||
content = f"({', '.join(args)})" | ||
result_node = call.arg_list_node | ||
elif timeout is None: | ||
fix_node = argument.node | ||
result_node = argument.node | ||
content = "5" | ||
else: | ||
# If the timeout parameter is set to be zero, the class will raise | ||
# a ValueError to prevent the creation of a non-blocking socket. A | ||
# negative value also raises ValueError. So there is no need to | ||
# check for these values. | ||
return | ||
|
||
fixes = Rule.get_fixes( | ||
context=context, | ||
deleted_location=Location(fix_node), | ||
description="Set timeout parameter to a small number of seconds.", | ||
inserted_content=content, | ||
) | ||
return Result( | ||
rule_id=self.id, | ||
location=Location(node=result_node), | ||
message=self.message.format(call.name_qualified), | ||
fixes=fixes, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_ssl_context_as_var.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_ssl_context_none.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_ssl_no_timeout.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# level: WARNING | ||
# start_line: 10 | ||
# end_line: 10 | ||
# start_column: 20 | ||
# end_column: 79 | ||
import nntplib | ||
import ssl | ||
|
||
|
||
s = nntplib.NNTP_SSL("news.gmane.io", ssl_context=ssl.create_default_context()) | ||
s.login("user", "password") | ||
f = open("article.txt", "rb") | ||
s.post(f) | ||
s.quit() |
10 changes: 10 additions & 0 deletions
10
tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_ssl_timeout_5.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# level: NONE | ||
import nntplib | ||
import ssl | ||
|
||
|
||
s = nntplib.NNTP_SSL("news.gmane.io", ssl_context=ssl.create_default_context(), timeout=5) | ||
s.login("user", "password") | ||
f = open("article.txt", "rb") | ||
s.post(f) | ||
s.quit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
tests/unit/rules/python/stdlib/nntplib/examples/nntplib_nntp_timeout_none.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# level: WARNING | ||
# start_line: 10 | ||
# end_line: 10 | ||
# start_column: 42 | ||
# end_column: 46 | ||
import nntplib | ||
import ssl | ||
|
||
|
||
s = nntplib.NNTP("news.gmane.io", timeout=None) | ||
s.starttls(context=ssl.create_default_context()) | ||
s.login("user", "password") | ||
f = open("article.txt", "rb") | ||
s.post(f) | ||
s.quit() |
49 changes: 49 additions & 0 deletions
49
tests/unit/rules/python/stdlib/nntplib/test_nntplib_no_timeout.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Copyright 2024 Secure Sauce LLC | ||
import os | ||
|
||
import pytest | ||
|
||
from precli.core.level import Level | ||
from precli.parsers import python | ||
from precli.rules import Rule | ||
from tests.unit.rules import test_case | ||
|
||
|
||
class TestNntplibNoTimeout(test_case.TestCase): | ||
@classmethod | ||
def setup_class(cls): | ||
cls.rule_id = "PY042" | ||
cls.parser = python.Python() | ||
cls.base_path = os.path.join( | ||
"tests", | ||
"unit", | ||
"rules", | ||
"python", | ||
"stdlib", | ||
"nntplib", | ||
"examples", | ||
) | ||
|
||
def test_rule_meta(self): | ||
rule = Rule.get_by_id(self.rule_id) | ||
assert rule.id == self.rule_id | ||
assert rule.name == "no_timeout" | ||
assert ( | ||
rule.help_url | ||
== f"https://docs.securesauce.dev/rules/{self.rule_id}" | ||
) | ||
assert rule.default_config.enabled is True | ||
assert rule.default_config.level == Level.WARNING | ||
assert rule.default_config.rank == -1.0 | ||
assert rule.cwe.id == 1088 | ||
|
||
@pytest.mark.parametrize( | ||
"filename", | ||
[ | ||
"nntplib_nntp_ssl_no_timeout.py", | ||
"nntplib_nntp_ssl_timeout_5.py", | ||
"nntplib_nntp_timeout_none.py", | ||
], | ||
) | ||
def test(self, filename): | ||
self.check(filename) |