Skip to content

fix(providers): pin vLLM provider endpoint to validated IP#5077

Merged
waleedlatif1 merged 1 commit into
stagingfrom
worktree-fix+vllm-ssrf
Jun 16, 2026
Merged

fix(providers): pin vLLM provider endpoint to validated IP#5077
waleedlatif1 merged 1 commit into
stagingfrom
worktree-fix+vllm-ssrf

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Fix SSRF in the vLLM provider: the user-supplied endpoint (request.azureEndpoint) was used as the OpenAI-compatible base URL with no validation, letting an authenticated user point the backend at internal hosts / cloud-metadata (169.254.169.254, RFC-1918, etc.).
  • Now validate the user endpoint with the central validateUrlWithDNS guard and pin the connection to the resolved IP via createPinnedFetch, wired into the OpenAI client's fetch option — defeating private-IP access and DNS rebinding.
  • Mirrors the Azure OpenAI/Anthropic precedent (fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP #5060): env-configured VLLM_BASE_URL stays trusted/unvalidated; only the attacker-controllable per-request endpoint is validated + pinned.

Type of Change

  • Bug fix (security)

Testing

  • apps/sim/providers/vllm/index.test.ts — added SSRF coverage (no-endpoint env fallback, validate+pin, blocked-IP reject without issuing a request, unresolvable-IP reject). 15/15 pass.
  • tsc --noEmit, biome check, and bun run check:api-validation all clean.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Validate the user-supplied vLLM endpoint (request.azureEndpoint) against
the central SSRF guard and pin the connection to the resolved IP before
issuing any request, mirroring the Azure OpenAI/Anthropic providers. The
operator-configured VLLM_BASE_URL stays trusted and unvalidated.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 16, 2026 12:07am

Request Review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor

cursor Bot commented Jun 16, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Security fix on attacker-controlled outbound URLs in the provider request path; behavior change only when azureEndpoint is set, with regression tests added.

Overview
Closes an SSRF gap in the vLLM provider: per-request azureEndpoint was used as the OpenAI client baseURL with no checks, so callers could aim the backend at internal or metadata hosts.

When a user-supplied endpoint is present, the provider now runs validateUrlWithDNS and wires createPinnedFetch into the OpenAI client’s fetch option so outbound traffic is pinned to the resolved IP (blocking private/metadata targets and DNS rebinding). Operator VLLM_BASE_URL is unchanged—no validation or pinning, matching Azure provider behavior.

Tests in index.test.ts cover env-only base URL, validate+pin success, blocked validation (no request), and missing pinnable IP.

Reviewed by Cursor Bugbot for commit 2656dc5. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2656dc5. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes an SSRF vulnerability in the vLLM provider where a user-supplied azureEndpoint was forwarded directly to the OpenAI client without any validation, allowing an authenticated user to route backend requests to cloud-metadata or RFC-1918 addresses.

  • SSRF guard: validateUrlWithDNS is now called on request.azureEndpoint before the OpenAI client is constructed; on failure the error is surfaced and no network request is made.
  • DNS-rebinding prevention: the resolved IP from validation is pinned via createPinnedFetch, which injects an undici dispatcher that always connects to the pre-resolved IP, mirroring the Azure OpenAI/Anthropic pattern (fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP #5060).
  • Tests: four new SSRF-specific tests in index.test.ts cover the env-fallback path, the validate-and-pin happy path, blocked-IP rejection, and the edge case where DNS resolves but returns no pinnable IP.

Confidence Score: 5/5

Safe to merge — the change is a focused, well-tested security patch with no regressions in existing tests.

The implementation correctly validates user-supplied endpoints with the central SSRF guard, pins connections to the resolved IP to defeat DNS rebinding, and leaves the operator-controlled env URL untouched. The logic mirrors the already-reviewed Azure OpenAI provider. Four new tests verify the exact failure modes (blocked IP, unresolvable host, missing resolvedIP, and env-fallback bypass). No correctness or behavioral regressions were identified in the changed files.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/providers/vllm/index.ts Adds SSRF guard and DNS-pinning for user-supplied azureEndpoint; env-configured VLLM_BASE_URL remains trusted/unvalidated; OpenAI client receives pinnedFetch only when a user endpoint is present; pattern mirrors Azure OpenAI provider.
apps/sim/providers/vllm/index.test.ts Refactors mock setup to capture OpenAI constructor args via openAIArgs and adds four targeted SSRF-protection tests; beforeEach resets openAIArgs in-place correctly; mocks for validateUrlWithDNS and createPinnedFetch are wired through vi.hoisted so they apply before module evaluation.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller
    participant vllmProvider
    participant validateUrlWithDNS
    participant createPinnedFetch
    participant OpenAI

    Caller->>vllmProvider: "executeRequest({ azureEndpoint, ... })"

    alt azureEndpoint is present (user-supplied)
        vllmProvider->>validateUrlWithDNS: validateUrlWithDNS(endpoint, "vLLM endpoint")
        validateUrlWithDNS-->>vllmProvider: "{ isValid, resolvedIP, error }"

        alt "isValid === false"
            vllmProvider-->>Caller: throw Error("Invalid vLLM endpoint: ...")
        else resolvedIP is undefined
            vllmProvider-->>Caller: throw Error("could not resolve a pinnable IP address")
        else valid + resolvedIP present
            vllmProvider->>createPinnedFetch: createPinnedFetch(resolvedIP)
            createPinnedFetch-->>vllmProvider: pinnedFetch (undici dispatcher pinned to IP)
            vllmProvider->>OpenAI: "new OpenAI({ baseURL, fetch: pinnedFetch })"
        end
    else no azureEndpoint (uses env VLLM_BASE_URL)
        vllmProvider->>OpenAI: "new OpenAI({ baseURL }) — no pinning"
    end

    OpenAI-->>vllmProvider: response
    vllmProvider-->>Caller: ProviderResponse / StreamingExecution
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Caller
    participant vllmProvider
    participant validateUrlWithDNS
    participant createPinnedFetch
    participant OpenAI

    Caller->>vllmProvider: "executeRequest({ azureEndpoint, ... })"

    alt azureEndpoint is present (user-supplied)
        vllmProvider->>validateUrlWithDNS: validateUrlWithDNS(endpoint, "vLLM endpoint")
        validateUrlWithDNS-->>vllmProvider: "{ isValid, resolvedIP, error }"

        alt "isValid === false"
            vllmProvider-->>Caller: throw Error("Invalid vLLM endpoint: ...")
        else resolvedIP is undefined
            vllmProvider-->>Caller: throw Error("could not resolve a pinnable IP address")
        else valid + resolvedIP present
            vllmProvider->>createPinnedFetch: createPinnedFetch(resolvedIP)
            createPinnedFetch-->>vllmProvider: pinnedFetch (undici dispatcher pinned to IP)
            vllmProvider->>OpenAI: "new OpenAI({ baseURL, fetch: pinnedFetch })"
        end
    else no azureEndpoint (uses env VLLM_BASE_URL)
        vllmProvider->>OpenAI: "new OpenAI({ baseURL }) — no pinning"
    end

    OpenAI-->>vllmProvider: response
    vllmProvider-->>Caller: ProviderResponse / StreamingExecution
Loading

Reviews (4): Last reviewed commit: "fix(providers): pin vLLM provider endpoi..." | Re-trigger Greptile

@waleedlatif1 waleedlatif1 merged commit 4a7c2ef into staging Jun 16, 2026
10 checks passed
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR patches an SSRF vulnerability in the vLLM provider where the user-supplied azureEndpoint field was forwarded directly to the OpenAI SDK as baseURL without any validation, allowing an authenticated user to target internal hosts or cloud-metadata endpoints. The fix mirrors the already-landed Azure OpenAI/Anthropic pattern: validate the user-controlled endpoint via validateUrlWithDNS (DNS resolution + private-IP blocklist) and pin the outbound connection to the returned IP via createPinnedFetch, passing the pinned fetch function into the OpenAI client's fetch option.

  • The operator-configured VLLM_BASE_URL env var is intentionally left unvalidated (trusted path), consistent with the Azure providers.
  • Four new unit tests cover: no-endpoint env fallback, validate+pin happy path, blocked-IP rejection before any request is issued, and missing resolved-IP rejection.
  • validateUrlWithDNS is called without allowHttp: true, so the same HTTPS-only constraint applied to Azure endpoints is now applied to user-supplied vLLM endpoints — HTTP non-localhost endpoints will be rejected.

Confidence Score: 4/5

Safe to merge; the SSRF guard is correct and the pinning wiring mirrors the already-landed Azure providers exactly.

The fix is well-structured and the test suite thoroughly covers the new code paths. The only open question is whether user-supplied HTTP vLLM endpoints should be allowed — the current code blocks them, which is consistent with the Azure provider pattern but may be a breaking change for self-hosted deployments using plain HTTP inside private networks. Worth an explicit decision before merge.

apps/sim/providers/vllm/index.ts — the allowHttp omission deserves an explicit yes/no decision.

Important Files Changed

Filename Overview
apps/sim/providers/vllm/index.ts Adds SSRF guard + IP pinning for user-supplied endpoints; mirrors azure-openai pattern exactly. HTTP non-localhost endpoints are now rejected (intentional security hardening).
apps/sim/providers/vllm/index.test.ts Adds four targeted SSRF test cases covering no-endpoint env path, validate+pin, blocked-IP rejection, and missing resolvedIP rejection. Test structure correctly captures OpenAI constructor args to verify pinning wiring.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant vllmProvider
    participant validateUrlWithDNS
    participant createPinnedFetch
    participant OpenAI SDK

    Client->>vllmProvider: "executeRequest({ azureEndpoint: "https://my-vllm.example.com", ... })"

    alt azureEndpoint is provided (user-controlled)
        vllmProvider->>validateUrlWithDNS: validateUrlWithDNS(endpoint, "vLLM endpoint")
        validateUrlWithDNS-->>vllmProvider: "{ isValid: false, error: "..." }"
        vllmProvider-->>Client: throw Error("Invalid vLLM endpoint: ...")

        validateUrlWithDNS-->>vllmProvider: "{ isValid: true, resolvedIP: "203.0.113.10" }"
        vllmProvider->>createPinnedFetch: createPinnedFetch("203.0.113.10")
        createPinnedFetch-->>vllmProvider: pinnedFetch (IP-pinned undici agent)
        vllmProvider->>OpenAI SDK: new OpenAI({ baseURL, apiKey, fetch: pinnedFetch })
    else no azureEndpoint (env VLLM_BASE_URL used)
        vllmProvider->>OpenAI SDK: new OpenAI({ baseURL: env.VLLM_BASE_URL, apiKey })
        Note over vllmProvider,OpenAI SDK: No validation or pinning (trusted path)
    end

    OpenAI SDK->>OpenAI SDK: All HTTP calls use pinnedFetch (IP locked to 203.0.113.10)
    OpenAI SDK-->>Client: Response
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant vllmProvider
    participant validateUrlWithDNS
    participant createPinnedFetch
    participant OpenAI SDK

    Client->>vllmProvider: "executeRequest({ azureEndpoint: "https://my-vllm.example.com", ... })"

    alt azureEndpoint is provided (user-controlled)
        vllmProvider->>validateUrlWithDNS: validateUrlWithDNS(endpoint, "vLLM endpoint")
        validateUrlWithDNS-->>vllmProvider: "{ isValid: false, error: "..." }"
        vllmProvider-->>Client: throw Error("Invalid vLLM endpoint: ...")

        validateUrlWithDNS-->>vllmProvider: "{ isValid: true, resolvedIP: "203.0.113.10" }"
        vllmProvider->>createPinnedFetch: createPinnedFetch("203.0.113.10")
        createPinnedFetch-->>vllmProvider: pinnedFetch (IP-pinned undici agent)
        vllmProvider->>OpenAI SDK: new OpenAI({ baseURL, apiKey, fetch: pinnedFetch })
    else no azureEndpoint (env VLLM_BASE_URL used)
        vllmProvider->>OpenAI SDK: new OpenAI({ baseURL: env.VLLM_BASE_URL, apiKey })
        Note over vllmProvider,OpenAI SDK: No validation or pinning (trusted path)
    end

    OpenAI SDK->>OpenAI SDK: All HTTP calls use pinnedFetch (IP locked to 203.0.113.10)
    OpenAI SDK-->>Client: Response
Loading

Reviews (2): Last reviewed commit: "fix(providers): pin vLLM provider endpoi..." | Re-trigger Greptile

Comment thread apps/sim/providers/vllm/index.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/providers/vllm/index.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2656dc5. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2656dc5. Configure 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.

1 participant