{"answers":{"affected":"2 open source packages ship an affected version, including @frontmcp/adapters.","exploited":"There is no known exploitation of GHSA-8q49-2h5h-434x in the wild and no public exploit code.","fix":"Upgrade @frontmcp/adapters to 1.5.6 or later.","what":"GHSA-8q49-2h5h-434x is a server-side request forgery vulnerability in @frontmcp/adapters, published on 24 Jul 2026. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-8q49-2h5h-434x and fix it if it is affected.\n\nWhat it is: GHSA-8q49-2h5h-434x is a server-side request forgery vulnerability in @frontmcp/adapters, published on 24 Jul 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-8q49-2h5h-434x in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- @frontmcp/adapters (npm): affected all versions; fixed in 1.5.6\n- @frontmcp/adapters (npm): affected all versions; fixed in 1.5.6\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-8q49-2h5h-434x/ (Markdown: https://insights.sbomify.com/GHSA-8q49-2h5h-434x/index.md)","id":"GHSA-8q49-2h5h-434x","markdown":"https://insights.sbomify.com/GHSA-8q49-2h5h-434x/index.md","name":"GHSA-8q49-2h5h-434x","record":{"aliases":[],"analysis":{"component_type":"library","fix_type":"upgrade","impacts":["data_read","security_bypass"],"network_reachable":"yes","non_default_config":true,"product_category":"OpenAPI adapter library","requires_auth":"none","summary":"An SSRF vulnerability in the OpenAPI adapter's spec poller allows attackers to access internal resources when polling is enabled against untrusted URLs.","user_interaction":"not_required","vuln_class":"ssrf"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"## Summary\n\nThe OpenAPI adapter's spec-change **poller** (`OpenApiSpecPoller`) re-fetched the\nconfigured spec `url` on a timer using a raw global `fetch()`, bypassing the SSRF\nguard (`safeFetch` / `assertUrlSafe`) that `OpenAPIToolGenerator.fromURL()` applies\nto the initial spec load. As a result, the pinning/DNS-resolution hardening delivered\nvia `mcp-from-openapi \u003e= 2.5.0` (advisory GHSA-65h7-9wrw-629c) protected the initial\nload but **not** the recurring poll of the same URL. When polling is enabled against\nan untrusted or attacker-influenceable spec URL, this is an unguarded SSRF vector.\n\n## Details\n\nThe initial spec load is guarded. `OpenapiAdapter` resolves a secure `refResolution`\npolicy and passes it to the guarded loader:\n\n```ts\n// libs/adapters/src/openapi/openapi.adapter.ts — initializeGenerator()\nreturn await OpenAPIToolGenerator.fromURL(this.options.url, {\n  // ...\n  followRedirects: this.options.loadOptions?.followRedirects ?? false,\n  refResolution, // secure default: external $refs off, internal targets blocked\n});\n```\n\nBut the poller — which re-fetches **the same URL** on every interval — did not:\n\n```ts\n// libs/adapters/src/openapi/openapi-spec-poller.ts — doFetch() (vulnerable, \u003c= 1.5.5)\nconst controller = new AbortController();\nconst timeout = setTimeout(() =\u003e controller.abort(), this.fetchTimeoutMs);\ntry {\n  const response = await fetch(this.url, {   // \u003c-- raw global fetch, no SSRF guard\n    headers,\n    signal: controller.signal,\n  });\n  // ...hash the body, fire onChanged...\n}\n```\n\nBecause `doFetch()` never called `safeFetch`, none of the guard's protections applied\nto the polled request:\n\n- no allow-list / block-list enforcement (`allowedHosts` / `blockedHosts`);\n- no internal/private/loopback/link-local/CGNAT/cloud-metadata IP blocking;\n- no DNS resolution of the hostname (so a DNS name that resolves to an internal IP,\n  e.g. `http://127.0.0.1.nip.io/`, was reached);\n- no connection **pinning** to the validated IP (DNS-rebinding TOCTOU);\n- no per-hop re-validation of HTTP redirects.\n\nThis is the identical threat model to `fromURL()` / external `$ref` resolution\n(GHSA-65h7-9wrw-629c), applied to a request path that the fix for that advisory did\nnot cover.\n\n## Impact\n\nA server that enables spec polling against an untrusted or attacker-influenceable\nspec URL will, on every poll interval, issue a server-side `GET` to whatever host the\nURL (or a DNS name it resolves to, or a redirect it returns) points at — including\ninternal-only addresses unreachable from the public internet. Consequences include:\n\n- reading cloud-instance metadata endpoints (e.g. `169.254.169.254`) — credential /\n  token theft;\n- probing and reaching internal services and private-range hosts (internal network\n  scanning);\n- DNS-rebinding to swap a public host for an internal one between validation and\n  connection.\n\nThe poller issues `GET` requests only, so the primary impact is **confidentiality**\n(reaching and reading internal endpoints); the fetched body is content-hashed to\ndetect change and the subsequent tool rebuild goes back through the guarded\n`fromURL()` path.\n\n## Preconditions\n\nExploitation requires **both**:\n\n1. `polling.enabled: true` on an `OpenapiAdapter` (polling is off by default and\n   requires the URL-based `url` option, not an inline `spec`); **and**\n2. the spec `url` is untrusted / attacker-influenceable (e.g. it is derived from user\n   input, a tenant-supplied value, or otherwise not a fixed trusted constant), or an\n   otherwise-trusted spec host is attacker-controlled or can redirect.\n\nServers that poll a fixed, trusted, first-party spec URL are not exposed in practice,\nthough they still benefit from the guard as defense-in-depth.\n\n## Proof of concept\n\n```ts\nimport { OpenapiAdapter } from '@frontmcp/adapters';\n\n// url is attacker-influenceable and points (directly, via DNS, or via redirect)\n// at an internal target; polling re-fetches it every interval.\nconst adapter = OpenapiAdapter.init({\n  name: 'evil',\n  url: 'http://169.254.169.254/latest/meta-data/', // or http://127.0.0.1.nip.io/...\n  polling: { enabled: true, intervalMs: 5000 },\n});\n\nawait adapter.fetch();   // initial load IS guarded (blocked)\nadapter.startPolling();  // \u003c= 1.5.5: each poll issues an UNGUARDED GET to the internal target\n```\n\nOn `\u003c= 1.5.5` the timed poll reaches the internal address. On the patched version the\npoll fails closed (no request is made; the failure is logged) exactly as the initial\nload does.\n\n## Patch\n\nThe fix routes the poller through the same SSRF guard as the initial load, with the\nsame policy, so both paths share one DNS resolution + connection pinning and cannot\ndiverge:\n\n- `OpenApiSpecPoller.doFetch()` now calls `safeFetch(this.url, { headers, timeoutMs,\n  followRedirects, ssrf })` from `mcp-from-openapi` instead of the global `fetch()`.\n- `OpenapiAdapter.startPolling()` injects the adapter's resolved policy into the\n  poller: `ssrf: normalizeSsrfOptions(this.resolveRefResolution())` and\n  `followRedirects: loadOptions?.followRedirects ?? false` — identical to what\n  `fromURL()` receives.\n- `SpecPollerOptions` gained optional `ssrf` / `followRedirects`; standalone use of\n  `OpenApiSpecPoller` defaults to the secure policy (internal targets blocked,\n  redirects not followed).\n\nFiles changed:\n\n- `libs/adapters/src/openapi/openapi-spec-poller.ts`\n- `libs/adapters/src/openapi/openapi-spec-poller.types.ts`\n- `libs/adapters/src/openapi/openapi.adapter.ts`\n\nRequires `mcp-from-openapi \u003e= 2.5.0` (already a dependency at `2.5.1`), which exports\n`safeFetch` / `normalizeSsrfOptions` and performs the resolved-IP validation and\nconnection pinning.\n\n## Remediation\n\nUpgrade `@frontmcp/adapters` to `1.5.6` or later. No configuration change is required:\npolling now inherits the same secure defaults as the initial spec load (external\ntargets blocked, redirects not followed). To poll a genuinely internal or localhost\nspec server in a trusted environment, opt in explicitly with\n`loadOptions.refResolution.allowInternalIPs: true` — the same knob that gates the\ninitial load.\n\n## Workarounds\n\nFor users who cannot upgrade immediately:\n\n- disable polling (`polling.enabled: false`) on adapters whose spec `url` is not a\n  fixed, trusted, first-party value; or\n- only enable polling against spec URLs you fully control, served over HTTPS from a\n  host that cannot be made to redirect to internal targets; and\n- enforce network egress controls / an allow-list at the platform layer so the server\n  cannot reach internal ranges or cloud-metadata endpoints.","ecosystems":["npm"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"FrontMCP: Server-Side Request Forgery (SSRF) in the OpenAPI adapter spec-change poller","id":"GHSA-8q49-2h5h-434x","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"npm","product":null,"products":[],"type":"ssrf","year":"2026"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"npm","fixed":["1.5.6"],"introduced":["0"],"last_affected":[],"package":"@frontmcp/adapters","purl":null,"source":"GHSA-8q49-2h5h-434x"},{"ecosystem":"npm","fixed":["1.5.6"],"introduced":["0"],"last_affected":[],"package":"@frontmcp/adapters","purl":"pkg:npm/%40frontmcp/adapters","source":"GHSA-8q49-2h5h-434x"}],"products":null,"published":"2026-07-24","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"An SSRF vulnerability in the OpenAPI adapter's spec poller allows attackers to access internal resources when polling is enabled against untrusted URLs.","url":"https://insights.sbomify.com/GHSA-8q49-2h5h-434x/"}