mirror of
https://github.com/n8n-io/n8n.git
synced 2025-11-20 17:46:34 +00:00
fix(core): Protect against name-mangling in native Python runner (#21482)
This commit is contained in:
parent
fbd60d2a07
commit
9a56529d5d
@ -122,7 +122,7 @@ TASK_REJECTED_REASON_OFFER_EXPIRED = (
|
||||
TASK_REJECTED_REASON_AT_CAPACITY = "No open task slots - runner already at capacity"
|
||||
|
||||
# Security
|
||||
BUILTINS_DENY_DEFAULT = "eval,exec,compile,open,input,breakpoint,getattr,object,type,vars,setattr,delattr,hasattr,dir,memoryview,__build_class__,globals,locals"
|
||||
BUILTINS_DENY_DEFAULT = "eval,exec,compile,open,input,breakpoint,getattr,object,type,vars,setattr,delattr,hasattr,dir,memoryview,__build_class__,globals,locals,license,help,credits,copyright"
|
||||
BLOCKED_NAMES = {
|
||||
"__loader__",
|
||||
"__builtins__",
|
||||
@ -183,6 +183,7 @@ ERROR_STDLIB_DISALLOWED = "Import of standard library module '{module}' is disal
|
||||
ERROR_EXTERNAL_DISALLOWED = "Import of external package '{module}' is disallowed. Allowed external packages: {allowed}"
|
||||
ERROR_DANGEROUS_NAME = "Access to name '{name}' is disallowed, because it can be used to bypass security restrictions."
|
||||
ERROR_DANGEROUS_ATTRIBUTE = "Access to attribute '{attr}' is disallowed, because it can be used to bypass security restrictions."
|
||||
ERROR_NAME_MANGLED_ATTRIBUTE = "Access to name-mangled attributes (pattern: _ClassName__attr) is disallowed for security reasons."
|
||||
ERROR_DYNAMIC_IMPORT = (
|
||||
"Dynamic __import__() calls are not allowed for security reasons."
|
||||
)
|
||||
|
||||
@ -10,6 +10,7 @@ from src.constants import (
|
||||
ERROR_RELATIVE_IMPORT,
|
||||
ERROR_DANGEROUS_NAME,
|
||||
ERROR_DANGEROUS_ATTRIBUTE,
|
||||
ERROR_NAME_MANGLED_ATTRIBUTE,
|
||||
ERROR_DYNAMIC_IMPORT,
|
||||
BLOCKED_ATTRIBUTES,
|
||||
BLOCKED_NAMES,
|
||||
@ -62,6 +63,11 @@ class SecurityValidator(ast.NodeVisitor):
|
||||
node.lineno, ERROR_DANGEROUS_ATTRIBUTE.format(attr=node.attr)
|
||||
)
|
||||
|
||||
if node.attr.startswith("_") and "__" in node.attr:
|
||||
parts = node.attr.split("__", 1)
|
||||
if len(parts) == 2 and parts[0].startswith("_"):
|
||||
self._add_violation(node.lineno, ERROR_NAME_MANGLED_ATTRIBUTE)
|
||||
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Call(self, node: ast.Call) -> None:
|
||||
|
||||
@ -136,6 +136,20 @@ class TestAttributeAccessValidation(TestTaskAnalyzer):
|
||||
for code in allowed_attributes:
|
||||
analyzer.validate(code)
|
||||
|
||||
def test_name_mangled_attributes_blocked(self, analyzer: TaskAnalyzer) -> None:
|
||||
exploit_attempts = [
|
||||
"license._Printer__filenames",
|
||||
"obj._SomeClass__private_attr",
|
||||
"help._Helper__name",
|
||||
"credits._Printer__data",
|
||||
"instance._MyClass__secret",
|
||||
]
|
||||
|
||||
for code in exploit_attempts:
|
||||
with pytest.raises(SecurityViolationError) as exc_info:
|
||||
analyzer.validate(code)
|
||||
assert "name-mangled" in exc_info.value.description.lower()
|
||||
|
||||
|
||||
class TestDynamicImportDetection(TestTaskAnalyzer):
|
||||
def test_various_dynamic_import_patterns(self, analyzer: TaskAnalyzer) -> None:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user