Skip to content

release: 0.6.0 — fail-CLOSED policy fetch, CSRF Bearer bypass, WS HMA…#29

Closed
maltsev-dev wants to merge 2 commits into
masterfrom
release/0.6.0
Closed

release: 0.6.0 — fail-CLOSED policy fetch, CSRF Bearer bypass, WS HMA…#29
maltsev-dev wants to merge 2 commits into
masterfrom
release/0.6.0

Conversation

@maltsev-dev

Copy link
Copy Markdown
Member

…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

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.

Files: 1 new test, 18 src/runtime/test, 1 docs.

What

Why

How

Test plan

  • Unit tests pass (per-repo, e.g. cd backend && cargo test, cd frontend && npm test)
  • Lint passes (per-repo, e.g. cd frontend && npm run lint)
  • Type-check passes (per-repo, e.g. cd frontend && npm run type-check)
  • Manually verified in dev / staging

Risk

Checklist

  • I have read the repo's CONTRIBUTING.md (if present)
  • My change does not introduce new lint warnings
  • I have updated the CHANGELOG (if user-visible)
  • I have considered backwards compatibility

Comment thread src/nullrun/transport.py
Hex-encoded HMAC-SHA256 signature
"""
body_hash = hashlib.sha256(body.encode('utf-8')).hexdigest()
body_hash = hashlib.sha256(body.encode("utf-8")).hexdigest()
Comment thread src/nullrun/transport.py
secret_key.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
secret_key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256
secret_key.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
secret_key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256
# helper's bytes — the failure mode is silent 401 on the wire).
expected_body_hash = hashlib.sha256(body).hexdigest()
expected_msg = f"{headers['X-Signature-Timestamp']}:{api_key}:{expected_body_hash}".encode()
expected_sig = hmac.new(
@maltsev-dev maltsev-dev force-pushed the release/0.6.0 branch 2 times, most recently from bf520b4 to 35f275c Compare June 23, 2026 08:55
…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

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.

Files: 1 new test, 18 src/runtime/test, 1 docs.
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.26087% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/nullrun/runtime.py 75.55% 9 Missing and 2 partials ⚠️
src/nullrun/transport.py 78.57% 4 Missing and 2 partials ⚠️
src/nullrun/transport_websocket.py 83.33% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@maltsev-dev maltsev-dev deleted the release/0.6.0 branch June 23, 2026 10:25
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.

2 participants