Files
hermes-agent/scripts/check_bash_shebangs.py

52 lines
1.5 KiB
Python

#!/usr/bin/env python3
"""Require PATH-resolved Bash in scripts, generated scripts, and examples.
Nix and other non-FHS environments need not provide Bash at a fixed bin path.
Scan tracked text, including extensionless scripts and embedded shell payloads.
"""
from __future__ import annotations
import argparse
from pathlib import Path
import re
import subprocess
FIXED_BASH = re.compile(rb"#![ \t]*/(?:usr/)?bin/bash\b")
def check(root: Path) -> list[str]:
tracked = subprocess.run(
["git", "ls-files", "-z"], cwd=root, check=True, capture_output=True,
).stdout
findings = []
for name in tracked.split(b"\0"):
if not name:
continue
relative = name.decode("utf-8", errors="surrogateescape")
path = root / relative
if path.is_symlink() or not path.is_file():
continue
data = path.read_bytes()
if b"\0" in data:
continue
for number, line in enumerate(data.splitlines(), 1):
if FIXED_BASH.search(line):
findings.append(f"{relative}:{number}: use #!/usr/bin/env bash")
return findings
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1])
args = parser.parse_args()
findings = check(args.root)
for finding in findings:
print(finding)
print(f"Bash shebang check: {len(findings)} violation(s)")
return int(bool(findings))
if __name__ == "__main__":
raise SystemExit(main())