* fix(security): guard goes active; CEO A2A respects no-comms roles; ack notifications expire ROBOCO_GUARD_PASSIVE_MODE defaults to false in both compose files — the deferred post-calibration flip; fail_secure stays off and the env override remains the rollback. can_a2a_direct no longer short-circuits the CEO past the no-comms set (auditor/pr_reviewer/prompter/secretary), now canonical in foundation.policy.communications.NO_COMMS_ROLES and shared with the content-actions gate; the A2A service refuses at conversation creation instead of silently suppressing the wake. Ack-required notifications get expires_at stamped from ROBOCO_NOTIFICATION_ACK_TTL_HOURS (default 48, 0 disables), so the re-escalation sweeper's expires_at query matches rows for the first time. * refactor(notification): extract _ack_and_expiry — xenon rank back under B The expires_at stamping pushed _create_notification_with_session to rank C; the requires_ack + expiry derivation moves into a helper with the same semantics and comments. * test(conftest): dispose the global DB engine after every test Production code reaching get_db_context()/get_engine() lazily creates the process-global engine bound to the current event loop; with per-test function-scoped loops, any later test touching the global path inherits a dead-loop engine and dies with 'Future attached to a different loop' — the order-dependent class that has been wandering the suite (cloud_auth login, metrics, tasks-routes, full-lifecycle) whenever collection order shifts. An autouse fixture now close_db()s after every test, keeping the global path loop-local; no-op when untouched. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
3.7 KiB
HTTP Security Guard
What It Is
RoboCo's HTTP request layer is protected by fastapi-guard (v7.2.1), implemented in roboco/security.py and wired into the app in roboco/api/app.py's create_app.
Enable/Disable
| Variable | Default | Effect |
|---|---|---|
ROBOCO_GUARD_ENABLED |
false |
Master switch. Off = completely inert — no middleware is mounted, the request path is entirely unchanged, and nothing is logged or blocked. |
ROBOCO_GUARD_PASSIVE_MODE |
see below | When the guard is enabled, controls whether it blocks matching requests or only logs them. |
As of 2026-07-19 the guard is gated off by default in config, but the NAS build compose arms it ON in ACTIVE enforcement (ROBOCO_GUARD_PASSIVE_MODE=false) — passive/log-only calibration came back clean, and the CEO approved the flip now that cloud auth + Tailscale are armed. A matching request on that deploy is actually blocked, not just logged. The registry compose still ships it fully off (see Enforcement Posture below).
When Armed
With ROBOCO_GUARD_ENABLED=true, a SecurityMiddleware sits outermost in the middleware stack, and per-route decorators add rate limits, request-size caps, content-type filters, a signature-based WAF (detects SQL injection, XSS, path traversal, and suspicious URL patterns), security response headers, cloud-provider/honeypot checks, and an emergency lockdown switch.
On top of those generic checks, three RoboCo-specific custom validators run against request bodies:
| Validator | Blocks |
|---|---|
| Prompt-injection detection | Bodies attempting to inject instructions |
| Secret-exfil detection | Bodies carrying literal credential-shaped strings (e.g. sk-ant-..., ghp_..., postgres connection URLs) or phrasing like "reveal your api keys" |
| Internal-SSRF detection | Fetch-type bodies targeting internal/metadata hosts (e.g. 169.254.169.254, roboco-* internal service hostnames) |
Enforcement Posture
ROBOCO_GUARD_PASSIVE_MODE decides what happens on a match: true (passive) detects and logs only, and never blocks a request. false (enforce) actually blocks the matching request — this is how the NAS build compose is armed today (its default flipped from true to false once passive-mode calibration reviewed clean). The registry compose omits the guard trio entirely, leaving a fresh third-party deploy on the safe config default (guard off).
A blocked request gets a generic 400 or 403 response — no rule or signature detail is returned, so the response body can't be used to fingerprint what tripped the guard.
WAF Calibration for Agent Traffic
Agent traffic legitimately carries code, SQL, diffs, file paths, HTML snippets, and URLs — for example inside note / commit / dm bodies. To avoid false positives, the free-text body fields on those routes are excluded from WAF signature scanning via excluded_detection_body_fields in build_security_config, so normal code/SQL/diff/HTML payloads from agents are not flagged by the WAF layer.
The three custom validators above are not covered by that exclusion — they scan those same bodies regardless of the WAF exclusion. See docs/rag/troubleshooting/blocked-http-requests.md for what this means in practice and what not to put in a request body.
Scanner Auto-Ban
A separate layer targets automated scanners (not agents — agents run on Docker-internal IPs). Repeated probes to scanner fingerprints on /api paths (recon/sensitive_file/cms_probing categories) trip a per-IP auto-ban in active mode, and nginx drops the classic root scanner paths (/.env, /wp-login.php, /.git/config, …) at the edge with 444. This does not affect legitimate agent traffic to the gateway verbs.