Test shebang lines

Executable scripts must have shebang (#!) line to be effectively
executable on most Unix-like systems. Enforce this, and conversely
enforce that files with a shebang line are executable.

Check that the specified interperter is consistent with the file
extension.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine 2020-08-08 23:15:18 +02:00
parent 15898eec23
commit 4aebb8d936

View file

@ -172,6 +172,53 @@ class PermissionIssueTracker(FileIssueTracker):
self.files_with_issues[filepath] = None self.files_with_issues[filepath] = None
class ShebangIssueTracker(FileIssueTracker):
"""Track files with a bad, missing or extraneous shebang line.
Executable scripts must start with a valid shebang (#!) line.
"""
heading = "Invalid shebang line:"
# Allow either /bin/sh, /bin/bash, or /usr/bin/env.
# Allow at most one argument (this is a Linux limitation).
# For sh and bash, the argument if present must be options.
# For env, the argument must be the base name of the interpeter.
_shebang_re = re.compile(rb'^#! ?(?:/bin/(bash|sh)(?: -[^\n ]*)?'
rb'|/usr/bin/env ([^\n /]+))$')
_extensions = {
b'bash': 'sh',
b'perl': 'pl',
b'python3': 'py',
b'sh': 'sh',
}
def is_valid_shebang(self, first_line, filepath):
m = re.match(self._shebang_re, first_line)
if not m:
return False
interpreter = m.group(1) or m.group(2)
if interpreter not in self._extensions:
return False
if not filepath.endswith('.' + self._extensions[interpreter]):
return False
return True
def check_file_for_issue(self, filepath):
is_executable = os.access(filepath, os.X_OK)
with open(filepath, "rb") as f:
first_line = f.readline()
if first_line.startswith(b'#!'):
if not is_executable:
# Shebang on a non-executable file
self.files_with_issues[filepath] = None
elif not self.is_valid_shebang(first_line, filepath):
self.files_with_issues[filepath] = [1]
elif is_executable:
# Executable without a shebang
self.files_with_issues[filepath] = None
class EndOfFileNewlineIssueTracker(FileIssueTracker): class EndOfFileNewlineIssueTracker(FileIssueTracker):
"""Track files that end with an incomplete line """Track files that end with an incomplete line
(no newline character at the end of the last line).""" (no newline character at the end of the last line)."""
@ -292,6 +339,7 @@ class IntegrityChecker:
self.setup_logger(log_file) self.setup_logger(log_file)
self.issues_to_check = [ self.issues_to_check = [
PermissionIssueTracker(), PermissionIssueTracker(),
ShebangIssueTracker(),
EndOfFileNewlineIssueTracker(), EndOfFileNewlineIssueTracker(),
Utf8BomIssueTracker(), Utf8BomIssueTracker(),
UnixLineEndingIssueTracker(), UnixLineEndingIssueTracker(),