Uh oh!
There was an error while loading. Please reload this page.
feat(server): restore the Nitro control plane with registered routes - #24
feat(server): restore the Nitro control plane with registered routes#24RedStar071 wants to merge 2 commits into
Conversation
Restores apps/server removed in #21 and fixes the unresolved review findings from #2 against it: file-based routing is now actually enabled (serverDir is opt-in in this Nitro release), so /health and the task API respond over HTTP, and the documented AGENT_ZERO_PORT is honored through a start wrapper that maps it onto NITRO_PORT before the listener boots. The build now runs tsdown alongside nitro build for the library surface and start wrapper.
The Nitro control plane is being restored in #24 and honors this variable, so dropping it as a leftover of the removed server no longer applies.
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
| export const taskInput = z | ||
| .object({ | ||
| repository: z.string().min(1), |
There was a problem hiding this comment.
Unauthenticated task route accepts arbitrary local paths
POST /tasks accepts any non-empty repository value without authenticating the caller, authorizing the requested checkout, or constraining it to an approved root. The value then reaches loadConfig, createRunner, and agent.run, allowing a client that can reach the listener to process arbitrary server-local directories and their repository policy. Resolve an authorized server-side repository identifier instead of accepting a filesystem path, and enforce canonical containment within managed checkout roots before task execution.
Artifacts
Baseline before PR #24 with no POST task route
- A detached worktree at the parent commit was checked for the task POST route and HTTP handler; both were absent, establishing the pre-change baseline.
Runtime API validation source for unauthenticated task path acceptance
- This executed script starts the built Nitro server without model credentials and sends unauthenticated POST requests using an empty and a controlled existing directory path.
Unauthenticated POST task path acceptance after PR #24
- The live server returned HTTP 400 for an empty path and HTTP 200 for a controlled existing server-local directory without an Authorization header, with a completed agent task; this confirms the exposure.
- The focused server HTTP test suite was executed and all seven tests passed, confirming the checked transport behavior remains executable.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/router.ts
Line: 31
Comment:
**Unauthenticated task route accepts arbitrary local paths**`POST /tasks` accepts any non-empty `repository` value without authenticating the caller, authorizing the requested checkout, or constraining it to an approved root. The value then reaches `loadConfig`, `createRunner`, and `agent.run`, allowing a client that can reach the listener to process arbitrary server-local directories and their repository policy. Resolve an authorized server-side repository identifier instead of accepting a filesystem path, and enforce canonical containment within managed checkout roots before task execution.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.POST /tasks is unauthenticated transport, so the requested repository is untrusted input. The route no longer accepts a raw filesystem path: the body carries an identifier that must canonically resolve (realpath, so symlinks cannot escape) to a directory strictly inside the operator configured AGENT_ZERO_CHECKOUT_ROOT. Without a configured root the route fails closed with 403 and runs nothing. Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Restores
apps/server, which #21 removed, and fixes the two P1 review findings from #2 that were never resolved against it: the built server registered no routes (404 on every path) and ignored the documentedAGENT_ZERO_PORT.Routes actually register now. The root cause of the original 404s is that this Nitro release ships with filesystem scanning disabled (
serverDirdefaults tofalse), so route files were silently ignored no matter where they lived. The config opts in explicitly:Handlers under
server/routes/are one-line shells (health.get.ts,tasks/index.get.ts,tasks/index.post.ts,tasks/[id].get.ts,tasks/[id]/evidence.get.ts) over the transport-independent task API restored insrc/router.ts, with the HTTP shaping (404s, body validation via thetaskInputschema) insrc/http.tsso it stays unit-testable without a listener.AGENT_ZERO_PORTis honored.aube run startboots through a wrapper that maps the documented variable ontoNITRO_PORTbefore the listener reads its environment; explicitNITRO_PORT/PORTstill win. The mapping is a pure tested function.Task checkouts are authorized against a managed root.
POST /tasksis unauthenticated transport, so therepositoryfield is no longer a raw filesystem path: it is an identifier that must canonically resolve (viarealpath, so symlinks cannot escape) to a directory strictly inside the operator-configuredAGENT_ZERO_CHECKOUT_ROOT. Without a configured root the route fails closed with 403 and runs nothing. The resolution is a pure tested function insrc/checkout.ts.Build follows the repo toolchain.
buildrunsnitro build && tsdown, so the listener comes from the Nitro pipeline while the library surface and start wrapper go through the shared tsdown config, addressing the tsdown-bypass note from #2.Verified against a real built server in the sandbox: with
AGENT_ZERO_PORT=4040the listener binds 4040 (3000 refuses),GET /healthandGET /tasksreturn 200,GET /tasks/missing-taskreturns a JSON 404, and malformed or schema-invalidPOST /tasksbodies return 400. The full check suite (check:repo,lint:ci,typecheck,testwith 28 server tests,build) passes locally; the lockfile gains only the new importer (pure additions, resolved with aube 1.38.0).One behavior note:
taskResponse/evidenceResponsekeep the in-memory task store semantics of the original server; the restoredrouter.tsonly adapts to the currentmodelFromEnvironment(config.model)signature and reports version 0.3.0.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Confidence Score: 5/5
No blocking failure remains.
The prior path-access concern was tested through the HTTP endpoint and its predicted unauthorized execution did not occur.
What T-Rex did
Reviews (2): Last reviewed commit: "fix(server): authorize task checkouts ag..." | Re-trigger Greptile