Skip to content

fix(lambda-filter): sandbox model-generated lambda in Smart Transform#13736

Open
Jiangrong-W wants to merge 2 commits into
langflow-ai:mainfrom
Jiangrong-W:harness-fix/langflow-lambda-filter-llm-response-eval
Open

fix(lambda-filter): sandbox model-generated lambda in Smart Transform#13736
Jiangrong-W wants to merge 2 commits into
langflow-ai:mainfrom
Jiangrong-W:harness-fix/langflow-lambda-filter-llm-response-eval

Conversation

@Jiangrong-W

@Jiangrong-W Jiangrong-W commented Jun 18, 2026

Copy link
Copy Markdown

The Smart Transform component (LambdaFilterComponent) asked the LLM to return a Python lambda and passed the model-emitted string straight to eval() with full interpreter builtins. Validation only checked that the text started with lambda and contained :, so a model that was steered (e.g. via prompt injection in the data being transformed) into emitting lambda x: __import__("os").system(...) would run arbitrary code inside the Langflow server process on the authenticated build/run path.

Harden the evaluation instead of removing the feature:

  • Parse the generated lambda with ast.parse and require it to be a single Lambda expression.
  • Walk the AST and reject imports, dunder attribute/name access (the classic ().__class__...__subclasses__() escape), and an explicit denylist of dangerous builtin names (__import__, eval, exec, open, getattr, globals, ...).
  • Compile and evaluate with a restricted __builtins__ namespace that exposes only safe data-shaping helpers (len, sorted, comprehensions, etc.) and no module globals.

Legitimate data-transformation lambdas (filtering, mapping, slicing, attribute/method calls on the input) keep working unchanged; the existing integration tests pass. Errors remain ValueError so existing handlers and flow validation continue to catch them.

Adds regression tests pinning both the allowed and rejected lambda sets.

Summary by CodeRabbit

  • Tests
    • Added security tests for lambda expression validation and execution.
  • Refactor
    • Lambda expressions are now securely validated and executed to prevent malicious code injection.

The Smart Transform component (LambdaFilterComponent) asked the LLM to
return a Python lambda and passed the model-emitted string straight to
`eval()` with full interpreter builtins. Validation only checked that the
text started with `lambda` and contained `:`, so a model that was steered
(e.g. via prompt injection in the data being transformed) into emitting
`lambda x: __import__("os").system(...)` would run arbitrary code inside
the Langflow server process on the authenticated build/run path.

Harden the evaluation instead of removing the feature:
- Parse the generated lambda with `ast.parse` and require it to be a
  single `Lambda` expression.
- Walk the AST and reject imports, dunder attribute/name access (the
  classic `().__class__...__subclasses__()` escape), and an explicit
  denylist of dangerous builtin names (`__import__`, `eval`, `exec`,
  `open`, `getattr`, `globals`, ...).
- Compile and evaluate with a restricted `__builtins__` namespace that
  exposes only safe data-shaping helpers (`len`, `sorted`, comprehensions,
  etc.) and no module globals.

Legitimate data-transformation lambdas (filtering, mapping, slicing,
attribute/method calls on the input) keep working unchanged; the existing
integration tests pass. Errors remain `ValueError` so existing handlers
and flow validation continue to catch them.

Adds regression tests pinning both the allowed and rejected lambda sets.

Signed-off-by: christop <825583681@qq.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 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: 84b863d5-129c-4c48-b92a-57c5576864d3

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

Replaces direct eval(lambda_text) in LambdaFilterComponent with an AST-based sandbox. Adds a safe builtins allowlist, a forbidden-name set, an attribute dunder guard, an AST walker that rejects unsafe nodes, and a _compile_lambda method that compiles under restricted globals. A new TestCompileLambdaSecurity test class validates both the allow and block paths.

Changes

Lambda Evaluation Sandbox

Layer / File(s) Summary
AST sandbox: allowlist, validation, compilation, and wire-up
src/lfx/src/lfx/components/llm_operations/lambda_filter.py
Adds ast import, _SAFE_LAMBDA_BUILTINS allowlist, _FORBIDDEN_LAMBDA_NAMES set, and _attribute_is_allowed guard. Introduces _assert_lambda_ast_is_safe (AST walker rejecting imports, unsafe dunders, forbidden names) and _compile_lambda (parses, validates, compiles with {"__builtins__": _SAFE_LAMBDA_BUILTINS}, no locals). Lambda extraction switches from eval(lambda_text) to _compile_lambda(lambda_text).
Security test suite
src/backend/tests/unit/components/llm_operations/test_lambda_filter.py
Adds TestCompileLambdaSecurity with LEGITIMATE_LAMBDAS and MALICIOUS_LAMBDAS parameter lists. Tests verify that legitimate lambdas parse and execute correctly, malicious lambdas raise ValueError at parse time, a malicious import raises ValueError during processing, and _SAFE_LAMBDA_BUILTINS includes len/sorted while excluding open, eval, exec, __import__, compile, getattr, globals.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

bug

Suggested reviewers

  • Adam-Aghili
🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: hardening the lambda evaluation in Smart Transform by adding sandboxing.
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 Coverage For New Implementations ✅ Passed PR includes comprehensive test coverage: new TestCompileLambdaSecurity class with 4 test methods covering 17+ test cases (7 legitimate lambdas, 10 malicious lambdas, plus E2E and allowlist tests),...
Test Quality And Coverage ✅ Passed Tests cover main functionality (legitimate and malicious lambdas), validate actual behavior (not smoke tests), follow pytest patterns, include security regression testing with parametrized cases, a...
Test File Naming And Structure ✅ Passed Test file follows correct patterns: test_lambda_filter.py with pytest structure; 18 organized test classes; 51 descriptive test methods using test_should_* naming; parametrized tests with 7 legitim...
Excessive Mock Usage Warning ✅ Passed TestCompileLambdaSecurity uses zero mocks (0 @patch, 0 MagicMock, 0 AsyncMock). Tests instantiate real component objects and directly exercise actual lambda parsing/validation logic with 17 paramet...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actions github-actions Bot added the bug Something isn't working label Jun 18, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jun 18, 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: 1

🧹 Nitpick comments (1)
src/backend/tests/unit/components/llm_operations/test_lambda_filter.py (1)

124-137: 💤 Low value

Consider adding a format string edge case to test coverage.

Python's format string protocol can access attributes on arguments without triggering the AST dunder check (the dunder is inside a string constant). While this can't execute code (format can't call methods), it could leak class information:

'lambda x: "{0.__class__.__name__}".format(x)'

Adding this to either LEGITIMATE_LAMBDAS (if info disclosure is acceptable) or MALICIOUS_LAMBDAS (if you want to block it) would document the expected behavior and prevent future regressions.

🤖 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/llm_operations/test_lambda_filter.py`
around lines 124 - 137, Add a format string edge case test to document expected
behavior for attribute access via Python's format string protocol. Include the
lambda expression that uses format string notation to access class information
(e.g., "{0.__class__.__name__}".format(x)) to either the LEGITIMATE_LAMBDAS or
MALICIOUS_LAMBDAS list depending on whether information disclosure is acceptable
in your security policy. This test case prevents future regressions and
clarifies how the lambda filter handles format string-based attribute access
that bypasses direct dunder notation checks.
🤖 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/lfx/src/lfx/components/llm_operations/lambda_filter.py`:
- Around line 94-113: The _FORBIDDEN_LAMBDA_NAMES frozenset is missing
"breakpoint" as a forbidden builtin, which is a security concern since the
breakpoint builtin (Python 3.7+) can invoke an interactive debugger and allow
inspection of process state. Add "breakpoint" to the _FORBIDDEN_LAMBDA_NAMES
frozenset to maintain consistency with the explicit parse-time rejection pattern
and keep the security contract auditable.

---

Nitpick comments:
In `@src/backend/tests/unit/components/llm_operations/test_lambda_filter.py`:
- Around line 124-137: Add a format string edge case test to document expected
behavior for attribute access via Python's format string protocol. Include the
lambda expression that uses format string notation to access class information
(e.g., "{0.__class__.__name__}".format(x)) to either the LEGITIMATE_LAMBDAS or
MALICIOUS_LAMBDAS list depending on whether information disclosure is acceptable
in your security policy. This test case prevents future regressions and
clarifies how the lambda filter handles format string-based attribute access
that bypasses direct dunder notation checks.
🪄 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: 8157796f-39f3-4225-88e0-23dac243bb3a

📥 Commits

Reviewing files that changed from the base of the PR and between 43d3510 and a080f0c.

📒 Files selected for processing (2)
  • src/backend/tests/unit/components/llm_operations/test_lambda_filter.py
  • src/lfx/src/lfx/components/llm_operations/lambda_filter.py

Comment on lines +94 to +113
# Builtin/global names that must never be referenced from a generated lambda.
# Even though they are already absent from ``_SAFE_LAMBDA_BUILTINS`` (so they
# would raise ``NameError`` at runtime), rejecting them at parse time gives a
# clear, fail-fast error and keeps the security contract explicit and auditable.
_FORBIDDEN_LAMBDA_NAMES: frozenset[str] = frozenset(
{
"__import__",
"compile",
"delattr",
"eval",
"exec",
"getattr",
"globals",
"input",
"locals",
"open",
"setattr",
"vars",
}
)

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider adding breakpoint to the forbidden names list.

The breakpoint builtin (Python 3.7+) can invoke an interactive debugger, potentially allowing an attacker to inspect state or interact with the process. While it's not in _SAFE_LAMBDA_BUILTINS (so it would raise NameError at runtime), adding it here maintains the pattern of explicit parse-time rejection and keeps the security contract auditable.

🛡️ Suggested addition
 _FORBIDDEN_LAMBDA_NAMES: frozenset[str] = frozenset(
     {
         "__import__",
+        "breakpoint",
         "compile",
         "delattr",
         "eval",
🤖 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/llm_operations/lambda_filter.py` around lines 94 -
113, The _FORBIDDEN_LAMBDA_NAMES frozenset is missing "breakpoint" as a
forbidden builtin, which is a security concern since the breakpoint builtin
(Python 3.7+) can invoke an interactive debugger and allow inspection of process
state. Add "breakpoint" to the _FORBIDDEN_LAMBDA_NAMES frozenset to maintain
consistency with the explicit parse-time rejection pattern and keep the security
contract auditable.

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