Skip to content

Correct proxyabl SSRF marketing to match what actually runs (#43)#48

Open
davidcrowe wants to merge 1 commit into
mainfrom
fix/proxyabl-ssrf-wire
Open

Correct proxyabl SSRF marketing to match what actually runs (#43)#48
davidcrowe wants to merge 1 commit into
mainfrom
fix/proxyabl-ssrf-wire

Conversation

@davidcrowe

Copy link
Copy Markdown
Collaborator

Fixes #43.

Recommendation: correct the marketing (the issue's "Or" option), don't force-wire

proxyabl-core's assertUrlSafe/executeProxyRequest (DNS-resolving private-IP blocking, host allowlist, redirect:"manual", size/timeout caps) are exported and unit-tested but have zero consumers outside their own tests. The README/docs implied the gateway proxy runs this engine — it does not.

I investigated the "preferred" wiring option and concluded it's the wrong call here, because the router's model is genuinely different (not a bug to paper over):

  • The three user-facing router fetches — mcpHandler, restToolHandler, proxyHandler — all forward to an operator-configured backend (functionsBase / proxy target). The caller controls the tool name / path, not the host, which is pinned to the configured backend via a host+protocol equality check.

  • executeProxyRequest is built for the opposite case — proxying arbitrary, caller-influenced URLs against an allowlist. Dropping it into the router would:

    1. reject configured backends that are legitimately internal (its private-IP blocking),
    2. change response semantics — it buffers+caps and throws on non-2xx instead of passing the upstream status/body through, and
    3. block upstream redirects the router may rely on.

    That's a breaking change to live gateway behavior in order to satisfy a marketing line — backwards.

  • OSS has no arbitrary-URL / custom-connector path today (executeProxyRequest's ancestor served custom HTTP tools that live only in prod), so the engine has no natural OSS consumer yet.

So this closes the credibility gap the honest way: make the docs match reality.

Changes (docs only)

  • README module table: the proxyabl row now describes the router accurately (configured-backend, path-allowlisted, identity-aware) and points to the proxyabl-core SSRF primitive; added an SSRF note spelling out the two threat models.
  • docs/packages.md: split the proxyabl entry into the core SSRF-safe primitive vs the pass-through Express router, and stated the router does not route through the engine (+ the convergence direction).
  • proxyabl-core/README: added a scope note so a reader — or a reviewer grepping assertUrlSafe and finding no consumers — sees the primitive is intentional and standalone, not abandoned.

proxyabl-core's own SSRF description was already accurate for the primitive and is otherwise unchanged.

The real fix (flagged, not attempted here)

One canonical proxyabl-core engine consumed by both the OSS router and prod's fork (integrations/custom/security.ts) — the proxyabl convergence epic. When an arbitrary-URL connector path lands in OSS, executeProxyRequest is the thing to wire there. Separately, adding redirect:"manual" to the router's fetches is a defensible standalone hardening, but it's a behavior change that deserves its own tested PR, not a rider on a docs correction.

If you'd rather take the deeper wiring now, happy to — but I'd want it as its own change with real upstream testing. Not merged.

proxyabl-core's assertUrlSafe/executeProxyRequest (DNS-resolving private-IP
blocking, host allowlist, redirect:"manual", size/timeout caps) are exported
and unit-tested but have zero consumers outside their own tests. The README
and docs implied the gateway proxy runs this engine; it does not.

Decision: correct the marketing (the issue's "Or" option), not wire the engine
into the router — because the router's model is genuinely different, not a bug
to paper over:

- The three user-facing router fetches (mcpHandler, restToolHandler,
  proxyHandler) all forward to an operator-CONFIGURED backend (functionsBase /
  proxy target). The caller controls the tool name / path, not the host, which
  is pinned to the configured backend via a host+protocol equality check.
- executeProxyRequest is built for the opposite case — proxying arbitrary,
  caller-influenced URLs against an allowlist. Dropping it into the router
  would (a) reject configured backends that are legitimately internal
  (private-IP blocking), (b) change response semantics (it buffers+caps and
  throws on non-2xx instead of passing upstream status/body through), and
  (c) block upstream redirects the router may rely on. That is a breaking
  change to live gateway behavior to satisfy a marketing line — backwards.
- OSS has no arbitrary-URL / custom-connector path today (executeProxyRequest's
  ancestor served custom HTTP tools that live only in prod), so the engine has
  no natural OSS consumer yet.

Changes (docs only):
- README module table: proxyabl row now describes the router accurately
  (configured-backend, path-allowlisted, identity-aware) and points to the
  proxyabl-core SSRF primitive.
- README: added an "SSRF note" spelling out the two threat models.
- docs/packages.md: split the proxyabl entry into the core SSRF-safe primitive
  vs the pass-through Express router, and noted the router does not route
  through the engine + the convergence direction.
- proxyabl-core/README: added a scope note so a reader (or a reviewer grepping
  assertUrlSafe) sees the primitive is intentional and standalone, not dead.

The proxyabl-core README's own SSRF description was already accurate for the
primitive and is unchanged except for the note. The real fix — one canonical
proxyabl-core engine consumed by both the OSS router and prod's fork — is the
convergence epic; flagged, not attempted here.
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.

proxyabl SSRF engine is dead code — wire assertUrlSafe into the router or correct the marketing

1 participant