Commit fbcc09b
Yogthos
fix(perm): M3 — route bash through
Closes dirge-6ab (P0 security). Second milestone of the staged
permission refactor; depends on M1 (8dd5b01).
## What
`check_bash_segments` in src/agent/tools/bash.rs migrates from the
legacy `check_perm` / `check_perm_path` to the M1 chokepoint
`enforce()`. Two behavior changes ride along:
1. **Redirect targets route through `write` tool rules.** Pre-M3 the
C4 audit fix routed `> file` / `>> file` / `&> file` / `1> file` /
`2> file` targets through `check_perm_path(tool="bash", &target)`
— looking the target path up against BASH rules (command-style
globs). No bash pattern matches a bare path, so the call fell to
`default_action = Allow`. Result: `echo hi > /etc/passwd` ran
without prompting even though writing to `/etc/` directly would
be denied. Post-M3 the target routes through `tool="write"` via
`Scope::PathResolve`, so the user's write deny lists (or M4's
incoming Ask-default for unmatched paths) actually apply.
2. **Subshell / command-substitution / complex commands prompt
whole-command.** Already the prior behavior, just routed through
`enforce` now. Matches maki's `is_complex_bash` posture
(permissions.rs:457-475): when tree-sitter can't safely split,
the WHOLE command goes to the user for confirmation.
The compound-statement segmenting (the part the user explicitly
called out: "agent runs `git diff && rm -rf /` — Maki uses
tree-sitter to figure out the permissions requested are `git *` and
`rm *`") was already working correctly in dirge via the existing
`parse_bash_segments_full` in src/semantic/adapters/bash.rs. Each
segment is independently routed through `enforce`. The default
`rm -rf /**` deny rule catches the dangerous segment even when the
leading segment is allowed.
## Tests added
Three regression tests pin the M3 invariants:
- `compound_command_denies_dangerous_segment`: `git diff &&
rm -rf /` — second segment hits the rm deny rule. Pins the
segment-walking invariant end-to-end (separate from the parser
tests which only verify the segmenting itself).
- `redirect_target_routes_through_write_rules`: write deny of
`/etc/**` blocks `echo hi > /etc/passwd`. The exact regression
the C4 audit fix attempted to close but mis-routed.
- `redirect_target_allowed_when_write_permits`: cwd-relative
target with no deny rule passes — guards against an
over-broad deny regression.
## Behavior shift for users
`echo … > /tmp/anything` (or any external path) now prompts when
no explicit write rule covers it, because the external-path
catch-all at `permission/checker.rs:434` upgrades unmatched-Allow
to Ask for paths outside the working directory. Previously the
redirect bypassed this gate via the wrong-namespace lookup.
This is a tighter posture than before but consistent with how
`write` already behaves for direct file operations. Users who
need silent /tmp redirects can add:
```jsonc
"permission": {
"write": { "/tmp/**": "allow" }
}
```
## Test
- 1201/1202 tests pass (1 pre-existing unrelated Clojure failure
per 8dd5b01 commit note)
- Binary at ~/bin/dirge (11:00)enforce; redirect targets gate via write rules1 parent 8dd5b01 commit fbcc09b
2 files changed
Lines changed: 133 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
9 | | - | |
| 7 | + | |
10 | 8 | | |
11 | 9 | | |
12 | 10 | | |
| |||
326 | 324 | | |
327 | 325 | | |
328 | 326 | | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
329 | 335 | | |
330 | 336 | | |
331 | 337 | | |
332 | 338 | | |
333 | 339 | | |
334 | 340 | | |
335 | | - | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
336 | 348 | | |
337 | 349 | | |
338 | 350 | | |
339 | | - | |
| 351 | + | |
340 | 352 | | |
341 | 353 | | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
350 | 366 | | |
351 | | - | |
| 367 | + | |
352 | 368 | | |
353 | 369 | | |
354 | 370 | | |
| |||
390 | 406 | | |
391 | 407 | | |
392 | 408 | | |
393 | | - | |
| 409 | + | |
| 410 | + | |
394 | 411 | | |
395 | 412 | | |
396 | | - | |
| 413 | + | |
397 | 414 | | |
398 | 415 | | |
399 | 416 | | |
| |||
687 | 704 | | |
688 | 705 | | |
689 | 706 | | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
690 | 806 | | |
0 commit comments