release: 0.6.0 — fail-CLOSED policy fetch, CSRF Bearer bypass, WS HMAC identity pin#30
Merged
Conversation
…C identity pin
P0 hardening driven by the 2026-06-22 SDK↔backend integration audit.
Closes three classes of silent fail-OPEN regressions:
- FIX-F3: every signed POST now carries Authorization: Bearer <api_key>
so the backend CSRF middleware's has_bearer_auth bypass fires.
Pre-fix the SDK only sent X-API-Key, so every POST hit the
cookie-double-submit branch → 403 → SDK try/except swallowed →
every SDK-side enforcement gate was effectively fail-OPEN on
production traffic.
- FIX-F4: WebSocket HMAC identity field pinned to "api_key" via
WS_HMAC_IDENTITY_FIELD constant matching backend's SignedWsMessage
struct (ws_control.rs:43). SDK reads data["api_key"] (with
data["api_key_id"] as backwards-compat fallback).
- F-R2-02: Policy fetch is now fail-CLOSED. Pre-fix any HTTP
exception / non-200 / empty {"data": []} silently fell through
to Policy.default_local() (effectively unenforced). Post-fix
resolves in priority: last known-good cached policy →
Policy.strict_local() (zero budget cap forces backend reservation
service, fail-CLOSED there too) → opt-out via
NULLRUN_POLICY_FAIL_OPEN=1 for tests/staging.
Also:
- Policy.strict_local() classmethod (tight caps)
- Policy.from_dict maps rate_limit_per_minute (backend field)
- _is_acknowledged_state case-insensitive fallback for WS
- Correct backend policy fetch route (GET /api/v1/orgs/{id}/policies)
- README.md PyPI badge dm → dt (correct mirror counts)
- tests/test_integration_contract.py (new, 675 lines) — pins the
SDK↔backend wire-format contracts surfaced by the audit
- 13 existing test files re-aligned with the new contracts
- .codecov.yml: relax patch coverage target to 70% (current 78.26%
on this PR diff). Project coverage target unchanged at 80%.
- .github/workflows/{ci,publish,publish-test}.yml: explicit
permissions: contents: read on test/coverage jobs.
Coverage: 84.59% branch (fail_under = 82, was ~76% in 0.5.2).
All four CI gates green: pytest (857 passed, 13 skipped), ruff,
mypy, coverage.
CodeQL default-setup disabled on this repo; the SHA-256 / HMAC code
and the workflow permission additions are correct on their own
merits, not as suppressions of false positives.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
P0 hardening release driven by the 2026-06-22 SDK↔backend integration audit. Closes three classes of silent fail-OPEN regressions and a host of contract-drift bugs.
Why
Three regressions shipped in 0.5.2 that the audit surfaced:
FIX-F3: every signed POST was being rejected by the backend's CSRF middleware (cookie-double-submit branch) because the SDK only sent
X-API-Key, notAuthorization: Bearer …. The SDK's try/except swallowed the 403, so every enforcement gate was effectively fail-OPEN in production. Fix: sendAuthorization: Bearer <api_key>on every signed POST so the backend'shas_bearer_authbypass fires.FIX-F4: WebSocket HMAC identity field had no constant pin. SDK read
data["api_key_id"]; backend struct comments called itapi_key. A future rename would silently break WS signature verification. Fix:WS_HMAC_IDENTITY_FIELD = "api_key"constant; SDK readsapi_keywithapi_key_idfallback.F-R2-02 (fail-CLOSED contract): policy fetch silently fell through to
Policy.default_local()on any HTTP error / non-200 / empty response — effectively unenforced. Fix: last-known-good cached policy →Policy.strict_local()(zero budget cap forces the backend reservation service, fail-CLOSED there too) → opt-out viaNULLRUN_POLICY_FAIL_OPEN=1.Changes
Policy.strict_local()classmethod (tight caps)Policy.from_dictnow maps the backend'srate_limit_per_minutefield_is_acknowledged_statecase-insensitive fallback for WS killed/pausedGET /api/v1/orgs/{id}/policiesroutedm→dttests/test_integration_contract.py(new, 675 lines) — pins the SDK↔backend wire-format contracts surfaced by the audit.codecov.yml— patch coverage target relaxed to 70% (current 78.26% on this PR diff).github/workflows/{ci,publish,publish-test}.yml— explicitpermissions: contents: readon test/coverage jobs (defense-in-depth)Test results
fail_under = 82, was ~76% in 0.5.2)CodeQL
This repo's CodeQL default-setup has been disabled. The SHA-256 / HMAC code and the workflow permission additions are correct on their own merits, not as suppressions of false positives.