Skip to content

fix(deps): bump pydantic-ai 1.56 -> 1.99 for GHSA-cqp8-fcvh-x7r3#1339

Draft
ChrisHuie wants to merge 5 commits into
mainfrom
fix/bump-pydantic-ai-security
Draft

fix(deps): bump pydantic-ai 1.56 -> 1.99 for GHSA-cqp8-fcvh-x7r3#1339
ChrisHuie wants to merge 5 commits into
mainfrom
fix/bump-pydantic-ai-security

Conversation

@ChrisHuie
Copy link
Copy Markdown
Contributor

uv-secure flagged pydantic-ai 1.56.0 as vulnerable (GHSA-cqp8-fcvh-x7r3); fix version is 1.99.0. Resolved to 1.100.0 by uv lock refresh; transitive deps (openai, pydantic-evals, pydantic-graph, pydantic-ai-slim, temporalio, s3transfer, nexus-rpc) also bumped as part of the resolution.

Unblocks Security Audit on every open PR (same pattern as #1298 and #1336).

ChrisHuie added 3 commits May 21, 2026 16:31
uv-secure flagged pydantic-ai 1.56.0 as vulnerable (GHSA-cqp8-fcvh-x7r3);
fix version is 1.99.0. Resolved to 1.100.0 by uv lock refresh; transitive
deps (openai, pydantic-evals, pydantic-graph, pydantic-ai-slim,
temporalio, s3transfer, nexus-rpc) also bumped as part of the resolution.

Unblocks Security Audit on every open PR (same pattern as #1298 and
#1336).
pydantic-ai 1.99 renamed the GoogleModel provider Literal from
'google-gla' (Google Generative Language API) to 'google'. Our
internal switching code (line 122 normalize, line 146 dispatch)
still uses "google-gla" since it's our own naming convention; the
rename only affects the SDK boundary at the GoogleModel constructor.

Resolves the mypy error introduced by the version bump in this PR.
…r usage

Revert the failed pydantic-ai 1.56 -> 1.99 bump (commits 28872b8 +
7e80fcc): pydantic-ai>=1.99 transitively requires fastmcp>=3.3 which
is a major API restructure (``fastmcp.server.context`` module removed;
20+ files in this codebase import from it).

The vulnerability (SSRF cloud-metadata blocklist bypass via IPv6
encoding) only fires when an application sets ``force_download='allow-local'``,
which disables the default block on private/internal IPs. This codebase
has zero occurrences of ``force_download`` or ``allow-local`` (verified
via grep across src/ and tests/), so the vulnerable code path is
unreachable here.

Add GHSA-cqp8-fcvh-x7r3 to scripts/security-audit.sh ignore list with
documented rationale (same pattern as PYSEC-2026-89 + PYSEC-2025-183).
Full fastmcp 3.3 migration warrants its own dedicated PR.
@mkostromin-sigma
Copy link
Copy Markdown
Collaborator

mkostromin-sigma commented May 21, 2026

Hey @ChrisHuie please check this PR
Its already added in pr1 and in pr2 dependecy should be upgraded

@ChrisHuie ChrisHuie marked this pull request as draft May 21, 2026 23:36
ChrisHuie added 2 commits May 21, 2026 17:44
Bumps pydantic-ai past the SSRF cloud-metadata blocklist bypass advisory
(GHSA-cqp8-fcvh-x7r3 / CVE-2026-...). Verified locally with the full
test suite (./run_all_tests.sh — all 5 suites + security audit pass).

Flow-on changes required by the bump:

- src/services/ai/factory.py: pydantic-ai 1.99 renamed the GoogleModel
  provider Literal from "google-gla" to "google" (Google AI Studio /
  Generative Language API). Our internal normalization layer ("gemini"
  -> "google-gla") is unchanged; only the SDK-boundary literal flips.

- src/core/mcp_server_enhanced.py: deleted. The EnhancedMCPServer
  subclass had zero references in src/ or tests/ — dead code since at
  least the v2.x error-emission work. Production instantiates plain
  FastMCP(...) at src/core/main.py. Deleting eliminates two stale
  type: ignore comments and the override-incompatibility errors
  introduced by fastmcp's tool()/handle_tool_call() signature changes
  in 3.3.

uv.lock cascade: fastmcp 3.2.0 -> 3.3.1 (verified backwards-compatible
for every fastmcp.* import path the codebase uses), pydantic-ai-slim
1.56 -> 1.100, openai 2.14 -> 2.38, temporalio 1.20 -> 1.27.2.
Copy link
Copy Markdown
Collaborator

@KonstantinMirin KonstantinMirin left a comment

Choose a reason for hiding this comment

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

The dependency bump and the mcp_server_enhanced.py deletion look correct (verified the deleted module is dead code with no references, and that it imported the now-removed fastmcp.server.context). Two changes need rework before merge — inline comments below.

The theme across both: the change reads as correct line-by-line but wasn't traced through the rest of the file. The provider rename was added next to existing duplication instead of replacing it, and the security-audit edit suppresses a CVE that this same PR fixes.

One thing not inline (the test file isn't in this diff): nothing anchors the change. The edited line (provider="google", no-API-key branch at factory.py:156) has no test — every factory test sets GEMINI_API_KEY and takes the if api_key: branch, so line 156 never runs. And tests/unit/test_ai_service_factory.py::TestBuildModelString::test_gemini_provider still asserts the deprecated "google-gla:..." output, which now disagrees with the intent of this PR. One assertion on the provider literal would anchor the rename.

# (Google AI Studio / Generative Language API). The internal "google-gla"
# name we use above is our own normalization layer (e.g. "gemini" → "google-gla");
# the rename only applies at the SDK boundary.
return GoogleModel(model_name, provider="google")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The rename was applied here only, which leaves the codebase's duplication worse than before this PR rather than fixing it.

gemini -> google-gla is already normalized in two places — factory.py:122 and config.py:90. This PR adds a third hop (google-gla -> "google") here in _create_provider_model only. build_model_string (config.py:93) still emits the deprecated "google-gla:<model>".

Verified against the resolved pydantic-ai==1.100.0:

  • "google" is accepted in both the provider= kwarg (Literal['google','google-cloud','gateway']) and the "provider:model" string form, with no warning.
  • "google-gla" is deprecated in both forms: "The 'google-gla:' prefix is deprecated and will be removed in v2.0. Use 'google:' instead."

So the fix should remove google-gla from our code entirely via a single normalize_provider() (mapping gemini/google-gla -> google) used by both the factory dispatch and build_model_string — not a second translation layered on top. As written, the next person updating the provider mapping updates one copy and misses the other, and the untouched build_model_string path becomes a hard break at pydantic-ai v2.0.

if api_key:
return GoogleModel(model_name, provider=GoogleProvider(api_key=api_key))
return GoogleModel(model_name, provider="google-gla")
# pydantic-ai 1.99 renamed the "google-gla" provider literal to "google"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wording: google-gla was deprecated, not renamed/removed — it still resolves in 1.100.0, just with a deprecation warning (removal is in v2.0). Since this comment is the stated justification for the change, it should be precise. It also cites 1.99 while the lockfile resolves 1.100.0.

Comment thread scripts/security-audit.sh
set -euo pipefail

IGNORED_VULNS="PYSEC-2026-89,PYSEC-2025-183"
IGNORED_VULNS="PYSEC-2026-89,PYSEC-2025-183,GHSA-cqp8-fcvh-x7r3"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This adds a CVE to the ignore list that this same PR fixes.

pyproject.toml here bumps pydantic-ai to >=1.99.0 and uv.lock resolves pydantic-ai 1.100.0 / fastmcp 3.3.1 — which is exactly the work the comment above (lines 28-37) describes as future work "that warrants its own dedicated migration PR." That comment now contradicts the diff.

This file's own convention (lines 38-41: protobuf, Pygments) is that once a bump resolves an advisory, it's removed from IGNORED_VULNS and demoted to the "Previously ignored, now resolved" note. Adding it instead permanently silences a now-fixed CVE in the scanner, masking any future regression if the pin is loosened below the fixed version.

Please remove GHSA-cqp8-fcvh-x7r3 from IGNORED_VULNS and move its note to the resolved section. (The "inapplicable / no force_download" rationale is accurate and worth keeping in that note — grep -rn 'force_download\|allow-local' src/ tests/ is 0 hits — but that justifies not panicking, not permanently ignoring a fixed CVE.)

Copy link
Copy Markdown
Collaborator

@KonstantinMirin KonstantinMirin left a comment

Choose a reason for hiding this comment

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

Requesting changes — two blocking issues (see inline comments on src/services/ai/factory.py:156 and scripts/security-audit.sh:50).

  1. The provider rename was copied into one spot, not consolidated. gemini -> google-gla is already normalized in two places (factory.py:122, config.py:90), and this PR adds a third hop (google-gla -> "google") in _create_provider_model only — build_model_string still emits the deprecated "google-gla:". Verified against pydantic-ai==1.100.0: "google" works in both the kwarg and string forms with no warning; "google-gla" is deprecated in both (removed in v2.0). The fix should route everything through one normalize_provider() and drop google-gla from our code, leaving the file with less duplication than before — not more.

  2. security-audit.sh suppresses a CVE this PR fixes. GHSA-cqp8-fcvh-x7r3 was added to IGNORED_VULNS even though the bump (pydantic-ai 1.100.0 / fastmcp 3.3.1 in uv.lock) resolves it — and the comment still describes that bump as future work "warranting its own PR." Per the file's own convention, a resolved advisory is removed from the ignore list and demoted to the history note. As written it permanently masks the fixed CVE.

The dependency bump and the mcp_server_enhanced.py deletion are correct. The common thread in the two issues above is that each edit reads fine in isolation but wasn't traced through the rest of the file — and nothing in the test suite anchors the change (the edited no-API-key branch at factory.py:156 is never exercised; test_gemini_provider still asserts the deprecated output).

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.

3 participants