Skip to content

Commit b2fa4dc

Browse files
Kamesh Akellacursoragent
authored andcommitted
fix(langgraph): resolve summarizer PR workflow and CodeQL issues
Fix the ci_failure_summarizer template issues called out on PR #272 by making the local CLI example type-checkable, guarding nullable summary inserts, hardening image path serving, and cleaning the remaining template lint violations. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f2ba377 commit b2fa4dc

4 files changed

Lines changed: 32 additions & 5 deletions

File tree

agents/langgraph/templates/ci_failure_summarizer/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from langgraph.checkpoint.postgres import PostgresSaver
3636
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
3737
from pydantic import BaseModel, Field
38+
from werkzeug.utils import safe_join
3839

3940
logger = logging.getLogger(__name__)
4041

@@ -639,11 +640,10 @@ async def playground():
639640
async def serve_image(filename: str):
640641
"""Serve images from the project-level images directory."""
641642
base = _IMAGES_DIR.resolve()
642-
file_path = (base / filename).resolve()
643-
try:
644-
file_path.relative_to(base)
645-
except ValueError:
643+
safe_path = safe_join(str(base), filename)
644+
if safe_path is None:
646645
raise HTTPException(status_code=404, detail="Image not found")
646+
file_path = Path(safe_path).resolve()
647647
if not file_path.is_file():
648648
raise HTTPException(status_code=404, detail="Image not found")
649649
return FileResponse(file_path)

agents/langgraph/templates/ci_failure_summarizer/src/ci_failure_summarizer/failure_evidence.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
r"pytest\.fail\(|ERROR tests/|Process completed with exit code|During handling of the above exception",
2121
re.IGNORECASE,
2222
)
23+
_ROUTE_SERVER_NOT_FOUND_RE = re.compile(
24+
r'routes\.route\.openshift\.io\s+"[^"]+"\s+not found',
25+
re.IGNORECASE,
26+
)
2327

2428

2529
def _truncate_excerpt(text: str) -> str | None:
@@ -87,7 +91,7 @@ def _score_error_marker(marker: str) -> tuple[int, int]:
8791
score -= 100
8892
if "container_image" in lower:
8993
score += 120
90-
if "routenotfounderror" in lower or "routes.route.openshift.io" in lower:
94+
if "routenotfounderror" in lower or _ROUTE_SERVER_NOT_FOUND_RE.search(marker):
9195
score += 120
9296
if "error from server" in lower:
9397
score += 30

agents/langgraph/templates/ci_failure_summarizer/tests/test_github_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from unittest.mock import MagicMock
88

99
import requests
10+
from ci_failure_summarizer.failure_evidence import select_best_error_marker
1011
from ci_failure_summarizer.github_client import (
1112
GitHubActionsClient,
1213
canonical_workflow_file,
@@ -228,6 +229,16 @@ def test_fetch_job_logs_prefers_failed_step_section_over_cleanup_tail():
228229
assert "Removing includeIf entries" not in (result.excerpt or "")
229230

230231

232+
def test_select_best_error_marker_prefers_specific_route_not_found_marker():
233+
wrapper = "E Failed: Pre-deployed agent route not found"
234+
route_marker = (
235+
'E oc stderr: Error from server (NotFound): routes.route.openshift.io '
236+
'"langflow-simple-tool-calling-agent" not found'
237+
)
238+
239+
assert select_best_error_marker([wrapper, route_marker]) == route_marker
240+
241+
231242
def test_list_jobs_parses_fixture_payload():
232243
payload = json.loads((FIXTURES / "github_failure_run.json").read_text())
233244
client = GitHubActionsClient("red-hat-data-services/agentic-starter-kits")

agents/langgraph/templates/ci_failure_summarizer/tests/test_summarize_api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,15 @@ def test_summarize_maps_run_mismatch_to_400(self, summarize_client):
181181

182182
assert response.status_code == 400
183183
assert "Run 999 is not from workflow" in response.json()["detail"]
184+
185+
def test_images_route_rejects_path_traversal(self, summarize_client, tmp_path):
186+
import main
187+
188+
images_dir = tmp_path / "images"
189+
images_dir.mkdir()
190+
(tmp_path / "secret.txt").write_text("secret")
191+
192+
with patch.object(main, "_IMAGES_DIR", images_dir):
193+
response = summarize_client.get("/images/../secret.txt")
194+
195+
assert response.status_code == 404

0 commit comments

Comments
 (0)