feat(rings): add command denylist enforcement for sandbox execution#3109
Conversation
🤖 AI Agent: test-generator — View details
Test coverage looks good. No gaps identified. |
🤖 AI Agent: security-scanner — View details
No security issues found. |
🤖 AI Agent: breaking-change-detector — View details
No breaking changes detected. |
🤖 AI Agent: code-reviewer — View details
TL;DR: 0 blockers, 1 warning. The PR introduces a robust command denylist enforcement mechanism, but the denylist source and update process should be clarified.
Action items: None. Warnings:
|
🤖 AI Agent: docs-sync-checker — Docs Sync
Docs Sync
|
|
🔴 Contributor Check: HIGH
Automated check by AGT Contributor Check. |
PR Review Summary
Verdict: AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims. |
72c0151 to
1063cea
Compare
Signed-off-by: jlaportebot <jlaportebot@gmail.com>
Signed-off-by: jlaportebot <jlaportebot@gmail.com>
35ee837 to
f544b7d
Compare
imran-siddique
left a comment
There was a problem hiding this comment.
Reviewing this alongside #3114 (AppArmor profile, just merged) -- the two are complementary layers, #3109 provides the Python-side ring enforcement before a subprocess is even spawned. That's the right defense-in-depth shape.
A few things before I can approve:
-
Does
check_command()handle shell-expanded commands? A policy that deniescurlcan be bypassed withsh -c 'cu'+'rl ...'unless the check happens at the shell-expanded level. How does the ring handle indirect invocation? -
What's the behavior when the ring enforcement is bypassed entirely -- i.e. code that directly calls
os.systemorsubprocess.runwithout going throughRingEnforcer? Is there a hook or is this trust-on-call-site? -
Does this need a rebase on top of the recent AppArmor merge (#3114)? The branch may have conflicts with the shim logging changes from #3019.
Address those and I'll approve.
imran-siddique
left a comment
There was a problem hiding this comment.
Good defense-in-depth addition. The implementation is clean:
- Case-insensitive matching via
.lower()prevents the obvious bypass. - Shell metacharacter stripping (;, &, |) before checking is the right order -- strip injection-relevant chars, then check the base name.
- Checking only the first token (command name) is correct; arguments don't affect whether the binary is denylisted.
- The
CommandCheckResultdataclass withmatched_denylist_entryis useful for structured logging. - 21 tests covering the full DENIED_COMMANDS list, injection attempts, case variations, and partial matches.
The comment in the PR body about callers needing to check both ring-level subprocess permission AND command-level denylist is important and worth documenting in the method docstring too (if not already there). Only the maintainer gate is failing.
|
@MohammadHaroonAbuomar can you take a look and merge when you get a chance? @imran-siddique has reviewed and approved this one. |
Summary
Adds command denylist enforcement to the RingEnforcer for sandbox execution environments.
Changes
CommandCheckResult dataclass: Structured result type for command validation with
allowed,reason,command, andmatched_denylist_entryfields.check_command() method: New method on
RingEnforcerthat validates subprocess commands against the globalDENIED_COMMANDSlist fromhypervisor.sandbox.Security features:
Comprehensive test coverage: 21 tests covering:
Test Results
All 21 new tests pass, plus all 54 existing ring enforcement tests pass (75 total).
Integration
The
check_command()method operates independently of ring-level resource checks. Callers should validate both:check_resource(ring, ResourceType.SUBPROCESS)check_command(command)This follows the existing pattern where resource constraints and command validation are separate concerns.