VYPR
Critical severityNVD Advisory· Published Feb 4, 2026· Updated Feb 5, 2026

n8n is vulnerable to Python sandbox escape

CVE-2026-25115

Description

n8n is an open source workflow automation platform. Prior to version 2.4.8, a vulnerability in the Python Code node allows authenticated users to break out of the Python sandbox environment and execute code outside the intended security boundary. This issue has been patched in version 2.4.8.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
n8nnpm
< 2.4.82.4.8

Affected products

1

Patches

1
8607d372f78c

refactor(core): Improve Python match extraction handling (#24975)

https://github.com/n8n-io/n8nIván OvejeroJan 28, 2026via ghsa
3 files changed · +75 0
  • packages/@n8n/task-runner-python/src/constants.py+1 0 modified
    @@ -179,6 +179,7 @@
     ERROR_DYNAMIC_IMPORT = (
         "Dynamic __import__() calls are not allowed for security reasons."
     )
    +ERROR_MATCH_PATTERN_ATTRIBUTE = "Match pattern extracting attribute '{attr}' is disallowed, because it can be used to bypass security restrictions."
     ERROR_WINDOWS_NOT_SUPPORTED = (
         "Error: This task runner is not supported on Windows. "
         "Please use a Unix-like system (Linux or macOS)."
    
  • packages/@n8n/task-runner-python/src/task_analyzer.py+12 0 modified
    @@ -14,6 +14,7 @@
         ERROR_NAME_MANGLED_ATTRIBUTE,
         ERROR_DYNAMIC_IMPORT,
         ERROR_DANGEROUS_STRING_PATTERN,
    +    ERROR_MATCH_PATTERN_ATTRIBUTE,
         BLOCKED_ATTRIBUTES,
         BLOCKED_NAMES,
     )
    @@ -140,6 +141,17 @@ def visit_Constant(self, node: ast.Constant) -> None:
     
             self.generic_visit(node)
     
    +    def visit_MatchClass(self, node: ast.MatchClass) -> None:
    +        """Detect match patterns that extract blocked attributes, e.g. `case AttributeError(obj=x)`"""
    +
    +        for attr in node.kwd_attrs:
    +            if attr in BLOCKED_ATTRIBUTES:
    +                self._add_violation(
    +                    node.lineno, ERROR_MATCH_PATTERN_ATTRIBUTE.format(attr=attr)
    +                )
    +
    +        self.generic_visit(node)
    +
         def _check_format_string(self, s: str, lineno: int) -> None:
             """Check if a string contains format patterns that access blocked attributes."""
     
    
  • packages/@n8n/task-runner-python/tests/unit/test_task_analyzer.py+62 0 modified
    @@ -242,6 +242,68 @@ def test_fstring_blocked_attributes_detected(self, analyzer: TaskAnalyzer) -> No
                 assert "disallowed" in exc_info.value.description.lower()
     
     
    +class TestMatchPatternValidation(TestTaskAnalyzer):
    +    def test_match_pattern_with_blocked_attributes_blocked(
    +        self, analyzer: TaskAnalyzer
    +    ) -> None:
    +        attempts = [
    +            """
    +ex = None
    +try:
    +    pass
    +except Exception as e:
    +    ex = e
    +match ex:
    +    case AttributeError(obj=rip):
    +        pass
    +""",
    +            """
    +match error:
    +    case ValueError(obj=x):
    +        pass
    +""",
    +            """
    +match e:
    +    case Exception(__traceback__=tb):
    +        pass
    +""",
    +        ]
    +
    +        for code in attempts:
    +            with pytest.raises(SecurityViolationError) as exc_info:
    +                analyzer.validate(code)
    +            assert "disallowed" in exc_info.value.description.lower()
    +
    +    def test_safe_match_patterns_allowed(self, analyzer: TaskAnalyzer) -> None:
    +        safe_patterns = [
    +            """
    +match value:
    +    case 1:
    +        pass
    +    case "hello":
    +        pass
    +""",
    +            """
    +match point:
    +    case Point(x=x, y=y):
    +        pass
    +""",
    +            """
    +match data:
    +    case {"key": value}:
    +        pass
    +""",
    +            """
    +match result:
    +    case [first, *rest]:
    +        pass
    +""",
    +        ]
    +
    +        for code in safe_patterns:
    +            analyzer.validate(code)
    +
    +
     class TestAllowAll(TestTaskAnalyzer):
         def test_allow_all_bypasses_validation(self) -> None:
             security_config = SecurityConfig(
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.