Repository navigation
Conversation
… gateway identity Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
|
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
|
| connection: Final = LensConnection.from_env(environ) | ||
| except ValueError: | ||
| return None |
There was a problem hiding this comment.
Advertised setup leaves evals disabled
Setting only LITELLM_LENS_URL and LITELLM_LENS_GATEWAY_SECRET still returns 501. LensConnection.from_env also requires LITELLM_LENS_SERVICE_TOKEN, but the setup instructions and error message omit it. Validate eval configuration separately, or document and report the third requirement
| @router.get("/lens/datasets/resolve", include_in_schema=False) | ||
| async def resolve_dataset(request: Request, auth: Auth, upstream: Upstream) -> Response: | ||
| return await forward(request, "/lens/datasets/resolve", auth, upstream) |
There was a problem hiding this comment.
Ordinary virtual keys are rejected before /lens/datasets/resolve, contract case reads, and deeper eval paths such as /lens/evals/runs/run-1/finish reach Lens. Auth runs the existing route checks, which treat these unmatched paths as admin-only. Register the new paths for the intended callers without widening access to existing admin routes
Knowledge Base Used:
| app.dependency_overrides[user_api_key_auth] = lambda: auth | ||
| app.dependency_overrides[eval_upstream] = lambda: upstream | ||
| app.dependency_overrides[dataset_store_factory] = lambda: lambda: store |
There was a problem hiding this comment.
These tests replace authentication and configuration, and use MockTransport for Lens. Repository rules require live end-to-end proof through the real proxy before merging. Add that proof for eval writes, dataset reads, failures, and unchanged admin reads
Rule Used: Flag if the PR has no proof of e2e testing (if e2e testing is applicable here. If it's like an AGENTS.md change or something then probably no need to have e2e test proof) that either doesn't exist, claims that the box that it was trying to run in doe... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
|
||
| @router.api_route("/lens/evals/{path:path}", methods=["GET", "POST", "PUT"], include_in_schema=False) | ||
| async def forward_evals(path: str, request: Request, auth: Auth, upstream: Upstream) -> Response: | ||
| return await forward(request, f"/lens/evals/{quote(path, safe='/')}", auth, upstream) |
There was a problem hiding this comment.
Medium: Parent-directory traversal bypasses route restrictions
A key restricted to /lens/evals can request GET /lens/evals/%2e%2e/datasets/resolve?name=regressions&revision=7 and have it forwarded to /lens/datasets/resolve with a signed identity, despite the direct route being disallowed. ASGI decodes %2e%2e to .., quote() preserves the dots, and HTTPX removes the parent-directory segment before sending; reject decoded dot segments before forwarding. (asgi.readthedocs.io)
| return await forward(request, f"/lens/evals/{quote(path, safe='/')}", auth, upstream) | |
| if any(segment in {".", ".."} for segment in path.split("/")): | |
| raise HTTPException(400, "Invalid eval path") | |
| return await forward(request, f"/lens/evals/{quote(path, safe='/')}", auth, upstream) |
PR overviewThe PR adds proxy forwarding to Lens for evaluation and contract dataset routes. A parent-directory traversal allows an API key restricted to evaluation routes to reach the dataset-resolution endpoint with a signed identity, bypassing route restrictions. This authorization bypass remains open, and no issues have been addressed yet. Open issues (1)
Fixed/addressed: 0 · PR risk: 7/10 |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
TLDR
Problem this solves:
How it solves it:
/lens/evals/*,/lens/datasets/resolveand contract case reads to LensUser Flow
Before: a team running
lens evalin CI against the proxy cannot create a runX-Lens-Contract: 1lens evalexits with an infra errorAfter: the same request reaches Lens and the run is recorded for the key's team
LITELLM_LENS_URLandLITELLM_LENS_GATEWAY_SECRET(at least 32 characters) and restarts the proxyX-Lens-Contract: 1return the dataset and its casesPre-Submission checklist
uv run pytest tests/unit/<your_test_file>.py -vType
🆕 New Feature
Caveats (if any)
Medium
Low
models, since Lens never reads itFinal Attestation
Link to Devin session: https://app.devin.ai/sessions/8dd673b2547a428cab2c3582aff5a3af
Open in Devin Desktop: https://app.devin.ai/desktop/session/8dd673b2547a428cab2c3582aff5a3af?variant=devin
Requested by: @ishaan-berri