@@ -1376,3 +1376,61 @@ def write_text_atomic(filepath, content):
13761376 except OSError :
13771377 pass
13781378 raise
1379+
1380+ # ═══════════════════════════════════════════════════════════════
1381+ # Auto-Discovered Feeds: Schema- und URL-Validierung
1382+ # ═══════════════════════════════════════════════════════════════
1383+
1384+ def validate_auto_feeds (auto_data ):
1385+ """Filtert auto_discovered_feeds.json-Eintraege auf safe Schema + URL.
1386+
1387+ Hintergrund (FIX BUG-AUTOFEEDS-VALIDATE): Vorher las
1388+ update_combined_blacklist die Datei direkt mit
1389+ ``auto_data.get("feeds", [])`` ohne Schema-/URL-Pruefung. Risiko:
1390+ Ein Angreifer mit Repo-Schreibrechten konnte malicious URLs in
1391+ den Feed-Loop einschmuggeln, ohne den Code-Review-Pfad ueber
1392+ SOURCES zu durchlaufen. fetch_url's SSRF-Schutz blockt zwar
1393+ localhost/RFC1918, aber externe Angreifer-URLs mit boeswilligen
1394+ IP-Listen waeren durch.
1395+
1396+ Akzeptiert:
1397+ - dict-Root mit "feeds"-Liste
1398+ - pro Eintrag: dict mit string-keys "name" und "url"
1399+ - URL muss http:// oder https:// sein
1400+
1401+ Args:
1402+ auto_data: Geparstes JSON aus auto_discovered_feeds.json.
1403+
1404+ Returns:
1405+ tuple[list[dict], int]: (akzeptierte_feeds, anzahl_verworfen)
1406+
1407+ Raises:
1408+ ValueError: Wenn Root nicht dict oder feeds nicht list ist
1409+ (= grundsaetzlich kaputtes Schema, kein partieller
1410+ Restore moeglich).
1411+ """
1412+ if not isinstance (auto_data , dict ):
1413+ raise ValueError (
1414+ f"auto_discovered_feeds Root ist { type (auto_data ).__name__ } , "
1415+ f"erwartet dict" )
1416+ raw_feeds = auto_data .get ("feeds" , [])
1417+ if not isinstance (raw_feeds , list ):
1418+ raise ValueError (
1419+ f"auto_discovered_feeds 'feeds' ist { type (raw_feeds ).__name__ } , "
1420+ f"erwartet list" )
1421+ accepted = []
1422+ rejected = 0
1423+ for feed in raw_feeds :
1424+ if not isinstance (feed , dict ):
1425+ rejected += 1
1426+ continue
1427+ name = feed .get ("name" )
1428+ url = feed .get ("url" )
1429+ if not isinstance (name , str ) or not isinstance (url , str ):
1430+ rejected += 1
1431+ continue
1432+ if not (url .startswith ("https://" ) or url .startswith ("http://" )):
1433+ rejected += 1
1434+ continue
1435+ accepted .append ({"name" : name , "url" : url })
1436+ return accepted , rejected
0 commit comments