fix(monitor): clarify Lynis removal outcomes and threshold guidance

This commit is contained in:
martino
2026-09-29 00:42:44 +02:00
parent 72f77069ca
commit d27012806b
15 changed files with 485 additions and 75 deletions
@@ -0,0 +1,78 @@
"""Inert branch tests for the real Lynis deletion/uninstall producers."""
import ast
import os
import subprocess
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[3]
def extracted(path, name, namespace):
tree = ast.parse((ROOT / path).read_text())
fn = next(x for x in tree.body if isinstance(x, ast.FunctionDef) and x.name == name)
fn.decorator_list = []
exec(compile(ast.fix_missing_locations(ast.Module(body=[fn], type_ignores=[])), str(path), 'exec'), namespace)
return namespace[name]
class LynisFeedbackProducer(unittest.TestCase):
def test_actual_ui_feedback_handlers_all_shipped_locales(self):
result = subprocess.run(['node', str(ROOT / '.github/scripts/tests/fixtures/lynis_feedback_contract.cjs')],
cwd=ROOT, capture_output=True, text=True, env=os.environ.copy())
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_uninstall_nothing_to_remove_is_not_reported_as_uninstalled(self):
fake_os = SimpleNamespace(path=SimpleNamespace(exists=lambda p: False, join=lambda *p: '/'.join(p)), remove=lambda p: self.fail(p))
result = extracted('AppImage/scripts/security_manager.py', 'uninstall_lynis', {'os': fake_os})()
self.assertFalse(result[0])
self.assertEqual(result[3], 'no_files')
manager = SimpleNamespace(uninstall_lynis=lambda: result)
route = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_uninstall', {'security_manager': manager, 'jsonify': lambda data: data})
self.assertEqual(route()['outcome'], 'no_files')
def test_report_outcomes(self):
paths = ['/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log']
for fail_at in (None, 0, 1, 'missing'):
with self.subTest(fail_at=fail_at):
removed = []
def remove(p):
if fail_at in (0, 1) and p == paths[fail_at]:
raise PermissionError('inert denial')
removed.append(p)
fake_os = SimpleNamespace(path=SimpleNamespace(isfile=lambda p: fail_at != 'missing'), remove=remove)
ns = {'security_manager': object(), 'jsonify': lambda data: data}
orig_import = __import__
with patch('builtins.__import__', side_effect=lambda n, *a, **kw: fake_os if n == 'os' else orig_import(n, *a, **kw)):
result = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_report_delete', ns)()
body, status = result if isinstance(result, tuple) else (result, 200)
self.assertEqual(removed, paths[:fail_at] if fail_at in (0, 1) else ([] if fail_at == 'missing' else paths))
self.assertEqual(body['success'], fail_at is None)
if fail_at == 'missing':
self.assertEqual(body['outcome'], 'no_files')
elif fail_at in (0, 1):
self.assertEqual(status, 500)
self.assertEqual(body['partial'], fail_at == 1)
self.assertIn('inert denial', body['message'])
def test_uninstall_outcomes_and_route(self):
targets = ['/opt/lynis', '/usr/local/bin/lynis', '/var/log/lynis-report.dat', '/var/log/lynis.log', '/var/log/lynis-output.log']
for fail_at in (None, 0, 2):
with self.subTest(fail_at=fail_at):
removed = []
def remove(path):
if fail_at is not None and path == targets[fail_at]: raise PermissionError('inert denial')
removed.append(path)
fake_os = SimpleNamespace(path=SimpleNamespace(exists=lambda p: p in targets, join=lambda *parts: '/'.join(parts)), remove=remove)
fake_shutil = SimpleNamespace(rmtree=remove)
orig_import = __import__
with patch('builtins.__import__', side_effect=lambda n, *a, **kw: fake_shutil if n == 'shutil' else orig_import(n, *a, **kw)):
result = extracted('AppImage/scripts/security_manager.py', 'uninstall_lynis', {'os': fake_os})()
self.assertEqual(removed, targets[:fail_at] if fail_at is not None else targets)
self.assertEqual(result[0], fail_at is None)
self.assertEqual(result[2], fail_at == 2)
manager = SimpleNamespace(uninstall_lynis=lambda: result)
route = extracted('AppImage/scripts/flask_security_routes.py', 'lynis_uninstall', {'security_manager': manager, 'jsonify': lambda data: data})
self.assertEqual(route()['partial'], fail_at == 2)
if __name__ == '__main__': unittest.main()