Skip to content

Commit 0fdfae4

Browse files
Add files via upload
1 parent d706671 commit 0fdfae4

4 files changed

Lines changed: 98 additions & 9 deletions

File tree

.github/workflows/asn_reputation_scorer.yml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,24 @@ jobs:
162162
if et_text:
163163
print(f" ET-Quelle: {et_url.split('/')[-1]}")
164164
break
165-
et_ips = set()
166-
# FIX: strikte IPV4_RE aus netshield_common nutzen statt inline
167-
# \b-Regex – konsistent mit der Versions-String-Filterung.
168-
for m in IPV4_RE.finditer(et_text):
169-
et_ips.add(m.group(1))
165+
# FIX BUG-WF5-IPV6-ASN: Vorher direkte IPV4_RE.finditer-Schleife –
166+
# IPV4_RE hat einen Lookbehind '(?<![\d.])' der ':' als Trenner
167+
# zulaesst. Folge: '::ffff:1.2.3.4' (IPv4-mapped IPv6) matchte den
168+
# IPv4-Suffix '1.2.3.4' und fuegte ihn als Phantom-Eintrag ein.
169+
# parse_entries() loest das via _is_in_ipv6_token-Heuristik
170+
# (Token enthaelt '::' oder >=2 Doppelpunkte → kein Match).
171+
# Konsequenz vor Fix: ASN-Reputation-Score wurde durch IPv6-mapped
172+
# Eintraege in ET-Feeds verzerrt – mit reproduzierbarem
173+
# asymmetrischen Effekt fuer ASN-Holder die viele AAAA-Records
174+
# haben. Jetzt: Konsistent mit dem Rest der Pipeline.
175+
# CIDR-Filter ('/' not in e): Vertrag bewahren – die alte Schleife
176+
# gab nur Plain-IPs zurueck, et_ips wird unten mit 'ip_str in et_ips'
177+
# verglichen wo ip_str eine Plain-IP ist. Ein '1.2.3.0/24' im Set
178+
# wuerde diesen Vergleich nicht treffen aber die set-Groesse
179+
# verzerren. Use_protected_check=False (Validierungs-Modus):
180+
# Whitelist-IPs bleiben drin, weil ASN-Scoring sie ueberhaupt
181+
# mitsehen soll.
182+
et_ips = {e for e in _parse_entries(et_text) if "/" not in e}
170183
print(f" Emerging Threats: {len(et_ips):,} IPs" if et_ips else " Emerging Threats: FEHLER – alle Quellen nicht erreichbar")
171184
172185
# Hilfsfunktion: Binary-Search CIDR-Check (sortierte Ranges)

.github/workflows/auto_feed_discovery.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,27 @@ jobs:
375375
if not db and os.path.exists(BACKUP_FILE):
376376
try:
377377
import gzip
378+
# FIX BUG-GZIP-BACKUP-LIMIT: identisch zu update_combined_blacklist.
379+
# Ohne Pre-Size + Streaming-Limit wuerde eine zip-Bombe im
380+
# Backup den Workflow per OOM kippen bevor _MIN_BACKUP_ENTRIES
381+
# je greift. Layer 1: 200 MB komprimiert hard-cap.
382+
# Layer 2: 2 GB Streaming-Read auf den dekomprimierten Text.
383+
_BACKUP_COMPRESSED_LIMIT = 200 * 1024 * 1024
384+
_BACKUP_DECOMPRESSED_LIMIT = 2 * 1024 * 1024 * 1024
385+
_bsize = os.path.getsize(BACKUP_FILE)
386+
if _bsize > _BACKUP_COMPRESSED_LIMIT:
387+
raise ValueError(
388+
f"Backup zu gross: {_bsize / 1024 / 1024:.1f} MB "
389+
f"> {_BACKUP_COMPRESSED_LIMIT / 1024 / 1024:.0f} MB "
390+
f"(moegliche zip-Bombe oder Repo-Korruption)")
378391
with gzip.open(BACKUP_FILE, "rt", encoding="utf-8") as _bf:
379-
db = json.load(_bf)
392+
_raw = _bf.read(_BACKUP_DECOMPRESSED_LIMIT + 1)
393+
if len(_raw) > _BACKUP_DECOMPRESSED_LIMIT:
394+
raise ValueError(
395+
f"Backup-Stream > "
396+
f"{_BACKUP_DECOMPRESSED_LIMIT / 1024 / 1024:.0f} MB "
397+
f"nach Dekomprimierung (zip-Bombe), verworfen")
398+
db = json.loads(_raw)
380399
# FIX BUG-BACKUP-GUARD: Backup-Plausibilitaet pruefen vor Restore.
381400
# Ein winziges/leeres Backup wuerde sonst die Cache-Recovery
382401
# mit kaputtem Stand zementieren - besser leer starten und

.github/workflows/feed_health_monitor.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,13 @@ jobs:
150150
code = r.status
151151
sample = r.read(2 * 1024 * 1024).decode("utf-8", errors="ignore") # 2 MB reichen für IP-Sample-Check; 25 MB war unnötiger Netzwerk-/Speicherdruck bei 15 parallelen Requests
152152
elapsed = round(time.time() - t0, 2)
153-
ip_count = len(set(IP_RE.findall(sample)))
153+
# FIX BUG-WF6-IPV6-HEALTH: vorher IP_RE.findall ohne IPv6-
154+
# Token-Schutz. ::ffff:1.2.3.4 haette den has_ips-Boolean
155+
# faelschlich auf True gesetzt (= Feed gilt als gesund).
156+
# parse_entries() filtert IPv6-Tokens via _is_in_ipv6_token.
157+
# Niedrige Praxis-Relevanz weil ip_count nur fuer Health-
158+
# Watchdog (Boolean), nicht fuer Datenfluss in Blacklists.
159+
ip_count = len(_parse_entries(sample))
154160
has_ips = ip_count > 0
155161
return {"name": name, "url": url, "status": code, "ok": code == 200,
156162
"has_ips": has_ips, "sample_ips": ip_count, "ms": int(elapsed*1000)}

.github/workflows/update_combined_blacklist.yml

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)