fix: protect URL components from SSRF#13643
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR adds comprehensive SSRF (Server-Side Request Forgery) protection to Langflow's outbound HTTP requests. The implementation includes DNS-pinning transport for both sync and async HTTP clients, SSRF-safe request wrappers, and migration of 10+ LLM and API components to use protected helpers instead of ChangesSSRF Protection Implementation and Component Migration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 8 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (8 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
There was a problem hiding this comment.
🧹 Nitpick comments (9)
src/lfx/src/lfx/utils/ssrf_httpx.py (1)
78-86: 📐 Maintainability & Code Quality | 💤 Low valueDocument caller responsibility for closing returned clients.
ssrf_protected_openai_clients_for_urlreturnshttpx.Clientandhttpx.AsyncClientinstances that are not context-managed here. The caller (typically LangChain/OpenAI SDK components) is responsible for their lifecycle. Consider adding a docstring note clarifying that these clients should be closed when no longer needed, or that they're intended to be passed to SDK classes that manage their lifecycle.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lfx/src/lfx/utils/ssrf_httpx.py` around lines 78 - 86, Update the docstring for ssrf_protected_openai_clients_for_url to explicitly state that it returns new httpx.Client and httpx.AsyncClient instances which are not context-managed by this function and therefore the caller is responsible for closing them (or passing them to SDK/LangChain components that will manage their lifecycle); mention the expected usage pattern (pass to OpenAI/LangChain constructors or close() / aclose() when done) so callers know to manage the clients' lifecycle.src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.py (1)
923-925: 📐 Maintainability & Code Quality | 💤 Low valueConsider moving the fixture to the top of the class for readability.
The
autousefixture works correctly regardless of position, but placing it near the other fixtures (lines 24-42) would improve discoverability and follow the common convention of grouping fixtures together.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.py` around lines 923 - 925, Move the autouse fixture disable_ssrf_protection into the top of the test class so it sits alongside the other fixtures for that class (near where the other fixtures are declared) to improve readability and discoverability; keep the monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED", "false") behavior and retain the `@pytest.fixture`(autouse=True) decorator and the same function name disable_ssrf_protection and signature so tests continue to run unchanged.src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py (2)
242-244: 📐 Maintainability & Code Quality | 💤 Low valueConsider moving the fixture to the top of the class for readability.
Same pattern as other test files - placing this fixture near the other fixtures would improve discoverability.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py` around lines 242 - 244, Move the autouse fixture disable_ssrf_protection to the top of the test class (near the other fixtures) so it’s easier to discover; specifically relocate the pytest.fixture(autouse=True) def disable_ssrf_protection(self, monkeypatch): monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED", "false") block to immediately follow the class declaration or the existing fixtures in the class so it appears with the other setup fixtures.
84-94: 📐 Maintainability & Code Quality | ⚡ Quick winTest assertion doesn't verify SSRF client kwargs are passed to ChatOpenAI.
The component now passes
**ssrf_client_kwargstoChatOpenAI(line 90 in the component), but this test's assertion on lines 84-93 doesn't account for those additional kwargs. When SSRF protection is disabled,ssrf_protected_openai_clients_for_urllikely returns an empty dict, so the test passes. However, this means the test doesn't verify the SSRF integration path.Consider adding a separate test case that enables SSRF protection and verifies the client kwargs are correctly passed through.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py` around lines 84 - 94, The current test only asserts ChatOpenAI was called without SSRF kwargs; add a new unit test that enables SSRF protection (mock ssrf_protected_openai_clients_for_url to return a non-empty dict) and ensure the component instantiates ChatOpenAI with those extra kwargs: assert mock_chat_openai.assert_called_once_with(...) includes the returned ssrf_client_kwargs along with existing params (base_url, api_key, model, temperature, max_tokens, timeout, max_retries, streaming) and that the function under test (the Litellm proxy constructor/creator that calls ChatOpenAI) returns the mock instance; locate the call site by looking for ChatOpenAI and ssrf_protected_openai_clients_for_url in test_litellm_proxy.py and mirror the existing test pattern but with SSRF enabled.src/backend/tests/unit/components/languagemodels/test_chatollama_component.py (1)
1264-1266: 📐 Maintainability & Code Quality | 💤 Low valueConsider moving the fixture to the top of the class for readability.
Same as the Ollama embeddings test file - placing this fixture near the other fixtures (lines 14-50) would improve discoverability.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/languagemodels/test_chatollama_component.py` around lines 1264 - 1266, Move the autouse fixture disable_ssrf_protection into the test class's fixture section so it's colocated with the other fixtures for readability; specifically, relocate the def disable_ssrf_protection(self, monkeypatch) fixture (which sets LANGFLOW_SSRF_PROTECTION_ENABLED to "false") up into the top of the class where the other fixtures are declared so it's easy to find and consistent with the Ollama embeddings tests.src/lfx/src/lfx/components/ollama/ollama_embeddings.py (2)
152-168: 📐 Maintainability & Code Quality | 💤 Low valueUnnecessary coroutine checks on synchronous
httpx.Response.json().
httpx.Response.json()is a synchronous method that returns the parsed dictionary directly, not a coroutine. Theasyncio.iscoroutine()checks on lines 153-154 and 167-168 are unnecessary.♻️ Proposed simplification
tags_response = await ssrf_safe_async_get(tags_url, headers=headers) tags_response.raise_for_status() models = tags_response.json() - if asyncio.iscoroutine(models): - models = await models await logger.adebug(f"Available models: {models}") # ... show_response = await ssrf_safe_async_post(show_url, json=payload, headers=headers) show_response.raise_for_status() json_data = show_response.json() - if asyncio.iscoroutine(json_data): - json_data = await json_data🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lfx/src/lfx/components/ollama/ollama_embeddings.py` around lines 152 - 168, Remove the unnecessary asyncio.iscoroutine checks and accompanying awaits around httpx Response.json() calls in ollama_embeddings.py: eliminate the iscoroutine checks for models = tags_response.json() and json_data = show_response.json() and do not await their results (they are synchronous), leaving models and json_data as the direct parsed dicts; update any logging/flow that assumed awaiting (references: variables/models and show_response.json() usage in the model filtering loop inside the Ollama embeddings logic).
77-112: 📐 Maintainability & Code QualityOllama embeddings: drop redundant SSRF-validation suggestion; minor cleanup for
asyncio.iscoroutine(response.json())
- The SSRF concern isn’t applicable:
ssrf_protected_httpx_client_kwargs_for_url()already validates viavalidate_and_resolve_url()and convertsSSRFProtectionErrorinto aValueErrorwith the"SSRF Protection: ..."prefix.asyncio.iscoroutine()checks aroundtags_response.json()/show_response.json()are unnecessary (httpxResponse.json()is synchronous); simplifying would improve clarity.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lfx/src/lfx/components/ollama/ollama_embeddings.py` around lines 77 - 112, Remove the redundant SSRF-validation guidance and unnecessary coroutine checks: rely on ssrf_protected_httpx_client_kwargs_for_url (which uses validate_and_resolve_url and already raises ValueError for SSRF) instead of suggesting extra SSRF handling, and simplify the response JSON handling by deleting any asyncio.iscoroutine(...) guards around tags_response.json() and show_response.json() and calling Response.json() directly (httpx Response.json() is synchronous); update related error/exception paths accordingly in build_embeddings and any helper functions that currently include those checks.src/backend/tests/unit/components/test_ssrf_guarded_url_components.py (1)
26-27: 📐 Maintainability & Code Quality | 💤 Low valueUnnecessary
@pytest.mark.asynciodecorator.Per project configuration (
asyncio_mode = 'auto'in pyproject.toml), async tests are auto-detected. The decorator can be removed.Based on learnings: "pytest-asyncio is configured with asyncio_mode = 'auto' in pyproject.toml. This means you do not need to decorate test functions or classes with pytest.mark.asyncio."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/test_ssrf_guarded_url_components.py` around lines 26 - 27, Remove the unnecessary pytest.mark.asyncio decorator above the async test function test_lmstudio_model_update_blocks_metadata_url_before_httpx(); because asyncio_mode is set to 'auto' the test framework will auto-detect async tests, so delete the line with `@pytest.mark.asyncio` and leave the async def unchanged.Source: Learnings
src/backend/tests/unit/components/languagemodels/test_deepseek.py (1)
115-118: 📐 Maintainability & Code Quality | 💤 Low valueMove fixture before test functions for better readability.
The
disable_ssrf_protectionfixture is defined after all test functions. While pytest collects fixtures regardless of position, placing fixtures at the top of the file (after imports) is the conventional pattern and improves readability.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/components/languagemodels/test_deepseek.py` around lines 115 - 118, Move the autouse fixture definition disable_ssrf_protection so it appears immediately after the module imports and before any test_... functions in test_deepseek.py; locate the monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED", "false") line inside the disable_ssrf_protection fixture and cut/paste the entire `@pytest.fixture`(autouse=True) def disable_ssrf_protection(...) block to the top of the file (after imports) to follow pytest conventions and improve readability while keeping its behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.py`:
- Around line 923-925: Move the autouse fixture disable_ssrf_protection into the
top of the test class so it sits alongside the other fixtures for that class
(near where the other fixtures are declared) to improve readability and
discoverability; keep the monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED",
"false") behavior and retain the `@pytest.fixture`(autouse=True) decorator and the
same function name disable_ssrf_protection and signature so tests continue to
run unchanged.
In
`@src/backend/tests/unit/components/languagemodels/test_chatollama_component.py`:
- Around line 1264-1266: Move the autouse fixture disable_ssrf_protection into
the test class's fixture section so it's colocated with the other fixtures for
readability; specifically, relocate the def disable_ssrf_protection(self,
monkeypatch) fixture (which sets LANGFLOW_SSRF_PROTECTION_ENABLED to "false") up
into the top of the class where the other fixtures are declared so it's easy to
find and consistent with the Ollama embeddings tests.
In `@src/backend/tests/unit/components/languagemodels/test_deepseek.py`:
- Around line 115-118: Move the autouse fixture definition
disable_ssrf_protection so it appears immediately after the module imports and
before any test_... functions in test_deepseek.py; locate the
monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED", "false") line inside the
disable_ssrf_protection fixture and cut/paste the entire
`@pytest.fixture`(autouse=True) def disable_ssrf_protection(...) block to the top
of the file (after imports) to follow pytest conventions and improve readability
while keeping its behavior unchanged.
In `@src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py`:
- Around line 242-244: Move the autouse fixture disable_ssrf_protection to the
top of the test class (near the other fixtures) so it’s easier to discover;
specifically relocate the pytest.fixture(autouse=True) def
disable_ssrf_protection(self, monkeypatch):
monkeypatch.setenv("LANGFLOW_SSRF_PROTECTION_ENABLED", "false") block to
immediately follow the class declaration or the existing fixtures in the class
so it appears with the other setup fixtures.
- Around line 84-94: The current test only asserts ChatOpenAI was called without
SSRF kwargs; add a new unit test that enables SSRF protection (mock
ssrf_protected_openai_clients_for_url to return a non-empty dict) and ensure the
component instantiates ChatOpenAI with those extra kwargs: assert
mock_chat_openai.assert_called_once_with(...) includes the returned
ssrf_client_kwargs along with existing params (base_url, api_key, model,
temperature, max_tokens, timeout, max_retries, streaming) and that the function
under test (the Litellm proxy constructor/creator that calls ChatOpenAI) returns
the mock instance; locate the call site by looking for ChatOpenAI and
ssrf_protected_openai_clients_for_url in test_litellm_proxy.py and mirror the
existing test pattern but with SSRF enabled.
In `@src/backend/tests/unit/components/test_ssrf_guarded_url_components.py`:
- Around line 26-27: Remove the unnecessary pytest.mark.asyncio decorator above
the async test function
test_lmstudio_model_update_blocks_metadata_url_before_httpx(); because
asyncio_mode is set to 'auto' the test framework will auto-detect async tests,
so delete the line with `@pytest.mark.asyncio` and leave the async def unchanged.
In `@src/lfx/src/lfx/components/ollama/ollama_embeddings.py`:
- Around line 152-168: Remove the unnecessary asyncio.iscoroutine checks and
accompanying awaits around httpx Response.json() calls in ollama_embeddings.py:
eliminate the iscoroutine checks for models = tags_response.json() and json_data
= show_response.json() and do not await their results (they are synchronous),
leaving models and json_data as the direct parsed dicts; update any logging/flow
that assumed awaiting (references: variables/models and show_response.json()
usage in the model filtering loop inside the Ollama embeddings logic).
- Around line 77-112: Remove the redundant SSRF-validation guidance and
unnecessary coroutine checks: rely on ssrf_protected_httpx_client_kwargs_for_url
(which uses validate_and_resolve_url and already raises ValueError for SSRF)
instead of suggesting extra SSRF handling, and simplify the response JSON
handling by deleting any asyncio.iscoroutine(...) guards around
tags_response.json() and show_response.json() and calling Response.json()
directly (httpx Response.json() is synchronous); update related error/exception
paths accordingly in build_embeddings and any helper functions that currently
include those checks.
In `@src/lfx/src/lfx/utils/ssrf_httpx.py`:
- Around line 78-86: Update the docstring for
ssrf_protected_openai_clients_for_url to explicitly state that it returns new
httpx.Client and httpx.AsyncClient instances which are not context-managed by
this function and therefore the caller is responsible for closing them (or
passing them to SDK/LangChain components that will manage their lifecycle);
mention the expected usage pattern (pass to OpenAI/LangChain constructors or
close() / aclose() when done) so callers know to manage the clients' lifecycle.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ac8ee590-66f9-4988-827f-3d4fbedd730a
📒 Files selected for processing (20)
src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.pysrc/backend/tests/unit/components/languagemodels/test_chatollama_component.pysrc/backend/tests/unit/components/languagemodels/test_deepseek.pysrc/backend/tests/unit/components/languagemodels/test_litellm_proxy.pysrc/backend/tests/unit/components/test_ssrf_guarded_url_components.pysrc/lfx/src/lfx/_assets/component_index.jsonsrc/lfx/src/lfx/components/deepseek/deepseek.pysrc/lfx/src/lfx/components/glean/glean_search_api.pysrc/lfx/src/lfx/components/homeassistant/home_assistant_control.pysrc/lfx/src/lfx/components/homeassistant/list_home_assistant_states.pysrc/lfx/src/lfx/components/huggingface/huggingface_inference_api.pysrc/lfx/src/lfx/components/litellm/litellm_proxy.pysrc/lfx/src/lfx/components/lmstudio/lmstudioembeddings.pysrc/lfx/src/lfx/components/lmstudio/lmstudiomodel.pysrc/lfx/src/lfx/components/ollama/ollama.pysrc/lfx/src/lfx/components/ollama/ollama_embeddings.pysrc/lfx/src/lfx/components/xai/xai.pysrc/lfx/src/lfx/utils/ssrf_httpx.pysrc/lfx/src/lfx/utils/ssrf_transport.pysrc/lfx/tests/unit/utils/test_ssrf_httpx.py
Codecov Report❌ Patch coverage is
❌ Your project check has failed because the head coverage (54.32%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## release-1.10.1 #13643 +/- ##
==================================================
- Coverage 58.54% 58.27% -0.28%
==================================================
Files 2302 2303 +1
Lines 219191 219248 +57
Branches 31131 32890 +1759
==================================================
- Hits 128322 127758 -564
- Misses 89411 90022 +611
- Partials 1458 1468 +10
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Cristhianzl
left a comment
There was a problem hiding this comment.
⚠️ Important (preferably this PR)
I1 — HuggingFace inference path is validation-only (DNS-rebinding window stays open)
File: src/lfx/src/lfx/components/huggingface/huggingface_inference_api.py:86 (build_embeddings)
Issue: build_embeddings calls validate_url_for_ssrf_or_raise(api_url) and then constructs HuggingFaceInferenceAPIEmbeddings, whose actual inference HTTP calls go through the langchain_community SDK client — which is not given the pinned transport. The OpenAI-compatible components (Ollama, LiteLLM, xAI, DeepSeek, LMStudio) inject the pinned client via http_client / sync_client_kwargs / async_client_kwargs, so their real request is pinned. The HF inference path is validated once, then the SDK re-resolves DNS on its own request.
Why it matters: validate_and_resolve_url's docstring explicitly sells DNS-rebinding prevention as the reason DNS pinning exists. For HF, an attacker-controlled domain with TTL=0 (public at validation, internal at the SDK's request) is not stopped on the inference path. Direct internal IPs/hostnames are still blocked at validation, so this is a residual rebinding gap, not a full bypass.
Suggested fix: If HuggingFaceInferenceAPIEmbeddings accepts a custom httpx client, pass a pinned one via ssrf_protected_openai_clients_for_url / ssrf_protected_httpx_client_kwargs_for_url. If it does not, document the residual rebinding risk for SDK-only components in a Why: comment at the validation site and track a follow-up — so the next reader knows the gap is known, not overlooked.
Code reference
def build_embeddings(self) -> Embeddings:
api_url = self.get_api_url()
validate_url_for_ssrf_or_raise(api_url) # point-in-time only; SDK call below is not pinned
...I2 — Inconsistent error contract for the same SSRF event
Files: src/lfx/src/lfx/components/glean/glean_search_api.py:85, src/lfx/src/lfx/components/homeassistant/home_assistant_control.py:137, src/lfx/src/lfx/components/ollama/ollama.py:330
Issue: The same blocked-URL event surfaces three different ways: Glean lets SSRFProtectionError propagate raw; HomeAssistant catches it and returns a "Error: SSRF Protection: {e}" string; the model components wrap it into ValueError("SSRF Protection: ..."). The test suite encodes all three (pytest.raises(SSRFProtectionError) for Glean, string-match for HomeAssistant, pytest.raises(ValueError, match="SSRF Protection") elsewhere).
Why it matters: A caller (or the UI) cannot rely on one error type/shape to detect an SSRF block. Tool components returning a string means the failure is in-band with normal output and easy to miss programmatically.
Suggested fix: Standardize per component category: tool components return a structured error consistently, model/embedding components raise ValueError("SSRF Protection: …") consistently, and avoid leaking the raw SSRFProtectionError to callers (Glean). Document the chosen contract once near the helper.
💡 Recommended (can ship as a follow-up)
R1 — Per-call client + re-resolution inside loops
File: src/lfx/src/lfx/components/ollama/ollama.py:423 (get_models), src/lfx/src/lfx/utils/ssrf_httpx.py:54
Issue: ssrf_safe_async_get/post each validate, resolve DNS, and build a fresh one-shot client per call. In get_models, ssrf_safe_async_post(show_url, …) runs once per model in a loop, so N model entries = N validations + N DNS resolutions + N clients.
Why it matters: Extra latency and connection churn on a hot path; the same host is re-resolved repeatedly. Not a correctness/security problem (re-validation is safe), purely efficiency.
Suggested fix: For loop call sites, resolve/validate once and reuse a single pinned client (create_ssrf_protected_client) for the batch of requests to the same host.
R2 — Document the unpinned fallback path
File: src/lfx/src/lfx/utils/ssrf_httpx.py:34-46 (_async_client_for_url / _sync_client_for_url)
Issue: When protection is enabled but validated_ips is empty (an allowlisted host such as localhost for Ollama), these return a plain unpinned httpx.Client()/AsyncClient(). This is intended — allowlisted hosts don't need pinning — but it isn't obvious that the only way to reach the fallback with protection enabled is an allowlist hit.
Why it matters: A future change to validate_and_resolve_url that returns empty IPs in a non-allowlist case would silently downgrade to an unpinned request.
Suggested fix: Add a one-line Why: comment stating the empty-IP branch is the allowlist case, or assert the host is allowlisted before returning the plain client.
R3 — Component tests cover only the block path
File: src/backend/tests/unit/components/test_ssrf_guarded_url_components.py
Issue: Every component test uses BLOCKED_URL (metadata IP) and asserts the call is gated. There is no component-level test that a valid public URL is allowed through with the pinned transport actually applied.
Why it matters: If a helper regressed to a no-op (e.g. returned {} kwargs unconditionally), the block-only tests would still pass while protection silently disappeared on the allow path.
Suggested fix: Add one allow-path test per integration style (one OpenAI-compatible, one ssrf_safe_*) asserting a public URL succeeds and the pinned transport / kwargs are present on the constructed client.
🟢 Nice-to-have
N1 — Mock usage here is correct; call it out so it isn't flagged
Files: src/lfx/tests/unit/utils/test_ssrf_httpx.py, src/backend/tests/unit/components/test_ssrf_guarded_url_components.py
Langflow prefers real integrations, but DNS rebinding and "never reached the network" assertions cannot be reproduced with a real sandbox — this is the documented exception. The patch("socket.getaddrinfo") + assert_not_called() approach is the right tool. No change needed; a one-line note in the PR description that the mocks are the sanctioned exception would pre-empt a reviewer objection.
N2 — component_index.json regenerated
File: src/lfx/src/lfx/_assets/component_index.json
Same note as the Smart Router PR: confirm the committed hashes equal a clean index rebuild and that CI's sha256 integrity check covers the file. The .secrets.baseline churn (line-number + generated_at) is benign — no new secret introduced.
Summary
httpxhelpers plus sync DNS pinning transport support for SDK clients.Validation
uv run ruff check src/lfx/src/lfx/utils/ssrf_httpx.py src/lfx/src/lfx/utils/ssrf_transport.py src/lfx/src/lfx/components/deepseek/deepseek.py src/lfx/src/lfx/components/glean/glean_search_api.py src/lfx/src/lfx/components/homeassistant/home_assistant_control.py src/lfx/src/lfx/components/homeassistant/list_home_assistant_states.py src/lfx/src/lfx/components/huggingface/huggingface_inference_api.py src/lfx/src/lfx/components/litellm/litellm_proxy.py src/lfx/src/lfx/components/lmstudio/lmstudioembeddings.py src/lfx/src/lfx/components/lmstudio/lmstudiomodel.py src/lfx/src/lfx/components/ollama/ollama.py src/lfx/src/lfx/components/ollama/ollama_embeddings.py src/lfx/src/lfx/components/xai/xai.py src/backend/tests/unit/components/test_ssrf_guarded_url_components.py src/lfx/tests/unit/utils/test_ssrf_httpx.py src/backend/tests/unit/components/languagemodels/test_deepseek.py src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py src/backend/tests/unit/components/languagemodels/test_chatollama_component.py src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.pyuv run pytest src/backend/tests/unit/components/test_ssrf_guarded_url_components.py src/backend/tests/unit/components/languagemodels/test_deepseek.py src/backend/tests/unit/components/languagemodels/test_litellm_proxy.py src/backend/tests/unit/components/languagemodels/test_chatollama_component.py src/backend/tests/unit/components/embeddings/test_ollama_embeddings_component.pyuv run pytest tests/unit/utils/test_ssrf_httpx.pyfromsrc/lfxafter isolateduv syncNotes
LANGFLOW_SSRF_ALLOWED_HOSTSpath when SSRF protection is enabled.Summary by CodeRabbit
Bug Fixes
Tests