Skip to content

Commit 5e43c02

Browse files
committed
fix: read the error class name via type() in the sanitized envelopes
Instance __getattribute__ can intercept __class__ access; type() cannot be intercepted, so the class-name interpolation can never escape the handler.
1 parent fb74513 commit 5e43c02

2 files changed

Lines changed: 24 additions & 2 deletions

File tree

test/api/test_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,28 @@ def _raising_func() -> None:
694694
assert "<unrenderable error>" in body["status_code_text"]
695695

696696

697+
def test_invoke_survives_error_with_hostile_class_access():
698+
class _HostileClassError(Exception):
699+
status_code = 403
700+
701+
def __getattribute__(self, name: str):
702+
if name == "__class__":
703+
raise RuntimeError("__class__ exploded")
704+
return super().__getattribute__(name)
705+
706+
def _raising_func() -> None:
707+
raise _HostileClassError("original message")
708+
709+
client = TestClient(wrap_in_fastapi(func=_raising_func, plugin_id="mock_plugin"))
710+
711+
resp = client.post("/invoke")
712+
assert resp.status_code == 200
713+
body = resp.json()
714+
assert body["status_code"] == 403
715+
assert "_HostileClassError" in body["status_code_text"]
716+
assert "original message" in body["status_code_text"]
717+
718+
697719
def test_precheck_survives_error_whose_str_raises():
698720
def _unrenderable_precheck() -> None:
699721
raise _UnrenderableError()

unstructured_platform_plugins/etl_uvicorn/api_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ async def _stream_response():
253253
filedata_meta.model_dump()
254254
),
255255
status_code=status_code_of(e),
256-
status_code_text=f"[{e.__class__.__name__}] {_safe_str(e)}",
256+
status_code_text=f"[{type(e).__name__}] {_safe_str(e)}",
257257
failure_category=failure_category_of(e),
258258
).model_dump_json()
259259
+ "\n"
@@ -308,7 +308,7 @@ async def _stream_response():
308308
message_channels=message_channels,
309309
filedata_meta=filedata_meta_model.model_validate(filedata_meta.model_dump()),
310310
status_code=status_code_of(invoke_error),
311-
status_code_text=f"[{invoke_error.__class__.__name__}] {_safe_str(invoke_error)}",
311+
status_code_text=f"[{type(invoke_error).__name__}] {_safe_str(invoke_error)}",
312312
failure_category=failure_category_of(invoke_error),
313313
file_data=request_dict.get("file_data", None),
314314
)

0 commit comments

Comments
 (0)