You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(daemon): refuse host path install sources on the HTTP surface (#2102)
* fix(daemon): refuse host path install sources on the HTTP surface
`install_from_source` accepted `source.kind: "path"` over `/rpc` behind nothing
but a non-empty check, so any caller who could reach the daemon with a valid
token could name a file the daemon user can read and have its bytes flow back
through the install pipeline.
The `url` kind already declares its trust boundary (credential rejection,
blocked hostnames, non-public-address rejection, redirect caps). The `path`
kind has no host-side equivalent, and it does not need one: it is a local
affordance for callers that already carry the daemon's own authority, so the
HTTP boundary refuses it outright instead of confining it.
`HttpInstallSource` carries the narrowed contract, so a path source cannot be
returned from the boundary again without failing to compile.
In-process callers are untouched — `admin.ts` and `diff-screenshot.ts` build
path sources without crossing `parseInstallSource`.
Refs #2097
* fix(daemon): refuse host path install sources through the remote proxy
The `parseInstallSource` gate covered one of the two RPC methods that can carry
an install source. The generic `agent_device.command` method copies `params.meta`
wholesale and `commandRpcParamsSchema` types `meta` as an opaque object, so
`meta.installSource` reached the install handler unparsed — and that is the
method the client actually uses.
Gating it at the daemon's HTTP boundary would take local callers with it: that
server binds loopback, and `agent-device proxy` puts the local daemon in HTTP
mode, so a local `install <path>` on a proxy host crosses the same boundary with
the same wire shape. The proxy is the seam that separates the daemon's host from
callers who are not on it, so the refusal belongs there — where it also covers
both RPC methods at once, because the proxy reads the body before the method.
An uploaded artifact still backs a path source across the proxy: the daemon
substitutes the uploaded file and never reads the wire path, and an upload id
the caller does not own throws rather than falling back to it. That fallback is
what would turn the carve-out into the hole, so it is now pinned by a test.
This refuses `install remote:<path>` through a proxy. Naming a file on the
daemon's host from a remote client is the reported vulnerability, so the
affordance cannot survive the fix; the client-side prefix is left in place.
Refs #2097
* refactor(proxy): give the host-path refusal its own module
Review: the narration I added carried what names, a module boundary, and test
names should carry, and `daemon-proxy.ts` had held no such comments before.
`proxy-install-source-admission.ts` now owns the rule, so the boundary states
it instead of a comment; `isBackedByUploadedArtifact` names what "unbacked"
means in `carriesUnbackedHostPathInstallSource`. The proxy owner returns to 443
lines from 495.
Refs #2097
0 commit comments