Skip to content

fix(global): connections refuse private hosts, metadata headers and under-scoped page calls - #329

Closed
osamanar wants to merge 1 commit into
mainfrom
fix/connection-ssrf-and-page-permissions
Closed

osamanar wants to merge 1 commit into
mainfrom
fix/connection-ssrf-and-page-permissions

Conversation

@osamanar

@osamanar osamanar commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes three pentest findings: SSRF-VULN-01, SSRF-VULN-02 (medium, CWE-918) and BFLA on connection page operations (medium, CWE-863).

SSRF-VULN-01: private destinations refused by default

  • New namesAPrivateHost (packages/operations/src/addresses/private-hosts.ts), with hosts read the way a URL parser reads them (canonical-hosts.ts). It refuses:
    • loopback, RFC1918, CGNAT, link-local (169.254/16, fe80::/10), unique-local, reserved, multicast and broadcast addresses;
    • IPv6 addresses that embed a private IPv4 address (IPv4-mapped, NAT64, 6to4);
    • single-label names such as metadata, and names under .localhost, .local, .internal and similar suffixes, plus metadata.azure.com.
  • hostRefusalOf applies this before allowed_hosts. It is the one check point, so it holds when a connection is added, at first use and on every outbound call and redirect.
  • namesTheLoopback now covers every loopback form.
  • New opt-in setting connections.private_hosts, empty by default. A host listed there must still pass allowed_hosts when that is set.

Breaking for operators: a connection to a private host, e.g. a local MCP server, now needs private_hosts: [localhost]. Tool servers configured in auto-runtime.yaml are not affected. Existing connections to private hosts will be refused after restart until the host is listed.

Not covered: DNS rebinding. fetch never exposes the address it resolved to. The docs now recommend blocking the metadata service at the network level.

SSRF-VULN-02: metadata headers reserved

  • Metadata, Metadata-Flavor, X-Google-Metadata-Request, X-AWS-EC2-Metadata-Token and X-AWS-EC2-Metadata-Token-TTL-Seconds are now reserved for both MCP and OpenAPI tool servers. This applies to entry headers and to credential header names.
  • Authorization is deliberately not reserved. Operator tool servers set it in headers, and connection credentials go in through it.

BFLA: page operations take the derived permission

  • read_link, approve, give_credential and consent start/finish no longer accept any one permission (allPermissions).
  • read_link now needs org:read. The commands need org:write, matching add_connection.
  • New tests show that org:read, playbook:read and playbook:write tokens are refused for these commands.

Docs

docs/self-host/connections.md, docs/self-host/configuration.md, docs/reference/http.md, the OpenAPI document and auto-runtime.schema.json are updated.

Checks

pnpm check passes: format, lint, typecheck, tests with 100% coverage, knip and sherif.

Related PRs (same pentest)

…nder-scoped page calls

A connection a person adds could point at any host, the runtime's own
network and the cloud's instance metadata service among them, because
allowed_hosts lets every host through until an operator sets it. Every
host check now refuses loopback, private, link-local, shared, reserved
and multicast addresses, IPv6 addresses that embed one, single-label
names and the internal suffixes, however the URL writes them, unless
the new connections.private_hosts setting lists the host. The check is
the one hostRefusalOf, so it holds when a connection is added, when it
is first used and on every outbound call and redirect.

The headers that open a cloud's instance metadata service (Metadata,
Metadata-Flavor, X-Google-Metadata-Request, X-AWS-EC2-Metadata-Token,
X-AWS-EC2-Metadata-Token-TTL-Seconds) are reserved for every tool server
type, in entry headers and credential header names alike.

The connection page operations (read_link, approve, give_credential and
consent start/finish) were permitted by any one permission, so a
playbook:read token of the person who added a connection could approve
it. They now take the derived permission: org:read for read_link,
org:write for the commands, as add_connection does.
@github-actions

Copy link
Copy Markdown
Contributor


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

const bitsInIpv6 = 128n;

function writtenHostOf(host: string): string {
const lowered = host.toLowerCase().replace(/\.+$/u, '');
@osamanar osamanar closed this Oct 25, 2026
@osamanar
osamanar deleted the fix/connection-ssrf-and-page-permissions branch October 25, 2026 23:15
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