Skip to content

fix(guardrails): deny sudo routes to the guardrail registration (#3105) - #3113

Merged
vybe merged 1 commit into
devfrom
feature/3105-sudo-guardrail-deny
Sep 30, 2026
Merged

vybe merged 1 commit into
devfrom
feature/3105-sudo-guardrail-deny

Conversation

@obasilakis

Copy link
Copy Markdown
Contributor

Summary

  • bash_deny gets two new patterns:
    • any sudo command naming /etc/claude-code, /opt/trinity or /etc/sudoers, plus visudo
    • root shells: sudo -i/-s/--login/--shell, sudo su, sudo <shell> with no script, and bare su
  • Each sudo option token has exactly one way to match, so match time stays linear. A hook that runs past its timeout does not deny.
  • Agent /health reports guardrails_registration (ok/missing/not_root_owned/writable). It stats the file on every request, and the value is an enum only, like clone_status.
  • Docs now say the registration cannot be rewritten without sudo, and that the regexes are a speed bump. This covers architecture security.md, requirements §28.1/§28.2.1, the guardrails feature flow and the user docs.

Changes

  • docker/base-image/hooks/guardrails-baseline.json
  • docker/base-image/agent_server/routers/info.py
  • tests/unit/test_3105_sudo_guardrail_deny.py
  • docs, plus the docs/security-reports/cso-diff-2026-09-30-3105-* audit

Behaviour change

sudo reads of those paths are denied too (for example sudo cat /etc/sudoers). Unchanged and still allowed: sudo apt-get install -s, sudo bash script.sh, sudo sh -c '…', sudo -u postgres psql.

Test Plan

  • cd tests && pytest unit/test_3105_sudo_guardrail_deny.py unit/test_guardrails.py unit/test_ent345_guardrail_registration.py unit/test_1439_clone_status_health.py -q
  • Base-image rebuild and live agent: sudo -i denied from chat; /health shows guardrails_registration: ok

Fixes #3105

🤖 Generated with Claude Code

…lth re-checks it per request

bash_deny gains two patterns: any sudo command naming /etc/claude-code,
/opt/trinity or /etc/sudoers (plus visudo), and root shells (sudo -i/-s/
--login/--shell, sudo su, sudo <shell> with no script, bare su). Each sudo
option token has one match path so matching stays linear; a hook that
outlasts its timeout does not deny.

Agent /health reports guardrails_registration (ok/missing/not_root_owned/
writable), stat'd on every request. Docs state the registration cannot be
rewritten without sudo, and that the deny patterns are a speed bump.

Fixes #3105

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge-train: batch validated on train/20260930-1240 (#3118 green on Tier 1 + Tier 2)

@vybe
vybe merged commit 503f604 into dev Sep 30, 2026
24 checks passed
@vybe

vybe commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

merge-train: merged. Follow-ups from validation, none of them blocking:

  • The deny regex can be bypassed. It misses path spellings like /etc/claude*, /etc//claude-code, /etc/./claude-code and cd /etc && sudo rm -rf claude-code. It also misses sudoedit, root shells inside quotes (bash -c 'sudo -i', sh -c 'su -'), and a bare su on a later line.
  • It also blocks harmless commands. These are denied: grep -rn sudo /opt/trinity/hooks, man visudo, and commit messages that contain su root.
  • Nothing reads guardrails_registration. The new /health field isn't read by monitoring_service.py, the way clone_status is.
  • The test has its own copy of the matcher. It should import compile_patterns from the hooks' lib.py instead.

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