abuse: классификатор ловит панели/github/hn и мульти-теги, реальный total из индекса, кап API поднят до 3000
Some checks failed
ci / test (3.10) (push) Has been cancelled
ci / test (3.11) (push) Has been cancelled
ci / test (3.12) (push) Has been cancelled

This commit is contained in:
Vlad Gothbreach
2026-10-27 01:28:28 +03:00
parent 61f15f88f2
commit 8de421b6af
13 changed files with 3082 additions and 93 deletions

View File

@@ -141,24 +141,27 @@ def _qualifies(source, title, text, tags):
sc -= 26
if not hits:
return 0, []
raw_key = bool(re.search(r"(sk-[A-Za-z0-9_\-]{20,}|gh[pousr]_[A-Za-z0-9]{20,})", blob))
if NOISE.search(source or "") and not raw_key:
raw_key = bool(re.search(r"\b(sk-[A-Za-z0-9_\-]{20,}|gh[pousr]_[A-Za-z0-9]{20,})", blob))
src = str(source or "")
known_prefix = src.startswith(("ab_", "tg_", "rad_", "reddit_", "discourse_", "xenforo", "pn_", "gh_", "hn_"))
if NOISE.search(src) and not (raw_key or known_prefix):
return 0, []
if cat is None:
# старые записи хранят категорию вместо имени источника
if not raw_key and not str(source or "").startswith(("ab_", "tg_", "rad_", "reddit_", "discourse_", "xenforo")):
return 0, []
elif cat not in ABUSE_CATS and not raw_key:
if cat is None and not raw_key and not known_prefix:
return 0, []
if cat == "abuse" and sc >= ABUSE_SRC_SC:
if cat is not None and cat not in ABUSE_CATS and not (raw_key or known_prefix):
return 0, []
# панели new-api, github/hn и abuse-источники считаем абузными по факту источника
if cat == "abuse" or known_prefix:
if sc >= ABUSE_SRC_SC or strong or len(hits) >= 2:
return sc or 1, hits
if strong and sc >= 16:
return sc, hits
if strong and sc >= 24:
if len(hits) >= 2 and sc >= 30:
return sc, hits
if len(hits) >= 2 and sc >= 40:
if len(hits) >= 3 and sc >= 20:
return sc, hits
return 0, []
def _connect():
con = sqlite3.connect(str(DB), timeout=60)
con.execute("PRAGMA busy_timeout=60000")
@@ -248,5 +251,26 @@ def scan(limit=120, tag=None, q=None, since_days=None, window=6000):
for it in items:
for t in it["tags"]:
counts[t] = counts.get(t, 0) + 1
return {"items": items[:limit], "total": len(items), "tags": counts,
# total = реальный размер выдачи в индексе, не размер окна чтения
total = len(items)
try:
con = _connect()
sql = "SELECT COUNT(*) FROM abuse_idx a JOIN items i ON i.id = a.id"
where, args = [], []
if tag:
where.append("(',' || a.tags || ',') LIKE ?")
args.append("%," + tag + ",%")
if q:
where.append("(i.title LIKE ? OR i.text LIKE ? OR i.source LIKE ?)")
args += ["%%%s%%" % q, "%%%s%%" % q, "%%%s%%" % q]
if since_days:
where.append("i.published >= date('now', ?)")
args.append("-%d day" % int(since_days))
if where:
sql += " WHERE " + " AND ".join(where)
total = con.execute(sql, args).fetchone()[0] or total
con.close()
except Exception:
pass
return {"items": items[:limit], "total": total, "tags": counts,
"labels": TAG_LABEL}

View File

@@ -30,9 +30,17 @@ def _clean(v, n=0):
# ---------- обзор ----------
def timeline(days=30, cat=None):
"""Записей по дням + отдельно уязвимости и сливы. Пустые дни заполняются нулями."""
BUCKET_SEEN = "date(seen,'unixepoch')"
def timeline(days=30, cat=None, mode="pub"):
"""Записей по дням + отдельно уязвимости и сливы. Пустые дни заполняются нулями.
mode="pub" — по дате публикации (или дате попадания в базу, если публикации нет);
mode="seen" — строго по времени импорта: «что комбайн принёс за день».
"""
days = max(1, min(365, int(days or 30)))
bucket = BUCKET_SEEN if mode == "seen" else BUCKET
names = [s.name for s in sources.all_sources() if s.category == cat] if cat not in (None, "", "all") else None
where, args = "", []
if names is not None:
@@ -40,7 +48,7 @@ def timeline(days=30, cat=None):
args = list(names)
sql = ("SELECT b, COUNT(*), SUM(CASE WHEN types LIKE '%,cve,%' THEN 1 ELSE 0 END),"
" SUM(CASE WHEN types LIKE '%,leak,%' OR types LIKE '%,cve,%' THEN 1 ELSE 0 END)"
" FROM (SELECT " + BUCKET + " AS b, types FROM items" + where + ") GROUP BY b ORDER BY b")
" FROM (SELECT " + bucket + " AS b, types FROM items" + where + ") GROUP BY b ORDER BY b")
got = {}
with store.db() as c:
for b, n, cve, sev in c.execute(sql, args):
@@ -52,8 +60,9 @@ def timeline(days=30, cat=None):
d = (today - _dt.timedelta(days=i)).isoformat()
n, cve, sev = got.get(d, (0, 0, 0))
out.append({"day": d, "n": n, "cve": cve, "sev": sev})
return {"ok": True, "days": days, "points": out,
return {"ok": True, "days": days, "mode": mode, "points": out,
"total": sum(p["n"] for p in out),
"last24": (out[-1] or {}).get("n", 0),
"max": max([p["n"] for p in out] or [0])}

View File

@@ -287,9 +287,9 @@ class Handler(BaseHTTPRequestHandler):
if tag in ("all", "все"):
tag = None
try:
lim = _intv(q, "limit", 40, 1, 200)
lim = _intv(q, "limit", 120, 1, 3000)
except ValueError:
lim = 40
lim = 120
return self._json(_ab.scan(limit=lim, tag=tag))
if path == "/api/summary":
@@ -403,7 +403,8 @@ class Handler(BaseHTTPRequestHandler):
if path == "/api/timeline":
days = _intv(q, "days", 30, 1, 365)
cat = (q.get("category") or [None])[0]
return self._json(panel_api.timeline(days=days, cat=cat))
mode = (q.get("mode") or ["pub"])[0]
return self._json(panel_api.timeline(days=days, cat=cat, mode=mode if mode in ("pub", "seen") else "pub"))
if path == "/api/suggest":
return self._json(panel_api.suggest((q.get("q") or [""])[0], _intv(q, "limit", 8, 1, 20)))

80
omni/sources_fresh.py Normal file
View File

@@ -0,0 +1,80 @@
"""Свежая волна источников omni: security/CVE/leaks/CN/RU/tg.
Сгенерировано tools/probe_fresh.py по факту живого fetch (n>0).
Отклонённые кандидаты и причины — в хвостовом комментарии.
Импортов из omni.sources нет намеренно (циклический импорт).
"""
def fresh(S):
"""S — конструктор Source; возвращает только проверенные сетью источники."""
return [
S("darkwebinformer", "abuse", "rss", "https://darkwebinformer.com/feed", "rss", ("leak", "news")),
S("databreaches_net", "abuse", "rss", "https://databreaches.net/feed/", "rss", ("breach", "news")),
S("hackread_breaches", "abuse", "rss", "https://hackread.com/category/data-breaches/feed/", "rss", ("breach")),
S("redpacketsecurity", "abuse", "rss", "https://www.redpacketsecurity.com/feed/", "rss", ("cve", "leak")),
S("tg_vulns", "abuse", "html", "https://t.me/s/vulns", "tg_web", ("tg", "leak", "ru")),
S("habr_ai", "community", "rss", "https://habr.com/ru/rss/hub/artificial_intelligence/", "rss", ("ru", "ai")),
S("infosec_writeups", "community", "rss", "https://infosecwriteups.com/feed", "rss", ("writeup")),
S("tg_ai_machinelearning", "community", "html", "https://t.me/s/ai_machinelearning_big_data", "tg_web", ("tg", "ai", "ru")),
S("tg_denissexy", "community", "html", "https://t.me/s/denissexy", "tg_web", ("tg", "ai", "ru")),
S("reddit_blueteamsec", "forum", "json", "https://www.reddit.com/r/blueteamsec/.json?limit=25", "reddit_listing", ("reddit", "blue-team")),
S("reddit_exploitdev", "forum", "json", "https://www.reddit.com/r/ExploitDev/.json?limit=25", "reddit_listing", ("reddit", "exploit")),
S("reddit_purpleteamsec", "forum", "json", "https://www.reddit.com/r/purpleteamsec/.json?limit=25", "reddit_listing", ("reddit", "purple-team")),
S("t00ls", "forum", "html", "https://www.t00ls.com/", "html_listing", ("cn", "forum")),
S("acunetix_blog", "security", "rss", "https://www.acunetix.com/blog/feed/", "rss", ("appsec")),
S("anquanke_news", "security", "html", "https://www.anquanke.com/news", "html_listing", ("cn", "news")),
S("arch_linux_asa", "security", "rss", "https://security.archlinux.org/advisory/feed.atom", "rss", ("cve", "linux")),
S("arcticwolf_blog", "security", "rss", "https://arcticwolf.com/feed/", "rss", ("cti")),
S("arstechnica_security", "security", "rss", "https://arstechnica.com/security/feed/", "rss", ("news")),
S("bitdefender_labs", "security", "rss", "https://www.bitdefender.com/blog/api/rss/labs/", "rss", ("research")),
S("chrome_releases_security", "security", "rss", "https://chromereleases.googleblog.com/feeds/posts/default", "rss", ("cve", "browser")),
S("cisco_security_blog", "security", "rss", "https://blogs.cisco.com/security/feed", "rss", ("cti", "research")),
S("citizenlab", "security", "rss", "https://citizenlab.ca/feed/", "rss", ("apt", "research")),
S("csoonline", "security", "rss", "https://www.csoonline.com/feed/", "rss", ("news")),
S("cybersecuritydive", "security", "rss", "https://www.cybersecuritydive.com/feeds/news/", "rss", ("news")),
S("elastic_security_labs", "security", "rss", "https://www.elastic.co/security-labs/rss/feed", "rss", ("research")),
S("github_advisories_api", "security", "json", "https://api.github.com/advisories?per_page=30&sort=published&direction=desc", "ghsa", ("cve", "ghsa")),
S("habr_infosecurity", "security", "rss", "https://habr.com/ru/rss/hub/infosecurity/", "rss", ("ru", "research")),
S("habr_news", "security", "rss", "https://habr.com/ru/rss/news/", "rss", ("ru", "news")),
S("hackmag", "security", "rss", "https://hackmag.com/rss", "rss", ("ru", "research")),
S("helpnetsecurity", "security", "rss", "https://www.helpnetsecurity.com/feed/", "rss", ("news")),
S("industrialcyber", "security", "rss", "https://industrialcyber.co/feed/", "rss", ("ics", "news")),
S("infosecurity_magazine", "security", "rss", "https://www.infosecurity-magazine.com/rss/news/", "rss", ("news")),
S("malwaretech_blog", "security", "rss", "https://www.malwaretech.com/feed", "rss", ("research")),
S("microsoft_security_blog", "security", "rss", "https://www.microsoft.com/en-us/security/blog/feed/", "rss", ("research")),
S("mozilla_advisories", "security", "html", "https://www.mozilla.org/en-US/security/advisories/", "html_listing", ("cve", "browser")),
S("ncsc_uk", "security", "rss", "https://www.ncsc.gov.uk/api/1/services/v1/all-rss-feed.xml", "rss", ("gov", "cve")),
S("paloalto_blog", "security", "rss", "https://www.paloaltonetworks.com/blog/feed/", "rss", ("research")),
S("seclists_fulldisclosure", "security", "rss", "https://seclists.org/rss/fulldisclosure.rss", "rss", ("mailing", "exploit")),
S("seclists_nmap", "security", "rss", "https://seclists.org/rss/nmap-dev.rss", "rss", ("mailing")),
S("secrss", "security", "html", "https://www.secrss.com/", "html_listing", ("cn", "news")),
S("sekoia_blog", "security", "rss", "https://blog.sekoia.io/feed/", "rss", ("research")),
S("sentinelone_blog", "security", "rss", "https://www.sentinelone.com/blog/feed/", "rss", ("research")),
S("sihaoyou_feed", "security", "rss", "https://www.4hou.com/feed", "rss", ("cn", "research")),
S("socradar_blog", "security", "rss", "https://socradar.io/feed/", "rss", ("cti")),
S("sonatype_blog", "security", "rss", "https://www.sonatype.com/blog/rss.xml", "rss", ("supply-chain")),
S("sploitus_rss", "security", "rss", "https://sploitus.com/rss", "rss", ("exploit")),
S("tg_bugbounty", "security", "html", "https://t.me/s/bugbounty", "tg_web", ("tg", "bugbounty")),
S("tg_hackernews_ru", "security", "html", "https://t.me/s/hackernews_ru", "tg_web", ("tg", "ru")),
S("tg_kalilinux", "security", "html", "https://t.me/s/kalilinux", "tg_web", ("tg", "tools")),
S("theregister_security", "security", "rss", "https://www.theregister.com/security/headlines.atom", "rss", ("news")),
S("ubuntu_usn", "security", "rss", "https://ubuntu.com/security/notices/rss.xml", "rss", ("cve", "linux")),
S("virustotal_blog", "security", "rss", "https://blog.virustotal.com/feeds/posts/default", "rss", ("malware")),
S("vuldb_recent", "security", "rss", "https://vuldb.com/?rss.recent", "rss", ("cve")),
S("xakep_ru", "security", "rss", "https://xakep.ru/feed/", "rss", ("ru", "news")),
]
# --- отклонено прогоном (не отдают записи без ключа/логина) ---
# darknetlive https://darknetlive.com/feed/ http 200, 127811b
# ransomlook_recent https://www.ransomlook.io/api/recent http 0, err:ReadTimeout: The read operation timed out, 0b
# ransomware_live_recent https://api.ransomware.live/recentvictims http 200, 76549b
# codeby_forum https://codeby.net/forums/ http 0, err:ConnectTimeout: timed out, 0b
# anti_malware_ru https://www.anti-malware.ru/rss.xml http 0, err:ConnectTimeout: timed out, 0b
# bdu_fstec https://bdu.fstec.ru/rss/vulxml http 0, err:ConnectTimeout: timed out, 0b
# cnews_security https://www.cnews.ru/inc/rss/news.xml http 0, err:ConnectTimeout: timed out, 0b
# freebuf_feed https://www.freebuf.com/feed http 405, 2569b
# kaspersky_ru_blog https://www.kaspersky.ru/blog/feed/ http 0, err:ConnectTimeout: timed out, 0b
# securitylab_rss https://www.securitylab.ru/rss/all.xml http 404, 17877b
# seebug_paper https://paper.seebug.org/rss/ http 0, err:ConnectTimeout: _ssl.c:989: The handshake operation timed out, 0b