diff --git a/README.md b/README.md index d4b42b4..345de5d 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,11 @@ Each layer ships as a framework-agnostic `-core` package plus an Express middlew | `@gatewaystack/transformabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/transformabl)](https://www.npmjs.com/package/@gatewaystack/transformabl) | PII detection, redaction, safety classification | | `@gatewaystack/validatabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/validatabl)](https://www.npmjs.com/package/@gatewaystack/validatabl) | Deny-by-default policy engine, scope/permission enforcement | | `@gatewaystack/limitabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/limitabl)](https://www.npmjs.com/package/@gatewaystack/limitabl) | Rate limits, budget tracking, agent guard | -| `@gatewaystack/proxyabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/proxyabl)](https://www.npmjs.com/package/@gatewaystack/proxyabl) | Auth mode routing, SSRF protection, identity-aware proxy | +| `@gatewaystack/proxyabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/proxyabl)](https://www.npmjs.com/package/@gatewaystack/proxyabl) | Auth-mode routing, identity-aware proxy to a configured backend (path-allowlisted). SSRF-safe fetch for arbitrary URLs is a `proxyabl-core` primitive — see note below. | | `@gatewaystack/explicabl` | [![npm](https://img.shields.io/npm/v/@gatewaystack/explicabl)](https://www.npmjs.com/package/@gatewaystack/explicabl) | Structured audit logging, health endpoints | +> **SSRF note (proxyabl).** The `proxyabl` Express router forwards to an operator-**configured** backend, with tool-name sanitization, path allowlisting, and host/protocol pinning to that backend — the target host is not caller-controlled. The DNS-resolving, private-IP-blocking SSRF engine (`assertUrlSafe` / `executeProxyRequest`) lives in `@gatewaystack/proxyabl-core` and is the primitive to use when you proxy **arbitrary, caller-influenced URLs**; the bundled router does not currently route through it (its threat model is different — a configured backend may legitimately be internal). See [`docs/packages.md`](docs/packages.md#proxyabl). + ## Full stack example Wire all six layers together. Each is optional — use only what you need. diff --git a/docs/packages.md b/docs/packages.md index c636227..3621f5e 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -30,7 +30,13 @@ GatewayStack ships as composable npm packages. Each governance layer has a `-cor `@gatewaystack/proxyabl-core` / `@gatewaystack/proxyabl` -**Execution Control & Identity-Aware Routing.** Five auth modes (API key, forward bearer, service OAuth, user OAuth, none), SSRF protection (host allowlist, private IP blocking), HTTP proxy with timeout/redirect/size controls, multi-provider registry. Express middleware serves PRM/OIDC metadata, enforces scope-to-tool mappings, and injects verified identity into downstream headers. +**Execution Control & Identity-Aware Routing.** Five auth modes (API key, forward bearer, service OAuth, user OAuth, none) and a multi-provider registry. + +**`proxyabl-core` — SSRF-safe fetch primitive.** `assertUrlSafe` / `executeProxyRequest` provide host-allowlisting, DNS-resolving private-IP blocking (IPv4 + IPv6, incl. IPv4-mapped IPv6), protocol enforcement, redirect blocking (`redirect: "manual"`), and timeout/response-size caps. Use these directly when you proxy **arbitrary, caller-influenced URLs** — that is the case SSRF protection is for. + +**`proxyabl` — Express gateway router.** Forwards to an operator-**configured** backend (`functionsBase` / proxy target). The caller controls the tool name / path, not the host: the router sanitizes the tool name, enforces a path allowlist, and pins the resolved URL's host + protocol to the configured backend. It is a pass-through proxy and **does not currently route through `proxyabl-core`'s `assertUrlSafe` engine** — the two have different threat models (a configured backend may legitimately be an internal address, which the private-IP-blocking engine would reject). The Express middleware also serves PRM/OIDC metadata, enforces scope-to-tool mappings, and injects verified identity into downstream headers. + +> Converging these — one canonical `proxyabl-core` engine consumed by both the OSS router and the production gateway (which forked an equivalent) — is tracked in the proxyabl convergence work. Until then, treat `assertUrlSafe` / `executeProxyRequest` as an available primitive, not something the bundled router runs for you. ## explicabl diff --git a/packages/proxyabl-core/README.md b/packages/proxyabl-core/README.md index d3e567f..d078b43 100644 --- a/packages/proxyabl-core/README.md +++ b/packages/proxyabl-core/README.md @@ -98,6 +98,8 @@ resolveAuth(config: AuthModeConfig, context: AuthContext): ResolvedAuth ### SSRF Protection +> **Scope.** `assertUrlSafe` / `executeProxyRequest` are the primitive for proxying **arbitrary, caller-influenced URLs** — call them directly from your handler. The Express router in [`@gatewaystack/proxyabl`](https://www.npmjs.com/package/@gatewaystack/proxyabl) is a different thing: it forwards to an operator-**configured** backend and does not route through this engine (a configured backend may legitimately be internal, which private-IP blocking would reject). Importing the router does not give you this SSRF check automatically. + ```ts assertUrlSafe(opts: UrlSafetyOptions): Promise ```