feat: write timestamped blocklist change reports on firewall sync

This commit is contained in:
zwitschi committed 2026-08-06 14:31:15 +02:00
1 parent 48c5629b4b
commit 80a67635de
4 files changed
+110 -3

No files matched your search

+67
View File
@@ -18,8 +18,12 @@ import ipaddress
import os
import re
import subprocess
from datetime import datetime
from typing import Any, Iterable
from .files import write_json
from .reporting import timestamp_name
#: A ``+ 1.2.3.4/32`` added line in a git patch; ``+++ b/...`` is the file
#: header and is excluded by the negative lookahead.
_ADDED_LINE_RE = re.compile(r"^\+(?!\+)(.*)$")
@@ -117,3 +121,66 @@ def compute_changes(current_v4: set[str], current_v6: set[str],
"removed_ipv4": sorted(current_v4 - complete_v4),
"removed_ipv6": sorted(current_v6 - complete_v6),
}
def blocklist_delta(history_v4: set[str], history_v6: set[str],
prev_v4: set[str], prev_v6: set[str],
complete_v4: set[str], complete_v6: set[str],
) -> dict[str, list[str]]:
"""Complete-set delta between the previous and current blocklist state.
``added`` are entries newly part of the blocklist (this run's targets not
blocked before); ``removed`` are entries no longer part of it — normally
empty in the cumulative model.
"""
before_v4 = history_v4 | prev_v4
before_v6 = history_v6 | prev_v6
return {
"added_ipv4": sorted(complete_v4 - before_v4),
"added_ipv6": sorted(complete_v6 - before_v6),
"removed_ipv4": sorted(before_v4 - complete_v4),
"removed_ipv6": sorted(before_v6 - complete_v6),
}
def write_blocklist_report(report_dir: str,
*,
history_v4: set[str], history_v6: set[str],
prev_v4: set[str], prev_v6: set[str],
complete_v4: set[str], complete_v6: set[str],
firewall_changes: dict | None = None,
dry_run: bool = False,
errors: Iterable[str] = (),
) -> str:
"""Write the timestamped blocklist report to ``<report_dir>/app/``.
Records the complete-set delta (added/removed) plus the full cumulative
blocked list (git history | working files | scan targets), and — when
supplied — the router sync changes. Returns the written file path.
"""
delta = blocklist_delta(
history_v4, history_v6, prev_v4, prev_v6, complete_v4, complete_v6)
payload = {
"generated_at": datetime.now().astimezone().isoformat(
timespec="seconds"),
"report_dir": report_dir,
"dry_run": dry_run,
"summary": {
"added": len(delta["added_ipv4"]) + len(delta["added_ipv6"]),
"removed": len(delta["removed_ipv4"]) + len(delta["removed_ipv6"]),
"total_blocked": len(complete_v4) + len(complete_v6),
},
"added_ipv4": delta["added_ipv4"],
"added_ipv6": delta["added_ipv6"],
"removed_ipv4": delta["removed_ipv4"],
"removed_ipv6": delta["removed_ipv6"],
"blocked_ipv4": sorted(complete_v4),
"blocked_ipv6": sorted(complete_v6),
"firewall_changes": firewall_changes,
"errors": list(errors),
}
app_dir = os.path.join(report_dir, "app")
os.makedirs(app_dir, exist_ok=True)
path = os.path.join(app_dir, f"{timestamp_name()}.json")
write_json(path, payload)
return path