Skip to content

Commit 766d34c

Browse files
Add files via upload
1 parent 7c3dc70 commit 766d34c

1 file changed

Lines changed: 79 additions & 37 deletions

File tree

scripts/netshield_common.py

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,29 @@ def parse_entries(text, use_protected_check=False):
461461
"""Universeller Parser: plain IPv4, CIDR, ip:port, ipset, FortiGate,
462462
Spamhaus DROP, URLhaus, CSV erste Spalte. Privates/Loopback gefiltert.
463463
464+
Hat zwei distinkte Modi - die Wahl ist sicherheitsrelevant:
465+
466+
- ``use_protected_check=False`` (Default, "Validierungs-Modus"):
467+
Filtert nur technisch nicht-oeffentliche IPs (RFC1918, Loopback,
468+
Reserved, Multicast, Class E, Link-Local, CGNAT, Doc-Ranges).
469+
Whitelist-IPs (z.B. 8.8.8.8, 1.1.1.1) kommen DURCH. Geeignet zum
470+
Validieren ob ein String eine "echte" IPv4 ist, oder fuer Tests
471+
ohne geladene Whitelist. Braucht KEIN load_whitelist() vorher.
472+
473+
- ``use_protected_check=True`` ("Pipeline-Modus"):
474+
Filtert zusaetzlich Whitelist-IPs heraus (via is_protected_entry).
475+
DAS ist der Modus den Workflows fuer Blacklist-Generierung
476+
verwenden muessen. Verlangt vorher load_whitelist(); sonst
477+
WhitelistNotLoadedError.
478+
479+
Wenn der Output dieser Funktion in eine produzierte Blacklist fliesst,
480+
MUSS use_protected_check=True gesetzt sein. Siehe BUG-WL1/WL3/WL7
481+
(historische False-Negative-Faelle wo Whitelist-IPs in Outputs landeten).
482+
Convenience-Wrapper: ``parse_entries_for_blacklist()``.
483+
464484
Args:
465485
text: Rohtext des Feeds.
466-
use_protected_check: Wenn True, wird is_protected_entry() statt
467-
is_valid_public_ipv4() verwendet (schließt Whitelist ein).
486+
use_protected_check: Pipeline-Modus aktivieren (siehe oben).
468487
469488
Returns:
470489
set[str]: Gefiltertes Set von IPs und CIDRs.
@@ -521,43 +540,46 @@ def parse_entries(text, use_protected_check=False):
521540
if not line:
522541
continue
523542

524-
# CSV: nur erste Spalte prüfen
525-
first_col = line.split(',')[0].strip()
526-
527-
# CIDR?
528-
cidr_m = CIDR_RE.match(first_col)
529-
if cidr_m:
530-
if cidr_check(cidr_m.group(1)):
531-
entries.add(str(ipaddress.ip_network(cidr_m.group(1), strict=False)))
532-
continue
533-
534-
# ip:port?
535-
ip_port = re.match(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):\d+', first_col)
536-
if ip_port:
537-
ip = ip_port.group(1)
538-
if ip_check(ip):
539-
entries.add(ip)
540-
continue
541-
542-
# Plain IP in erster Spalte?
543-
# (?![\d.]) statt \b: schliesst auch nachfolgenden Punkt aus,
544-
# damit '1.2.3.4.5' (Versions-String) nicht als '1.2.3.4' durchgeht.
545-
ip_m = re.match(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?![\d.])', first_col)
546-
if ip_m:
547-
ip = ip_m.group(1)
548-
if ip_check(ip):
549-
entries.add(ip)
550-
continue
551-
552-
# Fallback: alle IPs/CIDRs in der Zeile (URLhaus, JSON-Felder etc.)
543+
# CSV: nur erste Spalte... siehe Kommentar unten zur Cidr-Span-Strategie.
544+
# FIX BUG-MULTI-ENTRY: Vorher gab es drei Fast-Path-Bloecke
545+
# (CIDR-am-Anfang / ip:port / Plain-IP-am-Anfang) die jeweils
546+
# 'continue' am Ende hatten. Eine Zeile wie "5.5.5.0/24 6.6.6.6"
547+
# matchte CIDR_RE.match(first_col), legte den CIDR ab und sprang
548+
# aus der Zeile. Die "6.6.6.6" ging silent verloren. Genauso bei
549+
# "10.20.30.0/24 5.5.5.5" (privat-CIDR + oeffentliche IP - die IP
550+
# verschwand komplett).
551+
#
552+
# Heute betrifft das keinen der konsumierten Feeds (alle haben
553+
# genau 1 Eintrag/Zeile), aber auto_feed_discovery.yml nimmt
554+
# automatisch neue Feeds auf - dort waere der Bug ein latenter
555+
# Datenleck-Vektor.
556+
#
557+
# Fix: Die drei Fast-Pfade entfernt. Der Fallback unten ist ein
558+
# Superset:
559+
# - findet alle IPs/CIDRs in der Zeile (Multi-Entry)
560+
# - hat per cidr_spans Schutz gegen Phantom-IPs aus CIDR-
561+
# Netzadressen (kein "5.5.5.0" extra zur "5.5.5.0/24")
562+
# - IPV4_RE hat den Versions-String-Schutz ((?![\d.]))
563+
# bereits eingebaut, sodass "1.2.3.4.5" nicht als
564+
# "1.2.3.4" durchgeht
565+
# - ip:port wird durch IPV4_RE korrekt geparst (matcht IP vor ':')
566+
#
567+
# Verhaltensaenderung: Bei einer CSV-Zeile mit IPs in mehreren
568+
# Spalten (z.B. "1.2.3.4,US,reported_by:5.6.7.8") wird jetzt
569+
# auch die zweite IP erfasst, statt sie per first_col-Cut zu
570+
# verwerfen. In den 30+ derzeit konsumierten Feeds ist dieser
571+
# Fall null Mal vorhanden (verifiziert ueber 986k Zeilen).
572+
573+
# Inline-Kommentar wurde oben bereits per re.split([;#]) abgetrennt,
574+
# damit z.B. Spamhaus-DROP "1.2.3.0/24 ; SBL12345" sauber laeuft.
575+
576+
# Fallback: alle IPs/CIDRs in der Zeile.
553577
# FIX BUG-PARSE-DUAL: CIDR-Spans merken, damit IPV4_RE die Netzadresse
554578
# einer CIDR (z.B. 5.5.5.0 in 5.5.5.0/24) NICHT zusaetzlich als
555-
# Single-IP einfuegt. Vorher: Eine Zeile wie '{"net":"5.5.5.0/24"}'
556-
# erzeugte BEIDE Eintraege - die CIDR und ihre Netzadresse als IP.
557-
# Symptom: redundante Phantom-IPs in den Output-Listen, je nach Feed-
558-
# Format (URLhaus / JSON / Fliesstext). Spamhaus-DROP war nicht
559-
# betroffen, weil dort der ';'-Kommentar-Pfad greift und der Fallback
560-
# nicht erreicht wird.
579+
# Single-IP einfuegt. Vorher (vor dem Fix): Eine Zeile wie
580+
# '{"net":"5.5.5.0/24"}' erzeugte BEIDE Eintraege - die CIDR und ihre
581+
# Netzadresse als IP. Symptom: redundante Phantom-IPs in den
582+
# Output-Listen, je nach Feed-Format (URLhaus / JSON / Fliesstext).
561583
cidr_spans = []
562584
for cm in CIDR_RE.finditer(line):
563585
cidr_str = cm.group(1)
@@ -577,6 +599,26 @@ def parse_entries(text, use_protected_check=False):
577599
return entries
578600

579601

602+
def parse_entries_for_blacklist(text):
603+
"""Pipeline-Modus-Wrapper um parse_entries(use_protected_check=True).
604+
605+
Diese Funktion ist die EMPFOHLENE API fuer alle Workflows die einen
606+
Feed-Inhalt in eine produzierte Blacklist umsetzen wollen. Sie macht
607+
den Whitelist-Filter explizit (kein vergessener default-Argument-Bug
608+
der Form BUG-WL1/WL3/WL7) und verlangt implizit, dass load_whitelist()
609+
vorher gelaufen ist (sonst WhitelistNotLoadedError - Fail-Closed).
610+
611+
Args:
612+
text: Rohtext des Feeds.
613+
614+
Returns:
615+
set[str]: Gefiltertes Set von IPs und CIDRs - ohne Whitelist,
616+
ohne RFC1918/Reserved/Loopback, bereit zum Schreiben in eine
617+
Blacklist-Datei.
618+
"""
619+
return parse_entries(text, use_protected_check=True)
620+
621+
580622
# ═══════════════════════════════════════════════════════════════
581623
# Scoring-Modell
582624
# ═══════════════════════════════════════════════════════════════

0 commit comments

Comments
 (0)