53 lines
2.7 KiB
Python
Executable file
53 lines
2.7 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Static repository references; no privilege, network or host inventory reads."""
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
root = Path(sys.argv[1])
|
|
errors = []
|
|
def error(value): errors.append(value)
|
|
def lines(path):
|
|
try: return (root/path).read_text(encoding='utf-8').splitlines()
|
|
except OSError as exc: error(f'{path}: {exc}'); return []
|
|
for name in ('pacman','aur','orphans'):
|
|
path = f'pkglist/{name}.txt'
|
|
values = lines(path)
|
|
if values != sorted(set(values)) or any(not re.fullmatch(r'[A-Za-z0-9@._+:-]+', x) for x in values):
|
|
error(f'{path}: tri, doublon ou nom invalide')
|
|
if set(lines('pkglist/pacman.txt')) & set(lines('pkglist/aur.txt')):
|
|
error('paquet présent dans les deux listes')
|
|
for name in ('system-services','system-timers','on-demand-system-services'):
|
|
path = f'manifests/{name}.txt'
|
|
values = lines(path)
|
|
suffix = '.timer' if name == 'system-timers' else '.service'
|
|
if values != sorted(set(values)) or any(not re.fullmatch(r'[A-Za-z0-9@._:-]+', x) or not x.endswith(suffix) for x in values):
|
|
error(f'{path}: unité invalide, désordre ou doublon')
|
|
for name,needles in {
|
|
'templates/fstab.btrfs':('@ROOT_UUID@','@EFI_UUID@','subvol=/@/.snapshots'),
|
|
'templates/limine.conf':('@ROOT_UUID@',),
|
|
'templates/sudoers.d/batlimit':('@USER@',),
|
|
'templates/sudoers.d/vmware-stack':('@USER@',),
|
|
}.items():
|
|
value='\n'.join(lines(name))
|
|
for needle in needles:
|
|
if needle not in value: error(f'{name}: {needle} absent')
|
|
for path in [root/'README.md',*sorted((root/'docs').rglob('*.md'))]:
|
|
try: text=path.read_text(encoding='utf-8')
|
|
except OSError as exc: error(str(exc)); continue
|
|
for target in re.findall(r'\]\(([^)]+)\)',text):
|
|
if '://' in target or target.startswith('#') or target.startswith('mailto:'): continue
|
|
local=target.split('#',1)[0].split(' ',1)[0]
|
|
if local:
|
|
candidate=(path.parent/local).resolve()
|
|
if not candidate.is_relative_to(root.resolve()) or not candidate.exists():
|
|
error(f'{path.relative_to(root)}: lien local absent ou hors dépôt {target}')
|
|
for path in root.rglob('*'):
|
|
if '.git' in path.parts or not path.is_file(): continue
|
|
if path.suffix in ('.key','.pem','.p12','.sqlite','.sqlite3','.db') or path.name == '.netrc':
|
|
error(f'fichier potentiellement sensible : {path.relative_to(root)}')
|
|
for forbidden in ('userfs','inventory/packages','rootfs/etc/sudoers.d'):
|
|
if (root/forbidden).exists(): error(f'répertoire interdit : {forbidden}')
|
|
for item in errors: print('[!] '+item,file=sys.stderr)
|
|
print(f'Contrôle des listes, manifests, modèles et liens : {len(errors)} erreur(s).')
|
|
sys.exit(bool(errors))
|