From 0440b72cc2be771f357e3e378b70e6f6242d6f7e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 13 Jun 2026 17:40:15 +0100 Subject: [PATCH 1/2] Create vuln_search.py --- tools/vuln_search.py | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tools/vuln_search.py diff --git a/tools/vuln_search.py b/tools/vuln_search.py new file mode 100644 index 0000000..199c779 --- /dev/null +++ b/tools/vuln_search.py @@ -0,0 +1,53 @@ +"""Script to search for repos affected by an undisclosed vulnerability. + +1. Update TOKEN, SEARCH_TERM and REPO. +2. Import and run the save_dependents() function if this has not been done before (or within the last year). +3. Execute the script. +4. Use MIN_STARS/OFFSET to adjust how many dependents to search and resume from an interrupted search. +""" + +import json +from itertools import takewhile +from pathlib import Path +from time import sleep + +import requests +from github_dependents_info.gh_dependents_info import GithubDependentsInfo + +# https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api#authenticating-with-a-personal-access-token +TOKEN = "github_pat_XXX" +SEARCH_TERM = "" # What to use in code search. +REPO = "aiohttp" +MIN_STARS = 100 # Only search for repos with atleast this many stars. +OFFSET = 0 # To resume searching from an interrupted session. + +ORG = "aio-libs" +RATE_LIMIT = 60 / 9 # Docs say 10 per minute, but only 9 seem to be allowed. +DEPENDENTS_PATH = Path(f"{REPO}_dependents.json") + +def save_dependents(): + """Import and run this function to save the dependents, this may take ~6 hours for a project with 500k dependents.""" + gh_deps_info = GithubDependentsInfo(f"{ORG}/{REPO}", sort_key="stars", min_stars=0, json_output=True) + dependents = gh_deps_info.collect() + with DEPENDENTS_PATH.open("w") as f: + json.dump(dependents, f, indent=4) + +if __name__ == "__main__": + with DEPENDENTS_PATH.open() as f: + dependents = json.load(f) + + url = "https://api.github.com/search/code" + headers = {"Accept": "application/vnd.github+json", "Authorization": f"Bearer {TOKEN}"} + repos = takewhile(lambda r: r["stars"] >= MIN_STARS, dependents["all_public_dependent_repos"][OFFSET:]) + for i, repo in enumerate(repos, OFFSET): + sleep(RATE_LIMIT) + response = requests.get(url, headers=headers, params={"q": f"{SEARCH_TERM} repo:{repo['name']}"}) + response.raise_for_status() + + result = response.json() + for r in result["items"]: + print(r["repository"]["full_name"], r["path"]) + print(" ", r["html_url"]) + + if i % 100 == 0: + print("COUNT", i, "({} stars)".format(repo["stars"])) From f109e482974d315be239021ae4c75265a0ec0e3e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 13 Jun 2026 16:43:06 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tools/vuln_search.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tools/vuln_search.py b/tools/vuln_search.py index 199c779..2d88d61 100644 --- a/tools/vuln_search.py +++ b/tools/vuln_search.py @@ -25,23 +25,35 @@ RATE_LIMIT = 60 / 9 # Docs say 10 per minute, but only 9 seem to be allowed. DEPENDENTS_PATH = Path(f"{REPO}_dependents.json") + def save_dependents(): """Import and run this function to save the dependents, this may take ~6 hours for a project with 500k dependents.""" - gh_deps_info = GithubDependentsInfo(f"{ORG}/{REPO}", sort_key="stars", min_stars=0, json_output=True) + gh_deps_info = GithubDependentsInfo( + f"{ORG}/{REPO}", sort_key="stars", min_stars=0, json_output=True + ) dependents = gh_deps_info.collect() with DEPENDENTS_PATH.open("w") as f: json.dump(dependents, f, indent=4) + if __name__ == "__main__": with DEPENDENTS_PATH.open() as f: dependents = json.load(f) url = "https://api.github.com/search/code" - headers = {"Accept": "application/vnd.github+json", "Authorization": f"Bearer {TOKEN}"} - repos = takewhile(lambda r: r["stars"] >= MIN_STARS, dependents["all_public_dependent_repos"][OFFSET:]) + headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {TOKEN}", + } + repos = takewhile( + lambda r: r["stars"] >= MIN_STARS, + dependents["all_public_dependent_repos"][OFFSET:], + ) for i, repo in enumerate(repos, OFFSET): sleep(RATE_LIMIT) - response = requests.get(url, headers=headers, params={"q": f"{SEARCH_TERM} repo:{repo['name']}"}) + response = requests.get( + url, headers=headers, params={"q": f"{SEARCH_TERM} repo:{repo['name']}"} + ) response.raise_for_status() result = response.json()