Skip to content

fix: close tenant-isolation gaps in security hardening#14044

Merged
erichare merged 16 commits into
release-1.11.0from
security-hardening-p1-followups
Jul 13, 2026
Merged

fix: close tenant-isolation gaps in security hardening#14044
erichare merged 16 commits into
release-1.11.0from
security-hardening-p1-followups

Conversation

@erichare

@erichare erichare commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stacked security follow-up to #13530. This PR targets security-hardening so the original PR can enter the merge queue unchanged.

  • isolate shared MCP tool caches by authenticated user and disable cross-request caching without an authenticated scope
  • restrict npx/uvx MCP transports to operator-approved packages, rejecting direct URLs, paths, and package-injection options
  • harden published-image MCP execution against Docker host access and tenant-selected Python, Node.js, and shell entrypoints
  • recursively scrub structured credentials, authentication headers, credential-bearing URLs, and embedded MCP configs from public-flow responses
  • confine built-in local file reads and writes to the authenticated user's or executing flow's upload storage
  • refresh the component index, affected starter projects, and bundled agentic MCP snapshots with regression guards

Security invariants

  • one tenant cannot reuse another tenant's cached MCP tools or credentials
  • tenant-authored MCP configs cannot select arbitrary packages, scripts, Docker subcommands, host mounts/files/devices/namespaces, or Docker API access in published images
  • anonymous public-flow reads do not expose structured secrets that are not marked as password fields
  • restricted local-file components fail closed without a user/flow scope and reject other-tenant paths and escaping symlinks

Validation

  • 244 focused LFX security/component-index tests passed
  • 235 focused backend/API/component tests passed; 8 historical-version fixtures skipped as expected
  • Ruff format and lint passed for all 24 changed Python files
  • component index regenerated deterministically under Python 3.13 (129 components; integrity hash verified)
  • clean synthetic merge against security-hardening

Summary by CodeRabbit

  • New Features

    • Added stronger MCP server protection, including interpreter hardening, Docker isolation, and approved-package restrictions.
    • Improved MCP tool caching to respect authentication context and prevent stale or cross-context results.
    • Added scoped local-file access controls for reading and saving files.
  • Bug Fixes

    • Expanded secret removal when exposing public flows, including credentials in URLs, headers, and nested configuration.
    • Improved MCP configuration handling, headers, timeouts, and tool execution behavior.
  • Documentation

    • Updated authentication and security guidance with the new MCP hardening settings.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c91325e8-77fb-4bed-a2fa-65384c3a729d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The PR adds MCP runtime hardening and package allowlists, tenant-scoped local-file enforcement, expanded public-flow secret scrubbing, authenticated MCP cache partitioning, refreshed embedded components, and regression tests across Docker, MCP, file-security, and flow snapshots.

Changes

MCP execution hardening

Layer / File(s) Summary
MCP policy and runtime defaults
src/lfx/src/lfx/base/mcp/security.py, src/lfx/src/lfx/services/settings/groups/*, docker/*.Dockerfile, src/lfx/tests/unit/mcp/*
Docker validation blocks additional host-access flags, interpreter hardening rejects unsafe tenant commands, package runners use an allowlist, and published images enable the new defaults.
MCP cache partitioning and snapshots
src/lfx/src/lfx/components/models_and_agents/mcp_component.py, src/backend/base/langflow/agentic/flows/*.json, src/lfx/src/lfx/_assets/component_index.json
MCP caches include user, headers, and timeout context; bounded TTL/shared caches and non-cached tool outputs are propagated into embedded flows.
MCP validation coverage
src/lfx/tests/unit/components/models_and_agents/*, src/backend/tests/unit/agentic/flows/*
Tests cover cache-key isolation, configured hardening, and exact synchronization of bundled MCP component implementations.

Tenant-scoped file access

Layer / File(s) Summary
Scoped local-file enforcement
src/lfx/src/lfx/utils/file_path_security.py, src/lfx/src/lfx/components/**/*, src/lfx/src/lfx/_assets/component_index.json
Local paths are constrained to validated authenticated-user or flow storage scopes across readers, writers, directory loaders, agents, OpenAPI loading, and generated components.
Starter-flow propagation
src/backend/base/langflow/initial_setup/starter_projects/*.json
Embedded File and SaveToFile components in starter projects use component-derived access scopes when resolving local paths.
File-security tests
src/lfx/tests/unit/utils/test_file_path_security.py, src/backend/tests/unit/components/files_and_knowledge/test_directory_component.py
Tests cover scoped paths, traversal and symlink escapes, reserved files, missing scopes, and updated storage-scope errors.

Public-flow secret scrubbing

Layer / File(s) Summary
Structured secret scrubbing
src/backend/base/langflow/api/utils/core.py, src/backend/tests/unit/api/v1/test_public_flow_secret_scrub.py
Secret detection now handles nested values, credential-bearing URLs, headers, MCP configuration, and additional secret-like field names in public flow responses.

Starter flow schema refresh

Layer / File(s) Summary
Starter flow schema and component refresh
src/backend/base/langflow/initial_setup/starter_projects/Structured Data Analysis Agent.json
Connector metadata, component types, input types, dropdown options, tags, and embedded File/SaveToFile implementations are refreshed using explicit array formatting and updated code hashes.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: bug

Suggested reviewers: jordanrfrazier, cristhianzl, ogabrielluiz, adam-aghili


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error Real tests exist, but key new paths are untested: Dockerfile allowlist env isn't asserted, and public-flow secret scrub lacks query/fragment/network-path regressions. Add assertions for LANGFLOW_MCP_SERVER_ALLOWED_PACKAGES, extend secret-scrub cases for ?api_key=, #token=, and //user:pass@host, and add an unauthenticated build_config cache regression.
Docstring Coverage ⚠️ Warning Docstring coverage is 59.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning Async pytest usage looks fine, but coverage misses key regressions: URL query/fragment scrubbing, Docker allowlist env, unauthenticated build_config cache, and reserved-file denial. Add direct regressions for those branches, plus a test for the Docker allowed-packages ENV line and a file-path case that reaches reserved-file denial under an allowed tenant scope.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR’s main security and tenant-isolation hardening changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test File Naming And Structure ✅ Passed All added/updated test files follow test_*.py pytest conventions, use descriptive test names/classes/fixtures, and mark the integration path explicitly.
Excessive Mock Usage Warning ✅ Passed Mocks are limited to external boundaries (MCP client/settings/loaders); most new tests exercise real filesystem, API, and security logic end-to-end.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security-hardening-p1-followups

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Jul 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions

This comment has been minimized.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.11475% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.40%. Comparing base (6450159) to head (4a9b8da).
⚠️ Report is 5 commits behind head on release-1.11.0.

Files with missing lines Patch % Lines
src/lfx/src/lfx/base/mcp/security.py 89.61% 10 Missing and 6 partials ⚠️
src/lfx/src/lfx/utils/file_path_security.py 66.66% 7 Missing and 4 partials ⚠️
src/backend/base/langflow/api/utils/core.py 96.07% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.11.0   #14044      +/-   ##
==================================================
+ Coverage           60.16%   60.40%   +0.24%     
==================================================
  Files                2333     2341       +8     
  Lines              228353   230126    +1773     
  Branches            32038    34353    +2315     
==================================================
+ Hits               137386   139016    +1630     
- Misses              89351    89438      +87     
- Partials             1616     1672      +56     
Flag Coverage Δ
backend 66.75% <96.07%> (-0.17%) ⬇️
frontend 59.27% <ø> (+0.24%) ⬆️
lfx 58.25% <86.01%> (+0.68%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/lfx/src/lfx/base/data/base_file.py 48.48% <100.00%> (+0.22%) ⬆️
src/lfx/src/lfx/services/settings/groups/mcp.py 90.56% <100.00%> (+0.77%) ⬆️
...c/lfx/src/lfx/services/settings/groups/security.py 88.88% <ø> (+2.52%) ⬆️
src/backend/base/langflow/api/utils/core.py 82.25% <96.07%> (+5.56%) ⬆️
src/lfx/src/lfx/utils/file_path_security.py 73.68% <66.66%> (ø)
src/lfx/src/lfx/base/mcp/security.py 92.60% <89.61%> (ø)

... and 146 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 46%
46.01% (63924/138921) 70.04% (8777/12530) 44.16% (1441/3263)

Unit Test Results

Tests Skipped Failures Errors Time
5301 0 💤 0 ❌ 0 🔥 17m 58s ⏱️

Base automatically changed from security-hardening to release-1.11.0 July 13, 2026 19:35
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026
@erichare erichare force-pushed the security-hardening-p1-followups branch from e6b96b3 to 359344b Compare July 13, 2026 19:38
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (2)
src/lfx/tests/unit/services/settings/test_settings_composition.py (1)

252-255: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Lock down the interpreter-hardening default.

Add assert settings.mcp_server_interpreter_hardening is False alongside the other new security defaults to preserve the documented single-tenant compatibility contract.

As per coding guidelines, tests must verify newly changed behavior rather than act as placeholders.

🤖 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/tests/unit/services/settings/test_settings_composition.py` around
lines 252 - 255, Add an assertion for settings.mcp_server_interpreter_hardening
being False in the settings composition test, alongside the existing
security-default assertions. Ensure the test verifies the documented
single-tenant compatibility default without changing the other assertions.

Source: Coding guidelines

src/lfx/src/lfx/utils/file_path_security.py (1)

133-177: 🩺 Stability & Availability | 🔵 Trivial

Scope narrowing is a breaking change for deployments already running with restriction enabled.

Per the line-range summary, this changes the containment check from "path is inside config_dir" to "path is inside at least one allowed scope root." For any deployment that already had LANGFLOW_RESTRICT_LOCAL_FILE_ACCESS=true before this change, files previously saved/read directly under config_dir (outside any user/flow subdirectory) will now be rejected — silently breaking SaveToFileComponent append-mode continuity (a new, differently-scoped file gets created instead of appending) and any component reading a previously-valid absolute path from that era.

This is the right security direction, but worth a migration/upgrade note (or a one-time compatibility fallback) so operators upgrading with the flag already on aren't surprised by files "disappearing" or appends silently restarting.

🤖 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/file_path_security.py` around lines 133 - 177, The
updated scope containment in enforce_local_file_access breaks existing
restricted deployments using files directly under config_dir. Add an explicit
migration or upgrade note describing this behavior and the required relocation
of legacy files into user/flow storage scopes; do not broaden the security check
or add a compatibility fallback unless the project’s established migration
policy requires it.
🤖 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.

Inline comments:
In `@src/backend/base/langflow/api/utils/core.py`:
- Around line 165-175: Update _contains_url_credentials to inspect network-path
and relative URL references by removing the absolute-scheme requirement, while
preserving safe handling of invalid values. Detect credentials in parsed
userinfo and secret-named parameters from both query and fragment components. In
src/backend/tests/unit/api/v1/test_public_flow_secret_scrub.py lines 60-65, add
endpoint regressions covering ?api_key=..., `#access_token`=..., and
//user:password@host.

In `@src/backend/tests/unit/api/v1/test_public_flow_secret_scrub.py`:
- Around line 60-65: Expand the credential-scrubbing tests around the
authenticated_url fixture to cover absolute URLs with secret query parameters,
relative and network-path URLs, and secret fragments. Assert that each
credential value is scrubbed while the URL structure remains intact, exercising
the parse_qsl branch and the leak fixed in core.py.

In `@src/backend/tests/unit/test_docker_security_defaults.py`:
- Around line 15-20: Add an assertion to
test_published_images_enable_mcp_hardening for
LANGFLOW_MCP_SERVER_ALLOWED_PACKAGES=mcp-proxy,lfx, ensuring every
PUBLISHED_DOCKERFILES image preserves the documented package-runner allowlist
alongside the existing hardening settings.

In `@src/lfx/src/lfx/_assets/component_index.json`:
- Around line 7049-7050: Remove the character-based file path blocklist in
_process_docling_subprocess_impl, including the check for semicolons, pipes,
ampersands, and backticks. Pass the validated string path through the existing
argument-list subprocess invocation without rejecting legitimate filenames,
while preserving the current subprocess isolation and JSON stdin handling.
- Around line 7049-7050: The subprocess handling in
_process_docling_subprocess_impl can deadlock because stdout and stderr are read
only after proc.poll() exits, allowing large child output to fill the pipes.
Update this method to continuously drain both streams while the child runs, or
redirect the child output to a temporary file, while preserving the timeout,
heartbeat logging, and existing JSON/error processing.

In `@src/lfx/src/lfx/base/mcp/security.py`:
- Around line 576-587: Update the wrapped-command handling around
_validate_package_runner and _validate_interpreter_invocation to apply the
selected Docker policy to nested_args whenever nested_base == "docker", so
wrapped commands such as sh -c docker invocations receive the same hardened or
lenient validation as top-level Docker commands. Add a regression test covering
a wrapped Docker command with a host-root volume mount.
- Around line 366-373: Update the shell-wrapper validation around
_is_shell_exec_flag and its corresponding logic near the other referenced
location so -c or /c is accepted only when it appears in the expected leading
position before any script operand. Do not allow later flags, such as those
following /tenant/evil.sh, to bypass the approved-command check; preserve
rejection of direct shell scripts.

In `@src/lfx/src/lfx/components/models_and_agents/mcp_component.py`:
- Around line 369-387: The update_build_config flow still accesses the shared
servers cache based only on use_cache, allowing unauthenticated cross-request
reuse. In update_build_config, derive and use the same use_shared_cache
condition as update_tool_list by requiring _mcp_cache_user_id() to be set before
reading or writing self._shared_component_cache["servers"], while preserving
per-instance caching. Add a regression test covering unauthenticated
build_config requests and asserting the shared cache is neither read nor
updated.

In `@src/lfx/tests/unit/utils/test_file_path_security.py`:
- Around line 133-138: Update test_reserved_secret_file_blocked and the related
reserved-file tests so each candidate remains within an allowed scope root while
also matching _reserved_secret_paths. Configure scope_ids and directory layout
accordingly, then assert LocalFileAccessError contains the “server-managed file”
message to verify the reserved-file check, not generic scope rejection.

---

Nitpick comments:
In `@src/lfx/src/lfx/utils/file_path_security.py`:
- Around line 133-177: The updated scope containment in
enforce_local_file_access breaks existing restricted deployments using files
directly under config_dir. Add an explicit migration or upgrade note describing
this behavior and the required relocation of legacy files into user/flow storage
scopes; do not broaden the security check or add a compatibility fallback unless
the project’s established migration policy requires it.

In `@src/lfx/tests/unit/services/settings/test_settings_composition.py`:
- Around line 252-255: Add an assertion for
settings.mcp_server_interpreter_hardening being False in the settings
composition test, alongside the existing security-default assertions. Ensure the
test verifies the documented single-tenant compatibility default without
changing the other assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 167f2683-c1e3-4e25-aeea-9d6547af57c3

📥 Commits

Reviewing files that changed from the base of the PR and between e22c931 and 359344b.

📒 Files selected for processing (39)
  • docker/build_and_push.Dockerfile
  • docker/build_and_push_backend.Dockerfile
  • docker/build_and_push_base.Dockerfile
  • docker/build_and_push_ep.Dockerfile
  • docker/build_and_push_with_extras.Dockerfile
  • docs/docs/Develop/api-keys-and-authentication.mdx
  • src/backend/base/langflow/agentic/flows/SystemMessageGen.json
  • src/backend/base/langflow/agentic/flows/TemplateAssistant.json
  • src/backend/base/langflow/api/utils/core.py
  • src/backend/base/langflow/initial_setup/starter_projects/Content Aggregator.json
  • src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json
  • src/backend/base/langflow/initial_setup/starter_projects/Hybrid Search RAG.json
  • src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json
  • src/backend/base/langflow/initial_setup/starter_projects/Structured Data Analysis Agent.json
  • src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json
  • src/backend/tests/unit/agentic/flows/test_mcp_security_snapshots.py
  • src/backend/tests/unit/api/v1/test_public_flow_secret_scrub.py
  • src/backend/tests/unit/components/files_and_knowledge/test_directory_component.py
  • src/backend/tests/unit/components/models_and_agents/test_mcp_component_cache.py
  • src/backend/tests/unit/components/models_and_agents/test_mcp_component_dynamic.py
  • src/backend/tests/unit/test_docker_security_defaults.py
  • src/lfx/src/lfx/_assets/component_index.json
  • src/lfx/src/lfx/base/data/base_file.py
  • src/lfx/src/lfx/base/mcp/security.py
  • src/lfx/src/lfx/components/data_source/csv_to_data.py
  • src/lfx/src/lfx/components/data_source/json_to_data.py
  • src/lfx/src/lfx/components/files_and_knowledge/directory.py
  • src/lfx/src/lfx/components/files_and_knowledge/file.py
  • src/lfx/src/lfx/components/files_and_knowledge/save_file.py
  • src/lfx/src/lfx/components/langchain_utilities/csv_agent.py
  • src/lfx/src/lfx/components/langchain_utilities/json_agent.py
  • src/lfx/src/lfx/components/langchain_utilities/openapi.py
  • src/lfx/src/lfx/components/models_and_agents/mcp_component.py
  • src/lfx/src/lfx/services/settings/groups/mcp.py
  • src/lfx/src/lfx/services/settings/groups/security.py
  • src/lfx/src/lfx/utils/file_path_security.py
  • src/lfx/tests/unit/mcp/test_mcp_stdio_security.py
  • src/lfx/tests/unit/services/settings/test_settings_composition.py
  • src/lfx/tests/unit/utils/test_file_path_security.py

Comment thread src/backend/base/langflow/api/utils/core.py Outdated
Comment thread src/backend/tests/unit/api/v1/test_public_flow_secret_scrub.py
Comment thread src/backend/tests/unit/test_docker_security_defaults.py
Comment thread src/lfx/src/lfx/_assets/component_index.json Outdated
Comment thread src/lfx/src/lfx/base/mcp/security.py
Comment thread src/lfx/src/lfx/base/mcp/security.py
Comment thread src/lfx/src/lfx/components/models_and_agents/mcp_component.py
Comment thread src/lfx/tests/unit/utils/test_file_path_security.py
@github-actions github-actions Bot removed the bug Something isn't working label Jul 13, 2026
@erichare

Copy link
Copy Markdown
Collaborator Author

Also addressed the two CodeRabbit review-summary nitpicks in 3e3b70b: added the interpreter-hardening default assertion and documented the restricted-file upgrade/relocation behavior.

@github-actions github-actions Bot added the bug Something isn't working label Jul 13, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026
…rdening-p1-followups

# Conflicts:
#	src/lfx/src/lfx/_assets/component_index.json
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Build successful! ✅
Deploying docs draft.
Deploy successful! View draft

@erichare erichare merged commit d536c89 into release-1.11.0 Jul 13, 2026
169 checks passed
@erichare erichare deleted the security-hardening-p1-followups branch July 13, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant