Uh oh!
There was an error while loading. Please reload this page.
feat(jupyter): add Jupyter integration (contents, kernels, sessions) - #5527
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview 16 tools cover Contents (list/read/create/upload/rename/copy/delete), Kernels (list/start/stop/restart/interrupt), Kernelspecs, and Sessions. Most calls go through new internal routes A new Jupyter block exposes an operation dropdown and shared auth fields; tools, block metadata, Reviewed by Cursor Bugbot for commit 280ad02. Configure here. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Greptile SummaryThis PR adds a Jupyter integration for self-hosted servers. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (8): Last reviewed commit: "fix(jupyter): decode the whole path befo..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- 16 tools covering Contents, Kernels, Kernelspecs, and Sessions REST APIs - File upload/download via UserFile, following the Box upload pattern - Block with operation dropdown, token auth, and 8 catalog templates - Registered tools + block, generated docs, bumped API validation baseline
7cdc405 to
55caa9eCompare…uity, silent notebook fallback - Route uploads through validateUrlWithDNS + secureFetchWithPinnedIP (matches Grafana/1Password pattern) instead of a raw fetch to the user-supplied server URL - Replace the upload path/trailing-slash heuristic with an unambiguous directory + filename split - create_file no longer silently writes an empty notebook when notebook content is malformed JSON — it now errors clearly
Without it, Jupyter Server returns directory metadata with content: null, so jupyter_list_contents always reported an empty items array.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
encodeJupyterPath now rejects '.'/'..' segments across the whole path (shared by all 16 tools, not just upload); the upload route returns a clean 400 when it's hit.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…gaps - extract the traversal check out of encodeJupyterPath into a shared assertion, and apply it to body-only path fields (rename newPath, copy copyFromPath, session path) that never flowed through URL encoding and so skipped the check - pass stripAuthOnRedirect to the upload route's secureFetchWithPinnedIP call so a malicious Jupyter server can't redirect the PUT to another origin and receive the caller's token
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
A segment like %2e%2e wouldn't match the literal '..' check. Now decodes each segment before comparing, in addition to the literal check, so an already-encoded traversal attempt is caught too.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…for HTTP/private-host support and no redirects The generic external tool executor blocks plain-HTTP and non-localhost private-IP hosts by default, so every non-upload Jupyter operation could fail against typical self-hosted setups (LAN IP, docker hostname, or even literal localhost on a hosted deployment) even though the upload route worked via its own internal route. Added /api/tools/jupyter/proxy (DNS-pinned, allowHttp, maxRedirects: 0) that mirrors the upstream Jupyter response verbatim, matching the established pattern for self-hosted-arbitrary-host integrations (Grafana, 1Password) instead of the generic executor path. Each tool's request block now posts to the proxy instead of building a direct external URL; transformResponse and outputs are unchanged since the proxy response mirrors upstream status/body exactly. Also switches the upload route from stripAuthOnRedirect to maxRedirects: 0 — stronger, since it stops the uploaded file body (not just the token) from ever reaching a redirect target.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…separators in upload filename - The proxy route now independently validates the incoming path field for traversal segments instead of only relying on tool-side validation before the request reaches it — the route is a shared internal boundary, not something only our own tool code can call - The upload route's fileName can come from an advanced override or the legacy fileContent path and could itself contain '/' or '\', silently nesting the upload deeper than the directory param specified. Now rejected outright before joining.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
…-split segment A segment like foo%2f..%2fsecret has no literal slash, so splitting on literal '/' first and decoding each piece in isolation treats it as one opaque segment and never notices the '..' hiding behind the encoded slash. Decode the full path once, then split and check every segment the target server's own single URL-decode pass would see.
waleedlatif1
commented
Jul 9, 2026
waleedlatif1
commented
Jul 9, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 280ad02. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Summary
Type of Change
Testing
Tested manually (typecheck, biome,
check:api-validation:strict,check:bare-iconsall pass). Verified tool endpoints/fields against the Jupyter Server REST API spec and cross-checked the file-handling code against Box's established pattern with independent review passes.Checklist