Skip to content
Open
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
Expand Up @@ -22,3 +22,7 @@
**Vulnerability:** Server-Side Request Forgery (SSRF) / Local File Inclusion
**Learning:** Functions that fetch URLs provided via user inputs (e.g., `wait_for_url` fetching `--backend-ready-url` in CI scripts) can inadvertently read local files if they do not validate the scheme. Python's `urllib.request.urlopen` supports `file://` schemes, allowing attackers to access arbitrary file contents from the host machine or sandbox if they can control the URL parameter.
**Prevention:** Always validate URL inputs to restrict allowed schemes. Check that URLs explicitly start with `http://` or `https://` before fetching them with standard libraries like `urllib`.
## 2026-07-04 - Fix unvalidated URL scheme in NOEMA_LLM_API_URL
**Vulnerability:** urllib.request.urlopen in scripts/ci/noema_review_gate.py was calling the URL provided by NOEMA_LLM_API_URL environment variable without validating that it uses http:// or https:// scheme, potentially allowing file:// or other unexpected schemes to be used.
**Learning:** In Python, urllib.request.urlopen will happily read local files if provided with a file:// scheme. This could lead to a Server-Side Request Forgery (SSRF) or local file read vulnerability if the environment variable is attacker-controlled or misconfigured. This expands our previous understanding of URL scheme validation to environment variables.
**Prevention:** Always validate that URLs to be requested by urllib or other HTTP clients strictly start with http:// or https://, especially when the URL originates from environment variables or external configuration.
4 changes: 3 additions & 1 deletion scripts/ci/noema_review_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ def call_llm(repo: str, number: int, pr: dict[str, Any], diff: str, truncated: b
if not api_url or not api_key:
print("Noema LLM review unavailable: NOEMA_LLM_API_URL or NOEMA_LLM_API_KEY is not configured.")
return None
if not (api_url.startswith("http://") or api_url.startswith("https://")):
raise ValueError(f"Invalid NOEMA_LLM_API_URL scheme: {api_url}")

prompt = {
"role": "user",
Expand Down Expand Up @@ -304,7 +306,7 @@ def call_llm(repo: str, number: int, pr: dict[str, Any], diff: str, truncated: b
},
method="POST",
)
with urllib.request.urlopen(request, timeout=120) as response:
with urllib.request.urlopen(request, timeout=120) as response: # nosec B310 - scheme validated above
raw = response.read().decode("utf-8")
data = json.loads(raw)
content = (((data.get("choices") or [{}])[0].get("message") or {}).get("content") or "").strip()
Expand Down
5 changes: 5 additions & 0 deletions tests/test_noema_review_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ def fake_urlopen(request, timeout):
assert seen["url"] == "https://llm.example.test/chat"
assert seen["body"]["model"] == "review-model"

monkeypatch.setenv("NOEMA_LLM_API_URL", "file:///etc/passwd")
with pytest.raises(ValueError, match="Invalid NOEMA_LLM_API_URL scheme: file:///etc/passwd"):
noema.call_llm("owner/repo", 1, pr, "diff", True)

monkeypatch.setenv("NOEMA_LLM_API_URL", "https://llm.example.test/chat")
monkeypatch.setattr(
noema.urllib.request,
"urlopen",
Expand Down
Loading