Skip to content

feat(shell): filename globbing, increment 5 (#34)#42

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

feat(shell): filename globbing, increment 5 (#34)#42
hartsock merged 1 commit into
mainfrom
issue-34/globbing

Conversation

@hartsock

Copy link
Copy Markdown
Member

Increment 5 of #34 (ADR 0005 D3): the safe-subset engine now supports filename globbing * / ? / […].

What

  • parse.rs — an Arg enum (Lit | Glob); read_word marks a word Glob when it contains an unquoted glob metacharacter (quoted/escaped stay literal). The parser only marks; expansion is the executor's job.
  • shell_tool.rs — a pure fnmatch (*/?/[…] with ranges and !/^ negation) + expand_glob (last-segment glob, hidden-file skip, sorted; no match → the literal pattern, bash nullglob-off). Lowering happens in OsSpawner.

The fs enforcement point

Because bridle performs the directory listing, that fs_read is leash-checked in invoke (check_path_read on the listed dir) before any stage spawns — folded into atomic admission. A glob in the program position (argv[0]) is refused (we never exec a pattern). Redirect targets are taken literally (not globbed).

Testing (fully mocked + deep)

  • Pure (no fs): fnmatch cases; expand_glob with a fake dir-lister (sorted, hidden excluded, sub-path prefix preserved, no-match→literal).
  • Mocked: a glob arg reaches the spawner after the leash; glob-as-program denied; glob dir out of fs_read scope denied with nothing spawned.
  • Integration (real fs): cat *.rs → sorted file contents; unmatched glob → literal (cat fails).

Test plan

just check green (fmt + clippy all-features & no-default-features + workspace tests). Part of #34. Leaves only $VAR expansion (allowlist-only, increment 6) for Track A.

🤖 Generated with Claude Code

WHAT: The safe-subset engine now supports filename globbing (*, ?, [..]). parse.rs gains an Arg enum (Lit | Glob); read_word marks a word Glob when it contains an unquoted glob metachar (quoted/escaped stay literal). The executor expands globs against the filesystem: a pure fnmatch (* ? [..] with ranges + !/^ negation) + expand_glob (last-segment glob, hidden-file skip, sorted; no match => literal pattern, bash nullglob-off).

WHY: Because BRIDLE performs the glob's directory listing, that fs_read is leash-checked in invoke (check_path_read on the listed dir) BEFORE any stage spawns — folded into atomic admission. A glob in the program (argv[0]) position is refused (we never exec a pattern). Redirect targets are taken literally (not globbed).

TEST: 14 unit + 11 real-spawn integration. Pure: fnmatch cases, expand_glob with a fake dir-lister (sorted, hidden excluded, sub-path prefix, no-match=>literal). Mocked: glob arg reaches the spawner after the leash; glob-as-program denied; glob dir out of fs_read scope denied with nothing spawned. Real: cat *.rs => sorted file contents; unmatched glob => literal (cat fails). just check green.

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 fd81f74 into main Jun 25, 2026
1 check passed
@hartsock hartsock deleted the issue-34/globbing branch June 25, 2026 05:52
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