From 01438ff7bbf9dab380c6560de7339792bdd86c1a Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 6 Jun 2025 03:44:21 +0000 Subject: [PATCH] bunpen: apply BUNPEN_DISABLE regex matching to BUNPEN_DROP_SHELL, too --- pkgs/by-name/bunpen/config/cli.ha | 11 +++++- pkgs/by-name/bunpen/config/translate_opts.ha | 41 +++++++++++++------- pkgs/by-name/bunpen/integration_test | 16 +++++++- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/pkgs/by-name/bunpen/config/cli.ha b/pkgs/by-name/bunpen/config/cli.ha index c14e67da8..5dde81123 100644 --- a/pkgs/by-name/bunpen/config/cli.ha +++ b/pkgs/by-name/bunpen/config/cli.ha @@ -12,7 +12,7 @@ export type cli_opts = struct { // `--bunpen-debug` debug: uint, disable: str, - drop_shell: bool, + drop_shell: str, // [ "KEY=VALUE" ] env: []str, // `--bunpen-help` @@ -99,6 +99,9 @@ export fn usage() void = { fmt::println(" note that this doesn't enforce a complete match:")!; fmt::println(" BUNPEN_DISABLE=host would disable sandboxing for 'host', 'hostname', and so on")!; fmt::println(" consider BUNPEN_DISABLE='host$' to be more targeted")!; + fmt::println(" BUNPEN_DROP_SHELL=")!; + fmt::println(" instead of running the program, drop into an interactive shell")!; + fmt::println(" uses the same specifier syntax as BUNPEN_DISABLE")!; fmt::println(" BUNPEN_APPEND=--bunpen-flag1 --bunpen-flag2 ...")!; fmt::println(" act as though the provided arg string appeared at the end of the CLI")!; fmt::println("")!; @@ -141,6 +144,10 @@ export fn parse_args(args: []str) (cli_opts | errors::invalid) = { }; case void => void; }; + match (os::getenv("BUNPEN_DROP_SHELL")) { + case let d: str => parsed.drop_shell = d; + case void => void; + }; parse_args_into(&parsed, args)?; @@ -183,7 +190,7 @@ fn parse_args_into(parsed: *cli_opts, args: []str) (void | errors::invalid) = { case "--bunpen-debug=3" => parsed.debug = 3; case "--bunpen-debug=4" => parsed.debug = 4; case "--bunpen-dns" => idx += 1; parsed.dns = expect_arg("--bunpen-dns", next)?; - case "--bunpen-drop-shell" => parsed.drop_shell = true; + case "--bunpen-drop-shell" => parsed.drop_shell = "1"; case "--bunpen-env" => idx += 1; append(parsed.env, expect_arg("--bunpen-env", next)?); case "--bunpen-help" => parsed.help = true; case "--bunpen-keep-ipc" => parsed.keep_ipc = true; diff --git a/pkgs/by-name/bunpen/config/translate_opts.ha b/pkgs/by-name/bunpen/config/translate_opts.ha index 6ad499582..c254e9949 100644 --- a/pkgs/by-name/bunpen/config/translate_opts.ha +++ b/pkgs/by-name/bunpen/config/translate_opts.ha @@ -104,20 +104,8 @@ export fn ingest_cli_opts(opts: cli_opts) (cli_request | exec_params | help) = { req.exec_params = cli_opts_get_exec_params(opts, env); //---- ingest `disable` ----// - if (opts.disable != "") { - if (opts.disable == "1" || opts.disable == "all" || opts.disable == "ALL" || opts.disable == "*") + if (does_option_apply_to_self(opts.disable, &req.exec_params)) return req.exec_params; - match (regex::compile(opts.disable)) { - case let re: regex::regex => - defer regex::finish(&re); - if (regex::test(&re, req.exec_params.bin)) - return req.exec_params; - if (len(req.exec_params.args) > 0 && regex::test(&re, req.exec_params.args[0])) - return req.exec_params; - case let e: regex::error => - log::printfln("[config] failed to parse BUNPEN_DISABLE regex {}: {}", opts.disable, e); - }; - }; //---- ingest `caps` ----// req.resources.caps = resources::cap_array_to_caps(opts.keep_caps); @@ -179,7 +167,7 @@ export fn ingest_cli_opts(opts: cli_opts) (cli_request | exec_params | help) = { }; //---- ingest `drop_shell` (must be done after autodetect) ----// - if (opts.drop_shell) { + if (does_option_apply_to_self(opts.drop_shell, &req.exec_params)) { // ignore the original command, and overwrite it with an interactive shell. // TODO: respect the user's `$SHELL`. req.exec_params.bin = "/bin/sh"; @@ -190,6 +178,31 @@ export fn ingest_cli_opts(opts: cli_opts) (cli_request | exec_params | help) = { return req; }; +// consider a string like "", "0", "1", "all", "*", "sane-which", ...; +// return true if it either +// (1) looks truthy (1, all) +// (2) it looks to be referring to the current binary (*, sane-which) +fn does_option_apply_to_self(value: str, exec_params: *const exec_params) bool = { + if (value == "") + return false; // prevent over-eager regex match + + if (value == "1" || value == "all" || value == "ALL" || value == "*") + return true; + + match (regex::compile(value)) { + case let re: regex::regex => + defer regex::finish(&re); + if (regex::test(&re, exec_params.bin)) + return true; + if (len(exec_params.args) > 0 && regex::test(&re, exec_params.args[0])) + return true; + case let e: regex::error => + log::printfln("[config] failed to parse option regex {}: {}", value, e); + }; + + return false; +}; + // convert each item in `from` to a path, and append to `into`. // non-absolute paths are interpreted as relative to `env.pwd`. // a limited set of environment variables are expanded for each path; see `env_subst`. diff --git a/pkgs/by-name/bunpen/integration_test b/pkgs/by-name/bunpen/integration_test index 05571f239..358c32a14 100755 --- a/pkgs/by-name/bunpen/integration_test +++ b/pkgs/by-name/bunpen/integration_test @@ -47,11 +47,23 @@ test_02_env_arg_01_disable() { BUNPEN_DISABLE=1 bunpen true } -test_02_env_arg_02_append() { +test_02_env_arg_02_disable_by_name_match() { + BUNPEN_DISABLE=env bunpen env true +} + +test_02_env_arg_03_disable_by_name_no_match() { + BUNPEN_DISABLE=envv bunpen env true && return 1 || true +} + +test_02_env_arg_04_disable_by_name_regex_match() { + BUNPEN_DISABLE=e.v? bunpen env true +} + +test_02_env_arg_05_append() { BUNPEN_APPEND="--bunpen-path /" bunpen ls / } -test_02_env_arg_03_append_tolerates_whitespace() { +test_02_env_arg_06_append_tolerates_whitespace() { BUNPEN_APPEND=" --bunpen-debug=4 --bunpen-keep-pid --bunpen-path / " bunpen ls / }