Skip to content

fix(egress): bound provider error-body buffering in _open_provider (#1041) - #1133

Merged
seonghobae merged 1 commit into
mainfrom
fix/egress-open-provider-error-bounds-1041
Sep 11, 2026
Merged

seonghobae merged 1 commit into
mainfrom
fix/egress-open-provider-error-bounds-1041

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Bounds the response.read() in ModelClient._open_provider's HTTP >= 400 branch to MAX_PROVIDER_ERROR_BODY_BYTES + 1 and slices to the cap, so a hostile/misbehaving provider cannot force unbounded memory buffering on the error path.
  • Reuses the existing MAX_PROVIDER_ERROR_BODY_BYTES (65,536) from provider_errors.py; status/reason/headers are untouched, so 401/429/503 classification and retryable semantics are unchanged.
  • Same slice pattern as provider_error_body(exc) already uses, keeping the bound in one place.

Part of the #1041 response-read bounds sweep (sibling PRs: #1127, #1128, #1129, #1131, #1132).

Tests

  • New tests/test_open_provider_error_body_bounds.py (3 tests): read is bounded (read(65536 + 1)), oversized undeclared body is truncated while status 429 + retryable is True survives, and an in-bounds body reaches provider_error_body intact.
  • RED first (assert -1 == 65537, assert 131098 <= 65536), then GREEN.
  • Focused provider suites: 119 passed.
  • Full suite: 3496 passed, 2 skipped in 992.51s.
  • python -m compileall -q contextual_orchestrator tests OK; git diff --check clean.

Summary by CodeRabbit

  • 버그 수정

    • 외부 서비스 오류 응답을 제한된 크기까지만 처리하도록 개선해, 비정상적으로 큰 오류 응답으로 인한 리소스 부담을 줄였습니다.
    • 응답 본문이 잘리더라도 HTTP 상태 코드와 재시도 가능 여부가 올바르게 유지됩니다.
  • 테스트

    • 다양한 오류 응답 크기에서 본문 제한과 오류 분류가 정상적으로 동작하는지 검증했습니다.

_open_provider read an HTTP >=400 error body with an unbounded
response.read() before wrapping it in urllib.error.HTTPError. The
provider-failure taxonomy only ever inspects MAX_PROVIDER_ERROR_BODY_BYTES
of that body, so a hostile or broken provider could stream unbounded
data on a request that is rejected anyway.

Cap the read at MAX_PROVIDER_ERROR_BODY_BYTES + 1 and slice to the cap,
mirroring provider_error_body(). Status, reason, and headers are
preserved so 401/429/503 classification is unchanged.

Refs #1041
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0380b598-a036-407e-98e5-84bd376b52f4

📥 Commits

Reviewing files that changed from the base of the PR and between abc2dea and d222dce.

📒 Files selected for processing (2)
  • contextual_orchestrator/orchestrator.py
  • tests/test_open_provider_error_body_bounds.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Provider 오류 응답 본문을 MAX_PROVIDER_ERROR_BODY_BYTES까지 읽도록 변경했습니다. 초과 본문 절단, HTTP 상태 코드 보존, 재시도 분류, 제한 이내 JSON 처리를 테스트합니다.

Changes

Provider 오류 응답 처리

Layer / File(s) Summary
오류 응답 본문 읽기 제한
contextual_orchestrator/orchestrator.py, tests/test_open_provider_error_body_bounds.py
_open_provider가 오류 응답 본문을 MAX_PROVIDER_ERROR_BODY_BYTES + 1까지 읽은 후 제한 크기로 자릅니다. 테스트는 본문 크기 제한, 초과 본문 절단, 429 재시도 분류, 제한 이내 JSON 처리를 검증합니다.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: claude

Merge Risk: ⚪ Minimal · up to d222d

Provider error responses are now bounded without changing status or retry behavior, and no actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 1 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 _open_provider의 provider 오류 본문 버퍼링 크기를 제한하는 핵심 변경을 정확하고 간결하게 설명합니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 1 files. (1 skipped: 1 too large.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/egress-open-provider-error-bounds-1041

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.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

OpenCode reviewed the current-head product diff. Coverage is a separate gate.

Changed files

  • contextual_orchestrator/orchestrator.py — Python module behavior
  • tests/test_open_provider_error_body_bounds.py — regression suite

Changed behavior

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Python: orchestrator.py"]
  S1 --> I1["Python module behavior"]
  I1 --> R1["Review risk: Python: orchestrator.py"]
  R1 --> V1["pytest plus coverage"]
  Evidence --> S2["Test: test_open_provider_error_body_bounds.py"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: test_open_provider_error_body_bounds.py"]
  R2 --> V2["targeted test run"]
Loading

Findings

No source-backed product finding is synthesized from the coverage gate. A coverage miss belongs in the status comment.

  • Head SHA: d222dced2c6f578c75ffc8938bf4800cc510f3c9
  • Workflow run: 34554149064
  • Workflow attempt: 1
  • Coverage gate: failure

Review outcome

Coverage is a gate, not the review. This body reviews the changed product files.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Python: orchestrator.py"]
  S1 --> I1["Python module behavior"]
  I1 --> R1["Review risk: Python: orchestrator.py"]
  R1 --> V1["pytest plus coverage"]
  Evidence --> S2["Test: test_open_provider_error_body_bounds.py"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: test_open_provider_error_body_bounds.py"]
  R2 --> V2["targeted test run"]
Loading

@opencode-agent

opencode-agent Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

Coverage evidence did not pass, so approval is blocked. The formal pull-request review is the source-backed diff review, not this status comment.

@seonghobae
seonghobae marked this pull request as draft September 11, 2026 15:52
@seonghobae
seonghobae marked this pull request as ready for review September 11, 2026 15:52

@cwl-noema-review cwl-noema-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Noema LLM review

The change bounds the upstream error-body buffering in _open_provider to MAX_PROVIDER_ERROR_BODY_BYTES bytes by reading one extra byte and slicing, while preserving local status, reason, and headers before wrapping the response. The new test module directly exercises the bounded read size, truncation behavior, classification impact, and in-bounds JSON integrity. Import of the constant is verified by successful test import. With the reviewed probes falsifying concrete regression hypotheses, no blocking issues were found.

Reviewed changed lines

  • contextual_orchestrator/orchestrator.py:54 (RIGHT): The import adds MAX_PROVIDER_ERROR_BODY_BYTES, which the new test module also imports successfully, confirming the symbol is available.
  • contextual_orchestrator/orchestrator.py:2440 (RIGHT): The changed expression reads MAX_PROVIDER_ERROR_BODY_BYTES + 1 bytes and truncates to the cap, bounding memory usage. Tests confirm the size argument is passed, oversized bodies are truncated without breaking classification, and in-bounds bodies remain parseable.
  • tests/test_open_provider_error_body_bounds.py:105 (RIGHT): The test asserts that the transport requests exactly MAX_PROVIDER_ERROR_BODY_BYTES + 1, validating the bound is actually applied.
  • contextual_orchestrator/orchestrator.py:2440 (RIGHT): The slice-at-cap pattern is tested for oversized bodies to confirm status and headers are preserved and classification remains correct.
  • tests/test_open_provider_error_body_bounds.py:110 (RIGHT): The test verifies truncated oversized bodies retain status and produce correct retryability classification.
  • tests/test_open_provider_error_body_bounds.py:119 (RIGHT): The test verifies that a normal-sized error body survives the bound and parses intact, guarding against off-by-one truncation.

Adversarial validation

  • contextual_orchestrator/orchestrator.py:2440 (RIGHT) falsified: A body exactly at MAX_PROVIDER_ERROR_BODY_BYTES could lose its final byte due to the slice, breaking JSON parsing. — test_in_bounds_error_body_reaches_classification_intact passes and test_error_body_read_is_bounded confirms MAX+1 bytes are requested, demonstrating the over-read preserves exactly-cap bodies.
  • contextual_orchestrator/orchestrator.py:2440 (RIGHT) falsified: Truncating an oversized error body could cause classify_provider_failure to misclassify or crash due to invalid JSON. — test_oversized_error_body_is_truncated_but_status_preserved verifies error.code == 429 and classify_provider_failure(...).retryable is True, showing classification remains correct from the preserved status.
  • contextual_orchestrator/orchestrator.py:2440 (RIGHT) falsified: The bound could be ineffective if the size argument is not actually passed to response.read, allowing an unbounded read. — test_error_body_read_is_bounded records the requested size and asserts it equals MAX_PROVIDER_ERROR_BODY_BYTES + 1, confirming the size argument is supplied.
  • contextual_orchestrator/orchestrator.py:54 (RIGHT) falsified: The imported constant may not be exported from provider_errors, causing an ImportError at module load. — The new test module imports the same constant successfully, confirming the symbol is exported and importable.
  • Residual risk: Low. The bounded read mitigates an attacker-controlled memory amplification vector. The implementation relies on upstream HTTP handling honoring the size argument for standard http.client.HTTPResponse objects; a non-compliant transport response could still return more data, but that is outside the library's expected behavior. The +1 over-read before slicing correctly handles bodies exactly at the cap.

Findings

  • No blocking findings.
  • Result: APPROVE
  • Head SHA: d222dced2c6f578c75ffc8938bf4800cc510f3c9
  • Reviewer credential: noema-review-github-app-refresh
  • Actor: cwl-noema-review[bot]

@seonghobae
seonghobae merged commit 1ab7977 into main Sep 11, 2026
38 of 44 checks passed
@seonghobae
seonghobae deleted the fix/egress-open-provider-error-bounds-1041 branch September 11, 2026 17:21
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