diff --git a/.github/getTrending.py b/.github/getTrending.py index 944b617e32..c363be6a4d 100644 --- a/.github/getTrending.py +++ b/.github/getTrending.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 """Regenerate the Trending PoCs tables in README.md. -- Consider the latest 4 years (current year and previous 3). -- Require repository name to contain a CVE for that year (e.g., CVE-2025-1234). -- Require a non-empty description (we only want actual PoCs, not empty shells). -- Restrict to repositories updated in the last 4 days. -- Sort by most recently updated, then stars, and emit up to 20 rows per year. +Goals (matching the legacy README that worked well): +- Cover the current year plus the previous three. +- Keep the familiar heading “Latest 20 of N Repositories”. +- Only show repos updated in the last WINDOW_DAYS. +- Require a CVE-shaped repo name for that year and a non-empty description. +- Sort newest first, then by stars, and cap at MAX_ROWS per year. """ from __future__ import annotations @@ -21,6 +22,7 @@ import requests WINDOW_DAYS = 4 MAX_ROWS = 20 YEARS_BACK = 4 +MIN_STARS = 0 # keep low to capture fresh repos class Repo(TypedDict): @@ -53,34 +55,69 @@ def time_ago(updated_at: str, now: datetime) -> str: return "just now" -def fetch_trending(year: int, cutoff: datetime) -> List[Repo]: - query = f"CVE-{year} in:name stars:>2 pushed:>={cutoff.date().isoformat()} archived:false" +def _search_total(year: int) -> int: + """Return total repositories matching CVE-year (used for table heading).""" + stars_clause = f"stars:>{MIN_STARS}" if MIN_STARS >= 0 else "stars:>0" + query = f"CVE-{year} in:name {stars_clause} archived:false" url = "https://api.github.com/search/repositories" - params = { - "q": query, - "sort": "updated", - "order": "desc", - "per_page": 100, - "page": 1, - } - resp = requests.get(url, params=params, headers=github_headers(), timeout=30) + resp = requests.get( + url, params={"q": query, "per_page": 1}, headers=github_headers(), timeout=30 + ) resp.raise_for_status() - items: Iterable[Repo] = resp.json().get("items", []) + return int(resp.json().get("total_count", 0)) + + +def fetch_trending(year: int, cutoff: datetime) -> tuple[List[Repo], int]: + """Fetch and filter trending repos for a year, returning rows and total_count.""" + stars_clause = f"stars:>{MIN_STARS}" if MIN_STARS >= 0 else "stars:>0" + query = f"CVE-{year} in:name {stars_clause} archived:false pushed:>={cutoff.date().isoformat()}" + url = "https://api.github.com/search/repositories" + total_count = _search_total(year) pattern = re.compile(rf"cve-{year}-\d+", re.IGNORECASE) filtered: List[Repo] = [] - for item in items: - name = item.get("name", "") - updated_at = item.get("updated_at") - description = (item.get("description") or "").strip() - if not updated_at or not pattern.search(name or "") or not description: - continue - updated_dt = datetime.strptime(updated_at, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc) - if updated_dt < cutoff: - continue - filtered.append(item) + seen_urls: set[str] = set() + + # Walk multiple pages to gather enough fresh repos (up to MAX_ROWS). + for page in range(1, 2): + params = { + "q": query, + "sort": "updated", + "order": "desc", + "per_page": 100, + "page": page, + } + resp = requests.get(url, params=params, headers=github_headers(), timeout=30) + resp.raise_for_status() + items: Iterable[Repo] = resp.json().get("items", []) + if not items: + break + for item in items: + name = item.get("name", "") + updated_at = item.get("updated_at") + description = (item.get("description") or "").strip() + html_url = item.get("html_url") + if not updated_at or not html_url or not description: + continue + if not pattern.search(name or ""): + continue + updated_dt = datetime.strptime(updated_at, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc) + if updated_dt < cutoff: + continue + if html_url in seen_urls: + continue + seen_urls.add(html_url) + filtered.append(item) + if len(filtered) >= MAX_ROWS: + break + # Already sorted by updated desc; break ties by stars - filtered.sort(key=lambda r: (-datetime.strptime(r["updated_at"], "%Y-%m-%dT%H:%M:%SZ").timestamp(), -int(r.get("stargazers_count", 0)))) - return filtered[:MAX_ROWS] + filtered.sort( + key=lambda r: ( + -datetime.strptime(r["updated_at"], "%Y-%m-%dT%H:%M:%SZ").timestamp(), + -int(r.get("stargazers_count", 0)), + ) + ) + return filtered[:MAX_ROWS], total_count def build_rows(repos: List[Repo], now: datetime) -> List[str]: @@ -94,16 +131,16 @@ def build_rows(repos: List[Repo], now: datetime) -> List[str]: def main() -> None: - current_year = datetime.now(timezone.utc).year - cutoff = datetime.now(timezone.utc) - timedelta(days=WINDOW_DAYS) now = datetime.now(timezone.utc) + current_year = now.year + cutoff = now - timedelta(days=WINDOW_DAYS) output: List[str] = ['
| Stars | Updated | Name | Description | ||||
|---|---|---|---|---|---|---|---|
| 1 | +55 minutes ago | +CVE-2025-61882-CVE-2025-61884 | +🔍 Detect vulnerabilities CVE-2025-61882 and CVE-2025-61884 in Oracle E-Business Suite to help secure your systems from potential remote code execution threats. | +||||
| 1 | +1 hour ago | +CVE-2025-54253-Exploit-Demo | +🐙 CVE-2025-54253 exploit demo for Adobe AEM Forms on JEE: OGNL injection to RCE with PoC, Python 3.10 exploit code, reproducer and mitigation guidance. | +||||
| 1 | +1 hour ago | +CVE-2025-54424 | +CVE-2025-54424: 1Panel TLS client cert bypass enables RCE via forged CN 'panel_client' using a bundled scanning and exploitation tool. Affected: <= v2.0.5. 🔐 | +||||
| 360 | 2 hours ago | Next.js-RSC-RCE-Scanner-CVE-2025-66478 | A command-line scanner for batch detection of Next.js application versions and determining if they are affected by CVE-2025-66478 vulnerability. | ||||
| 1 | +2 hours ago | +CVE-2025-13780 | +A comprehensive vulnerability scanner for CVE-2025-13780, a Remote Code Execution (RCE) vulnerability in pgAdmin 4 versions ≤ 8.14. | +||||
| 2 | +10 hours ago | +CVE-2025-6218-WinRAR-RCE-POC | +Comprehensive analysis and proof-of-concept for CVE-2025-6218 - WinRAR path traversal RCE vulnerability affecting versions 7.11 and earlier | +||||
| 1 | +11 hours ago | +CVE-2025-55182-React2Shell-Exploit | +A proof-of-concept tool for demonstrating the critical React2Shell vulnerability | +||||
| 4 | 13 hours ago | @@ -99,10 +135,52 @@CVE-2025-54100 (CVSS 7.8 High) is a command injection vulnerability in the Invoke-WebRequest cmdlet of Windows PowerShell 5.1. It arises from improper neutralization of special elements during the automatic parsing of Web responses. | |||||
| 5 | -3 days ago | -CVE-2025-55182-golang-PoC | -CVE-2025-55182 React Server Components RCE - Go PoC | +2 | +1 day ago | +CVE-2025-31702 | +Repository with tools, exploits, and material associated with the analysis and discovery process of CVE-2025-31702 and other related security issues. | +
| 1 | +1 day ago | +CVE-2025-55182 | +React2Shell Vulnerability | +||||
| 1 | +1 day ago | +Blackash-CVE-2025-13780 | +CVE-2025-13780 | +||||
| 2 | +2 days ago | +CVE-2025-55182 | +This project provides a fully functional demonstration of CVE-2025-55182 (React2Shell) - a critical Remote Code Execution vulnerability in React Server Components and Next.js. | +||||
| 1 | +2 days ago | +react2shell-scanner-CVE-2025-55182 | +React2shell-web-scanner | +||||
| 1 | +2 days ago | +CVE-2025-55182-Waf | +CVE-2025-55182 RCE vulnerability in Next.js/React RSC servers (exploit and scanner) | +||||
| 1 | +2 days ago | +CVE-2025-55182 | +A command-line tool for detecting CVE-2025-55182 and CVE-2025-66478 in Next.js applications using React Server Components. | +||||
| 1 | +2 days ago | +CVE-2025-9074_DAEMON_KILLER | +The Ultimate DAEMON_KILLER. Control is an illusion. This Exploit forces CVE-2025-9074 to break the Docker cage. Advanced Container Escape & Root Escalation toolkit. Verify the vulnerability, take the host, destroy the logs. > We Are Fsociety_ |