Skip to content

[Phase 1/scan] Auth & security exposure hardening (0.19.0)#315

Merged
rennf93 merged 134 commits into
feature/video-engine-hyperframesfrom
fix/scan-auth-security
Jul 7, 2026
Merged

[Phase 1/scan] Auth & security exposure hardening (0.19.0)#315
rennf93 merged 134 commits into
feature/video-engine-hyperframesfrom
fix/scan-auth-security

Conversation

@rennf93

@rennf93 rennf93 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Phase 1 — Auth & security exposure hardening (rides 0.19.0)

First phase of the 2026-07-06 full-codebase-scan fix plan. Six verified-real auth/security fixes plus two related fixes, all under the existing ## [0.19.0] CHANGELOG section. Dev mode (cloud auth off) is byte-for-byte unchanged — every exposure hole here is cloud-auth-on only.

Fixes

  • slug-vs-UUID agent token root cause (9522cc01, on the base) — agent HMAC token was signed over the slug but MCP servers send X-Agent-ID=UUID → "signature mismatch" 401 loop. Sign over UUID + set container ROBOCO_AGENT_ID=UUID + startup heal resolves slug→UUID before verify.
  • C1 (dd381a67) — route get_current_agent_slug/get_current_agent_id/get_optional_agent_id through the cloud-auth dual-path gate. Closes the A2A/notification/stream header-spoof hole.
  • H1 (1a16a633) — omit UNSIGNED sentinel in mcp/utils.py 6th header builder (dev-mode RAG/docs/search 401).
  • H2 (fc7fb958) — startup guard: cloud_auth_enabled and panel_agent_token_set → fail loud (nginx CEO-token injection footgun).
  • C2 (74be4428) — panel proxy.ts auth-probe last-known-good cache (fail-open leaked dashboard chrome during a backend hiccup).
  • L31 (49021be8 + 504b46bc) — per-IP rate limit on POST /auth/login, keyed off X-Forwarded-For first hop (nginx is the entry point); fail-open on redis-down.
  • Secretary token-team (0dfd45ca + a3733076) — secretary token was signed over team "" but the driver sends X-Agent-Team=board/api/secretary/* 401. Sign over get_agent_team(SECRETARY_AGENT_ID).
  • Review fixes (504b46bc + 3e403a6d) — whole-branch opus review found the login limiter collapsed to a global throttle behind nginx (self-DoS amplifier) and the redis-down fail-open path was untested; both fixed + tested.

Scope note (acknowledged scope creep)

7b28aecd (tripped respawn-breaker self-heals after a cooldown) is NOT a scan fix — it's a dev-loop wedge fix that landed on this branch. It's well-tested and root-cause (the pr_reviewer-1 count=63 wedge was a downstream symptom of the auth 401 loop the slug→UUID fix addresses), so it rides here. Flagging so it's a conscious sign-off, not an accidental merge.

Verification

  • uv run ruff format + ruff check — clean.
  • uv run mypy roboco/ — clean (357 files).
  • uv run pytest tests/unit/ — 4847 passed.
  • uv run pytest tests/integration/ (in isolation) — 206 passed.
  • make e2e-smoke (with local DB) — 9 passed.
  • Panel pnpm typecheck/lint/test — exit 0.
  • Whole-branch opus review verdict: Ready to merge (no Critical; the two Important findings were fixed in 504b46bc).

Known pre-existing issue (not introduced here)

Running pytest tests/unit/ tests/integration/ in ONE invocation produces ~11 integration failures from DB-state contamination (the suites share the local :55432 DB). Each suite passes cleanly in isolation. This is a pre-existing test-isolation gap, not a Phase-1 regression — flagged for a separate cleanup.

Deploy note

The secretary token-team fix is NOT yet live — a redeploy (rebuild orchestrator image + restart) is needed for /api/secretary/* 401 to clear.

Model note

Implementers ran on glm-5.2 (the session model); the whole-branch review ran on opus. kimi-k2.7-code:cloud is not routable in this harness.

🤖 Generated with Claude Code


Phase 1b — Auth gate coverage under cloud_auth (appended to this PR)

CEO flagged the auth scan was "missing a couple." Verified root cause: several auth guards check _auth_required() (ROBOCO_AGENT_AUTH_REQUIRED, default false) instead of settings.cloud_auth_enabled (default true in docker-compose.yml). Under the default compose deploy (cloud_auth on, agent_auth_required off) these guards header-trust any forged X-Agent-Role with no token. Six fixes, all dev-mode (cloud_auth off) byte-for-byte unchanged:

  • F1 (ed6d3483) — _check_agent_auth_token (the shared chokepoint) now requires a verifiable HMAC token when cloud_auth_enabled OR _auth_required(). Closes the v1 /api/v1/flow/* + /api/v1/do/* role-guard header-spoof hole (every flow/do endpoint). Agents always carry a token, so they're unaffected; only forged no-token headers are rejected.
  • F2 (451a38bf) — _require_ceo (orchestrator control routes /api/orchestrator/*) async dual path: valid CEO HMAC token OR (under cloud_auth) a valid CEO session cookie. The panel is cookie-only (client.ts sends withCredentials + X-Agent-ID/Role but NO X-Agent-Token), so the panel reaches /api/orchestrator/* via the cookie. Mirrors the WS gate (websocket.py:_require_panel_token).
  • F3 (68275e65) — HTTP require_panel_token (the live-route gate for /api/prompter/live/* + /api/secretary/live/*) async dual path: token OR session cookie. This was the gap the original audit missed — HTTP require_panel_token was token-only and never consulted cloud_auth_enabled, so the prompter/secretary live routes were ungated under the default cloud-auth deploy (the WS gate was already cloud-auth-aware; HTTP was not).
  • F4 (4d57702a) — /api/settings (GET settings, GET feature-flags, PUT) gated behind require_panel_token. Closes unauthenticated settings read+write.
  • F5 (caec8b6f) — unauthenticated read routes gated: /api/agents, /api/kanban, /api/usage, /api/system/rate-limits behind require_panel_token; /api/a2a/tasks[/{id}] behind get_agent_context (agents still reach it via their HMAC token; the CEO cookie works too via the dual path). /.well-known/agent.json + /a2a/agents[/{id}/card] left ungated by design (agent discovery surface).
  • F5 cleanup (3d280369) — hoisted 19 deferred test imports to top-level (cleared PLC0415 surfaced after a correct ruff).
  • Task 6 (a103d2ba) — e2e smoke tests/e2e_smoke/test_auth_gate_coverage.py (4 cross-layer cases: forged ceo header → 401 on /api/orchestrator/status; forged developer → 401 on /api/v1/flow/give-me-work; no-cred → 401 on /api/settings; real CEO session cookie → 200) + CHANGELOG append under ## [0.19.0] ### Security. The e2e harness (tests/e2e_smoke/harness.py) gained two include_router calls mounting the orchestrator + settings routers on the e2e app (matching app.py prefixes) — additive, inert for the existing 9 e2e tests (full suite 13/13 green).

Verification

  • uv run ruff format + ruff check — clean.
  • uv run mypy roboco/ — clean (357 files).
  • uv run pytest tests/unit/api/ — 124 passed (the Phase 1b auth-gate suite); 517 unit tests pass overall.
  • make e2e-smoke (with local DB) — 13 passed (9 existing + 4 new auth-gate coverage).
  • Whole-branch opus review: Ready to push — no Critical, no Important. Three Minor (non-blocking, recorded): (1) the a2a /tasks dev-mode CurrentAgentContext dep is an intentional additive gate (agent-only route, agents always send X-Agent-ID); (2) the new dual-path cookie branches don't call _slide_session_cookie — mirrors the WS gate, and the CEO inevitably hits get_agent_context routes that do slide; (3) the cookie path is user-not-role-scoped — safe today because registration is disabled + seed.py upserts exactly one CEO row (same property as the WS gate), flagged for a future multi-user deployment. All 7 commits signed, no co-author lines, AGPL-3.0 only.

Model note

Implementer ran on sonnet; whole-branch review on opus. kimi-k2.7-code:cloud is not routable in this harness.

rennf93 added 12 commits July 6, 2026 09:28
A DB-durable PM-respawn counter (migration 051 / e2f7097) wedges forever
once tripped: the only reset was a task status change, which can't happen
while the breaker blocks the spawn. So a deploy that fixes the underlying
loop (auth/prompt/schema) couldn't clear the wedge without manual DELETE
surgery on respawn_tracker — the 2026-07-06 pr-reviewer-1 loop, where the
auth fix cleared the 401 but count=63 survived restart and kept skipping
the dispatcher spawn for an external-PR task.

Freeze last_check at the trip tick and, after pm_respawn_trip_cooldown_seconds
(default 300), let ONE spawn through. A still-wedged task re-trips after the
threshold (bounded re-burn ~3 spawns per window); a fixed one advances and
the status-change path fully resets. Restore re-stamps last_check to now, so
a freshly restored row still trips immediately — durability preserved, which
is why the migration-051 persistence tests still pass.
…wn fail-open test

nginx is the single entry point; request.client.host is the nginx peer IP,
collapsing every external client into one limiter bucket (self-DoS amp).
Read the downstream client IP from X-Forwarded-For (first hop) / X-Real-IP,
falling back to the peer. Adds coverage for the XFF keying, the redis-down
fail-open branch, and drops a redundant asyncio marker on a sync-TestClient
test.
…n get_current_agent_slug

login_max_attempts was the only bare cloud-auth field; add a Field
description matching the surrounding idiom. Replace cast('str', ctx.slug)
with a runtime assert that fails loud if the cloud-auth ctx invariant
breaks, and drop the now-unused cast import.
@github-project-automation github-project-automation Bot moved this to Backlog in RoboCo Kanban Jul 6, 2026
@github-actions github-actions Bot added documentation Docs, README, CHANGELOG, governance files area: panel Touches panel/ (Next.js control panel) area: api Touches roboco/api/ (FastAPI routes, schemas, app) tests Test suite changes area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) area: mcp Touches roboco/mcp/ (MCP server entry points) labels Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Thanks for opening your first pull request on RoboCo!

Quick checklist before review (most of these are enforced by CI, but worth a glance):

  • make quality — ruff format check, ruff check, mypy, pytest (≥80% coverage), and the rest of the gate
  • Panel changes pass pnpm lint and pnpm exec tsc --noEmit (run from panel/)
  • No # noqa / # type: ignore shortcuts; pre-existing violations in touched files are fixed
  • Added an entry under ## [Unreleased] in CHANGELOG.md
  • Signed the CLA (the bot will prompt you on this PR)
  • Signed your commits — master requires verified signatures (SSH signing setup)
  • Updated any affected docs under docs/

See CONTRIBUTING.md for the full workflow and the Code of Conduct for the community standards we follow.

Welcome aboard — a maintainer will review shortly.

@rennf93 rennf93 self-assigned this Jul 6, 2026
@rennf93 rennf93 moved this from Backlog to In review in RoboCo Kanban Jul 6, 2026
rennf93 and others added 22 commits July 7, 2026 06:35
…ep so live cost reflects Anthropic cache spend
[phase9] Auth hardening: bound agent tokens (iat/exp) + JWT jti revocation
[scan-fix] Phase 8: LLM providers / usage / billing
[Phase 7] schemas/conventions/MegaTask/API fixes (15 findings, scan-fix program)
Phase 6 — Panel frontend hardening (rides 0.19.0)
Phase 5 — Background-engine hardening (rides 0.19.0)
Phase 4 — Git / workflow hardening (rides 0.19.0)
Phase 3 — Data-integrity fixes (rides 0.19.0)
Phase 2 — State-machine & gateway integrity (rides 0.19.0)
@github-actions github-actions Bot added dependencies pyproject.toml / uv.lock or panel package manifests area: services Touches roboco/services/ (business logic, side effects) area: db Touches roboco/db/ or roboco/models/ area: alembic Touches alembic/ (database migrations) area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) labels Jul 7, 2026
@rennf93 rennf93 merged commit 2247aa3 into feature/video-engine-hyperframes Jul 7, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in RoboCo Kanban Jul 7, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area: alembic Touches alembic/ (database migrations) area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: db Touches roboco/db/ or roboco/models/ area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) area: mcp Touches roboco/mcp/ (MCP server entry points) area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) area: panel Touches panel/ (Next.js control panel) area: services Touches roboco/services/ (business logic, side effects) dependencies pyproject.toml / uv.lock or panel package manifests documentation Docs, README, CHANGELOG, governance files tests Test suite changes

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant