Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
MEDIUM severity

GHSA-4xgf-cpjx-pc3j

MEDIUM

GHSA-4xgf-cpjx-pc3j is a medium-severity (CVSS 5.3) Path Traversal vulnerability in pydantic-settings. O3 Security confirms whether GHSA-4xgf-cpjx-pc3j is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

pydantic-settings: NestedSecretsSettingsSource follows symlinks outside secrets_dir, enabling local file read and bypassing secrets_dir_max_size

Also known asCVE-2026-58203
Published
Jun 19, 2026
Updated
Sep 10, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 12, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-4xgf-cpjx-pc3j.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs8th percentile — riskier than 8% of all scored CVEsHighest risk
0.00%0.23%0.46%0.68%0.2%0.2%0.2%Aug 26Sep 26Sep 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-4xgf-cpjx-pc3j plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 0 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐍pydantic-settings

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

NestedSecretsSettingsSource reads secret values from files in a configured secrets_dir. When secrets_nested_subdir=True, a directory entry inside secrets_dir that is a symbolic link pointing outside secrets_dir is followed, so files outside the configured directory are read into settings values. The same code path bypasses the documented secrets_dir_max_size protection. An attacker or lower-privileged component able to influence entries in the configured secrets directory (for example, a writable or shared secrets mount) can turn this into an unintended local file read into settings and can defeat the advertised loading-size cap. This report does not claim network reachability by itself.

Details

NestedSecretsSettingsSource performed two passes over secrets_dir using two different, inconsistent directory-traversal implementations:

  • The size check in validate_secrets_path() used Path.glob('**/*'), which does not descend into a symbolically-linked directory.
  • The loader in load_secrets() used glob.iglob(f'{path}/**/*', recursive=True) followed by read_text(), which does follow symlinked directories and reads through the link target.

Because the two passes disagreed on symlinks, a symlinked directory inside secrets_dir whose target lives elsewhere was invisible to the size accounting (counted as 0 bytes) while still being fully read by the loader. This produces two distinct problems:

  1. Out-of-tree read (CWE-22 / CWE-59). A symlinked directory (or file) inside secrets_dir that resolves outside it is followed, and the external file's contents are loaded into the corresponding settings field.
  2. secrets_dir_max_size bypass (CWE-400). The size check never sees the out-of-tree content, so the documented size cap is neither respected nor able to reject the oversized external file. A related amplification exists for cyclic in-tree symlinks, which glob.iglob(recursive=True) re-traverses, inflating the size accounting and the number of loaded secrets.

Reproduction

In a clean Linux container, with a secrets_dir containing a symlink secrets/db -> /path/outside and an outside/passwd file of 512 bytes, while secrets_dir_max_size=100:

from pydantic import BaseModel
from pydantic_settings import (
    BaseSettings,
    SettingsConfigDict,
    NestedSecretsSettingsSource,
)


class Db(BaseModel):
    passwd: str | None = None


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        secrets_dir='secrets',
        secrets_nested_subdir=True,
        secrets_dir_max_size=100,  # outside/passwd is 512 bytes
    )
    db: Db = Db()

    @classmethod
    def settings_customise_sources(
        cls, settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings
    ):
        return (NestedSecretsSettingsSource(file_secret_settings),)

On affected versions, Settings().db.passwd is populated with the 512-byte out-of-tree file and no SettingsError is raised, even though the file exceeds secrets_dir_max_size.

Impact

Applications that opt into NestedSecretsSettingsSource with secrets_nested_subdir=True and load secrets from a directory whose entries can be influenced by an attacker or a lower-privileged component (for example, a writable or shared secrets mount, or a secrets directory partially populated from untrusted input) are affected. The impact is:

  • Confidentiality: files outside the configured secrets_dir can be read into settings values (local file read).
  • Integrity / availability of the safeguard: the advertised secrets_dir_max_size cap can be bypassed, and cyclic symlinks can inflate resource usage during loading.

The vulnerability requires the ability to place a symbolic link inside the configured secrets directory; it is not remotely reachable on its own. Applications that do not use NestedSecretsSettingsSource, or that point secrets_dir at a directory fully under the application's control, are not affected.

Mitigation

Upgrade to pydantic-settings 2.14.2, which:

  • walks the secrets directory explicitly and only descends into directories whose resolved path stays within secrets_dir, so symlinked directories pointing outside are never followed;
  • uses a single, cycle-safe iterator for both the size check and the loader, so the size accounting and the loaded set are always consistent and each real directory is visited at most once;
  • skips any file whose resolved path escapes secrets_dir, as defense in depth.

If upgrading is not immediately possible, ensure the configured secrets_dir is fully owned and controlled by the application (no writable or attacker-influenced entries), or avoid secrets_nested_subdir=True.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIpydantic-settings2.12.0&&< 2.14.22.14.2

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for pydantic-settings. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update pydantic-settings to 2.14.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-4xgf-cpjx-pc3j is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-4xgf-cpjx-pc3j is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-4xgf-cpjx-pc3j. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary `NestedSecretsSettingsSource` reads secret values from files in a configured `secrets_dir`. When `secrets_nested_subdir=True`, a directory entry inside `secrets_dir` that is a symbolic link pointing **outside** `secrets_dir` is followed, so files outside the configured directory are read into settings values. The same code path bypasses the documented `secrets_dir_max_size` protection. An attacker or lower-privileged component able to influence entries in the configured secrets directory (for example, a writable or shared secrets mount) can turn this into an unintended local file
O3 Security · Impact-Aware SCA

Is GHSA-4xgf-cpjx-pc3j in your dependencies?

O3 detects GHSA-4xgf-cpjx-pc3j across PyPI dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.

GHSA-4xgf-cpjx-pc3j: pydantic (Medium 5.3) | O3 Security