mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(security): active guard enforcement, CEO A2A target check, notification expiry (#595)
* 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>
This commit is contained in:
@@ -6,7 +6,7 @@ A call to the orchestrator API fails with a generic `400` or `403` and no detail
|
||||
|
||||
## 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-01 the guard is off by default, and wherever it is enabled it runs passive/log-only, so this is not something that occurs today — it's documented so the block is recognizable if/when enforcement is turned on later.
|
||||
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.
|
||||
|
||||
@@ -14,18 +14,22 @@ The guard never returns which rule or signature matched, by design, so the 400/4
|
||||
|
||||
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
|
||||
- Secret-exfil detection — literal credential-shaped strings (`sk-ant-...`, `ghp_...`, postgres connection URLs) or phrasing like "reveal your api keys"
|
||||
- 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 pattern `ROBOCO_ENCRYPTION_KEY=<...>` / `ROBOCO_AGENT_AUTH_SECRET=<...>` / `FERNET_KEY=<...>`** (matches `CLAUDE.md` and `.env.example`'s own env-var documentation verbatim — quoting or editing those lines in a `note`/`commit` body 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, regardless of whether enforcement is currently active:
|
||||
Follow these when composing `note` / `commit` / `dm` bodies or any fetch-type payload:
|
||||
|
||||
1. Never paste real secrets or credentials (API keys, tokens, DB connection strings) into a request body, even inside a code snippet or diff.
|
||||
2. Never aim a fetch/HTTP-call body at an internal service host (`roboco-*`) or a cloud metadata endpoint (`169.254.169.254`).
|
||||
3. 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.
|
||||
2. When discussing an env var like `ROBOCO_ENCRYPTION_KEY` in a note/commit body, don't write it as `NAME=value` (even a placeholder value) — write `ROBOCO_ENCRYPTION_KEY` and describe the value separately, e.g. "set to a generated Fernet key", to avoid the `NAME=<10+ chars>` credential-shape match.
|
||||
3. 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").
|
||||
4. Never aim a fetch/HTTP-call body at an internal service host (`roboco-*`) or a cloud metadata endpoint (`169.254.169.254`).
|
||||
5. 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-01)
|
||||
## Current Status (2026-07-19)
|
||||
|
||||
The guard is built on the `feature/fastapi-guard-hardening` branch, off by default (`ROBOCO_GUARD_ENABLED=false`), and wherever enabled it runs in passive/log-only mode. No agent request is being blocked by it today — the rules above are about good hygiene now and correctness later, not a live restriction.
|
||||
`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.
|
||||
|
||||
Reference in New Issue
Block a user