Skip to content

0.11.0 — deep-audit hardening

Choose a tag to compare

@lesnik512 lesnik512 released this 14 Jun 14:38
c27c163

Minor release. Additive only — no breaking changes.

This release ships the full remediation of the 2026-06-14 full-codebase deep
audit: 35 confirmed findings closed across security, correctness, public API,
test quality, and documentation.

New public names

from httpware import ResponseTooLargeError

AsyncClient / Client gain an opt-in max_error_body_bytes: int | None = None.

Security hardening

  • URL secret redaction. Request URLs that reach logs, OpenTelemetry span
    events, and StatusError messages/repr are now sanitized: user:pass@
    userinfo is stripped and the values of known-sensitive query/fragment
    parameters (api_key, access_token, token, secret, password, …) are
    masked with REDACTED. Redaction is centralized at the _emit_event
    emission boundary, so every resilience event is covered and a future event
    cannot reintroduce the leak. Non-secret URLs are byte-identical to before.
  • Bounded error bodies. With max_error_body_bytes set, stream() raises
    the new ResponseTooLargeError on a 4xx/5xx whose declared Content-Length
    exceeds the cap, before reading the body. Default (None) is unchanged.
    A chunked error body with no declared length is still read — a hard mid-read
    cap would require httpx2 private API; that deeper bound is tracked as
    deferred work.
  • Documented trust boundaries. trust_env=True proxy inheritance and the
    fact that exc.response.request exposes Authorization/Cookie headers are
    now called out in architecture/.

Correctness fixes

  • RetryBudget no longer spends a token on a Retry-After give-up. When a
    server's Retry-After exceeds max_delay, the give-up check now runs before
    budget.try_withdraw(), so a Retry-After flood can't drain shared-budget
    capacity for unrelated requests (sync and async).
  • Hostile headers no longer crash the retry loop. A Retry-After of a few
    hundred digits raised OverflowError; _parse_retry_after now degrades it
    to "no hint". full_jitter_delay likewise no longer raises at very large
    attempt indices — it clamps to max_delay as documented.
  • Decoder/extras robustness. The pydantic import is fully guarded so the
    decoder module loads without the extra (the friendly ImportError is the
    real fail-fast path), and msgspec's internal type walk raises a friendly
    ImportError rather than NameError when the extra is absent.
  • AsyncClient streaming-body detection now recognizes non-replayable sync
    iterables explicitly instead of relying on an undocumented httpx2 detail.
  • URL sanitizer no longer emits a malformed http:///path for the
    credentials-but-no-host edge case.

Public API

  • httpware.middleware now defines __all__, so from httpware.middleware import * no longer leaks httpx2, typing helpers, or submodules.

Quality

  • Test suite hardened: closed coverage gaps (sync terminal mapping, the
    CookieConflict branch, TimeoutError tripping the circuit breaker, the
    three never-constructed status errors), added sync/async parity mirrors and
    Client overload typing tests, replaced a flaky time.sleep-based bulkhead
    test with a deterministic barrier, and pinned a budget-test clock.
  • Documentation accuracy fixes across architecture/ and the docs site.

Shipped via

PRs #62 (pydantic isolation), #63 (security cluster), #64 (correctness +
public API), #65 (test quality), #66 (docs). See
planning/audits/2026-06-14-deep-audit.md
for the full audit.