Files
sim/apps
Waleed febc36ff9c fix(security): enforce URL validation across connectors, providers, and auth flows (SSRF + open-redirect hardening) (#4236)
* fix(workday): validate tenantUrl to prevent SSRF in SOAP client

* fix(workday): use validation.sanitized in buildWsdlUrl

* fix(security): enforce URL validation across connectors, providers, auth

- Azure OpenAI/Anthropic: validate user-supplied azureEndpoint with validateUrlWithDNS to block SSRF to private IPs, localhost (in hosted mode), and dangerous ports.
- ServiceNow connector: enforce ServiceNow domain allowlist via validateServiceNowInstanceUrl before calling the instance URL.
- Obsidian connector: validate vaultUrl with validateUrlWithDNS and reuse the resolved IP via secureFetchWithPinnedIPAndRetry to block DNS rebinding between validation and request.
- Signup + verify flows: pass redirect/callbackUrl/redirectAfter and stored inviteRedirectUrl through validateCallbackUrl; drop unsafe values and log a warning.
- lib/knowledge/documents/utils.ts: add secureFetchWithPinnedIPAndRetry wrapper around secureFetchWithPinnedIP (used by Obsidian).

* fix(obsidian): use isomorphic SSRF validation to unblock client build

The Obsidian connector is reachable from client bundles via `connectors/registry.ts` (the knowledge UI reads metadata like `.icon`/`.name`). Importing `validateUrlWithDNS` / `secureFetchWithPinnedIP` from `input-validation.server` pulled `dns/promises`, `http`, `https`, `net` into client chunks, breaking the Turbopack build:

  Module not found: Can't resolve 'dns/promises'
  ./apps/sim/lib/core/security/input-validation.server.ts [Client Component Browser]
  ./apps/sim/connectors/obsidian/obsidian.ts [Client Component Browser]
  ./apps/sim/connectors/registry.ts [Client Component Browser]

Once that file polluted a browser context, Turbopack also failed to resolve the Node builtins in its legitimate server-route imports, cascading the error across App Routes and Server Components.

Fix: switch the Obsidian connector to the isomorphic `validateExternalUrl` + `fetchWithRetry` helpers, matching the pattern used by every other connector in the registry. This keeps the core SSRF protections:
  - hosted Sim: blocks localhost, private IPs, HTTP (HTTPS enforced)
  - self-hosted Sim: allows localhost + HTTP, still blocks non-loopback private IPs and dangerous ports (22, 25, 3306, 5432, 6379, 27017, 9200)

Drops the DNS-rebinding defense specifically (the IP-pinned fetch chain). The trade-off is acceptable because the vault URL is entered by the workspace admin — not arbitrary untrusted input — and hosted deployments already force the plugin to be exposed through a public URL (tunnel/port-forward), making rebinding a narrow threat.

Also reverts the `secureFetchWithPinnedIPAndRetry` wrapper in `lib/knowledge/documents/utils.ts` (no longer needed, and its `.server` import was the original source of the client-bundle pollution).

* fix(servicenow): propagate URL validation errors in getDocument

Match listDocuments behavior — invalid instance URL should surface as a
configuration error rather than being swallowed into a "document not found"
null response during sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(obsidian): drop allowHttp to restore HTTPS enforcement in hosted mode

allowHttp: true permitted plaintext HTTP for all hosts in all deployment
modes, contradicting the documented policy. The default validateExternalUrl
behavior already allows http://localhost in self-hosted mode (the actual
Obsidian Local REST API use case) via the built-in carve-out, while correctly
rejecting HTTP for public hosts in hosted mode — which prevents leaking the
Bearer access token over plaintext.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 10:54:03 -07:00
..