@@ -184,6 +184,7 @@ jobs:
184184 safe_get_date, parse_date, sort_ips, write_ip_list,
185185 write_json_atomic, write_text_atomic,
186186 fetch_url, check_local_feed_age,
187+ validate_auto_feeds,
187188 IPV4_RE, CIDR_RE, TIMESTAMP_RE,
188189 )
189190 import ipaddress, json, os, re, sys, urllib.request
@@ -639,6 +640,19 @@ jobs:
639640 BACKUP_FILE = "seen_db_backup.json.gz"
640641 if os.path.exists(DB_FILE):
641642 try:
643+ # FIX BUG-SEENDB-LIMIT: Pre-Size-Check vor json.load.
644+ # seen_db.json ist heute ~600 MB. 5 GB Hard-Cap erlaubt
645+ # 8x Wachstum, schuetzt aber gegen ein versehentlich
646+ # oder boeswillig korruptes seen_db (z.B. via Cache-
647+ # Restore aus einem anderen Repo) das den Runner mit
648+ # 7 GB RAM via json.load OOM kippen wuerde.
649+ _SEENDB_LIMIT = 5 * 1024 * 1024 * 1024
650+ _dsize = os.path.getsize(DB_FILE)
651+ if _dsize > _SEENDB_LIMIT:
652+ raise ValueError(
653+ f"seen_db.json zu gross: {_dsize / 1024 / 1024:.1f} MB "
654+ f"> {_SEENDB_LIMIT / 1024 / 1024 / 1024:.0f} GB "
655+ f"(moegliche Korruption oder Cache-Mismatch)")
642656 with open(DB_FILE) as f:
643657 db = json.load(f)
644658 print(f"seen_db geladen: {len(db)} IPs")
@@ -648,8 +662,36 @@ jobs:
648662 if not db and os.path.exists(BACKUP_FILE):
649663 try:
650664 import gzip
665+ # FIX BUG-GZIP-BACKUP-LIMIT: Pre-Size + Streaming-Limit.
666+ # Vorher: gzip.open + json.load ohne irgendeine Groessen-
667+ # Pruefung. Wenn ein Angreifer mit Repo-Schreibrechten das
668+ # Backup durch eine zip-Bombe ersetzt (z.B. 100 MB
669+ # komprimiert -> 50 GB expandiert), erleidet der Workflow
670+ # OOM bevor der nachgelagerte _MIN_BACKUP_ENTRIES-Check
671+ # je greift. Realistisch: Repo-Write erforderlich, also
672+ # Insider-Threat-Vektor – Hardung lohnt trotzdem.
673+ # Layer 1: komprimierte Groesse hard-cap bei 200 MB
674+ # (aktuelles Backup ist ~70 MB, 200 MB ist 3x
675+ # headroom fuer normales Wachstum).
676+ # Layer 2: Streaming-Read mit 2 GB-Limit auf den
677+ # dekomprimierten JSON-Text. Das echte JSON
678+ # ist heute ~600 MB – 2 GB erlaubt 3x Wachstum.
679+ _BACKUP_COMPRESSED_LIMIT = 200 * 1024 * 1024
680+ _BACKUP_DECOMPRESSED_LIMIT = 2 * 1024 * 1024 * 1024
681+ _bsize = os.path.getsize(BACKUP_FILE)
682+ if _bsize > _BACKUP_COMPRESSED_LIMIT:
683+ raise ValueError(
684+ f"Backup zu gross: {_bsize / 1024 / 1024:.1f} MB "
685+ f"> {_BACKUP_COMPRESSED_LIMIT / 1024 / 1024:.0f} MB "
686+ f"(moegliche zip-Bombe oder Repo-Korruption)")
651687 with gzip.open(BACKUP_FILE, "rt", encoding="utf-8") as _bf:
652- db = json.load(_bf)
688+ _raw = _bf.read(_BACKUP_DECOMPRESSED_LIMIT + 1)
689+ if len(_raw) > _BACKUP_DECOMPRESSED_LIMIT:
690+ raise ValueError(
691+ f"Backup-Stream > "
692+ f"{_BACKUP_DECOMPRESSED_LIMIT / 1024 / 1024:.0f} MB "
693+ f"nach Dekomprimierung (zip-Bombe), verworfen")
694+ db = json.loads(_raw)
653695 # FIX BUG-BACKUP-GUARD: Backup ist nur dann nutzbar wenn es
654696 # eine plausible Anzahl Eintraege enthaelt. Ein winziges
655697 # Backup (durch frueheren BUG-BACKUP-GUARD-Schreibfehler oder
@@ -1010,7 +1052,16 @@ jobs:
10101052 try:
10111053 with open(AUTO_FEEDS_FILE) as af:
10121054 auto_data = json.load(af)
1013- auto_feeds = auto_data.get("feeds", [])
1055+ # FIX BUG-AUTOFEEDS-VALIDATE: validate_auto_feeds() filtert
1056+ # auf safe Schema (dict mit name/url als String) und URL-
1057+ # Whitelist (nur http/https). Vorher: direkter
1058+ # auto_data.get("feeds", []) ohne Pruefung – ein Angreifer
1059+ # mit Repo-Schreibrechten konnte malicious URLs in den
1060+ # Feed-Loop einschleusen ohne Code-Review-Pfad ueber SOURCES.
1061+ auto_feeds, _rejected = validate_auto_feeds(auto_data)
1062+ if _rejected:
1063+ print(f" WARNUNG: {_rejected} auto-feed Eintraege wegen "
1064+ f"Schema-/URL-Check verworfen")
10141065 print(f" Auto-discovered Feeds: {len(auto_feeds)}")
10151066
10161067 def fetch_auto(feed):
0 commit comments