Skip to content

Commit 51cc218

Browse files
authored
venv: do not prepend a truncated shebang interpreter (#2203)
1 parent 3c1c29a commit 51cc218

File tree

4 files changed

+24
-5
lines changed

4 files changed

+24
-5
lines changed

docs/changelog/2208.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent tox from using a truncated interpreter when using
2+
``TOX_LIMITED_SHEBANG`` -- by :user:`jdknight`.

docs/config.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,9 +1097,9 @@ Handle interpreter directives with long lengths
10971097
For systems supporting executable text files (scripts with a shebang), the
10981098
system will attempt to parse the interpreter directive to determine the program
10991099
to execute on the target text file. When ``tox`` prepares a virtual environment
1100-
in a file container which has a large length (e.x. using Jenkins Pipelines), the
1100+
in a file container which has a large length (e.g. using Jenkins Pipelines), the
11011101
system might not be able to invoke shebang scripts which define interpreters
1102-
beyond system limits (e.x. Linux as a limit of 128; ``BINPRM_BUF_SIZE``). To
1102+
beyond system limits (e.g. Linux has a limit of 128; ``BINPRM_BUF_SIZE``). To
11031103
workaround an environment which suffers from an interpreter directive limit, a
11041104
user can bypass the system's interpreter parser by defining the
11051105
``TOX_LIMITED_SHEBANG`` environment variable before invoking ``tox``::

src/tox/venv.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
from .config import DepConfig
2121

22+
#: maximum parsed shebang interpreter length (see: prepend_shebang_interpreter)
23+
MAXINTERP = 2048
24+
2225

2326
class CreationConfig:
2427
def __init__(
@@ -672,7 +675,7 @@ def prepend_shebang_interpreter(args):
672675
#
673676
# When preparing virtual environments in a file container which has large
674677
# length, the system might not be able to invoke shebang scripts which
675-
# define interpreters beyond system limits (e.x. Linux as a limit of 128;
678+
# define interpreters beyond system limits (e.g. Linux has a limit of 128;
676679
# BINPRM_BUF_SIZE). This method can be used to check if the executable is
677680
# a script containing a shebang line. If so, extract the interpreter (and
678681
# possible optional argument) and prepend the values to the provided
@@ -682,8 +685,9 @@ def prepend_shebang_interpreter(args):
682685
try:
683686
with open(args[0], "rb") as f:
684687
if f.read(1) == b"#" and f.read(1) == b"!":
685-
MAXINTERP = 2048
686-
interp = f.readline(MAXINTERP).rstrip().decode("UTF-8")
688+
interp = f.readline(MAXINTERP + 1).rstrip().decode("UTF-8")
689+
if len(interp) > MAXINTERP: # avoid a truncated interpreter
690+
return args
687691
interp_args = interp.split(None, 1)[:2]
688692
return interp_args + args
689693
except (UnicodeDecodeError, IOError):

tests/unit/test_venv.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from tox.interpreters import NoInterpreterInfo
1010
from tox.session.commands.run.sequential import installpkg, runtestenv
1111
from tox.venv import (
12+
MAXINTERP,
1213
CreationConfig,
1314
VirtualEnv,
1415
getdigest,
@@ -1149,6 +1150,18 @@ def test_tox_testenv_interpret_shebang_long_example(tmpdir):
11491150
assert args == expected + base_args
11501151

11511152

1153+
@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows")
1154+
def test_tox_testenv_interpret_shebang_skip_truncated(tmpdir):
1155+
testfile = tmpdir.join("check_shebang_truncation.py")
1156+
original_args = [str(testfile), "arg1", "arg2", "arg3"]
1157+
1158+
# interpreter (too long example)
1159+
testfile.write("#!" + ("x" * (MAXINTERP + 1)))
1160+
args = prepend_shebang_interpreter(original_args)
1161+
1162+
assert args == original_args
1163+
1164+
11521165
@pytest.mark.parametrize("download", [True, False, None])
11531166
def test_create_download(mocksession, newconfig, download):
11541167
config = newconfig(

0 commit comments

Comments
 (0)