Skip to content

Commit a49bbbd

Browse files
committed
gh-160732: protect venv prompts from expansion
1 parent 3acc941 commit a49bbbd

3 files changed

Lines changed: 44 additions & 1 deletion

File tree

‎Lib/test/test_venv.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,30 @@ def test_special_chars_bash(self):
770770
self.assertTrue(env_name.encode() in lines[0])
771771
self.assertEndsWith(lines[1], env_name.encode())
772772

773+
# gh-160732: prompt values must not be evaluated when bash displays PS1.
774+
@unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
775+
@unittest.skipIf(sys.platform == 'cygwin', 'fail to locate cygpython DLL')
776+
def test_prompt_command_substitution_bash(self):
777+
"""Test that command substitutions in the prompt are not evaluated."""
778+
rmtree(self.env_dir)
779+
bash = shutil.which('bash')
780+
if bash is None:
781+
self.skipTest('bash required for this test')
782+
marker = os.path.join(self.env_dir, 'prompt-command-ran')
783+
prompt = f'$(touch {shlex.quote(marker)})'
784+
builder = venv.EnvBuilder(clear=True, prompt=prompt)
785+
builder.create(self.env_dir)
786+
activate = os.path.join(self.env_dir, self.bindir, 'activate')
787+
result = subprocess.run(
788+
[bash, '--noprofile', '--norc', '-i'],
789+
input=f'source {shlex.quote(activate)}\nexit\n',
790+
stdout=subprocess.PIPE,
791+
stderr=subprocess.STDOUT,
792+
text=True,
793+
)
794+
self.assertEqual(result.returncode, 0)
795+
self.assertFalse(os.path.exists(marker))
796+
773797
# gh-124651: test quoted strings
774798
@unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
775799
@unittest.skipIf(sys.platform.startswith('netbsd'),

‎Lib/venv/scripts/common/activate‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ deactivate () {
2424
unset _OLD_VIRTUAL_PS1
2525
fi
2626

27+
if [ -n "${_OLD_VIRTUAL_PSVAR+x}" ] ; then
28+
psvar[9]="${_OLD_VIRTUAL_PSVAR}"
29+
unset _OLD_VIRTUAL_PSVAR
30+
fi
31+
2732
unset VIRTUAL_ENV
2833
unset VIRTUAL_ENV_PROMPT
2934
if [ ! "${1:-}" = "nondestructive" ] ; then
@@ -66,7 +71,19 @@ fi
6671

6772
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
6873
_OLD_VIRTUAL_PS1="${PS1:-}"
69-
PS1="("__VENV_PROMPT__") ${PS1:-}"
74+
if [ -n "${BASH_VERSION:-}" ] ; then
75+
# Bash expands PS1 again when it displays the prompt. Keep the prompt
76+
# in a variable so its contents are not interpreted as shell code.
77+
PS1='(${VIRTUAL_ENV_PROMPT}) '"${PS1:-}"
78+
elif [ -n "${ZSH_VERSION:-}" ] ; then
79+
# zsh expands prompt substitutions in variable values. Use a psvar
80+
# entry so the prompt is inserted as data instead.
81+
_OLD_VIRTUAL_PSVAR="${psvar[9]-}"
82+
psvar[9]="${VIRTUAL_ENV_PROMPT}"
83+
PS1='(%9v) '"${PS1:-}"
84+
else
85+
PS1='(${VIRTUAL_ENV_PROMPT}) '"${PS1:-}"
86+
fi
7087
fi
7188

7289
# Call hash to forget past commands. Without forgetting
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix command substitution in virtual environment prompts being evaluated by the
2+
generated Bash activation script.

0 commit comments

Comments
 (0)