Skip to content

feat(shell): file redirections, increment 3 (#34)#40

Merged
hartsock merged 1 commit into
mainfrom
issue-34/redirections
Jun 25, 2026
Merged

feat(shell): file redirections, increment 3 (#34)#40
hartsock merged 1 commit into
mainfrom
issue-34/redirections

Conversation

@hartsock

Copy link
Copy Markdown
Member

Increment 3 of #34 (ADR 0005 D3): the safe-subset engine now supports file redirections > / >> / <.

What

  • parse.rs — refactored into a read_word tokenizer + a Command { argv, redirects } model (Pipeline = Vec<Command>). >/>> (stdout truncate/append), < (stdin); quoted operators stay literal. Refused (Unsupported): fd-number forms (2>, 2>&1), >&/<& fd-duplication, heredocs (<<) — rather than risk silently mishandling bash's fd rules.
  • shell_tool.rsOsSpawner opens redirect targets and wires them (a redirect overrides the pipe for that fd); a last stage redirected to a file yields empty captured stdout.

The fs enforcement point

Because bridle performs the open, the target is leash-checked in invoke before any stage spawns>check_path_write, <check_path_read — folded into atomic admission (one out-of-scope program or redirect target denies the whole pipeline, no partial side effects). This is real L2 enforcement on opens bridle does itself (a spawned program's own opens remain L3's job, #35).

Testing (fully mocked + deep)

  • Unit (no real fs): redirects parsed + passed to the spawner; an out-of-scope redirect target is denied (DenialKind::Open) with the spawner never called; quoted > stays a literal arg.
  • Integration (tests/real_spawn.rs, real fs): real > truncate / >> append, < stdin feed (sort), pipeline + redirect on the last stage. Unique temp paths, cleaned up.

Test plan

just check green (fmt + clippy all-features & no-default-features + workspace tests). Part of #34.

🤖 Generated with Claude Code

WHAT: The safe-subset engine now supports file redirections: > (truncate), >> (append), and < (stdin). parse.rs refactors into a read_word tokenizer + a Command{argv, redirects} model (Pipeline = Vec<Command>); fd-number forms (2>, 2>&1), >&/<& fd-duplication, and heredocs (<<) are refused as Unsupported rather than risk silently mishandling bash's fd rules. The OsSpawner opens redirect targets and wires them (redirect overrides the pipe for that fd); last-stage stdout that goes to a file means empty captured stdout.

WHY: Because BRIDLE performs the redirect's file open, the target is leash-checked in invoke (> => check_path_write, < => check_path_read) BEFORE any stage spawns — a real fs enforcement point (unlike a spawned program's own opens, which are L3's job). This is part of atomic admission: a single out-of-scope program OR redirect target denies the whole pipeline with no partial side effects.

TEST: 26 unit (mock-spawner: redirects parsed + passed; out-of-scope redirect target denied with the spawner never called; quoted redirect operator stays literal) + 8 real-spawn integration (real > truncate/>> append, < stdin feed, pipeline+redirect on last stage). just check green (fmt + clippy all-features & no-default-features + workspace tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01HMGPEApE4XfwgMhgFbRn6c
@hartsock hartsock added the risk:low Low-risk change label Jun 25, 2026
@hartsock hartsock merged commit f66c837 into main Jun 25, 2026
1 check passed
@hartsock hartsock deleted the issue-34/redirections branch June 25, 2026 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:low Low-risk change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant