@@ -234,6 +234,95 @@ def test_deduplication(self):
234234 self .assertEqual (result , {"1.2.3.4" })
235235 self .assertEqual (len (result ), 1 )
236236
237+ def test_multi_entry_per_line_public_cidr_and_ip (self ):
238+ """FIX BUG-MULTI-ENTRY: Eine Zeile mit CIDR und IP per Whitespace
239+ getrennt darf nicht nach dem ersten Match abbrechen. Vorher hatte
240+ der Fast-Path-Block ein 'continue', sodass die hintere IP silent
241+ verloren ging.
242+
243+ Heute betrifft das keinen der konsumierten Feeds (alle 1
244+ Eintrag/Zeile, ueber 986k Zeilen verifiziert), aber
245+ auto_feed_discovery nimmt automatisch neue Feeds auf - waere
246+ ein latenter Datenleck-Vektor.
247+ """
248+ result = parse_entries ("5.5.5.0/24 6.6.6.6" )
249+ self .assertEqual (result , {"5.5.5.0/24" , "6.6.6.6" })
250+
251+ def test_multi_entry_private_cidr_with_public_ip (self ):
252+ """FIX BUG-MULTI-ENTRY: Privat-CIDR + oeffentliche IP auf einer
253+ Zeile. Der Privat-CIDR wird abgelehnt, aber die oeffentliche IP
254+ muss erhalten bleiben - Vorher: beide gingen verloren weil der
255+ Fast-Path nach dem CIDR-Match (auch bei Reject) 'continue' machte."""
256+ result = parse_entries ("10.20.30.0/24 5.5.5.5" )
257+ self .assertEqual (result , {"5.5.5.5" })
258+
259+ def test_multi_entry_two_public_ips (self ):
260+ """FIX BUG-MULTI-ENTRY: Zwei oeffentliche IPs per Whitespace getrennt."""
261+ result = parse_entries ("1.2.3.4 5.6.7.8" )
262+ self .assertEqual (result , {"1.2.3.4" , "5.6.7.8" })
263+
264+ def test_multi_entry_three_cidrs (self ):
265+ """FIX BUG-MULTI-ENTRY: Mehrere CIDRs per Whitespace, alle public."""
266+ result = parse_entries ("5.5.5.0/24 6.6.6.0/24 7.7.7.0/24" )
267+ self .assertEqual (result , {"5.5.5.0/24" , "6.6.6.0/24" , "7.7.7.0/24" })
268+
269+ def test_no_phantom_ip_from_cidr_network_address (self ):
270+ """Regression: BUG-PARSE-DUAL muss nach BUG-MULTI-ENTRY-Fix
271+ weiterhin verhindern, dass die Netzadresse einer CIDR (z.B.
272+ '5.5.5.0' aus '5.5.5.0/24') zusaetzlich als Plain-IP eingelegt
273+ wird. Der cidr_spans-Schutz im Fallback bleibt aktiv."""
274+ # CIDR alleine
275+ self .assertEqual (parse_entries ("5.5.5.0/24" ), {"5.5.5.0/24" })
276+ # CIDR in JSON
277+ self .assertEqual (parse_entries ('{"net":"5.5.5.0/24"}' ), {"5.5.5.0/24" })
278+ # CIDR mit Inline-Kommentar (Spamhaus-DROP)
279+ self .assertEqual (parse_entries ("5.5.5.0/24 ; SBL12345" ), {"5.5.5.0/24" })
280+ # CIDR-only mit Whitespace davor (auto-discovery sieht das oft so)
281+ self .assertEqual (parse_entries (" 5.5.5.0/24" ), {"5.5.5.0/24" })
282+
283+
284+ class TestParseEntriesForBlacklist (unittest .TestCase ):
285+ """FIX API-CLARITY: parse_entries_for_blacklist ist der empfohlene
286+ Wrapper fuer den Pipeline-Modus mit Whitelist-Filter. Macht den
287+ BUG-WL1/WL3/WL7-Vermeidungspfad explizit und Fail-Closed."""
288+
289+ def setUp (self ):
290+ # Pipeline-Modus braucht eine geladene Whitelist (sonst raises).
291+ netshield_common ._reset_whitelist_for_testing ()
292+ netshield_common .load_whitelist (min_entries = 5 )
293+
294+ def tearDown (self ):
295+ netshield_common ._reset_whitelist_for_testing ()
296+
297+ def test_filters_whitelist_ip (self ):
298+ """Eine Whitelist-IP (z.B. 1.0.0.1 aus whitelist.json) muss
299+ herausgefiltert werden - das ist der Sinn des Wrappers."""
300+ from netshield_common import parse_entries_for_blacklist
301+ # Mische Whitelist-IPs mit "normalen" Threat-IPs
302+ result = parse_entries_for_blacklist ("1.0.0.1\n 5.5.5.5\n 8.8.8.8\n 11.22.33.44" )
303+ # 1.0.0.1 und 8.8.8.8 sind in der Whitelist -> raus
304+ self .assertNotIn ("1.0.0.1" , result )
305+ self .assertNotIn ("8.8.8.8" , result )
306+ # 5.5.5.5 und 11.22.33.44 sind nicht in der Whitelist -> drin
307+ self .assertIn ("5.5.5.5" , result )
308+ self .assertIn ("11.22.33.44" , result )
309+
310+ def test_filters_private_ip (self ):
311+ """RFC1918/Reserved werden weiterhin gefiltert - der Wrapper
312+ ist ein Superset von is_valid_public_ipv4."""
313+ from netshield_common import parse_entries_for_blacklist
314+ result = parse_entries_for_blacklist ("192.168.1.1\n 5.5.5.5" )
315+ self .assertEqual (result , {"5.5.5.5" })
316+
317+ def test_equivalence_to_parse_entries_with_flag (self ):
318+ """Wrapper muss exakt dasselbe liefern wie parse_entries(use_protected_check=True)."""
319+ from netshield_common import parse_entries_for_blacklist
320+ text = "1.0.0.1\n 5.5.5.5\n 10.0.0.1\n 8.8.8.8\n 11.22.33.44\n 5.5.5.0/24"
321+ self .assertEqual (
322+ parse_entries_for_blacklist (text ),
323+ parse_entries (text , use_protected_check = True ),
324+ )
325+
237326
238327# ═══════════════════════════════════════════════════════════════
239328# IP-Validierung
0 commit comments