diff --git a/source/bear/man/bear.1 b/source/bear/man/bear.1 index 99bcc212a..6c9df4c2f 100644 --- a/source/bear/man/bear.1 +++ b/source/bear/man/bear.1 @@ -54,6 +54,12 @@ Force to use the dynamic linker method of \f[C]intercept\f[R] command. .TP --force-wrapper Force to use the compiler wrapper method of \f[C]intercept\f[R] command. +.TP +--enable-network-proxy +Forward HTTP proxy environment variables (\f[C]http_proxy\f[R], +\f[C]https_proxy\f[R], \f[C]grpc_proxy\f[R] and their capitalized +versions) to \f[C]intercept\f[R] command. +They are unset by default. .SH COMMANDS .TP \f[B]\f[CB]bear-intercept(1)\f[B]\f[R] diff --git a/source/bear/man/bear.1.md b/source/bear/man/bear.1.md index 4083acb14..ad0224cea 100644 --- a/source/bear/man/bear.1.md +++ b/source/bear/man/bear.1.md @@ -55,6 +55,11 @@ compilation database. \--force-wrapper : Force to use the compiler wrapper method of `intercept` command. +\--enable-network-proxy +: Forward HTTP proxy environment variables (`http_proxy`, `https_proxy`, + `grpc_proxy` and their capitalized versions) to `intercept` command. + They are unset by default. + # COMMANDS `bear-intercept(1)` diff --git a/source/bear/source/Application.cc b/source/bear/source/Application.cc index 7ef9246f5..896cee385 100644 --- a/source/bear/source/Application.cc +++ b/source/bear/source/Application.cc @@ -36,10 +36,11 @@ namespace { auto verbose = arguments.as_bool(flags::VERBOSE).unwrap_or(false); auto force_wrapper = arguments.as_bool(cmd::intercept::FLAG_FORCE_WRAPPER).unwrap_or(false); auto force_preload = arguments.as_bool(cmd::intercept::FLAG_FORCE_PRELOAD).unwrap_or(false); + auto enable_network_proxy = arguments.as_bool(cmd::intercept::FLAG_ENABLE_NETWORK_PROXY).unwrap_or(false); return rust::merge(program, command, rust::merge(library, wrapper, wrapper_dir)) .map( - [&environment, &output, &verbose, &force_wrapper, &force_preload](auto tuple) { + [&environment, &output, &verbose, &force_wrapper, &force_preload, &enable_network_proxy](auto tuple) { const auto&[program, command, pack] = tuple; const auto&[library, wrapper, wrapper_dir] = pack; @@ -57,6 +58,9 @@ namespace { if (force_preload) { builder.add_argument(cmd::intercept::FLAG_FORCE_PRELOAD); } + if (enable_network_proxy) { + builder.add_argument(cmd::intercept::FLAG_ENABLE_NETWORK_PROXY); + } if (verbose) { builder.add_argument(flags::VERBOSE); } @@ -146,13 +150,14 @@ namespace bear { rust::Result Application::parse(int argc, const char **argv) const { const flags::Parser intercept_parser("intercept", cmd::VERSION, { - {cmd::intercept::FLAG_OUTPUT, {1, false, "path of the result file", {cmd::intercept::DEFAULT_OUTPUT}, std::nullopt}}, - {cmd::intercept::FLAG_FORCE_PRELOAD, {0, false, "force to use library preload", std::nullopt, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_FORCE_WRAPPER, {0, false, "force to use compiler wrappers", std::nullopt, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_LIBRARY, {1, false, "path to the preload library", {cmd::library::DEFAULT_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_WRAPPER, {1, false, "path to the wrapper executable", {cmd::wrapper::DEFAULT_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_WRAPPER_DIR, {1, false, "path to the wrapper directory", {cmd::wrapper::DEFAULT_DIR_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_COMMAND, {-1, true, "command to execute", std::nullopt, std::nullopt}} + {cmd::intercept::FLAG_OUTPUT, {1, false, "path of the result file", {cmd::intercept::DEFAULT_OUTPUT}, std::nullopt}}, + {cmd::intercept::FLAG_FORCE_PRELOAD, {0, false, "force to use library preload", std::nullopt, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_FORCE_WRAPPER, {0, false, "force to use compiler wrappers", std::nullopt, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_ENABLE_NETWORK_PROXY, {0, false, "enable http and https proxy", std::nullopt, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_LIBRARY, {1, false, "path to the preload library", {cmd::library::DEFAULT_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_WRAPPER, {1, false, "path to the wrapper executable", {cmd::wrapper::DEFAULT_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_WRAPPER_DIR, {1, false, "path to the wrapper directory", {cmd::wrapper::DEFAULT_DIR_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_COMMAND, {-1, true, "command to execute", std::nullopt, std::nullopt}} }); const flags::Parser citnames_parser("citnames", cmd::VERSION, { @@ -164,16 +169,17 @@ namespace bear { }); const flags::Parser parser("bear", cmd::VERSION, {intercept_parser, citnames_parser}, { - {cmd::citnames::FLAG_OUTPUT, {1, false, "path of the result file", {cmd::citnames::DEFAULT_OUTPUT}, std::nullopt}}, - {cmd::citnames::FLAG_APPEND, {0, false, "append result to an existing output file", std::nullopt, ADVANCED_GROUP}}, - {cmd::citnames::FLAG_CONFIG, {1, false, "path of the config file", std::nullopt, ADVANCED_GROUP}}, - {cmd::intercept::FLAG_FORCE_PRELOAD, {0, false, "force to use library preload", std::nullopt, ADVANCED_GROUP}}, - {cmd::intercept::FLAG_FORCE_WRAPPER, {0, false, "force to use compiler wrappers", std::nullopt, ADVANCED_GROUP}}, - {cmd::bear::FLAG_BEAR, {1, false, "path to the bear executable", {cmd::bear::DEFAULT_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_LIBRARY, {1, false, "path to the preload library", {cmd::library::DEFAULT_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_WRAPPER, {1, false, "path to the wrapper executable", {cmd::wrapper::DEFAULT_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_WRAPPER_DIR, {1, false, "path to the wrapper directory", {cmd::wrapper::DEFAULT_DIR_PATH}, DEVELOPER_GROUP}}, - {cmd::intercept::FLAG_COMMAND, {-1, true, "command to execute", std::nullopt, std::nullopt}} + {cmd::citnames::FLAG_OUTPUT, {1, false, "path of the result file", {cmd::citnames::DEFAULT_OUTPUT}, std::nullopt}}, + {cmd::citnames::FLAG_APPEND, {0, false, "append result to an existing output file", std::nullopt, ADVANCED_GROUP}}, + {cmd::citnames::FLAG_CONFIG, {1, false, "path of the config file", std::nullopt, ADVANCED_GROUP}}, + {cmd::intercept::FLAG_FORCE_PRELOAD, {0, false, "force to use library preload", std::nullopt, ADVANCED_GROUP}}, + {cmd::intercept::FLAG_FORCE_WRAPPER, {0, false, "force to use compiler wrappers", std::nullopt, ADVANCED_GROUP}}, + {cmd::intercept::FLAG_ENABLE_NETWORK_PROXY, {0, false, "enable http and https proxy", std::nullopt, ADVANCED_GROUP}}, + {cmd::bear::FLAG_BEAR, {1, false, "path to the bear executable", {cmd::bear::DEFAULT_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_LIBRARY, {1, false, "path to the preload library", {cmd::library::DEFAULT_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_WRAPPER, {1, false, "path to the wrapper executable", {cmd::wrapper::DEFAULT_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_WRAPPER_DIR, {1, false, "path to the wrapper directory", {cmd::wrapper::DEFAULT_DIR_PATH}, DEVELOPER_GROUP}}, + {cmd::intercept::FLAG_COMMAND, {-1, true, "command to execute", std::nullopt, std::nullopt}} }); return parser.parse_or_exit(argc, const_cast(argv)); } @@ -186,6 +192,12 @@ namespace bear { return citnames.subcommand(args, envp); } if (auto intercept = ic::Intercept(log_config_); intercept.matches(args)) { + // Network proxy is disabled by default unless user explicitly enables it + if (!args.as_bool(cmd::intercept::FLAG_ENABLE_NETWORK_PROXY).unwrap_or(false)) { + for (auto proxyEnv : cmd::intercept::PROXY_ENV_VARS) { + unsetenv(proxyEnv); + } + } return intercept.subcommand(args, envp); } return rust::Err(std::runtime_error("Invalid subcommand")); diff --git a/source/config.h.in b/source/config.h.in index 52c45309f..50cc69512 100644 --- a/source/config.h.in +++ b/source/config.h.in @@ -102,6 +102,8 @@ namespace cmd { constexpr char FLAG_COMMAND[] = "--"; constexpr char FLAG_FORCE_WRAPPER[] = "--force-wrapper"; constexpr char FLAG_FORCE_PRELOAD[] = "--force-preload"; + constexpr char FLAG_ENABLE_NETWORK_PROXY[] = "--enable-network-proxy"; + constexpr const char* PROXY_ENV_VARS[] = {"http_proxy", "https_proxy", "grpc_proxy","all_proxy", "HTTP_PROXY", "HTTPS_PROXY", "GRPC_PROXY", "ALL_PROXY"}; constexpr char DEFAULT_OUTPUT[] = "events.json"; } diff --git a/source/libflags/source/Flags.cc b/source/libflags/source/Flags.cc index 9940625b2..39dcc6d65 100644 --- a/source/libflags/source/Flags.cc +++ b/source/libflags/source/Flags.cc @@ -110,11 +110,11 @@ namespace { // print flag name os << flag_name; // decide if the help text goes into the same line or not - if (flag_size > 22) { + if (flag_size > 25) { os << std::endl << std::string(15, ' '); } else { - os << std::string(23 - flag_size, ' '); + os << std::string(26 - flag_size, ' '); } os << option.help; // print default value if exists diff --git a/source/libflags/test/FlagsTest.cc b/source/libflags/test/FlagsTest.cc index 5aa723402..121aa8af8 100644 --- a/source/libflags/test/FlagsTest.cc +++ b/source/libflags/test/FlagsTest.cc @@ -177,16 +177,16 @@ namespace { const char *expected = "Usage: test [--flag] [--option ] [--options ] [--verbose] [-- ...]\n" "\n" - " --flag a single flag\n" - " --option a flag with a value\n" + " --flag a single flag\n" + " --option a flag with a value\n" " --options \n" " a flag with 3 values\n" - " --verbose run in verbose mode\n" - " -- ... rest of the arguments\n" + " --verbose run in verbose mode\n" + " -- ... rest of the arguments\n" "\n" "query options\n" - " --help print help and exit\n" - " --version print version and exit\n"; + " --help print help and exit\n" + " --version print version and exit\n"; std::ostringstream out; sut.print_help(nullptr, out); @@ -367,8 +367,8 @@ namespace { " dump\n" "\n" "query options\n" - " --help print help and exit\n" - " --version print version and exit\n"; + " --help print help and exit\n" + " --version print version and exit\n"; std::ostringstream out; sut.print_help(nullptr, out); @@ -378,11 +378,11 @@ namespace { const char *expected = "Usage: test append [--option ] [--verbose]\n" "\n" - " --option a flag with a value\n" - " --verbose run in verbose mode\n" + " --option a flag with a value\n" + " --verbose run in verbose mode\n" "\n" "query options\n" - " --help print help and exit\n"; + " --help print help and exit\n"; std::ostringstream out; sut.print_help(&append, out); diff --git a/test/cases/intercept/wrapper/shell_enable_network_proxy.sh b/test/cases/intercept/wrapper/shell_enable_network_proxy.sh new file mode 100644 index 000000000..670bb8487 --- /dev/null +++ b/test/cases/intercept/wrapper/shell_enable_network_proxy.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +# REQUIRES: shell, cc + +# If an unreachable or an invalid http proxy is set, an error is returned if '--enable-network-proxy' is set +# +# RUN: env http_proxy=http://localhost:9999 %{intercept} --enable-network-proxy --output %t.json -- %{shell} %s 2> %t.stderr +# RUN: grep "failed to connect to all addresses" %t.stderr +# RUN: assert_intercepted %t.json count -eq 0 + +# If no http proxy is set, it should run correctly even with '--enable-network-proxy' +# +# RUN: %{intercept} --enable-network-proxy --output %t.json -- %{shell} %s +# RUN: assert_intercepted %t.json count -eq 1 +# RUN: assert_intercepted %t.json contains -program %{c_compiler} -arguments %{c_compiler} -c shell_enable_network_proxy.c -o shell_enable_network_proxy.o + +# By default, even with an invalid http proxy set, it should run correctly +# +# RUN: env http_proxy=http://localhost:9999 %{intercept} --output %t.json -- %{shell} +# RUN: assert_intercepted %t.json count -eq 1 + +touch shell_enable_network_proxy.c + +$CC -c shell_enable_network_proxy.c -o shell_enable_network_proxy.o +