feat: execpolicy v2 (#6467)
## Summary
- Introduces the `codex-execpolicy2` crate.
- This PR covers only the prefix-rule subset of the planned execpolicy
v2 language; a richer language will follow.
## Policy
- Policy language centers on `prefix_rule(pattern=[...], decision?,
match?, not_match?)`, where `pattern` is an ordered list of tokens; any
element may be a list to denote alternatives. `decision` defaults to
`allow`; valid values are `allow`, `prompt`, and `forbidden`. `match` /
`not_match` hold example commands that are tokenized and validated at
load time (think of these as unit tests).
## Policy shapes
- Prefix rules use Starlark syntax:
```starlark
prefix_rule(
pattern = ["cmd", ["alt1", "alt2"]], # ordered tokens; list entries denote alternatives
decision = "prompt", # allow | prompt | forbidden; defaults to allow
match = [["cmd", "alt1"]], # examples that must match this rule (enforced at compile time)
not_match = [["cmd", "oops"]], # examples that must not match this rule (enforced at compile time)
)
```
## Response shapes
- Match:
```json
{
"match": {
"decision": "allow|prompt|forbidden",
"matchedRules": [
{
"prefixRuleMatch": {
"matchedPrefix": ["<token>", "..."],
"decision": "allow|prompt|forbidden"
}
}
]
}
}
```
- No match:
```json
"noMatch"
```
- `matchedRules` lists every rule whose prefix matched the command;
`matchedPrefix` is the exact prefix that matched.
- The effective `decision` is the strictest severity across all matches
(`forbidden` > `prompt` > `allow`).
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
2025-11-17 10:15:45 -08:00
|
|
|
|
|
|
|
|
# Example policy to illustrate syntax; not comprehensive and not recommended for actual use.
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["git", "reset", "--hard"],
|
|
|
|
|
decision = "forbidden",
|
2026-01-05 13:24:48 -08:00
|
|
|
justification = "destructive operation",
|
feat: execpolicy v2 (#6467)
## Summary
- Introduces the `codex-execpolicy2` crate.
- This PR covers only the prefix-rule subset of the planned execpolicy
v2 language; a richer language will follow.
## Policy
- Policy language centers on `prefix_rule(pattern=[...], decision?,
match?, not_match?)`, where `pattern` is an ordered list of tokens; any
element may be a list to denote alternatives. `decision` defaults to
`allow`; valid values are `allow`, `prompt`, and `forbidden`. `match` /
`not_match` hold example commands that are tokenized and validated at
load time (think of these as unit tests).
## Policy shapes
- Prefix rules use Starlark syntax:
```starlark
prefix_rule(
pattern = ["cmd", ["alt1", "alt2"]], # ordered tokens; list entries denote alternatives
decision = "prompt", # allow | prompt | forbidden; defaults to allow
match = [["cmd", "alt1"]], # examples that must match this rule (enforced at compile time)
not_match = [["cmd", "oops"]], # examples that must not match this rule (enforced at compile time)
)
```
## Response shapes
- Match:
```json
{
"match": {
"decision": "allow|prompt|forbidden",
"matchedRules": [
{
"prefixRuleMatch": {
"matchedPrefix": ["<token>", "..."],
"decision": "allow|prompt|forbidden"
}
}
]
}
}
```
- No match:
```json
"noMatch"
```
- `matchedRules` lists every rule whose prefix matched the command;
`matchedPrefix` is the exact prefix that matched.
- The effective `decision` is the strictest severity across all matches
(`forbidden` > `prompt` > `allow`).
---------
Co-authored-by: Michael Bolin <mbolin@openai.com>
2025-11-17 10:15:45 -08:00
|
|
|
match = [
|
|
|
|
|
["git", "reset", "--hard"],
|
|
|
|
|
],
|
|
|
|
|
not_match = [
|
|
|
|
|
["git", "reset", "--keep"],
|
|
|
|
|
"git reset --merge",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["ls"],
|
|
|
|
|
match = [
|
|
|
|
|
["ls"],
|
|
|
|
|
["ls", "-l"],
|
|
|
|
|
["ls", "-a", "."],
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["cat"],
|
|
|
|
|
match = [
|
|
|
|
|
["cat", "file.txt"],
|
|
|
|
|
["cat", "-n", "README.md"],
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["cp"],
|
|
|
|
|
decision = "prompt",
|
|
|
|
|
match = [
|
|
|
|
|
["cp", "foo", "bar"],
|
|
|
|
|
"cp -r src dest",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["head"],
|
|
|
|
|
match = [
|
|
|
|
|
["head", "README.md"],
|
|
|
|
|
["head", "-n", "5", "CHANGELOG.md"],
|
|
|
|
|
],
|
|
|
|
|
not_match = [
|
|
|
|
|
["hea", "-n", "1,5p", "CHANGELOG.md"],
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["printenv"],
|
|
|
|
|
match = [
|
|
|
|
|
["printenv"],
|
|
|
|
|
["printenv", "PATH"],
|
|
|
|
|
],
|
|
|
|
|
not_match = [
|
|
|
|
|
["print", "-0"],
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["pwd"],
|
|
|
|
|
match = [
|
|
|
|
|
["pwd"],
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
prefix_rule(
|
|
|
|
|
pattern = ["which"],
|
|
|
|
|
match = [
|
|
|
|
|
["which", "python3"],
|
|
|
|
|
["which", "-a", "python3"],
|
|
|
|
|
],
|
|
|
|
|
)
|