Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2023-10-24 - [CRITICAL] Prevent SSRF in urllib.request.urlopen
**Vulnerability:** The functions `_fetch_live_manifest` and `_fetch_live_runtime_proof` in `examples/generate_public_proof_bundle.py` use `urllib.request.urlopen` to fetch data from URLs provided via arguments. There was no validation to ensure the URL scheme was `http` or `https`. This could allow an attacker to pass a `file://` or `ftp://` scheme, potentially reading arbitrary local files or making unexpected requests.
**Learning:** Python's `urllib.request.urlopen` handles various protocols like `file://` by default. When using URLs from unverified sources, we must explicitly whitelist acceptable schemes.
**Prevention:** Always validate that URLs start with `http://` or `https://` (case-insensitive) before passing them to `urllib.request.urlopen` or similar functions.
22 changes: 22 additions & 0 deletions examples/generate_public_proof_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ def _public_manifest_receipt(payload):


def _fetch_live_manifest(url):
if not url or not (url.lower().startswith('http://') or url.lower().startswith('https://')):
return {
'included': False,
'attempted': True,
'route': url,
'http_status': 0,
'fetched_at': datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
'body_sha256': '',
'error': f"Invalid URL scheme in {url!r}",
'manifest': {},
}
try:
with urllib.request.urlopen(url, timeout=10) as response:
raw = response.read()
Expand Down Expand Up @@ -124,6 +135,17 @@ def _derive_sibling_url(url, old_suffix, new_suffix):


def _fetch_live_runtime_proof(url):
if not url or not (url.lower().startswith('http://') or url.lower().startswith('https://')):
return {
'included': False,
'attempted': True,
'route': url,
'http_status': 0,
'fetched_at': datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
'body_sha256': '',
'error': f"Invalid URL scheme in {url!r}",
'receipt': {},
}
try:
with urllib.request.urlopen(url, timeout=15) as response:
raw = response.read()
Expand Down