Skip to content

fix(security): vision.analyze skips SSRF preflight on image URL fetch #261

Description

@subinium

Scope

vision.analyze accepts an HTTP(S) URL as the url input and fetches the image directly at runtime. No SSRF preflight is performed before that fetch — unlike every other web tool in the codebase.

Current state

packages/tools/src/vision.ts (verified: no validateFetchUrl or safeFetchPreflight import). The image URL supplied by the caller (resolvedUrl) is passed verbatim inside the JSON body to the OpenAI completions endpoint. For HTTP(S) URLs, the function takes the raw imageSource and passes it through resolveImageUrl (lines 17–49), which returns it unchanged for any https?:// URL (line 19).

Contrast with web.fetch (packages/tools/src/index.ts:630), web.extractText (index.ts:1172), and web.crawl (index.ts:1352), all of which call safeFetchPreflight before fetching.

The fallback no-key path (lines 182–204) issues a direct fetch(resolvedUrl, { method: 'HEAD' }) with zero SSRF validation, exposing the host to metadata-endpoint probing even without an API key configured.

Issue #188 ([MEDIUM] Restrict LLM vision URL fetch to scheme allowlist (SSRF)) is open but scoped narrowly to "scheme allowlist". The actual gap is the missing safeFetchPreflight call for direct fetches in the fallback path.

Proposed

  1. Add safeFetchPreflight(imageSource) before resolveImageUrl when imageSource is an HTTP(S) URL. Reuse the existing helper from packages/core/src/security.ts (exported via resolveAndValidateUrl).
  2. Remove the fallback HEAD fetch entirely or gate it behind the same preflight.
  3. Apply redirect: 'manual' on the fallback HEAD fetch to close the redirect-chain bypass.

Acceptance

  • vision.analyze with url: "http://169.254.169.254/latest/meta-data/" returns ok: false, output: "URL blocked: ..." without making any network request.
  • Existing unit tests for vision pass.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority/criticalCritical — fix before next releasesecuritySecurity findingsource/auditInternal audit finding

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions