* 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.6 KiB
Blocked HTTP Requests (Security Guard)
Symptom
A call to the orchestrator API fails with a generic 400 or 403 and no detail about what was flagged.
Cause
RoboCo's HTTP security layer (fastapi-guard; see docs/rag/architecture/http-security-guard.md) rejected the request. This can only happen when both ROBOCO_GUARD_ENABLED=true and ROBOCO_GUARD_PASSIVE_MODE=false (enforce mode) are set. As of 2026-07-19 that is the default on the NAS build compose — a matching request is actually blocked there, not just logged.
The guard never returns which rule or signature matched, by design, so the 400/403 body itself gives you nothing to act on. Avoiding the triggers below is the only real mitigation.
What Gets Flagged
Generic WAF signatures (SQL injection, XSS, path traversal, suspicious URL patterns) are excluded from scanning on the free-text body fields of note / commit / dm, so normal code, SQL, diffs, file paths, and URLs in those bodies are safe from that layer. Three custom validators scan those same bodies regardless of that exclusion:
- Prompt-injection detection — phrasing like "ignore previous instructions", or "bypass/disable/override the guard/filter/restriction" (a real risk in this repo's own security-work commit messages and notes:
roboco/security.py's own doctring vocabulary uses "guard", "bypass", "disable" constantly) - Secret-exfil detection — literal credential-shaped strings (
sk-ant-...,ghp_..., postgres connection URLs) or the literal doc patternROBOCO_ENCRYPTION_KEY=<...>/ROBOCO_AGENT_AUTH_SECRET=<...>/FERNET_KEY=<...>(matchesCLAUDE.mdand.env.example's own env-var documentation verbatim — quoting or editing those lines in anote/commitbody trips this) or phrasing like "reveal your api keys" - Internal-SSRF detection — fetch-type bodies targeting internal or metadata hosts (
169.254.169.254,roboco-*internal service hostnames)
The three custom validators scan the raw request body regardless of which top-level field the text sits in — unlike the WAF's field exclusion, there is no safe field for these three.
Solution: Hygiene Rules
Follow these when composing note / commit / dm bodies or any fetch-type payload:
- Never paste real secrets or credentials (API keys, tokens, DB connection strings) into a request body, even inside a code snippet or diff.
- When discussing an env var like
ROBOCO_ENCRYPTION_KEYin a note/commit body, don't write it asNAME=value(even a placeholder value) — writeROBOCO_ENCRYPTION_KEYand describe the value separately, e.g. "set to a generated Fernet key", to avoid theNAME=<10+ chars>credential-shape match. - Avoid "bypass/override/disable the guard/filter/restriction/safety" phrasing in commit messages or notes about this security layer itself — describe the change without that verb+noun adjacency (e.g. "excludes X from the WAF scan" instead of "bypasses the guard's WAF scan").
- Never aim a fetch/HTTP-call body at an internal service host (
roboco-*) or a cloud metadata endpoint (169.254.169.254). - Code, SQL, diffs, file paths, and HTML snippets are otherwise fine to include — the WAF layer is calibrated to exclude legitimate agent content on those fields (this exclusion does not cover the three custom validators above).
Current Status (2026-07-19)
ROBOCO_GUARD_ENABLED is off by default in config; the NAS build compose arms it ON with ROBOCO_GUARD_PASSIVE_MODE=false (enforce). A matching request on that deploy is genuinely blocked, not just logged — the rules above are a live restriction there, not just future-proofing. The registry compose and a bare config default both stay off.