Skip to content

#972 rebased onto main, plus exec builtin filter coverage - #1314

Draft
hartsock wants to merge 3 commits into
reubeno:mainfrom
hartsock:demo/972-exec-fix
Draft

hartsock wants to merge 3 commits into
reubeno:mainfrom
hartsock:demo/972-exec-fix

Conversation

@hartsock

Copy link
Copy Markdown
Contributor

Draft, and mostly not my work. The first commit is @reubeno's #972 (static-filter-attempt-minimized), rebased onto current main. The second is one fix on top. Opened as a draft so the rebase is reviewable rather than described — take it, adapt it, or close it; it's your design and I'd rather you drive it.

Context in #1183: you asked me in June whether the statically registered filters would serve our use case, I didn't answer and pushed a competing seam in #1184 instead. #1184 is now closed. This is me answering properly.

Commit 1 — #972 rebased onto 30a3bce

One cherry-pick. Only two files conflicted, brush-core/src/commands.rs and brush-core/src/shell/execution.rs. Two things in execute_via_external needed carrying forward that the branch predates, and are easy to lose in a mechanical rebase:

  • main's argv0 override — execute_external_command grew an argv0_override parameter, which the branch's call site doesn't pass;
  • main's $_ update after external commands (update_last_arg_variable), which the branch's finally block drops.

Both are restored in the rebase.

Commit 2 — route the exec builtin through the filter

brush-builtins/src/exec.rs composes its own std::process::Command and calls CommandExt::exec, replacing the shell process image without ever reaching execute_external_command. So a CmdExecFilter that denies rm is still replaceable by exec /bin/rm. #972 touches no brush-builtins/ files, so the gap is inherited.

This runs the same pre_external_cmd filter in the builtin, before cmd.exec(), and resolves the program first (new commands::resolve_external_program) so the filter is shown the executable that will actually run — otherwise the OS redoes its own PATH search against the child environment, which need not match the shell's.

PreFilterResult is #[non_exhaustive], and this site is about to replace the process image, so an unrecognized decision fails closed rather than being guessed at as "continue".

Test. Cases run in a dedicated child process — this test binary, re-invoked — because a successful exec replaces the process image and a broken implementation takes the test runner with it. Each asserts the marker the denied program would have created does not exist, and that the filter was consulted exactly once. Against the unfiltered exec.rs they fail with the denied program ran and replaced the shell: /tmp/.../marker exists.

What I found porting a real policy onto this

Offered as feedback, not as requests. Our downstream is an object-capability layer for LLM coding agents; the shell is the tool surface, and a per-invocation grant has to be enforced in-process because a name containing a path separator bypasses both PATH and the builtin table.

  1. ExternalCmdParams exposes neither the command name as written nor argv0 — only the ExternalCommand (program, args, envs, cwd). A policy can't match on the spelling used, and can't see exec -a impostor /usr/bin/touch. I dropped two tests in the port because the information isn't there.
  2. cmd_exec_filter is #[serde(skip, default = …)] — a serialized shell returns with a freshly-defaulted filter rather than the installed one. If the default is the no-op, a round-trip silently unconfines; if it isn't, a stateful policy still returns without its state.
  3. A successful exec never returns, so post_external_cmd can never fire for it. An audit log assuming pre/post pairing will mis-attribute it — probably worth a line in filter-architecture.md.
  4. Redirections aren't covered. SourceFilter handles ./source, but >, >> and <> reach Shell::open_file unfiltered. All four open_file call sites already know their access intent explicitly, so it needn't be recovered after the fact.
  5. A denial can be swallowed by an enclosing while … done, which for a runaway-bounding policy means the leash doesn't hold. We needed a notion of a terminating error downstream.
  6. A filter that can rewrite the command means what was authorized and what runs can differ, and under composition a later filter can rewrite what an earlier one approved. Worth a documented rule about which filter's output is final.

Validation

At the head commit against base 30a3bce: cargo xtask ci pre-commit — fmt, build, clippy --workspace --all-features --all-targets, schemas clean; unit 390/390; integration (nextest --workspace) 2709/2709; cargo test --test brush-compat-tests unchanged from clean main.

cargo deny reports advisories FAILED, identically on clean main: chacha20 0.10.1 was yanked after the lockfile was pinned. cargo update -p chacha20 resolves to 0.10.2.

Authored with AI assistance and reviewed and tested by a human contributor; commits carry Assisted-by: trailers. The first commit is @reubeno's, unmodified except for the rebase conflicts noted above.

reubeno and others added 2 commits August 29, 2026 13:41
… filter

`exec` outside a subshell composes its own std::process::Command and calls
CommandExt::exec, replacing the shell process image without ever reaching
commands::execute_external_command. It is therefore not covered by the
filtering applied there: a CmdExecFilter that denies `rm` is still replaceable
by `exec /bin/rm`.

Run the same pre_external_cmd filter in the builtin, before cmd.exec(), and
honor its decision. The program is resolved first (new
commands::resolve_external_program, mirroring external dispatch) so the filter
is shown the executable that will actually run; otherwise the OS would redo its
own PATH search against the child environment, which need not match the
shell's.

PreFilterResult is #[non_exhaustive]; this site is about to replace the process
image, so an unrecognized decision fails closed rather than being guessed at as
"continue".

Tests run in a dedicated child process: a successful exec replaces the process
image and a broken implementation would take the test runner with it. Against
the unfiltered exec.rs they fail with "the denied program ran and replaced the
shell: /tmp/.../marker exists".

Two limits found while porting, noted for follow-up rather than worked around:
ExternalCmdParams exposes neither the command name as written nor argv0, so a
policy cannot match on the spelling used or see an `exec -a` override; and a
successful exec never returns, so post_external_cmd can never fire for it.

Assisted-by: Claude Code:claude-opus-5
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

Public API changes for crate: brush-core

Removed items

-pub struct brush_core::extensions::DefaultPlaceholder
-impl brush_core::extensions::PlaceholderBehavior for brush_core::extensions::DefaultPlaceholder
-impl brush_core::extensions::PlaceholderBehavior for brush_core::extensions::DefaultPlaceholder
-impl<EF: brush_core::extensions::ErrorFormatter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF>
-impl<EF: brush_core::extensions::ErrorFormatter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF>
-impl<EF: brush_core::extensions::ErrorFormatter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF>
-pub type brush_core::extensions::ShellExtensionsImpl<EF>::ErrorFormatter = EF
-pub type brush_core::extensions::ShellExtensionsImpl<EF>::ErrorFormatter = EF
-pub type brush_core::extensions::ShellExtensionsImpl<EF>::ErrorFormatter = EF
-pub trait brush_core::extensions::PlaceholderBehavior: core::clone::Clone + core::default::Default + core::marker::Send + core::marker::Sync + 'static

Added items

+pub fn brush_core::commands::resolve_external_program<SE: brush_core::extensions::ShellExtensions>(&mut brush_core::Shell<SE>, &str) -> core::option::Option<std::path::PathBuf>
+impl<EF: brush_core::extensions::ErrorFormatter, CF: brush_core::filter::CmdExecFilter, SF: brush_core::filter::SourceFilter> brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>
+pub const fn brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::cmd_exec_filter(&self) -> &CF
+pub const fn brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::error_formatter(&self) -> &EF
+pub const fn brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::source_filter(&self) -> &SF
+impl<EF: brush_core::extensions::ErrorFormatter, CF: brush_core::filter::CmdExecFilter, SF: brush_core::filter::SourceFilter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>
+impl<EF: brush_core::extensions::ErrorFormatter, CF: brush_core::filter::CmdExecFilter, SF: brush_core::filter::SourceFilter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>
+impl<EF: brush_core::extensions::ErrorFormatter, CF: brush_core::filter::CmdExecFilter, SF: brush_core::filter::SourceFilter> brush_core::extensions::ShellExtensions for brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::CmdExecFilter = CF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::CmdExecFilter = CF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::CmdExecFilter = CF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::ErrorFormatter = EF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::ErrorFormatter = EF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::ErrorFormatter = EF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::SourceFilter = SF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::SourceFilter = SF
+pub type brush_core::extensions::ShellExtensionsImpl<EF, CF, SF>::SourceFilter = SF
+pub type brush_core::extensions::ShellExtensions::CmdExecFilter: brush_core::filter::CmdExecFilter
+pub type brush_core::extensions::ShellExtensions::SourceFilter: brush_core::filter::SourceFilter
+pub mod brush_core::filter
+#[non_exhaustive] pub enum brush_core::filter::PostFilterResult<O>
+pub brush_core::filter::PostFilterResult::Return(O)
+#[non_exhaustive] pub enum brush_core::filter::PreFilterResult<I, O>
+pub brush_core::filter::PreFilterResult::Continue(I)
+pub brush_core::filter::PreFilterResult::Return(O)
+#[non_exhaustive] pub struct brush_core::filter::ExternalCmdParams<'a, SE: brush_core::extensions::ShellExtensions>
+pub brush_core::filter::ExternalCmdParams::command: brush_core::filter::ExternalCommand
+pub brush_core::filter::ExternalCmdParams::shell: &'a brush_core::Shell<SE>
+impl<'a, SE: brush_core::extensions::ShellExtensions> brush_core::filter::ExternalCmdParams<'a, SE>
+pub const fn brush_core::filter::ExternalCmdParams<'a, SE>::new(&'a brush_core::Shell<SE>, brush_core::filter::ExternalCommand) -> Self
+pub struct brush_core::filter::ExternalCommand
+impl brush_core::filter::ExternalCommand
+pub fn brush_core::filter::ExternalCommand::arg(&mut self, impl core::convert::AsRef<std::ffi::os_str::OsStr>) -> &mut Self
+pub fn brush_core::filter::ExternalCommand::args(&self) -> &[std::ffi::os_str::OsString]
+pub fn brush_core::filter::ExternalCommand::args_extend<I, S>(&mut self, I) -> &mut Self where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<std::ffi::os_str::OsStr>
+pub const fn brush_core::filter::ExternalCommand::clear_env(&mut self) -> &mut Self
+pub fn brush_core::filter::ExternalCommand::current_dir(&self) -> core::option::Option<&std::path::Path>
+pub fn brush_core::filter::ExternalCommand::env(&mut self, impl core::convert::AsRef<std::ffi::os_str::OsStr>, impl core::convert::AsRef<std::ffi::os_str::OsStr>) -> &mut Self
+pub const fn brush_core::filter::ExternalCommand::env_clear(&self) -> bool
+pub fn brush_core::filter::ExternalCommand::envs(&self) -> &[(std::ffi::os_str::OsString, std::ffi::os_str::OsString)]
+pub fn brush_core::filter::ExternalCommand::into_std_command(self) -> std::process::Command
+pub fn brush_core::filter::ExternalCommand::new(impl core::convert::AsRef<std::ffi::os_str::OsStr>) -> Self
+pub fn brush_core::filter::ExternalCommand::program(&self) -> &std::ffi::os_str::OsStr
+pub fn brush_core::filter::ExternalCommand::set_current_dir(&mut self, impl core::convert::AsRef<std::path::Path>) -> &mut Self
+pub fn brush_core::filter::ExternalCommand::set_program(&mut self, impl core::convert::AsRef<std::ffi::os_str::OsStr>) -> &mut Self
+pub struct brush_core::filter::FilterStack<First, Second>
+pub brush_core::filter::FilterStack::first: First
+pub brush_core::filter::FilterStack::second: Second
+impl<First, Second> brush_core::filter::FilterStack<First, Second>
+pub const fn brush_core::filter::FilterStack<First, Second>::new(First, Second) -> Self
+impl<First: brush_core::filter::CmdExecFilter, Second: brush_core::filter::CmdExecFilter> brush_core::filter::CmdExecFilter for brush_core::filter::FilterStack<First, Second>
+impl<First: brush_core::filter::CmdExecFilter, Second: brush_core::filter::CmdExecFilter> brush_core::filter::CmdExecFilter for brush_core::filter::FilterStack<First, Second>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_external_cmd(&self, brush_core::filter::ExternalCmdOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::ExternalCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_external_cmd(&self, brush_core::filter::ExternalCmdOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::ExternalCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_simple_cmd(&self, brush_core::filter::SimpleCmdOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::SimpleCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_simple_cmd(&self, brush_core::filter::SimpleCmdOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::SimpleCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_external_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::ExternalCmdParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::ExternalCmdParams<'a, SE>, brush_core::filter::ExternalCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_external_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::ExternalCmdParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::ExternalCmdParams<'a, SE>, brush_core::filter::ExternalCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_simple_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SimpleCmdParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::SimpleCmdParams<'a, SE>, brush_core::filter::SimpleCmdOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_simple_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SimpleCmdParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::SimpleCmdParams<'a, SE>, brush_core::filter::SimpleCmdOutput>
+impl<First: brush_core::filter::SourceFilter, Second: brush_core::filter::SourceFilter> brush_core::filter::SourceFilter for brush_core::filter::FilterStack<First, Second>
+impl<First: brush_core::filter::SourceFilter, Second: brush_core::filter::SourceFilter> brush_core::filter::SourceFilter for brush_core::filter::FilterStack<First, Second>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_source_script(&self, brush_core::filter::SourceScriptOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::SourceScriptOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::post_source_script(&self, brush_core::filter::SourceScriptOutput) -> brush_core::filter::PostFilterResult<brush_core::filter::SourceScriptOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_source_script<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SourceScriptParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::SourceScriptParams<'a, SE>, brush_core::filter::SourceScriptOutput>
+pub async fn brush_core::filter::FilterStack<First, Second>::pre_source_script<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SourceScriptParams<'a, SE>) -> brush_core::filter::PreFilterResult<brush_core::filter::SourceScriptParams<'a, SE>, brush_core::filter::SourceScriptOutput>
+impl<First: core::clone::Clone, Second: core::clone::Clone> core::clone::Clone for brush_core::filter::FilterStack<First, Second>
+pub fn brush_core::filter::FilterStack<First, Second>::clone(&self) -> Self
+impl<First: core::default::Default, Second: core::default::Default> core::default::Default for brush_core::filter::FilterStack<First, Second>
+pub fn brush_core::filter::FilterStack<First, Second>::default() -> Self
+pub struct brush_core::filter::NoOpCmdExecFilter
+impl brush_core::filter::CmdExecFilter for brush_core::filter::NoOpCmdExecFilter
+impl brush_core::filter::CmdExecFilter for brush_core::filter::NoOpCmdExecFilter
+pub fn brush_core::filter::NoOpCmdExecFilter::post_external_cmd(&self, brush_core::filter::ExternalCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::post_external_cmd(&self, brush_core::filter::ExternalCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::post_simple_cmd(&self, brush_core::filter::SimpleCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::post_simple_cmd(&self, brush_core::filter::SimpleCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::pre_external_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::ExternalCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::ExternalCmdParams<'a, SE>, brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::pre_external_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::ExternalCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::ExternalCmdParams<'a, SE>, brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::pre_simple_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SimpleCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SimpleCmdParams<'a, SE>, brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpCmdExecFilter::pre_simple_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SimpleCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SimpleCmdParams<'a, SE>, brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub struct brush_core::filter::NoOpSourceFilter
+impl brush_core::filter::SourceFilter for brush_core::filter::NoOpSourceFilter
+impl brush_core::filter::SourceFilter for brush_core::filter::NoOpSourceFilter
+pub fn brush_core::filter::NoOpSourceFilter::post_source_script(&self, brush_core::filter::SourceScriptOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SourceScriptOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpSourceFilter::post_source_script(&self, brush_core::filter::SourceScriptOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SourceScriptOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpSourceFilter::pre_source_script<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SourceScriptParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SourceScriptParams<'a, SE>, brush_core::filter::SourceScriptOutput>> + core::marker::Send
+pub fn brush_core::filter::NoOpSourceFilter::pre_source_script<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SourceScriptParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SourceScriptParams<'a, SE>, brush_core::filter::SourceScriptOutput>> + core::marker::Send
+#[non_exhaustive] pub struct brush_core::filter::SimpleCmdParams<'a, SE: brush_core::extensions::ShellExtensions>
+pub brush_core::filter::SimpleCmdParams::args: alloc::borrow::Cow<'a, [alloc::string::String]>
+pub brush_core::filter::SimpleCmdParams::command_name: alloc::borrow::Cow<'a, str>
+pub brush_core::filter::SimpleCmdParams::shell: &'a brush_core::Shell<SE>
+impl<'a, SE: brush_core::extensions::ShellExtensions> brush_core::filter::SimpleCmdParams<'a, SE>
+pub fn brush_core::filter::SimpleCmdParams<'a, SE>::args(&self) -> &[alloc::string::String]
+pub fn brush_core::filter::SimpleCmdParams<'a, SE>::args_to_strings(&self) -> alloc::borrow::Cow<'_, [alloc::string::String]>
+pub fn brush_core::filter::SimpleCmdParams<'a, SE>::command_name(&self) -> &str
+pub fn brush_core::filter::SimpleCmdParams<'a, SE>::from_command_args(&'a brush_core::Shell<SE>, impl core::convert::Into<alloc::borrow::Cow<'a, str>>, &'a [brush_core::commands::CommandArg]) -> Self
+pub fn brush_core::filter::SimpleCmdParams<'a, SE>::new(&'a brush_core::Shell<SE>, impl core::convert::Into<alloc::borrow::Cow<'a, str>>, impl core::convert::Into<alloc::borrow::Cow<'a, [alloc::string::String]>>) -> Self
+#[non_exhaustive] pub struct brush_core::filter::SourceScriptParams<'a, SE: brush_core::extensions::ShellExtensions>
+pub brush_core::filter::SourceScriptParams::args: alloc::borrow::Cow<'a, [alloc::string::String]>
+pub brush_core::filter::SourceScriptParams::path: alloc::borrow::Cow<'a, std::path::Path>
+pub brush_core::filter::SourceScriptParams::shell: &'a brush_core::Shell<SE>
+impl<'a, SE: brush_core::extensions::ShellExtensions> brush_core::filter::SourceScriptParams<'a, SE>
+pub fn brush_core::filter::SourceScriptParams<'a, SE>::args(&self) -> &[alloc::string::String]
+pub fn brush_core::filter::SourceScriptParams<'a, SE>::new(&'a brush_core::Shell<SE>, impl core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>>, impl core::convert::Into<alloc::borrow::Cow<'a, [alloc::string::String]>>) -> Self
+pub fn brush_core::filter::SourceScriptParams<'a, SE>::path(&self) -> &std::path::Path
+pub trait brush_core::filter::CmdExecFilter: core::clone::Clone + core::default::Default + core::marker::Send + core::marker::Sync + 'static
+pub fn brush_core::filter::CmdExecFilter::post_external_cmd(&self, brush_core::filter::ExternalCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::CmdExecFilter::post_simple_cmd(&self, brush_core::filter::SimpleCmdOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::CmdExecFilter::pre_external_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::ExternalCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::ExternalCmdParams<'a, SE>, brush_core::filter::ExternalCmdOutput>> + core::marker::Send
+pub fn brush_core::filter::CmdExecFilter::pre_simple_cmd<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SimpleCmdParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SimpleCmdParams<'a, SE>, brush_core::filter::SimpleCmdOutput>> + core::marker::Send
+pub trait brush_core::filter::CmdExecFilterExt: brush_core::filter::CmdExecFilter + core::marker::Sized
+pub fn brush_core::filter::CmdExecFilterExt::and_then<F: brush_core::filter::CmdExecFilter>(self, F) -> brush_core::filter::FilterStack<Self, F>
+impl<T: brush_core::filter::CmdExecFilter> brush_core::filter::CmdExecFilterExt for T
+pub fn T::and_then<F: brush_core::filter::CmdExecFilter>(self, F) -> brush_core::filter::FilterStack<Self, F>
+pub trait brush_core::filter::SourceFilter: core::clone::Clone + core::default::Default + core::marker::Send + core::marker::Sync + 'static
+pub fn brush_core::filter::SourceFilter::post_source_script(&self, brush_core::filter::SourceScriptOutput) -> impl core::future::future::Future<Output = brush_core::filter::PostFilterResult<brush_core::filter::SourceScriptOutput>> + core::marker::Send
+pub fn brush_core::filter::SourceFilter::pre_source_script<'a, SE: brush_core::extensions::ShellExtensions>(&self, brush_core::filter::SourceScriptParams<'a, SE>) -> impl core::future::future::Future<Output = brush_core::filter::PreFilterResult<brush_core::filter::SourceScriptParams<'a, SE>, brush_core::filter::SourceScriptOutput>> + core::marker::Send
+pub trait brush_core::filter::SourceFilterExt: brush_core::filter::SourceFilter + core::marker::Sized
+pub fn brush_core::filter::SourceFilterExt::and_then<F: brush_core::filter::SourceFilter>(self, F) -> brush_core::filter::FilterStack<Self, F>
+impl<T: brush_core::filter::SourceFilter> brush_core::filter::SourceFilterExt for T
+pub fn T::and_then<F: brush_core::filter::SourceFilter>(self, F) -> brush_core::filter::FilterStack<Self, F>
+pub type brush_core::filter::ExternalCmdOutput = core::result::Result<brush_core::results::ExecutionSpawnResult, brush_core::error::Error>
+pub type brush_core::filter::SimpleCmdOutput = core::result::Result<brush_core::results::ExecutionSpawnResult, brush_core::error::Error>
+pub type brush_core::filter::SourceScriptOutput = core::result::Result<brush_core::results::ExecutionResult, brush_core::error::Error>
+pub macro brush_core::with_filter!
+pub brush_core::CreateOptions::cmd_exec_filter: <SE as brush_core::extensions::ShellExtensions>::CmdExecFilter
+pub brush_core::CreateOptions::source_filter: <SE as brush_core::extensions::ShellExtensions>::SourceFilter
+pub const fn brush_core::Shell<SE>::cmd_exec_filter(&self) -> &<SE as brush_core::extensions::ShellExtensions>::CmdExecFilter
+pub const fn brush_core::Shell<SE>::source_filter(&self) -> &<SE as brush_core::extensions::ShellExtensions>::SourceFilter
+pub type brush_core::ShellBuilderState::CmdExecFilter
+pub type brush_core::ShellBuilderState::SourceFilter
+pub type brush_core::ShellExtensions::CmdExecFilter: brush_core::filter::CmdExecFilter
+pub type brush_core::ShellExtensions::SourceFilter: brush_core::filter::SourceFilter

Changed items

-pub struct brush_core::extensions::ShellExtensionsImpl<EF: brush_core::extensions::ErrorFormatter>
+pub struct brush_core::extensions::ShellExtensionsImpl<EF: brush_core::extensions::ErrorFormatter, CF: brush_core::filter::CmdExecFilter, SF: brush_core::filter::SourceFilter>

Performance Benchmark Report

Benchmark name Baseline (μs) Test/PR (μs) Delta (μs) Delta %
clone_shell_object 17.82 μs 17.53 μs -0.28 μs 🟢 -1.59%
eval_arithmetic 0.15 μs 0.16 μs 0.00 μs ⚪ Unchanged
expand_one_string 1.87 μs 1.90 μs 0.03 μs ⚪ Unchanged
for_loop 32.03 μs 33.55 μs 1.52 μs 🟠 +4.74%
full_peg_complex 59.45 μs 59.24 μs -0.21 μs ⚪ Unchanged
full_peg_for_loop 6.40 μs 6.36 μs -0.05 μs ⚪ Unchanged
full_peg_nested_expansions 16.57 μs 16.83 μs 0.26 μs ⚪ Unchanged
full_peg_pipeline 4.41 μs 4.40 μs -0.01 μs ⚪ Unchanged
full_peg_simple 1.91 μs 1.84 μs -0.07 μs 🟢 -3.52%
function_call 3.54 μs 3.79 μs 0.25 μs ⚪ Unchanged
instantiate_shell 55.09 μs 53.04 μs -2.05 μs 🟢 -3.73%
instantiate_shell_with_init_scripts 26722.23 μs 29023.65 μs 2301.42 μs 🟠 +8.61%
parse_peg_bash_completion 2136.00 μs 2129.55 μs -6.45 μs ⚪ Unchanged
parse_peg_complex 21.10 μs 21.18 μs 0.08 μs ⚪ Unchanged
parse_peg_for_loop 2.07 μs 2.04 μs -0.03 μs ⚪ Unchanged
parse_peg_pipeline 2.21 μs 2.21 μs -0.00 μs ⚪ Unchanged
parse_peg_simple 1.18 μs 1.14 μs -0.04 μs ⚪ Unchanged
run_echo_builtin_command 17.68 μs 17.29 μs -0.39 μs ⚪ Unchanged
tokenize_sample_script 3.37 μs 3.38 μs 0.02 μs 🟠 +0.48%

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
brush-builtins/src/exec.rs 🟢 88.57% 🟢 87.1% 🔴 -1.47%
brush-core/src/commands.rs 🟢 93.79% 🟢 94.27% 🟢 0.48%
brush-core/src/error.rs 🟢 90.91% 🟢 94.81% 🟢 3.9%
brush-core/src/extensions.rs 🔴 0% 🔴 47.06% 🟢 47.06%
brush-core/src/filter.rs 🔴 0% 🟢 76.58% 🟢 76.58%
brush-core/src/shell.rs 🟢 94.78% 🟢 92.68% 🔴 -2.1%
brush-core/src/shell/builder.rs 🟢 79.8% 🟢 80.2% 🟢 0.4%
Overall Coverage 🟢 77.7% 🟢 77.73% 🟢 0.03%

Minimum allowed coverage is 70%, this run produced 77.73%
Maximum allowed coverage difference is -5%, this run produced 0.03%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants