* fix: pin postgres image to bookworm in docker_example to prevent collation mismatch
The postgres:16 tag silently moved its base from Debian Bookworm
(glibc 2.36) to Trixie (glibc 2.41), causing a recurring collation
version mismatch warning on existing langflow-postgres volumes.
Pin to postgres:16-bookworm in both docker-compose files and update
the README so existing data volumes keep matching the OS locale data.
Refs: https://github.com/langflow-ai/langflow/issues/9608
* docs: pin postgres image to bookworm in current docs compose snippets
Mirror the docker_example pin in the four current docs that publish
copyable Compose snippets pairing postgres:16 with a persistent
langflow-postgres volume. Prevents the same Bookworm-to-Trixie
collation version mismatch warning when users follow docs instead
of docker_example.
Versioned historical docs are left as-is.
Refs: https://github.com/langflow-ai/langflow/issues/9608
* fix: switch postgres pin from bookworm to trixie for OS consistency
Aligns the pinned postgres base with the langflow runtime image, which
moved to Debian Trixie in #12990. Keeping postgres on bookworm would
have diverged the stack and locked the database to an aging glibc that
will receive fewer security backports as bookworm ages into oldstable.
The pin itself still solves the original bug — postgres:16 cannot
silently roll its OS underneath an existing volume.
Document the one-time REFRESH COLLATION VERSION step for users
upgrading from a bookworm-initialized volume in docker_example/README.md.
Refs: https://github.com/langflow-ai/langflow/issues/9608
(cherry picked from commit 7504eb4c72)
* fix: stabilize model toggle and refresh Agent dropdown after provider changes
Two related regressions in the Model Provider management flow on
release-1.9.3:
1. Toggle bounce — rapid clicks on a model Switch flickered between on
and off before settling. ``handleModelToggle`` performed an
optimistic ``setQueryData`` on ``useGetEnabledModels`` but never
cancelled in-flight refetches. With React Query defaults
(``staleTime: 0``, ``refetchOnWindowFocus: true``), a background
refetch could land mid-debounce and overwrite the optimistic state
with stale server data, then the deferred mutation eventually
corrected it. Fix: ``cancelQueries`` before each optimistic update,
per the canonical TanStack Query optimistic-updates pattern.
2. Agent dropdown stayed stale after the provider modal closed —
disabled models remained listed in the Agent's Language Model
dropdown. The post-close ``isRefreshingAfterClose`` loading gate
only waited for ``useGetModelProviders`` to settle, not
``useGetEnabledModels``. When the providers refetch finished first,
the loading state cleared and ``groupedOptions`` ran against the
still-stale enabled-models cache. Fix: track both queries'
``isFetching`` flags in the gate's effect.
No behavioral change to the sticky-default UX — the user's previously
selected model continues to show in the dropdown with the wrench
affordance when globally disabled, as designed.
Also narrows the pre-existing ``catch (error: any)`` patterns in the
edited hook file to ``unknown`` with a small shared ``getErrorMessage``
helper, satisfying the staged-file no-explicit-any pre-commit lint.
Tests:
- New ``useProviderConfiguration`` unit test asserts
``cancelQueries`` runs before ``setQueryData`` on toggle.
- Extended ``ModelInputComponent`` test covers the dual-query loading
gate (providers settles first, enabled-models still in flight →
loading persists; then enabled-models settles → loading clears).
* fix: re-apply pending overlay through entire mutation lifecycle
Addresses review feedback on #13113: ``cancelQueries`` only cancels
refetches that are already in flight at click time. A new
``useGetEnabledModels`` refetch can still start during the 1s
debounce window (or while the mutation is in flight) and overwrite
the optimistic cache with stale server state, producing the same
bounce we just fixed.
Two coordinated changes keep the optimistic state protected for the
entire pending-toggle window:
1. ``pendingModelToggles`` is no longer cleared upfront when the
debounced flush sends a mutation. Instead, ``clearSentToggles``
removes only the entries whose value still matches what we sent
on ``onSettled`` / ``onError``. This preserves the overlay across
the in-flight mutation window AND correctly handles the case
where the user re-toggled the same model mid-flight (now a fresh
intent that survives clearing).
2. A new ``useEffect`` subscribes to ``useGetEnabledModels`` and
re-applies the pending overlay whenever the data emission drifts
from the user's pending intent. This catches any refetch
(window focus, mount, reconnect, stale-time expiry) that lands
between click and ``onSettled``, regardless of when it started.
Tests:
- New ``re-applies the pending overlay when a refetch surfaces
stale data`` test simulates a stale refetch and asserts the
effect re-applies the optimistic overlay.
- New ``does not re-overlay when no toggles are pending`` test
guards against spurious overlay calls on mount.
* fix: split overlay vs unsent buffers to prevent duplicate model toggle mutations
Addresses a follow-up race introduced by the previous commit: keeping
``pendingModelToggles`` populated through ``onSettled`` (so the
re-overlay effect could repel mid-flight refetches) also caused the
next flush to snapshot the in-flight entries again, sending duplicate
requests with non-deterministic success/failure ordering. The same
risk applied to ``flushPendingChanges`` on modal close.
Split the single buffer into two refs with single responsibilities:
- ``overlayToggles``: the union of every toggle still protecting the
UI. The re-overlay effect re-applies this whenever
``useGetEnabledModels`` emits new data. Drained per-key on
``onSettled``/``onError`` only when the overlay value still matches
what we sent (a user re-toggle mid-flight becomes a fresh intent
and must survive the clear).
- ``unsentToggles``: the strict subset that has NOT been sent in a
mutation yet, or was re-toggled since the last send. Drained
immediately at flush time so subsequent flushes never resend an
in-flight payload.
``handleModelToggle`` populates both buffers; flushes drain only
``unsentToggles`` and use ``overlayToggles`` for cache protection.
Two new unit tests:
- ``does not resend in-flight toggles when a new toggle is flushed``
asserts that after toggling A then B (with A's mutation in flight),
the second flush carries ONLY B.
- ``re-sends a model when the user re-toggles it after the previous
flush fired`` asserts that re-toggling the same model produces a
second mutation (the re-toggle is a fresh intent, not a duplicate).
* fix: drop sticky-default UX, treat disabled-selected model like any other
Removes the sticky-default carve-out so a globally-disabled model is
never shown in the Agent's Language Model dropdown, regardless of
whether it's the node's saved selection. Drops the Wrench/Configure
affordance — there's no per-trigger "this model isn't enabled for
your user" UX anymore; the dropdown is the single source of truth.
Changes in ``modelInputComponent``:
- ``groupedOptions``: drop the ``isStickyNotEnabled`` filter
bypass and the zero-provider import fallback. Disabled models
never pass the filter, even when tagged ``not_enabled_locally``.
- ``selectedModel``: drop the saved-value preservation branch.
If the saved name isn't in ``flatOptions``, fall through to the
first available option (or ``null`` if nothing is available).
- Auto-select effect: fires whenever the saved value isn't in
``flatOptions``, not just when the value is empty. This
realigns the node's stored value with the trigger so the
rendered selection and the run-time selection don't diverge.
- ``hasEnabledProviders``: redefined as "at least one model of
this component's type is enabled across any provider"
(derived from ``groupedOptions``). Routes a configured-but-
all-disabled provider to the Setup Provider CTA — same UX as
the never-configured case.
- Remove the Wrench JSX, the ``showConfigureAffordance``
derivation, and the now-orphaned ``hasProcessedEmptyRef``.
Tests:
- ``renders the Setup Provider CTA when no models are enabled``
(replaces the old disabled-combobox expectation).
- ``auto-selects an available model when the saved value is
globally disabled``.
- ``renders the Setup Provider CTA when a provider is configured
but all its models are disabled``.
- ``never renders the Configure wrench affordance``.
* refactor: extract toggle queue into useModelToggleQueue hook
Addresses review I1 (file size + mixed responsibilities) and R1, R2,
R3, R4 in one pass.
I1 — Extract toggle-queue logic out of useProviderConfiguration. The
parent hook drops from 821 lines to 601 and now owns a single
responsibility (variable CRUD + provider lifecycle). The toggle queue
— overlay/unsent buffers, debounced flush, awaitable flush, optimistic
cache management, re-overlay effect — lives in a new 292-line
useModelToggleQueue.ts with one clear responsibility.
R1 — Inside the new hook, DRY the two flush variants behind shared
helpers:
- ``buildAndConsumeToggleBatch()`` snapshots unsentToggles into the
mutation payload, captures the pre-toggle cache for rollback, and
drains unsentToggles atomically.
- ``rollbackToggleBatch()`` drains overlay BEFORE restoring
previousData (load-bearing order — otherwise the re-overlay effect
would re-apply the stale overlay over the rollback).
The two flush callers now differ only in ``mutate`` vs awaitable
``mutateAsync`` plumbing.
R2 — Explicit loop-guard comment on the drift check inside the
re-overlay effect. Names the ``drifted`` short-circuit as the
termination condition so a future refactor that "simplifies" to an
unconditional re-apply doesn't silently introduce a render loop.
R3 — Adversarial coverage for the mutation error path:
- ``rolls back to previousData when the toggle mutation fails``
- ``does not re-apply the overlay after a failed mutation drains it``
(asserts the drain-before-rollback ordering)
- ``preserves a mid-flight re-toggle when the original mutation
fails`` (asserts a user re-toggle survives the originating
mutation's failure)
R4 — ``flushPendingChanges`` now invalidates the affected queries
inline on success, instead of relying on the caller's
``refreshAllModelInputs`` for the load-bearing invalidation. The
caller's refresh remains as an additive per-node template refresh.
Test infra: moved toggle-queue tests from
``useProviderConfiguration.test.tsx`` to a dedicated
``useModelToggleQueue.test.tsx`` that exercises the extracted hook
directly. Debounce mock changed from synchronous pass-through to
explicit ``runDebounced()`` so individual tests can choose whether to
exercise the debounced path or the awaitable ``flushPendingChanges``
path.
95/95 tests pass (87 prior + new R3 coverage + R4 success-path
invalidation).
* feat: Add SSRF protection with DNS rebinding prevention
- Implement DNS pinning to prevent DNS rebinding attacks
- Enable SSRF protection by default (ssrf_protection_enabled = True)
- Add validate_and_resolve_url() function for DNS pinning
- Create SSRFProtectedTransport for custom HTTP transport
- Add comprehensive test suite for DNS rebinding protection
- Change from warn_only=True to warn_only=False for enforcement
This fixes a HIGH severity SSRF vulnerability (CVSS 8.6) that could allow
attackers to bypass SSRF protection using DNS rebinding attacks to access:
- Internal services and private networks
- Cloud metadata endpoints (AWS, GCP, Azure)
- Localhost services
The DNS pinning implementation ensures that the IP address validated during
security checks is the same IP used for the actual HTTP request, eliminating
the TOCTOU (Time-of-Check to Time-of-Use) vulnerability.
Changes:
- src/lfx/src/lfx/components/data_source/api_request.py: Integrate DNS pinning
- src/lfx/src/lfx/utils/ssrf_protection.py: Add validate_and_resolve_url()
- src/lfx/src/lfx/utils/ssrf_transport.py: Custom transport with DNS pinning
- src/lfx/src/lfx/services/settings/base.py: Enable SSRF protection by default
- src/backend/tests/unit/components/data_source/test_dns_rebinding.py: Test suite
Tested and verified working in GUI with DNS pinning logs visible.
* fix(security): implement network-level DNS pinning for SSRF protection with HTTPS support
This commit implements proper DNS rebinding protection that works with both HTTP and HTTPS by using network-level DNS pinning instead of URL rewriting.
## Changes
### Security Fixes
- **ssrf_protection.py**: Fixed critical vulnerability where IP validation returned early on first allowlisted IP, skipping validation of remaining IPs. Now validates ALL resolved IPs before making decisions.
- Blocks hostname if ANY resolved IP is blocked (even if others are allowlisted)
- Supports partial allowlisting (uses only allowlisted IPs for DNS pinning)
- Updated documentation to reflect that SSRF protection is now enabled by default
- Changed warn_only default from True to False for stricter security
### DNS Pinning Implementation
- **ssrf_transport.py**: Created DNSPinningNetworkBackend class that implements network-level DNS pinning
- Extends httpcore.AsyncNetworkBackend to intercept TCP connections
- Connects to pinned IP at network layer while preserving hostname for TLS
- Works with both HTTP and HTTPS (fixes HTTPS regression from URL rewriting approach)
- Properly handles TLS SNI and certificate verification
### Testing
- **test_dns_rebinding.py**: Updated all DNS rebinding tests to verify network-level behavior
- Tests now patch AutoBackend.connect_tcp to capture actual IP connections
- Verifies DNS pinning prevents rebinding attacks
- Verifies hostname preservation for TLS
- Tests IPv6 support
- All 7 tests passing
## Technical Details
The previous URL rewriting approach (replacing hostname with IP in URL) broke HTTPS because:
1. TLS certificate is issued for hostname, not IP address
2. Certificate verification fails when connecting to IP
3. This was a regression - HTTPS worked before but was vulnerable
The new network-level approach:
1. Resolves DNS during validation and pins IPs
2. Uses custom AsyncNetworkBackend to intercept TCP connections
3. Connects to pinned IP at network layer
4. Preserves original hostname for TLS SNI and certificate verification
5. Works transparently with both HTTP and HTTPS
## Function Clarification
Two validation functions exist for different purposes:
- validate_url_for_ssrf(): Simple validation for URL component (no HTTP requests)
- validate_and_resolve_url(): Validation + DNS pinning for API Request component (makes HTTP requests)
Fixes DNS rebinding vulnerability while maintaining HTTPS compatibility.
* fix(security): support multiple IPs for dual-stack and load-balanced hosts
This commit fixes DNS pinning to properly handle hosts that resolve to multiple
IP addresses (dual-stack IPv4/IPv6, load balancing, etc.).
## Changes
### DNS Pinning Enhancement
- **ssrf_transport.py**: Updated to accept and use list of validated IPs
- Changed pinned_ips from dict[str, str] to dict[str, list[str]]
- DNSPinningNetworkBackend now tries IPs in order with fallback
- Supports dual-stack (IPv4+IPv6) and load-balanced hosts
- Falls back to next IP if connection fails
- **api_request.py**: Pass all validated IPs instead of just first one
- Changed from validated_ips[0] to validated_ips
- Enables proper failover for unreachable IPs
### Testing
- **test_dns_rebinding.py**: Added test_dns_pinning_with_multiple_ips_fallback
- Verifies system tries multiple IPs when first fails
- Tests dual-stack scenario (IPv4 fails, IPv6 succeeds)
- Confirms IPs are tried in order
- All 8 tests passing
## Technical Details
**Previous behavior:**
- Only used first validated IP (validated_ips[0])
- Failed if that single IP was unreachable
- Broke dual-stack and load-balanced hosts
**New behavior:**
- Accepts full list of validated IPs
- Tries each IP in order until one succeeds
- Properly supports:
- Dual-stack hosts (IPv4 + IPv6)
- Load-balanced hosts (multiple IPs)
- Failover scenarios
This maintains security (all IPs validated during SSRF check) while improving
reliability and compatibility with modern networking configurations.
* fix: improve SSRF protection with DNS pinning and fix resource leak
- Implement network-level DNS pinning to prevent DNS rebinding attacks
- Fix resource leak in SSRFProtectedTransport by not calling super().__init__()
- Simplify error messages in SSRF protection (remove verbose explanations)
- Restore warn_only parameter in validate_url_for_ssrf() for backward compatibility
- Remove warn_only from validate_and_resolve_url() (not needed in API Request)
- Switch from private AutoBackend to public httpcore.AnyIOBackend() API
- Add noqa comments for unavoidable FBT and B008 warnings (matching parent class signature)
- Support dual-stack (IPv4/IPv6) and load-balanced hosts with multiple IPs
- Move imports to top of file for better code organization
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* test: fix SSRF protection test mocks to use validate_and_resolve_url
- Updated test mocks from validate_url_for_ssrf to validate_and_resolve_url
- Fixed test_ssrf_protection_disabled_by_default to explicitly disable protection
- All 12 SSRF protection tests now passing
* fix: read SSRF allowlist directly from environment for test compatibility
- Modified get_allowed_hosts() to read from os.getenv() first
- Fixes test isolation issue where settings service cached old values
- Ensures patch.dict() in tests properly overrides allowlist
- Maintains backward compatibility with settings service fallback
* test: fix test_ssrf_protection_enforcement_mode to properly mock validation
The test was failing because it wasn't mocking validate_and_resolve_url,
so the actual SSRF protection wasn't being triggered. Updated the test to:
- Mock validate_and_resolve_url to raise SSRFProtectionError
- Follow the same pattern as other SSRF protection tests
- Properly test that API Request component enforces blocking (no warn-only mode)
* refactor: rewrite SSRF protection tests to test real implementation
BREAKING CHANGE: Completely rewrote SSRF protection tests to properly test
actual security behavior instead of mocking core functions.
Changes:
- Removed all mocks of validate_and_resolve_url() - tests now verify real SSRF blocking
- Added tests that verify DNS pinning actually prevents rebinding attacks
- Test real blocking of private IPs (127.0.0.1, 192.168.x.x, 10.x.x.x, 172.16.x.x)
- Test real blocking of cloud metadata endpoints (169.254.169.254)
- Verify allowlist functionality with actual hostnames, IPs, and CIDR ranges
- Test that custom DNS pinning transport is used when protection is enabled
- Test that normal httpx client is used when protection is disabled
- Fixed environment variable caching in is_ssrf_protection_enabled()
Why this matters:
The old tests were mocking validate_and_resolve_url(), which meant they weren't
actually testing if SSRF protection works. They were just testing error handling.
Real SSRF vulnerabilities could have slipped through.
The new tests:
1. Let the real SSRF protection code run
2. Verify actual private IPs are blocked
3. Verify public URLs are allowed
4. Verify allowlist bypasses work correctly
5. Verify DNS pinning transport is actually used
All 41 tests pass (37 passed, 4 skipped for version checks).
---------
Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix: pin postgres image to bookworm in docker_example to prevent collation mismatch
The postgres:16 tag silently moved its base from Debian Bookworm
(glibc 2.36) to Trixie (glibc 2.41), causing a recurring collation
version mismatch warning on existing langflow-postgres volumes.
Pin to postgres:16-bookworm in both docker-compose files and update
the README so existing data volumes keep matching the OS locale data.
Refs: https://github.com/langflow-ai/langflow/issues/9608
* docs: pin postgres image to bookworm in current docs compose snippets
Mirror the docker_example pin in the four current docs that publish
copyable Compose snippets pairing postgres:16 with a persistent
langflow-postgres volume. Prevents the same Bookworm-to-Trixie
collation version mismatch warning when users follow docs instead
of docker_example.
Versioned historical docs are left as-is.
Refs: https://github.com/langflow-ai/langflow/issues/9608
* fix: switch postgres pin from bookworm to trixie for OS consistency
Aligns the pinned postgres base with the langflow runtime image, which
moved to Debian Trixie in #12990. Keeping postgres on bookworm would
have diverged the stack and locked the database to an aging glibc that
will receive fewer security backports as bookworm ages into oldstable.
The pin itself still solves the original bug — postgres:16 cannot
silently roll its OS underneath an existing volume.
Document the one-time REFRESH COLLATION VERSION step for users
upgrading from a bookworm-initialized volume in docker_example/README.md.
Refs: https://github.com/langflow-ai/langflow/issues/9608
* chore: import AGENTS.md from CLAUDE.md so Claude Code loads project instructions
Claude Code does not yet natively support AGENTS.md. The previous CLAUDE.md
was a human-readable redirect, but the agent never followed it. Using the
@AGENTS.md import directive (which Claude Code does support) makes the
project instructions auto-load.
* chore: clarify dev-deps sync and pre-commit workflow in AGENTS.md
- Note that running tests inside a sub-package needs uv sync --group dev
--package <name>; the default sync skips sub-package dev deps and leaves
things like fakeredis uninstalled.
- Tighten the pre-commit workflow section: pre-commit hooks already run
ruff/biome on commit, so the previous 5-step manual sequence overstated
what's needed.
* chore(starter-projects): new Vector Store RAG template
Update the RAG starter flow to align with the new knowledge base
paradigm: removes the inline knowledge base creation sub-flow and
points users to the dedicated Knowledge Ingestion UI for loading
content into their vector store.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(starter-projects): tweak Vector Store RAG README description
Drop the outdated reference to two sub-flows in the in-template
README and refresh the saved metadata.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: autofix of starter project json
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
- Update builder stage: bookworm-slim → trixie-slim (5 Dockerfiles)
- Update runtime stage: python:3.12.13-slim-trixie → python:3.12-slim-trixie
- Add pull: true to 6 Docker build steps in nightly workflow
- Forces pulling latest base images instead of using cached layers
This resolves CVE vulnerabilities in Docker images by ensuring we use
the latest Debian Trixie base images instead of cached Bookworm layers.
- Add pull: true to all docker build steps in nightly workflow
- Forces Docker to pull latest python:3.12-slim-trixie instead of using cached layers
- Ensures Debian Trixie base images are used instead of cached Bookworm layers
- Resolves issue where Docker layer caching prevented CVE fixes from taking effect
Adds a validate-pr-title job to ci.yml that fails on PRs whose title
ends with '...' or '…'. Wired into the CI Success aggregate so it acts
as a merge gate. Skipped on merge_group, workflow_dispatch, and
workflow_call where no PR title is available.
Frontend was forcing event_delivery=polling in the build query, overriding
LANGFLOW_EVENT_DELIVERY=streaming on the server. Token-by-token streaming
in the Playground never worked because the frontend always took the polling
code path (customPollBuildEvents) instead of SSE (performStreamingRequest).
Aligns three default fallbacks with the server doc default (STREAMING):
- buildUtils.buildFlowVertices: query-param default
- utilityStore.eventDelivery: initial store value (race window before /config)
- use-get-config: fallback when /config omits event_delivery
buildFlowVerticesWithFallback already retries with POLLING on
STREAMING_NOT_SUPPORTED / ENDPOINT_NOT_AVAILABLE, so streaming-first is safe.
Fixes#12291
* fix(frontend): paginator empty state shows "0 items" instead of "1-0 of 0 items"
When totalRowsCount is 0 the paginator rendered "1-0 of 0 items" and
"of 0 pages", with the next button enabled because pageIndex (1) was
not equal to maxIndex (0). Hide the range when empty, floor maxIndex
to 1, and disable both navigation buttons when there is nowhere to go.
* Update legacy paginator tests to assert new empty-state copy
fix(mcp): make run_tool wait_for timeout configurable, raise floor to 180s
The hardcoded `timeout=30.0` on `asyncio.wait_for(session.call_tool(...))`
in both `MCPSseClient.run_tool` and `MCPStreamableHttpClient.run_tool` was
truncating in-flight tool calls whose servers needed longer than 30s to
respond — most visibly large MCP composers whose tool responses include
sizeable catalogs.
Failure mode:
LLM → AgentExecutor → run_tool (1 call from the LLM)
├── attempt 1: get_session (probe ..._1 fails → swap ..._2)
│ → call_tool → 30s wait_for fires, TimeoutError raised
│ (server is still working — response ignored on arrival)
└── attempt 2: get_session (probe ..._2 fails → swap ..._3)
→ call_tool → 30s wait_for fires again
→ util.py dedup ("Repeated TimeoutError, not retrying")
→ ValueError("Maximum retries exceeded ...")
← AgentExecutor surfaces error → flow fails
The retry loop's TimeoutError dedup correctly stops looping once it sees
the same error twice, but every call against a slow server hit the same
ceiling and never had a chance to succeed. The session swaps between
attempts also produced excess HTTP churn against the server.
Both `run_tool` methods now resolve the timeout from
`get_settings_service().settings.mcp_server_timeout` (already used for
connection setup at lines 1617 / 1887, env: `LANGFLOW_MCP_SERVER_TIMEOUT`)
with a `max(setting, 180.0)` floor so default deployments don't end up
*shorter* than the previous hardcoded 30s — the Pydantic default for
`mcp_server_timeout` is 20.
Touched:
src/lfx/src/lfx/base/mcp/util.py
- L1677, L1689 (stdio run_tool)
- L1960, L1972 (streamable HTTP run_tool)
Unchanged on purpose:
- L1217, L1381 session-creation `timeout=30.0` (separate concern).
- L1060 `_validate_session_connectivity` 3.0s probe (separate
concern; addressed in follow-up if probe thrash persists).
Tuning:
- LANGFLOW_MCP_SERVER_TIMEOUT now governs both connection setup and
tool-call wait_for. The 180s floor clamps low values; raise the env
var above 180 to take effect.
Repro / verification:
- Trace run that previously failed at 67.08s with "Maximum retries
exceeded with repeated TimeoutError errors" against
`mcp-guardium_get_service_info` (47KB response payload) now
completes on attempt 1.
Co-authored-by: MANSURA HABIBA <MANSURAH@ie.ibm.com>
* perf: Enhance Gunicorn preload functionality for Langflow
- Introduced a new preload module to optimize memory usage by running fork-safe initialization in the Gunicorn master process.
- Updated the lifespan management in `main.py` to check if the master has preloaded resources, allowing workers to inherit state and skip redundant setup.
- Adjusted the server loading process to accommodate the new preload logic, ensuring efficient resource management across worker processes.
* feat(cache): Implement teardown method for RedisCache to prevent socket leaks
- Added a `teardown` method to the `RedisCache` class to close the Redis connection, addressing potential socket leaks during process forking.
- Introduced unit tests to verify the functionality of the `teardown` method, ensuring it handles client closure and errors gracefully.
- Tests cover scenarios including normal closure, error handling during closure, and teardown with URL-based connections.
* feat(cache): Implement teardown method in RedisCache to prevent socket leaks
- Added a `teardown` method to the `RedisCache` class, ensuring proper closure of the Redis client connection before forking to avoid socket leaks.
- Created comprehensive unit tests to validate the functionality of the `teardown` method, covering various scenarios including normal operation and error handling.
- Updated existing tests to reflect the new teardown functionality and ensure RedisCache is recognized as an instance of `ExternalAsyncBaseCacheService`.
* refactor(preload): Simplify DB engine disposal logic in master preload
- Removed exception handling around the DB engine disposal to streamline the process, ensuring that the engine is disposed of without unnecessary error logging. This change enhances code clarity and maintains the intended functionality of resource management during the preload phase.
* refactor(preload): Streamline cache service teardown logic
- Simplified the cache service socket closure process in the master preload function to prevent sharing across forks. This change enhances code clarity by removing unnecessary exception handling while maintaining the intended functionality of resource management during the preload phase.
* feat(preload): Introduce per-step completion flags for improved state management
- Added completion flags in the preload state to track the status of various initialization steps, including profile picture copying, starter project creation, agentic global variable initialization, MCP server configuration, and flow loading.
- Updated the lifespan management in `main.py` to utilize these flags, allowing the system to skip redundant setup tasks if they have already been completed during the preload phase.
- This enhancement improves resource management and ensures that the application behaves correctly in a multi-worker environment.
* refactor(lifespan): Replace temp dir management with get_owned_temp_dirs function
- Updated the lifespan management in `main.py` to utilize the new `get_owned_temp_dirs` function, which encapsulates the logic for determining temp directory ownership based on the process type (master or worker).
- Removed the `is_master` check from the lifespan function, simplifying the code and enhancing clarity regarding temp directory cleanup responsibilities.
- Added the `get_owned_temp_dirs` function in `preload.py` to centralize temp directory ownership logic, ensuring that workers do not attempt to clean up directories owned by the master process.
* refactor(lifespan): Enhance initialization flow with conditional gates
- Introduced conditional gates in the `get_lifespan` function to manage the initialization of profile pictures, super users, bundles, component types, and starter projects based on their completion status.
- Improved logging to provide clearer insights into which steps are being skipped or executed, enhancing the overall clarity of the initialization process.
- Updated the preload logic in `preload.py` to ensure that agentic global variables and MCP server configuration are only initialized when necessary, maintaining efficient resource management in a multi-worker environment.
* fix: resolve double-call of initialize_auto_login_default_superuser and add preload tests
- Fix double-call issue: setup_superuser() now handles AUTO_LOGIN completely with file lock
- Add comprehensive unit tests for preload.py covering failure-fallback contract
- Simplify code by doing superuser initialization in initialize_services() (called early in both preload and worker startup)
- File lock protects multi-worker race conditions when preload is disabled
- Tests verify critical step failures propagate, best-effort steps continue on failure
Made-with: Cursor
* fix(preload): resolve missing import and agentic initialization conflicts
Fixed critical bugs introduced in c0e81a5401 that caused preload failures:
1. Missing import: Added DEFAULT_SUPERUSER_PASSWORD to module-level imports
- Was only imported inside AUTO_LOGIN block but used when AUTO_LOGIN=false
- Caused NameError that crashed preload with "session scope error"
2. Removed agentic variable initialization from setup_superuser()
- Prevents double-initialization conflict with preload's dedicated step
- initialize_agentic_global_variables() in preload handles all users
3. Made teardown_superuser() more robust
- Now skips deletion instead of raising errors on FK constraints
- Prevents startup failures when default superuser has associated flows
Resolves: "An error occurred during the session scope" preload error
Resolves: Ghost thread warnings from incomplete initialization
Made-with: Cursor
* refactor(preload): Enhance state management and initialization logic
- Updated the `_PreloadState` class to include `bundles_loaded` and `types_cached` flags for better tracking of initialization steps.
- Modified the `get_lifespan` function to utilize the new state flags, improving the conditional logic for loading bundles and caching component types.
- Implemented a `reset` method in `_PreloadState` to ensure consistent state restoration after preload failures, enhancing reliability in multi-worker environments.
- Simplified the teardown process in `ExternalAsyncBaseCacheService` by making `teardown` an abstract method, allowing direct calls without fallback checks.
This refactor improves clarity and efficiency in the preload and lifespan management processes.
* refactor(lifespan): Improve component types caching and starter project creation logic
- Enhanced the `get_lifespan` function to utilize a local handle for component types when the cache is inherited, ensuring consistency in multi-worker environments.
- Added logging for scenarios where the component types cache is empty, allowing for cache rebuilding instead of skipping starter project creation.
- Streamlined the starter project creation process with improved error handling and logging, ensuring clarity on failures and skipped operations.
This refactor enhances the reliability and clarity of the initialization process in the application.
* fix(tests): Update teardown_superuser test to preserve default superuser if never logged
- Renamed the test function to clarify its purpose.
- Modified the test logic to ensure that the default superuser is not removed during teardown if it has never been logged in, preventing foreign key errors.
- Added assertions to verify that the superuser remains intact and its properties are correctly set after teardown.
This change enhances the reliability of the superuser management in the test suite.
* feat: Implement orphaned MCP server config migration and add unit tests
- Introduced `migrate_orphaned_mcp_servers_config` function to recover MCP server config files that become orphaned after a database reset while preserving the config directory.
- Added logic to handle scenarios with multiple orphaned files, ensuring safe migration and logging for manual recovery.
- Created comprehensive unit tests to validate the migration functionality, covering cases for single orphan recovery, multiple orphans, and scenarios with no orphans.
- Enhanced the setup of the superuser to include the migration process, improving the robustness of user configuration management.
This update enhances the application's ability to recover user configurations in containerized environments.
* fix(teardown): Improve superuser removal logic and error handling
- Updated the `teardown_superuser` function to remove the default superuser when AUTO_LOGIN is disabled and the user has never logged in, enhancing user management.
- Improved error handling to raise a `RuntimeError` if the removal fails, ensuring that issues are logged and not silently ignored.
- Adjusted the `DatabaseService` teardown process to reflect the updated superuser removal logic and ensure proper resource disposal.
This change enhances the reliability of the superuser management during application shutdown.
* fix(setup_superuser): Enhance error handling for AUTO_LOGIN timeout scenario
- Updated the `setup_superuser` function to provide clearer logging and error messages when the AUTO_LOGIN lock times out and no default superuser exists.
- Introduced a check to verify the existence of the default superuser in the database, raising a `RuntimeError` with a descriptive message if it is not found.
- Added unit tests to validate the behavior of the setup process under timeout conditions, ensuring that the application fails gracefully when required superuser credentials are missing.
This change improves the robustness of the superuser initialization process and enhances error visibility during startup.
* fix(preload): Update error handling to log exceptions with traceback for best-effort steps
- Modified the error handling in the `_run_master_preload` function to log exceptions with traceback instead of warnings for best-effort steps, ensuring better visibility into failures.
- Updated unit tests to reflect the change in logging behavior, verifying that exceptions are correctly logged during preload operations.
This change enhances the clarity of error reporting during the preload process, aiding in debugging and monitoring.
* fix(preload): Ensure cleanup of temporary directories during reset
- Updated the `reset` method in the `_PreloadState` class to call `cleanup()` on each `TemporaryDirectory` before clearing the `temp_dirs` list, preventing on-disk directory leaks during failed preloads.
- Added a unit test to verify that `cleanup()` is called for all temporary directories during the reset process, ensuring proper resource management.
This change enhances the reliability of the preload process by ensuring that temporary resources are properly cleaned up.
* refactor(preload): Introduce PreloadStep enumeration and streamline step completion logic
- Added a `PreloadStep` enumeration to define ordered preload phases, enhancing clarity and maintainability of the preload process.
- Replaced direct state attribute checks with the `is_step_complete` function to improve readability and encapsulate step completion logic.
- Updated the `_run_master_preload` function to utilize `mark_step_complete` for recording successful completion of preload steps, enforcing prerequisite ordering.
- Enhanced unit tests to validate the new step completion logic and ensure proper functionality of the preload process.
This refactor improves the structure and reliability of the preload mechanism, making it easier to manage and understand the initialization flow.
* refactor(superuser): Remove redundant checks for superuser credentials in setup functions
- Eliminated unnecessary validation for `DEFAULT_SUPERUSER` and `DEFAULT_SUPERUSER_PASSWORD` in both `initialize_auto_login_default_superuser` and `setup_superuser` functions, simplifying the initialization logic.
- This change streamlines the superuser setup process, assuming that the necessary credentials are managed externally, thus enhancing code clarity and maintainability.
* refactor(preload): Streamline error handling and step completion in preload process
- Introduced a `_best_effort` function to encapsulate error handling for preload steps, improving code readability and maintainability.
- Updated the `_run_master_preload` function to utilize the new error handling mechanism for various steps, ensuring consistent logging and state management.
- Enhanced the documentation in the preload module to clarify the handling of fork-unsafe resources and the overall preload process.
This refactor enhances the robustness and clarity of the preload mechanism, making it easier to manage and understand the initialization flow.
* refactor(preload): Enhance error handling and resource cleanup in preload process
- Wrapped post-initialization work in a try/finally block to ensure the DB engine and cache service are always disposed of, preventing resource leaks in forked workers.
- Updated logging to provide clearer visibility into the preload steps, including error handling for best-effort operations.
- Improved the structure of the `_run_master_preload` function by consolidating related operations and ensuring consistent cleanup.
This refactor enhances the robustness and clarity of the preload mechanism, ensuring safe resource management during initialization.
* [autofix.ci] apply automated fixes
* test(setup_superuser): remove default superuser before lock-timeout no-superuser case
The `initialized_services` fixture starts with `AUTO_LOGIN=false`, which
runs `setup_superuser` through the credentials-fallback path and creates
the default superuser. The "raises_when_no_superuser" test then mocked
the lock to time out, but the existence check found that pre-created
user and returned `AUTO_LOGIN_LOCK_TIMEOUT_SUPERUSER_PRESENT` instead of
raising `RuntimeError`. Delete the default superuser before mocking the
lock so the no-superuser branch is actually exercised.
---------
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
* fix: URLComponent ignores proxy in async mode (#12285)
The URLComponent fails to connect for users behind corporate proxies
because its asynchronous mode does not recognize standard system proxy
environment variables. The component defaults to use_async=True, which
initializes the RecursiveUrlLoader; the underlying async loader does
not natively respect system proxy environment variables, so the
component attempts a direct connection and fails in restricted network
environments.
Detect standard proxy environment variables (http_proxy, HTTP_PROXY,
https_proxy, HTTPS_PROXY) in URLComponent._create_loader. If a proxy
is detected and use_async is enabled, override use_async to False so
the loader uses its synchronous implementation, which natively
respects system proxies. Empty and whitespace-only values are
correctly evaluated as no-proxy and do not trigger the fallback.
Closes#10297
* fix(URLComponent): broaden proxy detection and harden tests
Address review findings on the proxy fix:
- Add ALL_PROXY and all_proxy to the detected env vars. ALL_PROXY is
commonly set in corporate and container environments (curl, git,
many Unix tools honour it), and is sometimes the only proxy var
configured.
- Replace the unused proxy_url string with a single boolean any() over
the env var keys, since only the presence of a proxy is consulted.
- Reorganize the proxy tests under TestURLComponentProxyHandling with
a shared default-attributes helper, parametrize over all six env var
spellings, and add coverage for multiple-simultaneous-proxies and
the use_async=False path (which should not log a warning).
* [autofix.ci] apply automated fixes
* fix: remove no_proxy="*" macOS startup hack so corporate proxies work
Two startup paths set `os.environ["no_proxy"] = "*"` on macOS, which
disables proxy use for every HTTP client in the process and every child
gunicorn worker (httpx, requests, urllib3 — and therefore the OpenAI,
Anthropic, Groq, etc. SDKs that wrap them). For users behind corporate
proxies on macOS this made every external LLM call unroutable, even with
HTTPS_PROXY properly set.
The override traces to commit history with no concrete justification —
the only artifact is a Stack Overflow link about a uWSGI segfault, but
Langflow uses gunicorn, not uWSGI. Bench-verified that gunicorn boots
cleanly and serves requests on macOS without it (1 worker via
LangflowApplication, OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES retained,
HTTPS_PROXY survives intact in parent and child).
Verification:
- gunicorn worker spawns and serves /health (200 OK), exits cleanly
- httpx, urllib, requests all resolve HTTPS_PROXY after the macOS init
(previously: all three returned empty proxy maps because of no_proxy=*)
- OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES still set — the actual
fork-safety fix is preserved
Closes the macOS half of #10297. Companion fix for the async URLComponent
half is in #12285 / branch pr-12285-rebased.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* [autofix.ci] apply automated fixes (attempt 3/3)
* Orjson update
---------
Co-authored-by: Diogo Veiga <diogo.veiga@tecnico.ulisboa.pt>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix: update Docker base images to latest Python 3.12 to resolve CVEs
- Update all Dockerfiles from python:3.12.13-slim-trixie to python:3.12-slim-trixie
- This ensures automatic security patches for Debian base image CVEs
- Affects nightly builds (base, main, main-all) and release builds (backend, ep)
- Using unpinned patch version (3.12 vs 3.12.13) follows security best practices
* fix: update builder stage to Debian Trixie for consistency
- Update all Dockerfiles from bookworm-slim to trixie-slim in builder stage
- Ensures consistency between builder and runtime Debian versions
- Eliminates CVEs in both build and runtime environments
- Uses ghcr.io/astral-sh/uv:python3.12-trixie-slim for all builders
* docs: Add DESIGN.md specification for Langflow's visual design system
Machine-readable design tokens (YAML frontmatter) paired with
human-readable design rationale following the open-source DESIGN.md
format from Google Labs. Intended for AI agents generating UI that
must match Langflow's visual identity.
Covers 129 color tokens, 15 typography scales, 50 component
definitions, spacing/rounding/elevation systems, dark mode strategy,
and the 14-hue data type color system used for node port encoding.
* ci: Add DESIGN.md lint workflow
Runs `@google/design.md lint` against DESIGN.md on pull requests that
modify the file. Catches spec violations, broken token references, and
WCAG AA contrast failures before merge. CLI version is pinned via env
var so format spec changes require a deliberate bump.
* ci: Integrate DESIGN.md lint into CI Success pipeline
Replaces the standalone workflow with a job inside ci.yml so it gates
the "CI Success" aggregate check. Path-filter skips the job on PRs
that don't touch DESIGN.md, so cost is near-zero for normal PRs.
* feat(lfx): add extension manifest schema, validate CLI, and error formatter (LE-1014)
Foundation for the Bundle Separation iteration. Defines what a valid
extension.json looks like (Pydantic models + Draft 2020-12 JSON Schema),
ships the offline `lfx extension validate` command, and ships the single
`format_extension_error` function that every other extension-system module
will use to render structured errors.
What's in lfx.extension:
- `ExtensionManifest` / `BundleRef` / `LangflowCompat` Pydantic models with
`extra="forbid"` so unknown fields fail loudly. Deferred fields (`services`,
`routes`, `hooks`, `starter_projects`, `userConfig`) are reserved as
None-only so non-null values produce a dedicated
`field-deferred-in-this-milestone` error instead of a generic schema wall.
`bundles` accepts a list but rejects length > 1 with
`multi-bundle-deferred-in-this-milestone` (validator-enforced; the loader
re-checks at install time in LE-1015).
- `schema.build_schema()` + `build_schema_json()` produce the publishable
artifact at schemas.langflow.org/extension/v1.json.
- `ExtensionError` typed envelope and `format_extension_error` -- one branch
per discriminant. Codes are registered in `ERROR_CODES`; an
`ExtensionError` constructed with an unknown code raises at construction
time, preventing producers from shipping without a matching renderer.
- `validate_extension` runs four passes: manifest discovery + schema,
path-safety (no `..`, no absolute paths, no symlink escape), AST
inspection of every `.py` (syntax, Component subclass present, build()
declared, top-level `import *`, top-level I/O primitives), and an opt-in
`--execute-imports` that runs each module in a subprocess with a
temporary HOME / TMPDIR / LANGFLOW_CONFIG_DIR and LANGFLOW_*/LFX_* env
vars stripped.
- `lfx extension validate` and `lfx extension schema` typer subcommands.
Acceptance-criteria coverage in tests/unit/extension/:
- Round-trips every v0 manifest field; deferred fields rejected; multi-bundle
rejected with the dedicated discriminant.
- JSON Schema validates the v0 example and rejects 12 malformed manifests
with distinct error paths (>= 10 required by the ticket).
- Median default-validate runtime < 100ms on the basic template.
- Crafted side-effect bundle: default validate does NOT execute it (canary
file is never written); `--execute-imports` DOES execute it and the canary
appears, while LANGFLOW_* env vars are NOT inherited by the subprocess.
- Snapshot tests for every code in ERROR_CODES; a guard test verifies
ERROR_CODES and the snapshot table are in lockstep so future additions
cannot ship without a format branch and a snapshot.
Wiring: `lfx extension` is a sub-app under the Authoring help panel so
future tickets (LE-1016 init/dev, LE-1018 reload) can attach without
colliding with the existing `lfx validate` (which validates flow JSON, not
extensions).
* fix(lfx): fall back to tomli on Python 3.10 in extension manifest loader
tomllib is stdlib only on 3.11+, but lfx supports 3.10-3.13. Use the
existing tomli runtime dependency as the 3.10 fallback (same API, so the
import alias keeps the rest of the module unchanged).
Fixes ModuleNotFoundError seen in CI on the 3.10 job.
* Update src/lfx/tests/unit/extension/test_schema.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update src/lfx/src/lfx/extension/schema.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update src/lfx/src/lfx/cli/_extension_commands.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: LangflowCompat -> LfxCompat
* fix: Remove references to ticket
* fix: encode maxItems constraint in schema
* fix: deferred fields and schema version
* Fix ruff errors
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: encode non-ASCII filenames in Content-Disposition headers
HTTP headers only support ASCII/latin-1. Placing Chinese or other
non-ASCII characters directly in filename="..." caused a latin-1
serialization error (500) on download.
Fix all four download routes to use an ASCII-safe fallback in
filename= and the URL-encoded form in filename*=UTF-8'':
- api/v1/files: /download/{flow_id}/{file_name}
- api/v1/flows_helpers: /flows/download/ (ZIP)
- api/v2/files: /files/{file_id} and /files/batch/
Also fix the frontend filename extraction regex in both
use-get-download-flows.ts and use-get-download-files.ts to parse
RFC 5987 (filename*=UTF-8''<encoded>) with decodeURIComponent,
falling back to plain filename= for older responses.
Closes#8105
* test: add non-ASCII filename encoding tests for file download routes
Cover the RFC 5987 Content-Disposition encoding fix across all
affected download endpoints:
- api/v1/files: parametrized test with Chinese, Japanese, accented
Latin, and ASCII filenames verifying filename*=UTF-8'' encoding
- api/v2/files: same parametrized test for single-file download plus
a batch download test verifying the ZIP Content-Disposition header
- flows download: test with Chinese flow names verifying the ZIP
response uses a decodable RFC 5987 filename
Reproduces the regression from issue #8105 where downloading files
with non-ASCII names returned HTTP 500.
* fix: use safe="" in quote() for RFC 5987 filename encoding and tighten test assertion
Ensures forward slashes and other special characters are percent-encoded
in Content-Disposition filename* values. Also fixes weak in-operator
assertion in v2 test to check the correct direction.
* fix: centralize RFC 5987 Content-Disposition encoding and patch header injection
Extract build_content_disposition() helper in api/utils/core.py that:
- Escapes double-quotes in the ASCII fallback (prevents parameter smuggling
via filenames like evil"; x=injected)
- Uses quote(safe="") to percent-encode forward slashes
- Returns the dual-param format (filename= + filename*=) for broad client
compatibility
Replace the duplicated 3-line encoding blocks in v1/files.py, v2/files.py,
flows_helpers.py, and projects_files.py with a single call to the helper.
projects_files.py was missing from the original fix: it used quote() without
safe="" and emitted only filename*= with no ASCII fallback.
* test: strengthen Content-Disposition header assertions
- v2/test_files.py: expected_rfc5987 now includes the file extension so a
regression that strips the extension from the RFC 5987 value is detected
- test_database.py: strengthen the dual-flow download assertion to verify
both filename= and filename*= params are present, not just the prefix
- test_database.py: rename test_download_flows_non_ascii_content_disposition
to test_download_flows_content_disposition_dual_param and update the
docstring to accurately reflect that the ZIP filename is always ASCII
(timestamp-based); the test now verifies the dual-param format
* fix: sanitize control chars and escape backslash in build_content_disposition
Strip ASCII control characters (CR/LF/NUL and others in 0x00-0x1f/0x7f range)
from filename before encoding to prevent header injection. Also escape backslash
before double-quote in the ASCII fallback per RFC 6266 §4.1 quoted-pair grammar.
* test: add adversarial tests for build_content_disposition and strengthen v2 assertion
Add direct unit tests for the helper covering: quote escaping, backslash escaping,
CRLF/NUL/control char stripping, empty filename, and very long names. Replace
weak substring assertions in the v2 single-file download test with exact equality
against the stored filename returned by the upload response.
* refactor: extract Content-Disposition parser to shared frontend util
Extract the duplicated 14-line RFC 5987 parsing block from use-get-download-files
and use-get-download-flows into a single parseContentDispositionFilename util.
Wraps decodeURIComponent in try/catch to gracefully fall back to the legacy
filename= param on malformed percent-sequences. Adds Jest tests covering all
branches: RFC 5987 priority, legacy fallback, CJK decode, null header, and
malformed input.
* test: replace Portuguese test filename with English equivalent
Rename the accented Latin test case from the Portuguese
arquivo_com_acentuação.txt to naïve_résumé.txt so all test
data uses English while still exercising the same ï/é encoding path.
* fix(frontend): render node dropdowns above build/notification overlays
ReactFlow nodes apply `transform` which creates a local stacking context.
The model selector and generic node dropdown previously rendered without
a Portal, so their `z-50` popover content was trapped inside that
context and got covered by the build status / error notification panel
(also `z-50`, sibling of the canvas).
Switch both `dropdownComponent` and `modelInputComponent` to always use
the portalled `PopoverContent` and bump the popover layer to `z-[60]`.
Mounting the popover into `document.body` escapes the node's stacking
context, and the higher z-index keeps it above the build panel
regardless of portal mount order.
* fix(frontend): flip node dropdowns above build/notification overlays
Prior commit moved the dropdown popovers to a Portal so they could
escape the node's stacking context, which fixed the overlap with the
build status / error notification panel but also pulled the popovers
out of the React Flow viewport's CSS scale transform — the dropdowns
no longer scaled with canvas zoom and looked oversized when zoomed out
and too small when zoomed in.
Switch to a non-portal-based fix: keep the popovers inline in the node
(so they continue to scale with canvas zoom like the rest of the node)
and let Radix flip them above the trigger when the build panel is on
screen.
- Subscribe to `useFlowStore` for `isBuilding` and `buildInfo` and
derive a `showingBuildPanel` flag in `dropdownComponent` and
`modelInputComponent`.
- Restore the original `PopoverContentWithoutPortal` selector in both
files (children / editNode / inspectionPanel keep portaled content).
- Always pass `avoidCollisions` and a dynamic
`collisionPadding.bottom` equal to `BUILD_PANEL_COLLISION_PADDING_PX`
(160) when the panel is visible, otherwise `0`. Radix then auto-flips
the popover above the trigger whenever the bottom region is reserved.
- Move `BUILD_PANEL_COLLISION_PADDING_PX` into
`constants/constants.ts` with a comment so the magic number is
defined once and documented.
* fix(model-input): hide wrench when saved model's provider is configured
When a user deactivates all models from a configured provider, the backend
still injects the saved selection into options as a sticky-default with
not_enabled_locally:true, causing the trigger to surface the Configure
wrench. The wrench is intended for imported flows that reference providers
the user hasn't set up — not for models the user has actively deactivated.
This change scopes the sticky-default affordance to providers that are not
configured locally:
- groupedOptions filters out sticky options whose provider is configured.
- selectedModel falls through to the first available option instead of
preserving the saved selection with the wrench flag in that case.
- The auto-default useEffect resets the persisted value when the saved
model is missing AND its provider is configured, so the flow no longer
references a model the user can't run.
Also tightens the value-prop type from `any` to `ModelOption[] | undefined`
on the component's BaseInputProps generic so the file passes the staged
biome no-any check (pre-existing violation that blocked the surgical edit).
* fix: Don't refresh list if no changes made
chore: update playwright to 1.59.0
- Update playwright dependency from 1.58.0 to 1.59.0
- Sync uv.lock with updated dependencies
Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
`BaseFileComponent._unpack_bundle._safe_extract_tar` accepted any TAR member
type and only checked that `output_dir / member.name` did not escape the
extract dir. That check was performed before extraction, so a symlink whose
*target* was an absolute path (or `../` escape) was extracted untouched.
Once on disk the link was iterated by `temp_dir_path.iterdir()` and handed
to `process_files()`, whose concrete implementations (FileComponent,
DoclingInline/Remote, NvidiaIngest, VideoFile, Unstructured) call
`path.read_bytes()` and follow the link to read arbitrary host files.
The reporter's exploit chain leaks `~/.langflow/secret_key`, forges a JWT
for an admin user, and then runs arbitrary code through the Python
interpreter node, achieving RCE.
Python's `tarfile` only defaults to the safe `data` filter on Python 3.14,
which langflow's `requires-python = ">=3.10,<3.14"` excludes — so every
supported interpreter was vulnerable.
Fix:
- `_safe_extract_tar` now rejects symbolic-link, hard-link, FIFO, and
device-node members with a `ValueError` and only extracts regular files
and directories.
- `_unpack_and_collect_files` skips any `is_symlink()` entries from the
extracted bundle directory and from recursive directory walks as
defense-in-depth in case a future bundle format slips a link through.
- New `tests/unit/base/data/test_base_file_unpack.py` covers symlink (abs
+ relative escape), hardlink, FIFO rejection, benign tar/zip extraction,
the post-extraction symlink filter, and an end-to-end repro mirroring
the advisory PoC (real filesystem symlink → tarfile.add).
Refs: https://github.com/langflow-ai/langflow/security/advisories/GHSA-ccv6-r384-xp75