# Variant Root Cause Analysis — CVE-2026-10129

## Summary

A distinct same-root-cause alternate trigger was found in Langflow's built-in `URL` data-source component (`lfx.components.data_source.url.URLComponent`). On the vulnerable `langflow==1.9.3` / `lfx==0.4.6` stack, `URLComponent` validates only the initial attacker-supplied URL and then delegates fetching to `RecursiveUrlLoader`, whose HTTP clients follow redirects without re-validating redirect destinations. A public URL that redirects to `127.0.0.1` therefore reaches a loopback-only marker service. This is a real alternate component entry point for the same redirect-SSRF class, but it is **not** a bypass of latest repository HEAD: the later fixed/latest `URLComponent` implementation blocks the redirect with per-hop SSRF revalidation.

## Fix Coverage / Assumptions

The original parent fix for the reproduced ticket is commit `44d43dbeaf9ce76e7ddf8371317232d07e3f6336` (`fix: re-validate redirects against SSRF protection in API Request component (#13394)`). It covers `src/lfx/src/lfx/components/data_source/api_request.py` by replacing automatic `httpx` redirect following with manual redirect handling that calls `validate_and_resolve_url()` for every `Location` hop.

The fix invariant is: if the API Request component follows a redirect, each redirected URL must pass the same SSRF denylist and DNS-pinning checks as the original URL. That invariant is sound for the parent `APIRequestComponent` path.

What the parent fix does **not** cover by itself is other components that accept attacker-controlled URLs and use their own HTTP clients. The tested gap is `URLComponent`, which in `lfx==0.4.6` uses `RecursiveUrlLoader` after only initial URL validation. Later repository commits separately fixed this component, especially `9a868386f2e35ad78041867b22e161665408a8d2` (`fix(components): follow HTTP redirects in URL component with per-hop SSRF revalidation (#13572)`). Latest HEAD tested here includes that URLComponent fix.

## Variant / Alternate Trigger

Tested alternate path:

- Entrypoint kind: `component_harness` representing Langflow flow execution of the `URL` data-source component.
- Component: `lfx.components.data_source.url.URLComponent`.
- Method: `fetch_url_contents()`.
- Attacker-controlled input: `urls=["https://postman-echo.com/redirect-to?url=http%3A%2F%2F127.0.0.1%3A9998%2Fmarker"]`.
- Internal target: loopback-only marker service on `127.0.0.1:9998/marker`.

Vulnerable code path:

1. `URLComponent.ensure_url()` validates the initial public URL.
2. `URLComponent._create_loader()` constructs `RecursiveUrlLoader`.
3. `URLComponent.fetch_url_contents()` calls `loader.load()`.
4. `RecursiveUrlLoader._get_child_links_recursive()` calls `requests.get(...)`, which follows redirects by default.
5. The redirect to `http://127.0.0.1:9998/marker` is fetched without Langflow re-validation.

Fixed/latest code path:

- `src/lfx/src/lfx/components/data_source/url.py` at HEAD `315cc41b43c446bfb0e63d420ca55021f6f8987e` contains `_fetch_with_revalidated_redirects()` and `_build_http_client()` and performs per-hop validation. The same redirect is blocked.

## Impact

- Package/component affected: `lfx` / Langflow `URL` data-source component.
- Affected version tested: installed reproduction stack `langflow==1.9.3`, `langflow-base==0.9.3`, `lfx==0.4.6`.
- Fixed/latest version tested: repository HEAD `315cc41b43c446bfb0e63d420ca55021f6f8987e`, by overlaying HEAD `url.py` and `ssrf_transport.py` into the repro venv without changing the repository checkout.
- Risk: high in affected deployments with untrusted or low-privileged flow authors. The component can reach loopback/private/link-local services despite SSRF protection, enabling internal data exposure and potential privilege escalation when internal credentials or admin APIs are reachable.

## Impact Parity

- Parent disclosed/claimed maximum impact: SSRF protection bypass chained to privilege escalation.
- Reproduced variant impact: internal loopback resource disclosure through a distinct component. The proof retrieves a loopback-only marker JSON containing `INTERNAL_ADMIN_TOKEN_CVE_2026_10129` through `URLComponent` output.
- Parity: `partial`. The same SSRF primitive is reproduced through a distinct component, but this variant script does not repeat the full admin-token promotion chain from the parent reproduction.
- Not demonstrated: full remote Langflow API flow creation/build for `URLComponent`, cloud metadata compromise, or final privilege escalation from this alternate component. The component harness is sufficient to prove the vulnerable server-side sink and fixed/latest blocking behavior.

## Root Cause

The root cause is the same redirect-validation error as the parent: SSRF validation was applied to only the original user-controlled URL while the actual HTTP client could follow a redirect to a new, unvalidated host.

For the parent, the downstream client was `httpx` inside `APIRequestComponent`. For the variant, the downstream client was `RecursiveUrlLoader` (`requests`/`aiohttp`) used by `URLComponent`. In both cases, the security boundary is crossed when an authenticated flow author supplies a URL to a server-side Langflow component while SSRF protection is expected to prevent loopback/internal network access.

Known fixes:

- Parent API Request fix: `44d43dbeaf9ce76e7ddf8371317232d07e3f6336`.
- URLComponent redirect fix: `9a868386f2e35ad78041867b22e161665408a8d2`.
- Latest tested HEAD: `315cc41b43c446bfb0e63d420ca55021f6f8987e`.

## Reproduction Steps

1. Run `bundle/vuln_variant/reproduction_steps.sh`.
2. The script:
   - Uses the successful repro virtualenv (`bundle/data/venv`) containing vulnerable `lfx==0.4.6`.
   - Starts a loopback-only marker service on `127.0.0.1:9998`.
   - Runs `bundle/vuln_variant/run_url_component.py` against vulnerable installed `URLComponent` with a public redirector URL.
   - Confirms the vulnerable component returns the loopback marker.
   - Creates an isolated Python overlay containing fixed/latest `URLComponent` copied from repository HEAD, without mutating the repro checkout.
   - Runs the same input against the fixed/latest overlay.
   - Confirms fixed/latest blocks the redirect and does not return the marker.
3. Expected script exit: `1`, because the variant is an alternate vulnerable-version trigger but not a HEAD bypass. The script ran twice successfully and produced this expected exit code both times.

## Evidence

Primary evidence:

- `bundle/logs/vuln_variant/reproduction_steps.log` — high-level run log and verdict.
- `bundle/logs/vuln_variant/url_component_vulnerable.json` — vulnerable URLComponent reached internal marker.
- `bundle/logs/vuln_variant/url_component_fixed_latest.json` — fixed/latest URLComponent blocked the same redirect.
- `bundle/vuln_variant/runtime_manifest.json` — runtime identity and evidence list.
- `bundle/logs/vuln_variant/latest_version.txt` and `bundle/logs/vuln_variant/latest_git_identity.txt` — exact fixed/latest source identity.

Key observed facts:

- Vulnerable log reports `variant_reached_internal=true`, `marker_in_output=true`, `lfx_version=0.4.6`, and `has_redirect_revalidation_method=false`.
- Fixed/latest log reports `variant_reached_internal=false`, `has_redirect_revalidation_method=true`, and an error blocking `http://127.0.0.1:9998/marker` because it resolves to `127.0.0.1`.
- Submitted vulnerable tag `v1.9.3` resolves to `322e4908b931a3efe2d65c6a7ad5ec373c5a5e1d`.
- Latest tested source resolves to `315cc41b43c446bfb0e63d420ca55021f6f8987e`.

## Recommendations / Next Steps

1. Do not ship only the API Request redirect fix as a product-wide SSRF fix. Include URLComponent redirect revalidation (`9a868386f2...`) or equivalent coverage.
2. Centralize safe HTTP helpers for all attacker-controlled URL inputs. Components should not call `requests.get`, `httpx` with automatic redirects, `aiohttp`, or third-party loaders on user URLs unless redirect handling re-enters Langflow SSRF validation per hop.
3. Add regression tests for every built-in component field that accepts a URL or base URL, using both direct internal URLs and public-to-internal redirects.
4. Prefer deny-by-default helper APIs that reject `follow_redirects=True` unless the helper owns per-hop validation.

## Additional Notes

- The target `SECURITY.md` was reviewed and does not exclude SSRF. The `RecursiveUrlLoader` documentation explicitly warns that crawlers can create SSRF risk; in Langflow this risk crosses a server-side trust boundary because flow authors control component inputs.
- The repository checkout was not changed. Fixed/latest testing used a temporary overlay under `bundle/vuln_variant/fixed_overlay`.
- Idempotency was confirmed: `bundle/vuln_variant/reproduction_steps.sh` was run twice; both runs completed and returned the expected non-bypass exit code (`1`).
