[POC] PAR: wire AllowedCommandPatterns + DeniedCommandPatterns from runCommand task input#50384
Draft
[POC] PAR: wire AllowedCommandPatterns + DeniedCommandPatterns from runCommand task input#50384
Conversation
Plumbs the two new rshell pattern axes from the runCommand task input
to the rshell interpreter:
RunCommandInputs gains:
AllowedCommandPatterns [][]string
DeniedCommandPatterns [][]string
Run() passes them straight to interp.AllowedCommandPatterns(...) and
interp.DeniedCommandPatterns(...) without any agent-side parsing
(the JSON wire format preserves the structured token-list shape
natively, so there's nothing to split or unflatten).
Operator-side intersection is deliberately skipped — for the POC the
backend is authoritative for patterns. CommandSpecs registration is
also skipped; rshell's built-in spec for `ip` covers the demo target,
and external-command pattern enforcement (kubectl/git/docker) needs
an ExecHandler anyway, which is a much bigger lift.
Includes a local replace directive in go.mod pointing at
~/dd/rshell. STRIP THIS BEFORE THIS PATCH GRADUATES OUT OF POC — it's
dev scaffolding that lets the pattern work be tested before a tagged
rshell release lands.
Tests:
- 5 new tests covering allow-pattern admit/block, deny-pattern
block-even-when-name-allows, deny doesn't false-positive on
siblings, missing pattern fields don't break the runner.
- 4 existing assertions updated for rshell's new
"not permitted by policy" error format (was "command not allowed").
Architectural cases (substitution-defeat, structural matching,
deny-first precedence) live in rshell's own test suite; here we only
verify field plumbing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Plumbs two new rshell pattern axes from the runCommand task input through the Datadog Agent's Private Action Runner (PAR) to the rshell interpreter:
AllowedCommandPatterns [][]string— argv-prefix patterns shaped(command [, subcommand_path...])that admit invocations whose leading structural tokens match (ignoring flags per a registeredCommandSpec).DeniedCommandPatterns [][]string— same shape, deny-first precedence: a deny match short-circuits the gate to a refusal even when an allow rule would otherwise admit.Run()passes both fields straight tointerp.AllowedCommandPatterns(...)/interp.DeniedCommandPatterns(...)without any agent-side parsing. The JSON wire format preserves the structured token-list shape natively, so there's nothing to split.Motivation
POC for the rshell permission-model RFC. The architectural claim is that fine-grained scope enforcement on shell commands has to happen post-expansion on the runner — substitution-defeat attacks like
$(printf ip) route showare invisible to any static caller. rshell already enforces this; this PR makes the surface area available end-to-end so the wf-actions-server can inject patterns into task payloads and have the agent honour them.The classic carve-out use case for the deny axis: an operator can express "allow
ipwholesale, but blockip routespecifically" withAllowedCommands={rshell:ip}plusDeniedCommandPatterns=[["ip","route"]].ip addrandip linkstill admit;ip routeis refused at the gate.Wire format
{ "command": "ip route show", "allowedCommands": ["rshell:ip"], "deniedCommandPatterns": [["ip", "route"]], "allowedPaths": {"default": ["/tmp"]} }Out of scope (intentionally)
RunCommandHandlerintersects backendAllowedCommandsandAllowedPathswith operator config. For patterns, the backend is authoritative for now — adding operator-side pattern config means extending the agent's YAML schema and intersection logic, neither of which adds to the POC's argument.ip(the only builtin with multi-token subcommand structure). External-command pattern enforcement (kubectl/git/docker) needs anExecHandlerfirst, which is a much bigger lift.replace github.com/DataDog/rshell => /Users/matthew.deguzman/dd/rshelldirective ingo.mod. Strip before this graduates out of POC — it's local-dev scaffolding that lets the pattern work be validated before a tagged rshell release lands.Describe how you validated your changes
End-to-end smoke test inside a Lima Linux VM with the local rshell built in:
```bash
RSH=./rshell
$RSH --allowed-commands rshell:ip --denied-command-patterns "ip route" -p /tmp -c 'ip route show'
rshell: ip route show: blocked by deny pattern "ip route"
exit 127 ✓
$RSH --allowed-commands rshell:ip --denied-command-patterns "ip route" -p /tmp -c 'ip addr show'
(real interface listings)
exit 0 ✓
```
Additional Notes
🤖 Generated with Claude Code