When Clauses
A when clause adds a condition to a rule. The rule only takes effect if both the pattern matches and the when expression evaluates to true. This lets you write rules that depend on environment variables, specific flag values, positional arguments, or defined path lists.
rules: - ask: 'terraform apply *' when: "env.TF_WORKSPACE == 'production'"In this example, terraform apply only triggers the ask prompt when the TF_WORKSPACE environment variable is set to production. In other workspaces, this rule is skipped.
Expression language
Section titled “Expression language”when clauses use CEL (Common Expression Language), a lightweight expression language designed for policy evaluation. runok uses the cel-interpreter crate to evaluate these expressions.
CEL expressions must evaluate to a boolean (true or false). If the expression returns a non-boolean value, runok reports a type error.
Context variables
Section titled “Context variables”Four context variables are available inside when expressions:
env — Environment variables
Section titled “env — Environment variables”A map of the current process environment variables.
# Only ask when deploying to production- ask: 'deploy *' when: "env.DEPLOY_ENV == 'production'"
# Block curl when a proxy is configured- deny: 'curl *' when: "env.HTTP_PROXY != ''"flags — Parsed command flags
Section titled “flags — Parsed command flags”A map of flags extracted from the matched command. Flag names have their leading dashes stripped (e.g., --request becomes request, -X becomes X).
- Flags with values:
flags.request→"POST"(string) - Boolean flags (no value):
flags.force→null
# Block POST/PUT/PATCH requests to production APIs- deny: 'curl -X|--request * *' when: "flags.request == 'POST' || flags.request == 'PUT'"args — Positional arguments
Section titled “args — Positional arguments”A list of positional arguments (non-flag tokens after the command name). Access by index with args[0], args[1], etc.
# Block terraform destroy on production- deny: 'terraform destroy *' when: "args[0] == 'production'"
# Ask when curl targets a production URL- ask: 'curl *' when: "args[0].startsWith('https://prod.')"paths — Defined path lists
Section titled “paths — Defined path lists”A map of named path lists from the definitions.paths section. Useful for checking whether a command operates on sensitive files.
definitions: paths: sensitive: - '.env' - '.envrc' - '~/.ssh/**'
rules: # Deny reading sensitive files when there are many defined sensitive paths - deny: 'cat <path:sensitive>' when: 'size(paths.sensitive) > 0'The paths variable is most useful for checking properties of the defined path list itself (e.g., its size), since the <path:sensitive> pattern already handles matching individual files against the list.
Operators
Section titled “Operators”CEL supports standard operators for building conditions:
Comparison
Section titled “Comparison”| Operator | Description |
|---|---|
== | Equal |
!= | Not equal |
<, > | Less than, greater than |
<=, >= | Less than or equal, greater than or equal |
Logical
Section titled “Logical”| Operator | Description |
|---|---|
&& | Logical AND |
|| | Logical OR |
! | Logical NOT |
String methods
Section titled “String methods”| Method | Description |
|---|---|
.startsWith(prefix) | Check if string starts with prefix |
.endsWith(suffix) | Check if string ends with suffix |
.contains(substr) | Check if string contains substring |
Collection
Section titled “Collection”| Expression | Description |
|---|---|
value in list | Check if value exists in a list |
size(list) | Get the length of a list or map |
Evaluation order
Section titled “Evaluation order”The when clause is evaluated after the pattern matches. The evaluation flow is:
- Check if the rule’s pattern matches the input command.
- If the pattern matches and a
whenclause is present, evaluate the CEL expression. - If the expression returns
true, the rule takes effect. - If the expression returns
false, the rule is skipped (as if it never matched).
This means the when clause acts as an additional filter, not a replacement for pattern matching. You still need a pattern that matches the command structure.
Error handling
Section titled “Error handling”| Error type | Cause | Behavior |
|---|---|---|
| Parse error | Invalid CEL syntax (e.g., @@@ invalid) | Evaluation fails with error |
| Eval error | Referencing an undeclared variable (e.g., missing.var) | Evaluation fails with error |
| Type error | Expression returns non-boolean (e.g., env.HOME) | Evaluation fails with error |
Errors in when clause evaluation cause the entire command evaluation to fail, rather than silently skipping the rule. This is intentional — a misconfigured when clause should be surfaced immediately.
Examples
Section titled “Examples”Environment-based gating
Section titled “Environment-based gating”rules: # Allow terraform plan everywhere, but ask before apply in production - allow: 'terraform plan *' - allow: 'terraform apply *' - ask: 'terraform apply *' when: "env.TF_WORKSPACE == 'production'"Flag-based restrictions
Section titled “Flag-based restrictions”rules: # Allow curl GET requests, but ask before POST to specific hosts - allow: 'curl -X|--request * *' - ask: 'curl -X|--request * *' when: "flags.request == 'POST' && args[0].endsWith('.internal')"Combined conditions
Section titled “Combined conditions”rules: # Deny destructive HTTP methods to production APIs - deny: 'curl -X|--request * *' when: "flags.request == 'POST' && args[0].startsWith('https://prod.')"Related
Section titled “Related”- Configuration Schema:
when— Configuration reference for thewhenfield. - Extensions — Custom validation beyond CEL expressions.