From 4c657db66eb303069117099aec9d0cddc15716bd Mon Sep 17 00:00:00 2001 From: gytic <149968794+gytic@users.noreply.github.com> Date: Thu, 13 Nov 2025 08:38:14 +0100 Subject: [PATCH 1/3] feat(wrapPackage): make shell escaping optional --- README.md | 1 + lib/default.nix | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 934b67b..1b40a60 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Arguments: - `args`: List of command-line arguments like argv in execve (default: auto-generated from `flags`) - Example: `[ "--silent" "--connect-timeout" "30" ]` - If provided, overrides automatic generation from `flags` +- `escapeArgs`: Whether to excape the final shell arguments, e.g. put quotes around them - `preHook`: Shell script executed before the command (default: `""`) - `passthru`: Additional attributes for the derivation's passthru (default: `{}`) - `aliases`: List of additional symlink names for the executable (default: `[]`) diff --git a/lib/default.nix b/lib/default.nix index 52ecc71..bdfff97 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -419,6 +419,8 @@ let flagSeparator ? " ", # " " for "--flag value" or "=" for "--flag=value" args ? generateArgsFromFlags flags flagSeparator, + # Whether to escape the final shell arguments, e.g. put quotes around them + escapeArgs ? true, preHook ? "", passthru ? { }, aliases ? [ ], @@ -460,7 +462,10 @@ let if args == [ ] then "" else - " \\\n " + lib.concatStringsSep " \\\n " (map wrapperLib.escapeShellArgWithEnv args); + let + escapeFn = if escapeArgs then wrapperLib.escapeShellArgWithEnv else x: x; + in + " \\\n " + lib.concatStringsSep " \\\n " (map escapeFn args); finalWrapper = wrapper { inherit From 7e6932ae89022dd2ccf25796b60842afd295665b Mon Sep 17 00:00:00 2001 From: gytic <149968794+gytic@users.noreply.github.com> Date: Thu, 13 Nov 2025 22:40:26 +0100 Subject: [PATCH 2/3] test(wrapPackage): add tests to verify shell escaping option --- checks/args-escape.nix | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 checks/args-escape.nix diff --git a/checks/args-escape.nix b/checks/args-escape.nix new file mode 100644 index 0000000..7b50d18 --- /dev/null +++ b/checks/args-escape.nix @@ -0,0 +1,54 @@ +{ + pkgs, + self, +}: +let + wrappedPackageEscaped = self.lib.wrapPackage { + inherit pkgs; + package = pkgs.hello; + args = [ + ''--verbose'' + ]; + }; + wrappedPackageUnescaped = self.lib.wrapPackage { + inherit pkgs; + package = pkgs.hello; + args = [ + ''--verbose'' + ]; + escapeArgs = false; + }; +in +pkgs.runCommand "args-escape-test" { } '' + echo "Testing escape option for wrapPackage" + + # Test 1: arguments are escaped by default + wrapperScript="${wrappedPackageEscaped}/bin/hello" + if [ ! -f "$wrapperScript" ]; then + echo "FAIL: Wrapper script not found" + exit 1 + fi + + if ! grep -q -- '"--verbose"' "$wrapperScript"; then + echo "FAIL: arguments are not escaped by default" + cat "$wrapperScript" + exit 1 + fi + + + # Test 2: arguments should not be escaped when option is set + wrapperScript="${wrappedPackageUnescaped}/bin/hello" + if [ ! -f "$wrapperScript" ]; then + echo "FAIL: Wrapper script not found" + exit 1 + fi + + if grep -q -- '"--verbose"' "$wrapperScript"; then + echo "FAIL: arguments were escaped" + cat "$wrapperScript" + exit 1 + fi + + echo "SUCCESS: all args-escape tests passed" + touch $out +'' From 93fd544fc47b0dbc52f94e72f311dfa15d02eea9 Mon Sep 17 00:00:00 2001 From: gytic <149968794+gytic@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:10:12 +0100 Subject: [PATCH 3/3] feat(wrapPackage): add option for and unescaped argument list in favor of esccape boolen --- README.md | 6 ++++- checks/args-direct.nix | 58 ++++++++++++++++++++++++++++++++++++++++-- checks/args-escape.nix | 54 --------------------------------------- lib/default.nix | 13 +++++----- 4 files changed, 67 insertions(+), 64 deletions(-) delete mode 100644 checks/args-escape.nix diff --git a/README.md b/README.md index 1b40a60..02d5f5a 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,11 @@ Arguments: - `args`: List of command-line arguments like argv in execve (default: auto-generated from `flags`) - Example: `[ "--silent" "--connect-timeout" "30" ]` - If provided, overrides automatic generation from `flags` -- `escapeArgs`: Whether to excape the final shell arguments, e.g. put quotes around them + - Will be escaped +- `unquotedArgs`: List of command-line arguments (unescaped) + - WARNING: These are not escaped and can therefore be interpreted as shell commands + - Example: `[ "--silet" "--conect-timeout" "30" ]` + - If provided, overrides `args` and `flags` - `preHook`: Shell script executed before the command (default: `""`) - `passthru`: Additional attributes for the derivation's passthru (default: `{}`) - `aliases`: List of additional symlink names for the executable (default: `[]`) diff --git a/checks/args-direct.nix b/checks/args-direct.nix index 35ecab3..3f9c277 100644 --- a/checks/args-direct.nix +++ b/checks/args-direct.nix @@ -4,7 +4,7 @@ }: let - wrappedPackage = self.lib.wrapPackage { + escapedArgsPackage = self.lib.wrapPackage { inherit pkgs; package = pkgs.hello; args = [ @@ -13,17 +13,49 @@ let "--verbose" ]; }; + unescapedArgsPackage = self.lib.wrapPackage { + inherit pkgs; + package = pkgs.hello; + unquotedArgs = [ + "--greeting" + "hi" + "--verbose" + ]; + }; in pkgs.runCommand "args-direct-test" { } '' echo "Testing direct args list..." - wrapperScript="${wrappedPackage}/bin/hello" + wrapperScript="${escapedArgsPackage}/bin/hello" + echo "Check escaped arguments" if [ ! -f "$wrapperScript" ]; then echo "FAIL: Wrapper script not found" exit 1 fi + if ! grep -q -- '"--greeting"' "$wrapperScript"; then + echo "FAIL: escaped --greeting not found" + cat "$wrapperScript" + exit 1 + fi + + if ! grep -q '"hi"' "$wrapperScript"; then + echo "FAIL: escaped 'hi' not found" + cat "$wrapperScript" + exit 1 + fi + + if ! grep -q -- '"--verbose"' "$wrapperScript"; then + echo "FAIL: escaped --verbose not found" + cat "$wrapperScript" + exit 1 + fi + echo "SUCCESS: escaped arguments passed" + + wrapperScript="${unescapedArgsPackage}/bin/hello" + echo "Check unescaped arguments" + # check that arguments are present if ! grep -q -- "--greeting" "$wrapperScript"; then echo "FAIL: --greeting not found" cat "$wrapperScript" @@ -36,12 +68,34 @@ pkgs.runCommand "args-direct-test" { } '' exit 1 fi + # check hat arguments are not escaped if ! grep -q -- "--verbose" "$wrapperScript"; then echo "FAIL: --verbose not found" cat "$wrapperScript" exit 1 fi + if grep -q -- '"--greeting"' "$wrapperScript"; then + echo "FAIL: escaped --greeting found (should be unescaped)" + cat "$wrapperScript" + exit 1 + fi + + if grep -q '"hi"' "$wrapperScript"; then + echo "FAIL: escaped 'hi' found (should be unescaped)" + cat "$wrapperScript" + exit 1 + fi + + + if grep -q -- '"--verbose"' "$wrapperScript"; then + echo "FAIL: escaped --verbose found (should be unescaped)" + cat "$wrapperScript" + exit 1 + fi + echo "SUCCESS: escaped arguments passed" + + echo "" echo "SUCCESS: Direct args test passed" touch $out '' diff --git a/checks/args-escape.nix b/checks/args-escape.nix deleted file mode 100644 index 7b50d18..0000000 --- a/checks/args-escape.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ - pkgs, - self, -}: -let - wrappedPackageEscaped = self.lib.wrapPackage { - inherit pkgs; - package = pkgs.hello; - args = [ - ''--verbose'' - ]; - }; - wrappedPackageUnescaped = self.lib.wrapPackage { - inherit pkgs; - package = pkgs.hello; - args = [ - ''--verbose'' - ]; - escapeArgs = false; - }; -in -pkgs.runCommand "args-escape-test" { } '' - echo "Testing escape option for wrapPackage" - - # Test 1: arguments are escaped by default - wrapperScript="${wrappedPackageEscaped}/bin/hello" - if [ ! -f "$wrapperScript" ]; then - echo "FAIL: Wrapper script not found" - exit 1 - fi - - if ! grep -q -- '"--verbose"' "$wrapperScript"; then - echo "FAIL: arguments are not escaped by default" - cat "$wrapperScript" - exit 1 - fi - - - # Test 2: arguments should not be escaped when option is set - wrapperScript="${wrappedPackageUnescaped}/bin/hello" - if [ ! -f "$wrapperScript" ]; then - echo "FAIL: Wrapper script not found" - exit 1 - fi - - if grep -q -- '"--verbose"' "$wrapperScript"; then - echo "FAIL: arguments were escaped" - cat "$wrapperScript" - exit 1 - fi - - echo "SUCCESS: all args-escape tests passed" - touch $out -'' diff --git a/lib/default.nix b/lib/default.nix index bdfff97..8a73fc7 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -419,8 +419,7 @@ let flagSeparator ? " ", # " " for "--flag value" or "=" for "--flag=value" args ? generateArgsFromFlags flags flagSeparator, - # Whether to escape the final shell arguments, e.g. put quotes around them - escapeArgs ? true, + unquotedArgs ? [ ], preHook ? "", passthru ? { }, aliases ? [ ], @@ -459,13 +458,13 @@ let # Generate flag arguments with proper line breaks and indentation flagsString = - if args == [ ] then + if args == [ ] && unquotedArgs == [ ] then "" else - let - escapeFn = if escapeArgs then wrapperLib.escapeShellArgWithEnv else x: x; - in - " \\\n " + lib.concatStringsSep " \\\n " (map escapeFn args); + " \\\n " + + lib.concatStringsSep " \\\n " ( + if unquotedArgs != [ ] then unquotedArgs else map wrapperLib.escapeShellArgWithEnv args + ); finalWrapper = wrapper { inherit