fix(core): Keep submodules of safe modules in native Python runner (#20284)

This commit is contained in:
Iván Ovejero
2025-10-02 09:54:24 +02:00
committed by GitHub
parent 44731a98ad
commit 7eb8a32450
4 changed files with 36 additions and 1 deletions
@@ -405,8 +405,12 @@ class TaskExecutor:
else:
safe_modules.update(external_allow)
# keep modules marked as safe and submodules of those
modules_to_remove = [
name for name in sys.modules.keys() if name not in safe_modules
name
for name in sys.modules.keys()
if name not in safe_modules
and not any(name.startswith(safe + ".") for safe in safe_modules)
]
for module_name in modules_to_remove:
@@ -27,9 +27,11 @@ class TaskRunnerManager:
self,
task_broker_url: str = LOCAL_TASK_BROKER_URL,
graceful_shutdown_timeout: float | None = None,
custom_env: dict[str, str] | None = None,
):
self.task_broker_url = task_broker_url
self.graceful_shutdown_timeout = graceful_shutdown_timeout
self.custom_env = custom_env or {}
self.subprocess: asyncio.subprocess.Process | None = None
self.stdout_buffer: list[str] = []
self.stderr_buffer: list[str] = []
@@ -49,6 +51,7 @@ class TaskRunnerManager:
self.graceful_shutdown_timeout
)
env_vars["PYTHONPATH"] = str(project_root)
env_vars.update(self.custom_env)
self.subprocess = await asyncio.create_subprocess_exec(
sys.executable,
@@ -27,6 +27,18 @@ async def broker():
await broker.stop()
@pytest_asyncio.fixture
async def manager_with_stdlib_wildcard():
manager = TaskRunnerManager(
custom_env={
"N8N_RUNNERS_STDLIB_ALLOW": "*",
}
)
await manager.start()
yield manager
await manager.stop()
def create_task_settings(
code: str,
node_mode: str,
@@ -230,3 +230,19 @@ async def test_timeout_during_execution(broker, manager):
assert error_msg["taskId"] == task_id
assert "timed out" in error_msg["error"]["message"].lower()
@pytest.mark.asyncio
async def test_stdlib_submodules_with_wildcard(broker, manager_with_stdlib_wildcard):
task_id = nanoid()
code = textwrap.dedent("""
from collections.abc import Iterable
result = isinstance([1, 2, 3], Iterable)
return [{"json": {"is_iterable": result}}]
""")
task_settings = create_task_settings(code=code, node_mode="all_items")
await broker.send_task(task_id=task_id, task_settings=task_settings)
result = await wait_for_task_done(broker, task_id)
assert result["data"]["result"] == [{"json": {"is_iterable": True}}]