Skip to content

Commit 9b37c73

Browse files
committed
Use web scraping to obtain github tags
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent 03f453c commit 9b37c73

1 file changed

Lines changed: 44 additions & 8 deletions

File tree

vulnerabilities/package_managers.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import asyncio
2424
from collections import namedtuple
25+
import aiohttp
26+
from bs4 import BeautifulSoup
2527
from dateutil import parser
2628
from json import JSONDecodeError
2729
from typing import Mapping
@@ -32,6 +34,7 @@
3234
from aiohttp import ClientSession
3335
from aiohttp.client_exceptions import ClientResponseError
3436
from aiohttp.client_exceptions import ServerDisconnectedError
37+
from requests.sessions import session
3538

3639

3740
Version = namedtuple("Version", field_names=["value", "release_date"])
@@ -393,22 +396,51 @@ class GitHubTagsAPI(VersionAPI):
393396
package_type = "github"
394397

395398
async def load_api(self, repo_set):
396-
async with client_session() as session:
399+
session = client_session()
400+
async with session as session:
397401
await asyncio.gather(
398402
*[
399-
self.fetch(owner_repo.lower(), session)
403+
self.fetch(owner_repo.lower())
400404
for owner_repo in repo_set
401405
if owner_repo.lower() not in self.cache
402406
]
403407
)
404408

405-
async def fetch(self, owner_repo: str, session) -> None:
409+
async def fetch(self, owner_repo: str, endpoint=None) -> None:
406410
# owner_repo is a string of format "{repo_owner}/{repo_name}"
407411
# Example value of owner_repo = "nexB/scancode-toolkit"
408-
endpoint = f"https://api.github.com/repos/{owner_repo}/git/refs/tags"
409-
resp = await session.request(method="GET", url=endpoint)
410-
resp = await resp.json()
411-
self.cache[owner_repo] = [release["ref"].split("/")[-1] for release in resp]
412+
if owner_repo not in self.cache:
413+
self.cache[owner_repo] = set()
414+
415+
if not endpoint:
416+
endpoint = f"https://github.com/{owner_repo}/tags"
417+
async with client_session() as session:
418+
resp = await session.get(endpoint)
419+
resp = await resp.read()
420+
421+
soup = BeautifulSoup(resp, features="lxml")
422+
for release_entry in soup.find_all("div", {"class": "commit"}):
423+
version = None
424+
for links in release_entry.find_all("a"):
425+
if f"/{owner_repo}/releases/tag/" in links["href"].lower():
426+
version = links["href"].split("/")[-1]
427+
break
428+
429+
release_date = release_entry.find("relative-time")["datetime"]
430+
self.cache[owner_repo].add(
431+
Version(value=version, release_date=parser.parse(release_date))
432+
)
433+
434+
url = None
435+
pagination_links = soup.find("div", {"class": "paginate-container"}).find_all("a")
436+
for link in pagination_links:
437+
if link.text == "Next":
438+
url = link["href"]
439+
break
440+
441+
if url:
442+
# FIXME: this could be asynced to improve performance
443+
await self.fetch(owner_repo, url)
412444

413445

414446
class HexVersionAPI(VersionAPI):
@@ -425,7 +457,11 @@ async def fetch(self, pkg, session):
425457
response = await session.request(method="GET", url=url)
426458
response = await response.json()
427459
for release in response["releases"]:
428-
versions.add(release["version"])
460+
versions.add(
461+
Version(
462+
value=release["version"], release_date=parser.parse(release["inserted_at"])
463+
)
464+
)
429465
except (ClientResponseError, JSONDecodeError):
430466
pass
431467

0 commit comments

Comments
 (0)