feat: enhance HTTP handling and configuration
All checks were successful
CI / lint-and-test (push) Successful in 24s
All checks were successful
CI / lint-and-test (push) Successful in 24s
- Introduced a new utility function `safe_urlopen` to ensure only allowed URL schemes (http, https) are opened, enhancing security against path traversal vulnerabilities. - Updated the `run.py` and `calendar_ics.py` files to utilize `safe_urlopen` for HTTP requests, improving error handling and security. - Added `HTTP_HOST` configuration to the settings, allowing dynamic binding of the HTTP server host. - Revised the `.env.example` file to include the new `HTTP_HOST` variable with a description. - Enhanced tests for `safe_urlopen` to validate behavior with disallowed URL schemes and ensure proper integration in existing functionality.
This commit is contained in:
36
duty_teller/utils/http_client.py
Normal file
36
duty_teller/utils/http_client.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Safe HTTP client: open URLs only for allowed schemes (e.g. https, http) to avoid path traversal (CWE-22)."""
|
||||
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def safe_urlopen(
|
||||
request: Request,
|
||||
timeout: float = 10,
|
||||
allowed_schemes: tuple[str, ...] = ("https", "http"),
|
||||
) -> "urlopen":
|
||||
"""Open URL only if its scheme is in allowed_schemes; otherwise raise ValueError.
|
||||
|
||||
Use this instead of raw urlopen() to satisfy Bandit B310 and prevent opening
|
||||
file:// or other non-HTTP schemes.
|
||||
|
||||
Args:
|
||||
request: urllib.request.Request (has .full_url or .get_full_url()).
|
||||
timeout: Timeout in seconds for the request.
|
||||
allowed_schemes: Tuple of lowercase scheme names, e.g. ("https", "http").
|
||||
|
||||
Returns:
|
||||
Context manager from urlopen (use with 'with').
|
||||
|
||||
Raises:
|
||||
ValueError: If the request URL scheme is not in allowed_schemes.
|
||||
"""
|
||||
url = request.get_full_url()
|
||||
parsed = urlparse(url)
|
||||
scheme = (parsed.scheme or "").lower()
|
||||
if scheme not in allowed_schemes:
|
||||
raise ValueError(
|
||||
f"URL scheme not allowed: {scheme!r} (allowed: {allowed_schemes})"
|
||||
)
|
||||
# Scheme validated above; only https/http reach here (B310).
|
||||
return urlopen(request, timeout=timeout) # nosec B310
|
||||
Reference in New Issue
Block a user