Skip to content

Placeholders

Tokens wrapped in <...> are placeholders — special tokens that match dynamically rather than by exact string comparison.

PlaceholderDescription
<cmd>Captures the wrapped command for further rule evaluation
<opts>Absorbs zero or more flag-like tokens
<vars>Absorbs zero or more KEY=VALUE tokens
<path:name>Matches against a named list of paths
<var:name>Matches against a typed variable definition

The <cmd> placeholder captures the remaining tokens as the wrapped command. The wrapped command is then evaluated against the other rules in the configuration. See Wrapped Command Recursion for details.

<cmd> only matches token sequences whose first token is not a flag (does not start with -). This prevents wrapper patterns from accidentally consuming flag arguments as commands. For example, command <cmd> does not match command -v a because -v is a flag, not a command name.

# sudo echo hello -> wrapped command is "echo hello"
- allow: 'sudo <cmd>'

Wrapper patterns are defined in the definitions.wrappers block and referenced by rules:

definitions:
wrappers:
- 'sudo <cmd>'
- 'xargs <opts> <cmd>'
rules:
- allow: 'echo *'
# With the wrapper definition, this also allows:
# sudo echo hello
# xargs -I{} echo hello

The <opts> placeholder absorbs zero or more flag-like tokens (tokens starting with -):

# Matches: env FOO=bar command
# Matches: env -i FOO=bar command
# Matches: env -u HOME -i FOO=bar command
- allow: 'env <opts> <vars> <cmd>'

<opts> stops consuming tokens when it encounters:

  • A token that does not start with -
  • The -- end-of-options marker

For short flags consisting of exactly - plus one ASCII letter (e.g., -n, -S), if the next token does not start with -, it is consumed as the flag’s argument:

# env -S "FOO=bar" command -> <opts> consumes "-S" and "FOO=bar"
- allow: 'env <opts> <cmd>'

The <vars> placeholder absorbs zero or more KEY=VALUE tokens — tokens that contain =:

# Matches: env command
# Matches: env FOO=bar command
# Matches: env FOO=bar BAZ=qux command
- allow: 'env <vars> <cmd>'

<vars> stops consuming tokens when it encounters a token without =.

The <path:name> placeholder matches a command argument against a named list of paths defined in the definitions.paths block.

definitions:
paths:
sensitive:
- /etc/passwd
- /etc/shadow
- /etc/sudoers
config:
- /etc/nginx/nginx.conf
- /etc/hosts

Reference a path list with <path:name>:

rules:
- deny: 'cat <path:sensitive>'
- deny: 'rm <path:sensitive>'
- allow: 'cat <path:config>'
CommandRuleResult
cat /etc/passwddeny: "cat <path:sensitive>"Denied
cat /etc/hostsallow: "cat <path:config>"Allowed
rm /etc/shadowdeny: "rm <path:sensitive>"Denied

Paths are normalized before comparison. The following path components are resolved:

  • . (current directory) is removed
  • .. (parent directory) is resolved
definitions:
paths:
sensitive:
- /etc/passwd
CommandMatches <path:sensitive>
cat /etc/passwdYes
cat /etc/./passwdYes (. removed)
cat /tmp/../etc/passwdYes (.. resolved)

This prevents bypassing path rules through path manipulation.

If a pattern references a path name that is not defined in definitions.paths, the pattern never matches:

# If "sensitive" is not defined, this rule has no effect
- deny: 'cat <path:sensitive>'

The <var:name> placeholder matches a command token against a typed variable definition in the definitions.vars block. It can be used in both argument position and command position.

Each variable has an optional type (default: literal) and a list of values. Values can be plain strings (inheriting the definition-level type) or objects with an explicit per-value type override:

definitions:
vars:
instance-ids:
values:
- i-abc123
- i-def456
- i-ghi789
test-script:
type: path
values:
- ./tests/run
runok:
values:
- runok # literal (default): exact match
- 'cargo run --' # literal: multi-word, consumes multiple tokens
- type: path
value: target/debug/runok # path: canonicalize before comparison
TypeMatching behavior
literalExact string match (default)
pathCanonicalize both sides before comparison, fallback to path normalization

Each value inherits the definition-level type unless it specifies its own type via the { type, value } form.

rules:
- allow: aws ec2 terminate-instances --instance-ids <var:instance-ids>
- allow: bash <var:test-script>
CommandRuleResult
aws ec2 terminate-instances --instance-ids i-abc123allow: "... --instance-ids <var:instance-ids>"Allowed
aws ec2 terminate-instances --instance-ids i-UNKNOWNallow: "... --instance-ids <var:instance-ids>"No match
bash ./tests/runallow: "bash <var:test-script>"Allowed
bash tests/runallow: "bash <var:test-script>"Allowed (path normalization)

<var:name> can also be used as the command name in a pattern. This is useful when the same tool can be invoked in multiple ways:

definitions:
vars:
runok:
values:
- runok
- 'cargo run --'
- type: path
value: target/debug/runok
rules:
- allow: '<var:runok> check'
CommandResult
runok checkAllowed
cargo run -- checkAllowed (multi-word value)
./target/debug/runok checkAllowed (path normalization)
node checkNo match

Multi-word values (e.g. "cargo run --") consume multiple leading tokens from the input command.

When type: path is set, both the command argument and the defined values are canonicalized (resolved to absolute paths via the filesystem). If the path does not exist on disk, logical normalization is used as a fallback (. removal and .. resolution).

This handles cases where the same file is referenced with different path forms:

definitions:
vars:
test-script:
type: path
values:
- ./tests/run
CommandMatches <var:test-script>
bash tests/runYes
bash ./tests/runYes
bash ./tests/../tests/runYes
bash ./scripts/deployNo

If a pattern references a variable name that is not defined in definitions.vars, the pattern never matches.

Placeholders can be combined to handle complex wrapper patterns:

definitions:
wrappers:
# Handles: env [-i] [-u NAME] [KEY=VALUE...] command [args...]
- 'env <opts> <vars> <cmd>'
# Handles: sudo [-u user] command [args...]
- 'sudo <opts> <cmd>'
# Handles: xargs [flags...] command [args...]
- 'xargs <opts> <cmd>'
# Handles: find [args...] -exec|-execdir|-ok|-okdir command [args...] \;|+
- "find * -exec|-execdir|-ok|-okdir <cmd> \\;|+"
  • <cmd> captures one or more tokens whose first token is not a flag (does not start with -); it tries all possible split points to find a valid wrapped command
  • Optional groups, path references, and variable references are not supported inside wrapper patterns