diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index 3798c7fa015..8edceac0241 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -15,6 +15,7 @@ #include "nix/store/globals.hh" #include "nix/store/path-with-outputs.hh" #include "nix/store/export-import.hh" +#include "nix/util/strings.hh" #include "man-pages.hh" @@ -534,17 +535,9 @@ static void opPrintEnv(Strings opFlags, Strings opArgs) for (auto & i : drv.env) logger->cout("export %1%; %1%=%2%\n", i.first, escapeShellArgAlways(i.second)); - /* Also output the arguments. This doesn't preserve whitespace in - arguments. */ - cout << "export _args; _args='"; - bool first = true; - for (auto & i : drv.args) { - if (!first) - cout << ' '; - first = false; - cout << escapeShellArgAlways(i); - } - cout << "'\n"; + /* Also output the arguments. */ + std::string argsStr = concatStringsSep(" ", drv.args); + cout << "export _args; _args=" << escapeShellArgAlways(argsStr) << "\n"; } static void opReadLog(Strings opFlags, Strings opArgs) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 6f649c8360b..04da8b020e4 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -160,6 +160,7 @@ suites = [ 'nix-profile.sh', 'suggestions.sh', 'store-info.sh', + 'store-print-env.sh', 'fetchClosure.sh', 'completions.sh', 'impure-derivations.sh', diff --git a/tests/functional/store-print-env.sh b/tests/functional/store-print-env.sh new file mode 100755 index 00000000000..ed5f6e7783b --- /dev/null +++ b/tests/functional/store-print-env.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +source common.sh + +clearStore + +# Regression test for nix-store --print-env argument escaping +# This tests that arguments in _args are properly escaped as a single string +# rather than double-escaped which could lead to command injection + +cat > "$TEST_ROOT/test-args.nix" <<'EOF' +derivation { + name = "test-print-env-args"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo hello world" ]; +} +EOF + +drvPath=$(nix-instantiate "$TEST_ROOT/test-args.nix") +output=$(nix-store --print-env "$drvPath" | grep "^export _args") + +# The output should be: export _args; _args='-c echo hello world' +# NOT: export _args; _args=''-c' 'echo hello world'' + +# Test that it can be safely evaluated +eval "$output" +expected="-c echo hello world" +# shellcheck disable=SC2154 # _args is set by the eval above +if [ "$_args" != "$expected" ]; then + echo "ERROR: _args not properly escaped!" + echo "Expected: $expected" + echo "Got: $_args" + echo "Raw output: $output" + exit 1 +fi