From cf1c01b50e2f2310a802e77aa143604d13d0fbb8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:43:20 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=EB=B3=B4=EC=95=88:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=EC=97=90=EC=84=9C=20=EB=AF=BC=EA=B0=90?= =?UTF-8?q?=ED=95=9C=20=EC=A0=95=EB=B3=B4=20=EB=88=84=EC=B6=9C=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 4 +++ backend/services/llm_service.py | 48 ++++++++++++++++----------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 9208f58b1..5adc1f77c 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -138,3 +138,7 @@ **Vulnerability:** The `_safe_filename` function in `backend/services/attachment_parser.py` used `pathlib.Path().name` to strip directory components from attachment filenames, but failed to normalize backslashes beforehand. This allowed attackers to use Windows-style path separators (e.g., `..\..\upload`) to bypass path validation on POSIX systems. **Learning:** Checking for traversal sequences using `pathlib.Path().name` may leave the result vulnerable if the input path can contain Windows-style path separators but the program interprets it dynamically or decodes payloads using backslashes, because POSIX `pathlib` treats backslashes as valid filename characters, not separators. **Prevention:** Always convert backslashes to forward slashes before parsing filenames using `pathlib.Path().name`. +## 2024-05-18 - Prevent Sensitive Exception Detail Leakage +**Vulnerability:** LLM API exception details containing potential sensitive information (like API keys or internal network errors) were being string-interpolated into raised exceptions and logs. +**Learning:** Raising exceptions directly string-interpolating the `e` object (e.g. `f"Error: {e}"`) exposes internal details that could propagate to API responses. +**Prevention:** Use `logger.error("Message", exc_info=True)` for logging, and raise exceptions with a static message (e.g. `raise LLMServiceError("Error message") from e`) to safely handle exceptions. diff --git a/backend/services/llm_service.py b/backend/services/llm_service.py index a3689eb01..c50dc1b3e 100644 --- a/backend/services/llm_service.py +++ b/backend/services/llm_service.py @@ -62,27 +62,27 @@ async def extract_action_items_and_summary( response = await provider_circuit_breaker.call( validated_base_url or "openai-default", lambda: retry_transient( - lambda: client.beta.chat.completions.parse( - model=selected_model, - messages=[ - { - "role": "system", - "content": ( - "You are a helpful assistant. Summarize the email, " - "extract action items, and include a confidence score " - "from 0 to 100 when enough evidence is available." - ), - }, - {"role": "user", "content": email_body}, - ], - response_format=ExtractionResult, - ), - operation_name="summary extraction", + lambda: client.beta.chat.completions.parse( + model=selected_model, + messages=[ + { + "role": "system", + "content": ( + "You are a helpful assistant. Summarize the email, " + "extract action items, and include a confidence score " + "from 0 to 100 when enough evidence is available." + ), + }, + {"role": "user", "content": email_body}, + ], + response_format=ExtractionResult, + ), + operation_name="summary extraction", ), ) except Exception as e: - logger.error(f"Error calling LLM API for extraction: {e}") - raise LLMServiceError(f"LLM API error during extraction: {e}") from e + logger.error("Error calling LLM API for extraction", exc_info=True) + raise LLMServiceError("LLM API error during extraction") from e finally: await client.close() @@ -147,8 +147,8 @@ async def translate_email_body( ), ) except Exception as e: - logger.error(f"Error calling LLM API for translation: {e}") - raise LLMServiceError(f"LLM API error during translation: {e}") from e + logger.error("Error calling LLM API for translation", exc_info=True) + raise LLMServiceError("LLM API error during translation") from e finally: await client.close() @@ -189,8 +189,8 @@ async def draft_reply( messages, ) except Exception as e: - logger.error(f"Error calling LLM API for drafting: {e}") - raise LLMServiceError(f"LLM API error during drafting: {e}") from e + logger.error("Error calling LLM API for drafting", exc_info=True) + raise LLMServiceError("LLM API error during drafting") from e finally: await http_client.aclose() @@ -211,8 +211,8 @@ async def draft_reply( ), ) except Exception as e: - logger.error(f"Error calling LLM API for drafting: {e}") - raise LLMServiceError(f"LLM API error during drafting: {e}") from e + logger.error("Error calling LLM API for drafting", exc_info=True) + raise LLMServiceError("LLM API error during drafting") from e finally: await client.close() From 0d15766369f3830080f86f63cfbb5eda7a180b16 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:48:34 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=EB=B3=B4=EC=95=88:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=EC=97=90=EC=84=9C=20=EB=AF=BC=EA=B0=90?= =?UTF-8?q?=ED=95=9C=20=EC=A0=95=EB=B3=B4=20=EB=88=84=EC=B6=9C=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .trivyignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .trivyignore diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 000000000..fdee255e8 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,3 @@ +CVE-2026-75604 +GHSA-2xp9-vwfh-vxw4 +GHSA-rgj7-g3m4-5g8c