Skip to content

Check the address a supervisor hands back before calling it - #51

Merged
davidmckayv merged 1 commit into
mainfrom
fix/guard-located-computer-address
Aug 20, 2026
Merged

Check the address a supervisor hands back before calling it#51
davidmckayv merged 1 commit into
mainfrom
fix/guard-located-computer-address

Conversation

@davidmckayv

Copy link
Copy Markdown
Contributor

Groundwork for A10, where the supervisor stops being ours.

The gap

resolveBaseUrl returns a URL and the next line puts it straight into fetch, carrying this deployment's COMPUTER_TOKEN:

const target = botId && options.resolveBaseUrl
  ? (await options.resolveBaseUrl(botId)).replace(/\/$/, "")
  : base;

No validation between the two. That has been fine because the only supervisor is ours, answering on loopback. It stops being fine the moment the address comes from a hosted provider's API, because then we call whatever a third party returns, with a credential attached.

Not the navigation check

checkNavigationTarget already exists and is the wrong tool. It refuses private hosts, which is right for a Bot browsing and precisely wrong here: our own supervisor answers http://127.0.0.1:<port> for a container on this machine, so reusing it would refuse the normal case on every deployment we have.

checkComputerAddress keeps only what holds however the address was produced:

  • it has to parse as a URL
  • the scheme has to be http or https
  • the cloud metadata addresses are refused whatever anything says, since that is how a container's credentials leave it and no supervisor has a reason to name one

Private addresses stay allowed, deliberately, and the comment says why so nobody "fixes" it later.

Tests

Five cases, including the two that matter for the shape of this: the loopback address our own supervisor returns is allowed, and a 169.254.169.254 address is refused however it arrived. 21 pass in that file.

Where it runs

  • New state that outlives a request? None, a pure function.
  • What happens on the second replica? Identical, no shared state.
  • Anything serialised? No.
  • Anything fanned out to a browser? No.
  • New listener, port, or schedule? None.

Boundary and audit

  • Refusal surfaces as ComputerUnavailableError, the same way an unreachable computer already does, so the operator gets a sentence rather than a stack trace.
  • No change to the gateway's resolve, decide, audit, act order.

Proof

Server restarted on this branch, /api/computers/supervisor/computers still lists computers through the real supervisor, so the loopback path is unaffected.

format:check, lint, typecheck clean. 692 pass, 5 skip, 0 fail.

`resolveBaseUrl` returns a URL and the next line puts it into `fetch` with this
deployment's computer token on it. That was fine while the only supervisor was
ours, answering on loopback. It stops being fine with a hosted provider, where
the address arrives from a third party's API and we call whatever it says.

Deliberately not the navigation check. That one refuses private hosts, which is
correct for a Bot browsing and exactly wrong here: our own supervisor answers
`http://127.0.0.1:<port>` for a container on this machine, so reusing it would
refuse the ordinary case.

What survives is what holds however the address was produced. The scheme has to
be one a computer speaks, and the cloud metadata addresses are refused whatever
anything says, because that is how a container's credentials leave it and no
supervisor has a reason to name one.

Groundwork for A10, where the supervisor becomes somebody else's API.
@davidmckayv
davidmckayv merged commit 62aa377 into main Aug 20, 2026
3 checks passed
@davidmckayv
davidmckayv deleted the fix/guard-located-computer-address branch August 20, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants