#!/usr/bin/python3# Validate basic syntax of shell script and yaml.import osimport reimport statimport subprocessimport yamlINITRD_SERVICES_WITHOUT_BEFORE = {# Depended on by other services'coreos-livepxe-rootfs.service',}validated=0def openat(dirfd, name, mode='r'):def opener(path, flags):return os.open(path, flags, dir_fd=dirfd)return open(name, mode, opener=opener)def validate_initrd_service(rootfd, name):with openat(rootfd, name) as fh:if ([l for l in fh.readlines() if l.startswith('Before=')] orname in INITRD_SERVICES_WITHOUT_BEFORE):global validatedvalidated += 1else:raise Exception(f'{name} has no Before= and may race with switch-root')BASH_UNBRACKETED_IF = re.compile(r'\sif\s+"?\$')def validate_shell(rootfd, name):subprocess.check_call(['bash', '-n', name], preexec_fn=lambda: os.fchdir(rootfd))with openat(rootfd, name) as fh:if BASH_UNBRACKETED_IF.search(fh.read()):raise Exception(f'Possible unbracketed conditional in {name}')global validatedvalidated +=1for root, dirs, files, rootfd in os.fwalk('.'):# Skip .gitif '.git' in dirs:dirs.remove('.git')for name in files:if name.endswith(('.yaml', '.yml')):with open(os.open(name, dir_fd=rootfd, flags=os.O_RDONLY)) as f:yaml.safe_load(f)validated +=1continueelif name.endswith('.sh'):validate_shell(rootfd, name)continueelif 'dracut/modules.d' in root and name.endswith('.service'):validate_initrd_service(rootfd, name)stbuf = os.lstat(name, dir_fd=rootfd)if not stat.S_ISREG(stbuf.st_mode):continueif not stbuf.st_mode & stat.S_IXUSR:continuemimetype = subprocess.check_output(['file', '-b', '--mime-type', name], encoding='UTF-8',preexec_fn=lambda: os.fchdir(rootfd)).strip()if mimetype == 'text/x-shellscript':validate_shell(rootfd, name)print(f"Validated {validated} files")