Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2026-08-28 - [Sentinel: Unhandled hmac.compare_digest TypeError (DoS)]
**취약점:** `hmac.compare_digest`에서 발생한 예외가 처리되지 않아 500 Internal Server Error를 유발하고, 이를 통한 서비스 거부(DoS) 공격이 가능함 (CWE-400).
**학습 내용:** Python의 `hmac.compare_digest`는 ASCII 문자가 아닌 문자가 포함된 문자열을 비교할 때 `TypeError`를 발생시킴. 악의적인 사용자가 X-API-Key 헤더에 비-ASCII 문자를 전송하면 401 에러 대신 애플리케이션의 에러율을 높여 서버 장애를 일으킬 수 있음.
**예방 조치:** 모든 API 키 문자열 입력을 `utf-8` 바이트로 인코딩한 후 `hmac.compare_digest`로 전달하여 안전하게 비교를 수행하도록 함.

## 2026-07-25 - [Cross-platform upload basename normalization]
**Behavior:** Upload metadata now interprets both forward slashes and backslashes as path separators before extracting a basename.
**Learning:** On POSIX systems, `pathlib.Path(filename).name` retains backslashes because they are ordinary characters there. That caused inconsistent manifest and converter filenames for Windows-style client paths. The upload itself is still written inside a trusted temporary workspace, and batch archive entry names are generated outputs; this change does not establish a filesystem traversal or archive-entry escape.
Expand Down
2 changes: 1 addition & 1 deletion saas_web.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Env-based API key reading remains the documented anti-pattern

AGENTS.md marks the CODEC_CARVER_API_KEYS env read in get_configured_api_keys as a known deviation to migrate to the credential registry. This PR edits the auth path but leaves that runtime env read in place.

(Refers to this code)

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions tests/test_saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,13 @@ def test_video_content_type_accepted_by_validator(self):
)
)

@patch.dict(os.environ, {"CODEC_CARVER_API_KEYS": "secret1,secret2"}, clear=True)
def test_hmac_non_ascii_api_key_401(self):
# Pass headers as bytes to bypass httpx's strict ASCII string check.
response = client.get("/jobs/123", headers={b"X-API-Key": "test🌟".encode("utf-8")})
self.assertEqual(response.status_code, 401)
self.assertEqual(response.json(), {"error": "Invalid or missing API key"})


if __name__ == "__main__":
unittest.main()
Loading