From 0ba19f1b42a6f54834c1658b18e45da7b4138df2 Mon Sep 17 00:00:00 2001 From: Nick Franck <46548427+CyMule@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:18:46 -0400 Subject: [PATCH 1/2] fix(auth): return a generic invalid-key error --- CHANGELOG.md | 6 ++++++ prepline_general/api/__version__.py | 2 +- prepline_general/api/general.py | 2 +- test_general/api/test_app.py | 16 ++++++++++------ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b75a37ce..4ebc78bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.1.9 + +### Security + +- **Avoid reflecting invalid API keys**: authentication failures now return a generic error message instead of including the submitted credential in the response and request logs. + ## 0.1.8 ### Features diff --git a/prepline_general/api/__version__.py b/prepline_general/api/__version__.py index 93b52aec..4eeb8fc3 100644 --- a/prepline_general/api/__version__.py +++ b/prepline_general/api/__version__.py @@ -1 +1 @@ -__version__ = "0.1.8" # pragma: no cover +__version__ = "0.1.9" # pragma: no cover diff --git a/prepline_general/api/general.py b/prepline_general/api/general.py index 8a7b3cb2..2b4a46e8 100644 --- a/prepline_general/api/general.py +++ b/prepline_general/api/general.py @@ -654,7 +654,7 @@ def general_partition( api_key = request.headers.get("unstructured-api-key") if api_key != api_key_env: raise HTTPException( - detail=f"API key {api_key} is invalid", status_code=status.HTTP_401_UNAUTHORIZED + detail="API key is invalid", status_code=status.HTTP_401_UNAUTHORIZED ) accept_type = request.headers.get("Accept") diff --git a/test_general/api/test_app.py b/test_general/api/test_app.py index ff5342d7..8f5684bf 100644 --- a/test_general/api/test_app.py +++ b/test_general/api/test_app.py @@ -1,4 +1,5 @@ import io +import logging import os import tempfile import uuid @@ -578,7 +579,7 @@ def test_general_api_returns_503(monkeypatch): assert response.status_code == 503 -def test_general_api_returns_401(monkeypatch): +def test_general_api_returns_401(monkeypatch, caplog): """ When UNSTRUCTURED_API_KEY is set, return a 401 if the unstructured-api-key header does not match """ @@ -596,13 +597,16 @@ def test_general_api_returns_401(monkeypatch): client = TestClient(app) test_file = Path("sample-docs") / "fake-xml.xml" - response = client.post( - MAIN_API_ROUTE, - files=[("files", (str(test_file), open(test_file, "rb")))], - headers={"unstructured-api-key": "helloworld"}, - ) + with caplog.at_level(logging.ERROR, logger="unstructured_api"): + response = client.post( + MAIN_API_ROUTE, + files=[("files", (str(test_file), open(test_file, "rb")))], + headers={"unstructured-api-key": "helloworld"}, + ) assert response.status_code == 401 + assert response.json() == {"detail": "API key is invalid"} + assert not any("helloworld" in record.getMessage() for record in caplog.records) class MockResponse: From 688606d89c2337e39c6084fdb5baec82eef6929a Mon Sep 17 00:00:00 2001 From: Nick Franck <46548427+CyMule@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:05:16 -0400 Subject: [PATCH 2/2] test(auth): assert against full captured log output for invalid key Check the complete formatted log text so exception messages and tracebacks are covered, and reuse the existing client and sample file for the invalid-key request. --- test_general/api/test_app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test_general/api/test_app.py b/test_general/api/test_app.py index 8f5684bf..9f76207d 100644 --- a/test_general/api/test_app.py +++ b/test_general/api/test_app.py @@ -595,8 +595,6 @@ def test_general_api_returns_401(monkeypatch, caplog): assert response.status_code == 200 - client = TestClient(app) - test_file = Path("sample-docs") / "fake-xml.xml" with caplog.at_level(logging.ERROR, logger="unstructured_api"): response = client.post( MAIN_API_ROUTE, @@ -606,7 +604,7 @@ def test_general_api_returns_401(monkeypatch, caplog): assert response.status_code == 401 assert response.json() == {"detail": "API key is invalid"} - assert not any("helloworld" in record.getMessage() for record in caplog.records) + assert "helloworld" not in caplog.text class MockResponse: