From 78fd5f3761d79ca5aaec7868d639cc3270ebff7f Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:17:25 +0000 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다. --- saas_web.py | 2 +- tests/test_saas_web.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/saas_web.py b/saas_web.py index 63265e94..071b1419 100644 --- a/saas_web.py +++ b/saas_web.py @@ -114,7 +114,7 @@ async def require_api_key(request: Request, call_next): if configured_keys and not (request.method == "GET" and request.url.path == "/"): provided_key = request.headers.get("x-api-key", "") if not any( - hmac.compare_digest(provided_key, key) for key in configured_keys + hmac.compare_digest(provided_key.encode("utf-8"), key.encode("utf-8")) for key in configured_keys ): return JSONResponse( status_code=401, diff --git a/tests/test_saas_web.py b/tests/test_saas_web.py index 3b57e033..150c19ab 100644 --- a/tests/test_saas_web.py +++ b/tests/test_saas_web.py @@ -688,6 +688,30 @@ def _post_shrink(self, headers=None): headers=headers or {}, ) + def test_non_ascii_header_does_not_crash(self): + import asyncio + import json + from starlette.requests import Request + from saas_web import require_api_key + + async def dummy_call_next(request: Request): + from starlette.responses import JSONResponse + return JSONResponse({"status": "ok"}) + + with patch.dict(os.environ, {"CODEC_CARVER_API_KEYS": "secret-key"}): + scope = { + "type": "http", + "method": "POST", + "path": "/shrink", + "headers": [(b"x-api-key", "secret-key😀".encode("utf-8"))], + } + req = Request(scope) + res = asyncio.new_event_loop().run_until_complete(require_api_key(req, dummy_call_next)) + + self.assertEqual(res.status_code, 401) + body = json.loads(res.body.decode("utf-8")) + self.assertEqual(body, {"error": "Invalid or missing API key"}) + def test_no_env_var_leaves_endpoints_open(self): with patch.dict(os.environ): os.environ.pop("CODEC_CARVER_API_KEYS", None) From 1addd7e8b6a3d9f1d7fc906e9c961787e15ac88b Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:40:44 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다. --- tests/test_saas_web.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/test_saas_web.py b/tests/test_saas_web.py index 150c19ab..c70f9b5d 100644 --- a/tests/test_saas_web.py +++ b/tests/test_saas_web.py @@ -699,6 +699,7 @@ async def dummy_call_next(request: Request): return JSONResponse({"status": "ok"}) with patch.dict(os.environ, {"CODEC_CARVER_API_KEYS": "secret-key"}): + # Test non-ASCII header fails closed with 401 (not 500) scope = { "type": "http", "method": "POST", @@ -706,12 +707,35 @@ async def dummy_call_next(request: Request): "headers": [(b"x-api-key", "secret-key😀".encode("utf-8"))], } req = Request(scope) - res = asyncio.new_event_loop().run_until_complete(require_api_key(req, dummy_call_next)) - + res = asyncio.run(require_api_key(req, dummy_call_next)) self.assertEqual(res.status_code, 401) body = json.loads(res.body.decode("utf-8")) self.assertEqual(body, {"error": "Invalid or missing API key"}) + # Test matching configured ASCII key success + scope_success = { + "type": "http", + "method": "POST", + "path": "/shrink", + "headers": [(b"x-api-key", b"secret-key")], + } + req_success = Request(scope_success) + res_success = asyncio.run(require_api_key(req_success, dummy_call_next)) + self.assertEqual(res_success.status_code, 200) + body_success = json.loads(res_success.body.decode("utf-8")) + self.assertEqual(body_success, {"status": "ok"}) + + # Test missing/incorrect ASCII 401 + scope_missing = { + "type": "http", + "method": "POST", + "path": "/shrink", + "headers": [(b"x-api-key", b"wrong-key")], + } + req_missing = Request(scope_missing) + res_missing = asyncio.run(require_api_key(req_missing, dummy_call_next)) + self.assertEqual(res_missing.status_code, 401) + def test_no_env_var_leaves_endpoints_open(self): with patch.dict(os.environ): os.environ.pop("CODEC_CARVER_API_KEYS", None) From 426660f7e6258314f3d7836276f1629128f70f30 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:22:56 +0000 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다. From f8be5e31d4e827eeac4d01077ff37c2884e47703 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:09:18 +0000 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다. From 6e1194aa3616736187ef3ffef0a1fa89927a8290 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:23:53 +0000 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다. From 2d68af060930ee88e137cf73133624c59cdd8a98 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 7 Sep 2026 02:03:14 +0000 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20500=20Internal=20Server=20Error=20in=20API=20Key=20?= =?UTF-8?q?Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Key 인증 로직에서 `hmac.compare_digest`에 non-ASCII 문자열이 전달될 때 발생하는 `TypeError`를 수정했습니다. 두 문자열을 비교하기 전에 모두 `utf-8`로 인코딩하도록 변경하여 500 내부 서버 오류(DoS 공격 가능성)를 방지했습니다. 관련된 유닛 테스트 케이스도 함께 추가했습니다.