Skip to content

🔒 Fix unsanitized exception data from OpenAI responses#86

Open
sonra44 wants to merge 1 commit intomainfrom
fix-openai-responses-unsanitized-data-13610696534626654639
Open

🔒 Fix unsanitized exception data from OpenAI responses#86
sonra44 wants to merge 1 commit intomainfrom
fix-openai-responses-unsanitized-data-13610696534626654639

Conversation

@sonra44
Copy link
Copy Markdown
Owner

@sonra44 sonra44 commented Apr 1, 2026

🎯 What: The vulnerability fixed
Unsanitized error response bodies from OpenAI were directly included in the OpenAIResponsesError exception message. This could lead to inclusion of control characters like newlines (\n, \r) or ANSI escape sequences (\x1b) in logs or terminal output.

⚠️ Risk: The potential impact if left unfixed
Though low impact because the data comes from OpenAI, injecting raw control characters into application logs or terminal output could lead to log injection (CRLF injection) or terminal sequence injection, allowing an attacker to manipulate logs, obscure malicious activity, or inject spoofed log entries.

🛡️ Solution: How the fix addresses the vulnerability
The detail string is now sanitized before being placed into the exception message. A generator expression "".join(c if c.isprintable() else " " for c in raw_detail) is used to safely replace all non-printable characters (including \n, \r, \t, and ANSI codes) with spaces. A unit test was also added to verify this behavior.


PR created automatically by Jules for task 13610696534626654639 started by @sonra44

Summary by Sourcery

Sanitize OpenAI HTTP error details before including them in OpenAIResponsesError messages to prevent control characters from reaching logs or terminal output.

Bug Fixes:

  • Prevent inclusion of non-printable control characters from OpenAI HTTP error bodies in OpenAIResponsesError messages by sanitizing the error detail string.

Tests:

  • Add a unit test verifying that OpenAIResponsesClient replaces non-printable characters in HTTP error payloads with spaces in the resulting exception message.

Summary by CodeRabbit

Улучшения

  • Исправления ошибок

    • Улучшена обработка ошибок при работе с API — сообщения об ошибках теперь отображаются в чистом виде без управляющих символов и посторонних кодов.
  • Тесты

    • Добавлены тесты для проверки корректной обработки ошибок API.

Co-authored-by: sonra44 <215552628+sonra44@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 1, 2026

Reviewer's Guide

Sanitizes OpenAI HTTP error response bodies before including them in OpenAIResponsesError messages and adds a regression test to ensure non-printable characters are stripped from logged error messages.

Sequence diagram for sanitized OpenAI error handling in create_response_json_schema

sequenceDiagram
    participant Caller
    participant OpenAIResponsesClient
    participant OpenAIAPI
    participant OpenAIResponsesError

    Caller->>OpenAIResponsesClient: create_response_json_schema()
    OpenAIResponsesClient->>OpenAIAPI: HTTP request for response schema
    OpenAIAPI-->>OpenAIResponsesClient: HTTPError exc

    Note over OpenAIResponsesClient: Error handling path
    OpenAIResponsesClient->>OpenAIResponsesClient: exc.read().decode(utf_8) as raw_detail
    OpenAIResponsesClient->>OpenAIResponsesClient: detail = sanitized(raw_detail)
    OpenAIResponsesClient->>OpenAIResponsesError: raise with message including detail
    OpenAIResponsesError-->>Caller: Exception with sanitized detail
Loading

Flow diagram for sanitizing OpenAI error response detail

flowchart TD
    A[Start error handling] --> B["Read exc.read() and decode utf_8 into raw_detail"]
    B --> C[Iterate characters in raw_detail]
    C --> D{Character c is printable?}
    D -- Yes --> E[Append c to detail]
    D -- No --> F[Append space to detail]
    E --> G{More characters?}
    F --> G
    G -- Yes --> C
    G -- No --> H[Join characters into final detail string]
    H --> I[Use sanitized detail in OpenAIResponsesError message]
    I --> J[End error handling]
Loading

File-Level Changes

Change Details Files
Sanitize HTTP error details from OpenAI before raising OpenAIResponsesError to prevent control characters from entering logs.
  • Capture the decoded HTTP error body in a raw_detail variable instead of using it directly.
  • Transform raw_detail into a sanitized detail string by replacing all non-printable characters with spaces using a generator expression.
  • Preserve the existing fallback behavior of setting detail to an empty string if decoding or sanitization fails, and continue to include the HTTP status code and sanitized detail in the OpenAIResponsesError message.
src/qiki/services/q_core_agent/core/openai_responses_client.py
Add a unit test verifying that OpenAIResponsesClient sanitizes HTTP error payloads by stripping control characters from the error message.
  • Create a pytest-based test module for OpenAIResponsesClient and OpenAIResponsesError.
  • Mock urllib.request.urlopen to raise an HTTPError whose payload contains ANSI escape codes and newline, carriage return, and tab characters.
  • Assert that the raised OpenAIResponsesError message contains the HTTP status code, excludes all control characters, and includes the expected sanitized text representation of the error body.
src/qiki/services/q_core_agent/tests/test_openai_responses_client.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Внесены изменения в обработку ошибок HTTP в OpenAIResponsesClient: добавлена санитизация непечатаемых символов (включая ANSI-коды) в тексте ошибки перед его включением в исключение. Добавлен новый тестовый модуль для проверки этого поведения.

Changes

Cohort / File(s) Summary
Улучшение обработки ошибок
src/qiki/services/q_core_agent/core/openai_responses_client.py
Модифицирована функция create_response_json_schema: добавлена санитизация непечатаемых символов в теле ошибки HTTP путём замены их на пробелы перед усечением и встраиванием в сообщение OpenAIResponsesError.
Новый тестовый модуль
src/qiki/services/q_core_agent/tests/test_openai_responses_client.py
Добавлен новый файл с тестом для проверки санитизации ошибок. Тест имитирует HTTP-ошибку 400 с ANSI-кодами и управляющими символами, проверяя, что они правильно удаляются из сообщения об ошибке.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Коды ANSI прячут, табуляцию гонят,
Ошибки теперь в чистоте, как снег блистают,
Зайчик с тестами нам помощь несёт,
Символы грязные в пробелы уходят,
И сообщенья ясны, как морозный рассвет! ❄️✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning Описание PR содержит подробное объяснение проблемы, риска и решения, но не соответствует требуемому шаблону репозитория с необходимыми секциями. Переформатируйте описание в соответствии с шаблоном репозитория, включив все обязательные секции: Summary, Task/Branch Contract, Visible Delta, Reproduction Command, Before/After, Impact Metric, Scope и Validation.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed Название хорошо отражает основное изменение: исправление проблемы безопасности с неочищенными данными исключений от OpenAI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-openai-responses-unsanitized-data-13610696534626654639

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • To further harden log safety and avoid excessively large exception messages, consider truncating or bounding the sanitized detail string before including it in the OpenAIResponsesError message (e.g., keeping only the first N characters and appending an ellipsis).
  • The test assert "Error [31m message details here " in error_msg is quite brittle due to the exact spacing; it may be more robust to assert absence of control characters and presence of key substrings separately rather than matching a full formatted string.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- To further harden log safety and avoid excessively large exception messages, consider truncating or bounding the sanitized `detail` string before including it in the `OpenAIResponsesError` message (e.g., keeping only the first N characters and appending an ellipsis).
- The test `assert "Error  [31m message   details   here  " in error_msg` is quite brittle due to the exact spacing; it may be more robust to assert absence of control characters and presence of key substrings separately rather than matching a full formatted string.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/qiki/services/q_core_agent/core/openai_responses_client.py`:
- Around line 115-119: The current try/except around reading the HTTPError body
swallows all exceptions and sets detail = "", losing context; change it to catch
specific decode/read errors (e.g., UnicodeDecodeError, ValueError, OSError) or
catch Exception as e and capture the error context into detail (e.g., include
e.__class__.__name__ and str(e) or a short fallback message) instead of blanking
it out, and either log the captured error via the same logger used in this
module or re-raise/convert to a domain error so callers can see the cause;
update the block that uses raw_detail, detail and exc (the HTTPError handling
code in openai_responses_client.py) to preserve and surface the underlying
exception information.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26f76ad6-bd1c-468f-a796-ba42aa699a24

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba569c and 60e495a.

📒 Files selected for processing (2)
  • src/qiki/services/q_core_agent/core/openai_responses_client.py
  • src/qiki/services/q_core_agent/tests/test_openai_responses_client.py

Comment on lines 115 to 119
try:
detail = exc.read().decode("utf-8")
raw_detail = exc.read().decode("utf-8")
detail = "".join(c if c.isprintable() else " " for c in raw_detail)
except Exception: # noqa: BLE001
detail = ""
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Не теряйте контекст при ошибке чтения тела HTTPError.

На Line 118 исключение перехватывается слишком широко и причина теряется (detail = ""). Это ухудшает диагностику инцидентов.

Предлагаемая правка
                 try:
-                    raw_detail = exc.read().decode("utf-8")
-                    detail = "".join(c if c.isprintable() else " " for c in raw_detail)
-                except Exception:  # noqa: BLE001
-                    detail = ""
+                    raw_detail = exc.read().decode("utf-8", errors="replace")
+                except Exception as read_exc:  # noqa: BLE001
+                    raw_detail = f"<failed to read error body: {type(read_exc).__name__}>"
+                detail = "".join(c if c.isprintable() else " " for c in raw_detail)

As per coding guidelines "Don't swallow exceptions silently; log context and re-raise or convert to a clear domain error".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/qiki/services/q_core_agent/core/openai_responses_client.py` around lines
115 - 119, The current try/except around reading the HTTPError body swallows all
exceptions and sets detail = "", losing context; change it to catch specific
decode/read errors (e.g., UnicodeDecodeError, ValueError, OSError) or catch
Exception as e and capture the error context into detail (e.g., include
e.__class__.__name__ and str(e) or a short fallback message) instead of blanking
it out, and either log the captured error via the same logger used in this
module or re-raise/convert to a domain error so callers can see the cause;
update the block that uses raw_detail, detail and exc (the HTTPError handling
code in openai_responses_client.py) to preserve and surface the underlying
exception information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant