Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

venv: do not prepend a truncated shebang interpreter #2203

Merged
merged 2 commits into from Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/changelog/2208.bugfix.rst
@@ -0,0 +1,2 @@
Prevent tox from using a truncated interpreter when using
``TOX_LIMITED_SHEBANG`` -- by :user:`jdknight`.
4 changes: 2 additions & 2 deletions docs/config.rst
Expand Up @@ -1097,9 +1097,9 @@ Handle interpreter directives with long lengths
For systems supporting executable text files (scripts with a shebang), the
system will attempt to parse the interpreter directive to determine the program
to execute on the target text file. When ``tox`` prepares a virtual environment
in a file container which has a large length (e.x. using Jenkins Pipelines), the
in a file container which has a large length (e.g. using Jenkins Pipelines), the
system might not be able to invoke shebang scripts which define interpreters
beyond system limits (e.x. Linux as a limit of 128; ``BINPRM_BUF_SIZE``). To
beyond system limits (e.g. Linux has a limit of 128; ``BINPRM_BUF_SIZE``). To
workaround an environment which suffers from an interpreter directive limit, a
user can bypass the system's interpreter parser by defining the
``TOX_LIMITED_SHEBANG`` environment variable before invoking ``tox``::
Expand Down
10 changes: 7 additions & 3 deletions src/tox/venv.py
Expand Up @@ -19,6 +19,9 @@

from .config import DepConfig

#: maximum parsed shebang interpreter length (see: prepend_shebang_interpreter)
MAXINTERP = 2048


class CreationConfig:
def __init__(
Expand Down Expand Up @@ -672,7 +675,7 @@ def prepend_shebang_interpreter(args):
#
# When preparing virtual environments in a file container which has large
# length, the system might not be able to invoke shebang scripts which
# define interpreters beyond system limits (e.x. Linux as a limit of 128;
# define interpreters beyond system limits (e.g. Linux has a limit of 128;
# BINPRM_BUF_SIZE). This method can be used to check if the executable is
# a script containing a shebang line. If so, extract the interpreter (and
# possible optional argument) and prepend the values to the provided
Expand All @@ -682,8 +685,9 @@ def prepend_shebang_interpreter(args):
try:
with open(args[0], "rb") as f:
if f.read(1) == b"#" and f.read(1) == b"!":
MAXINTERP = 2048
interp = f.readline(MAXINTERP).rstrip().decode("UTF-8")
interp = f.readline(MAXINTERP + 1).rstrip().decode("UTF-8")
if len(interp) > MAXINTERP: # avoid a truncated interpreter
return args
interp_args = interp.split(None, 1)[:2]
return interp_args + args
except (UnicodeDecodeError, IOError):
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/test_venv.py
Expand Up @@ -9,6 +9,7 @@
from tox.interpreters import NoInterpreterInfo
from tox.session.commands.run.sequential import installpkg, runtestenv
from tox.venv import (
MAXINTERP,
CreationConfig,
VirtualEnv,
getdigest,
Expand Down Expand Up @@ -1149,6 +1150,18 @@ def test_tox_testenv_interpret_shebang_long_example(tmpdir):
assert args == expected + base_args


@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows")
def test_tox_testenv_interpret_shebang_skip_truncated(tmpdir):
testfile = tmpdir.join("check_shebang_truncation.py")
original_args = [str(testfile), "arg1", "arg2", "arg3"]

# interpreter (too long example)
testfile.write("#!" + ("x" * (MAXINTERP + 1)))
args = prepend_shebang_interpreter(original_args)

assert args == original_args


@pytest.mark.parametrize("download", [True, False, None])
def test_create_download(mocksession, newconfig, download):
config = newconfig(
Expand Down