From 30fa2b07db5cacf5c8e507e48e2d0d6383179dfa Mon Sep 17 00:00:00 2001 From: stroblme Date: Wed, 14 Oct 2026 23:31:47 +0200 Subject: [PATCH] Remote exception names are cut to 128 identifier characters before a class is built for them --- backend/fluksio/flow/workers.py | 4 ++++ backend/tests/flow/test_remote.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/fluksio/flow/workers.py b/backend/fluksio/flow/workers.py index 7cb4b78..e678ae0 100644 --- a/backend/fluksio/flow/workers.py +++ b/backend/fluksio/flow/workers.py @@ -17,6 +17,7 @@ import itertools import logging import os import queue +import re import select import signal import subprocess @@ -102,6 +103,8 @@ class NodeCancelled(RemoteError): #: node code chose, so an unbounded cache is a node raising a class named after #: its loop counter. MAX_REMOTE_TYPES = 256 +#: How long one of those names may be. A remote worker's comes off the wire. +MAX_REMOTE_NAME = 128 _remote_types: dict[str, type[RemoteError]] = {} @@ -113,6 +116,7 @@ def _remote_class(name: str) -> type[RemoteError]: the author still reads ``ValueError: bad input`` rather than the name of the transport that carried it. """ + name = re.sub(r"\W", "_", name[:MAX_REMOTE_NAME]) or "RemoteError" cls = _remote_types.get(name) if cls is not None: return cls diff --git a/backend/tests/flow/test_remote.py b/backend/tests/flow/test_remote.py index 8edeb4b..16b6177 100644 --- a/backend/tests/flow/test_remote.py +++ b/backend/tests/flow/test_remote.py @@ -21,7 +21,12 @@ from fluksio.flow.remote import ( RemoteWorkerHub, WorkerInventory, ) -from fluksio.flow.workers import NodeTimeout, RemoteError +from fluksio.flow.workers import ( + MAX_REMOTE_NAME, + NodeTimeout, + RemoteError, + _remote_class, +) @pytest.fixture @@ -149,6 +154,13 @@ def test_a_failure_keeps_its_class_across_the_socket(loop): assert str(caught[0]) == "bad input" +def test_a_class_name_off_the_wire_is_bounded_and_identifier_safe(): + cls = _remote_class("Bad Name\n" + "x" * 10_000) + assert cls.__name__.isidentifier() + assert len(cls.__name__) == MAX_REMOTE_NAME + assert issubclass(cls, RemoteError) + + def test_a_worker_that_goes_away_fails_the_call_rather_than_hanging(loop): hub = RemoteWorkerHub() worker, socket = attach(hub, loop)