summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/hooks/sbang.py5
-rw-r--r--lib/spack/spack/test/sbang.py49
2 files changed, 54 insertions, 0 deletions
diff --git a/lib/spack/spack/hooks/sbang.py b/lib/spack/spack/hooks/sbang.py
index 33aa29691f..8b99ee7873 100644
--- a/lib/spack/spack/hooks/sbang.py
+++ b/lib/spack/spack/hooks/sbang.py
@@ -126,6 +126,11 @@ def filter_shebangs_in_directory(directory, filenames=None):
if not os.path.isfile(path):
continue
+ # only handle executable files
+ st = os.stat(path)
+ if not st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
+ continue
+
# only handle links that resolve within THIS package's prefix.
if os.path.islink(path):
real_path = os.path.realpath(path)
diff --git a/lib/spack/spack/test/sbang.py b/lib/spack/spack/test/sbang.py
index e0d7a8ccf3..4eb3b07a60 100644
--- a/lib/spack/spack/test/sbang.py
+++ b/lib/spack/spack/test/sbang.py
@@ -62,18 +62,27 @@ class ScriptDirectory(object):
with open(self.short_shebang, 'w') as f:
f.write(short_line)
f.write(last_line)
+ self.make_executable(self.short_shebang)
# Script with long shebang
self.long_shebang = os.path.join(self.tempdir, 'long')
with open(self.long_shebang, 'w') as f:
f.write(long_line)
f.write(last_line)
+ self.make_executable(self.long_shebang)
+
+ # Non-executable script with long shebang
+ self.nonexec_long_shebang = os.path.join(self.tempdir, 'nonexec_long')
+ with open(self.nonexec_long_shebang, 'w') as f:
+ f.write(long_line)
+ f.write(last_line)
# Lua script with long shebang
self.lua_shebang = os.path.join(self.tempdir, 'lua')
with open(self.lua_shebang, 'w') as f:
f.write(lua_line)
f.write(last_line)
+ self.make_executable(self.lua_shebang)
# Lua script with long shebang
self.lua_textbang = os.path.join(self.tempdir, 'lua_in_text')
@@ -81,12 +90,14 @@ class ScriptDirectory(object):
f.write(short_line)
f.write(lua_in_text)
f.write(last_line)
+ self.make_executable(self.lua_textbang)
# Node script with long shebang
self.node_shebang = os.path.join(self.tempdir, 'node')
with open(self.node_shebang, 'w') as f:
f.write(node_line)
f.write(last_line)
+ self.make_executable(self.node_shebang)
# Node script with long shebang
self.node_textbang = os.path.join(self.tempdir, 'node_in_text')
@@ -94,12 +105,14 @@ class ScriptDirectory(object):
f.write(short_line)
f.write(node_in_text)
f.write(last_line)
+ self.make_executable(self.node_textbang)
# php script with long shebang
self.php_shebang = os.path.join(self.tempdir, 'php')
with open(self.php_shebang, 'w') as f:
f.write(php_line)
f.write(last_line)
+ self.make_executable(self.php_shebang)
# php script with long shebang
self.php_textbang = os.path.join(self.tempdir, 'php_in_text')
@@ -107,6 +120,7 @@ class ScriptDirectory(object):
f.write(short_line)
f.write(php_in_text)
f.write(last_line)
+ self.make_executable(self.php_textbang)
# Script already using sbang.
self.has_sbang = os.path.join(self.tempdir, 'shebang')
@@ -114,15 +128,27 @@ class ScriptDirectory(object):
f.write(sbang_line)
f.write(long_line)
f.write(last_line)
+ self.make_executable(self.has_sbang)
# Fake binary file.
self.binary = os.path.join(self.tempdir, 'binary')
tar = which('tar', required=True)
tar('czf', self.binary, self.has_sbang)
+ self.make_executable(self.binary)
def destroy(self):
shutil.rmtree(self.tempdir, ignore_errors=True)
+ def make_executable(self, path):
+ # make a file executable
+ st = os.stat(path)
+ executable_mode = st.st_mode \
+ | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
+ os.chmod(path, executable_mode)
+
+ st = os.stat(path)
+ assert oct(executable_mode) == oct(st.st_mode & executable_mode)
+
@pytest.fixture
def script_dir(sbang_line):
@@ -134,6 +160,7 @@ def script_dir(sbang_line):
def test_shebang_handling(script_dir, sbang_line):
assert sbang.shebang_too_long(script_dir.lua_shebang)
assert sbang.shebang_too_long(script_dir.long_shebang)
+ assert sbang.shebang_too_long(script_dir.nonexec_long_shebang)
assert not sbang.shebang_too_long(script_dir.short_shebang)
assert not sbang.shebang_too_long(script_dir.has_sbang)
@@ -153,6 +180,11 @@ def test_shebang_handling(script_dir, sbang_line):
assert f.readline() == long_line
assert f.readline() == last_line
+ # Make sure this is untouched
+ with open(script_dir.nonexec_long_shebang, 'r') as f:
+ assert f.readline() == long_line
+ assert f.readline() == last_line
+
# Make sure this got patched.
with open(script_dir.lua_shebang, 'r') as f:
assert f.readline() == sbang_line
@@ -243,3 +275,20 @@ def test_install_sbang_too_long(tmpdir):
assert 'root is too long' in err
assert 'exceeds limit' in err
assert 'cannot patch' in err
+
+
+def test_sbang_hook_skips_nonexecutable_blobs(tmpdir):
+ # Write a binary blob to non-executable.sh, with a long interpreter "path"
+ # consisting of invalid UTF-8. The latter is technically not really necessary for
+ # the test, but binary blobs accidentally starting with b'#!' usually do not contain
+ # valid UTF-8, so we also ensure that Spack does not attempt to decode as UTF-8.
+ contents = b'#!' + b'\x80' * sbang.shebang_limit
+ file = str(tmpdir.join('non-executable.sh'))
+ with open(file, 'wb') as f:
+ f.write(contents)
+
+ sbang.filter_shebangs_in_directory(str(tmpdir))
+
+ # Make sure there is no sbang shebang.
+ with open(file, 'rb') as f:
+ assert b'sbang' not in f.readline()