summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/test/util/environment.py29
-rw-r--r--lib/spack/spack/util/environment.py32
2 files changed, 16 insertions, 45 deletions
diff --git a/lib/spack/spack/test/util/environment.py b/lib/spack/spack/test/util/environment.py
index ef7c151fee..adf82aa83f 100644
--- a/lib/spack/spack/test/util/environment.py
+++ b/lib/spack/spack/test/util/environment.py
@@ -160,22 +160,13 @@ def test_reverse_environment_modifications(working_env):
assert os.environ == start_env
-def test_escape_double_quotes_in_shell_modifications():
- to_validate = envutil.EnvironmentModifications()
-
- to_validate.set("VAR", "$PATH")
- to_validate.append_path("VAR", "$ANOTHER_PATH")
-
- to_validate.set("QUOTED_VAR", '"MY_VAL"')
-
- if sys.platform == "win32":
- cmds = to_validate.shell_modifications(shell="bat")
- assert r'set "VAR=$PATH;$ANOTHER_PATH"' in cmds
- assert r'set "QUOTED_VAR="MY_VAL"' in cmds
- cmds = to_validate.shell_modifications(shell="pwsh")
- assert "$Env:VAR='$PATH;$ANOTHER_PATH'" in cmds
- assert "$Env:QUOTED_VAR='\"MY_VAL\"'" in cmds
- else:
- cmds = to_validate.shell_modifications()
- assert 'export VAR="$PATH:$ANOTHER_PATH"' in cmds
- assert r'export QUOTED_VAR="\"MY_VAL\""' in cmds
+def test_shell_modifications_are_properly_escaped():
+ """Test that variable values are properly escaped so that they can safely be eval'd."""
+ changes = envutil.EnvironmentModifications()
+ changes.set("VAR", "$PATH")
+ changes.append_path("VAR", "$ANOTHER_PATH")
+ changes.set("RM_RF", "$(rm -rf /)")
+
+ script = changes.shell_modifications(shell="sh")
+ assert f"export VAR='$PATH{os.pathsep}$ANOTHER_PATH'" in script
+ assert "export RM_RF='$(rm -rf /)'" in script
diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py
index 0c5ac769c0..12b2bbb1d0 100644
--- a/lib/spack/spack/util/environment.py
+++ b/lib/spack/spack/util/environment.py
@@ -11,6 +11,7 @@ import os
import os.path
import pickle
import re
+import shlex
import sys
from functools import wraps
from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
@@ -63,26 +64,6 @@ Path = str
ModificationList = List[Union["NameModifier", "NameValueModifier"]]
-_find_unsafe = re.compile(r"[^\w@%+=:,./-]", re.ASCII).search
-
-
-def double_quote_escape(s):
- """Return a shell-escaped version of the string *s*.
-
- This is similar to how shlex.quote works, but it escapes with double quotes
- instead of single quotes, to allow environment variable expansion within
- quoted strings.
- """
- if not s:
- return '""'
- if _find_unsafe(s) is None:
- return s
-
- # use double quotes, and escape double quotes in the string
- # the string $"b is then quoted as "$\"b"
- return '"' + s.replace('"', r"\"") + '"'
-
-
def system_env_normalize(func):
"""Decorator wrapping calls to system env modifications,
converting all env variable names to all upper case on Windows, no-op
@@ -182,7 +163,7 @@ def _nix_env_var_to_source_line(var: str, val: str) -> str:
fname=BASH_FUNCTION_FINDER.sub(r"\1", var), decl=val
)
else:
- source_line = f"{var}={double_quote_escape(val)}; export {var}"
+ source_line = f"{var}={shlex.quote(val)}; export {var}"
return source_line
@@ -691,11 +672,10 @@ class EnvironmentModifications:
if new is None:
cmds += _SHELL_UNSET_STRINGS[shell].format(name)
else:
- if sys.platform != "win32":
- new_env_name = double_quote_escape(new_env[name])
- else:
- new_env_name = new_env[name]
- cmd = _SHELL_SET_STRINGS[shell].format(name, new_env_name)
+ value = new_env[name]
+ if shell not in ("bat", "pwsh"):
+ value = shlex.quote(value)
+ cmd = _SHELL_SET_STRINGS[shell].format(name, value)
cmds += cmd
return cmds