diff --git a/pyvulnerabilitylookup/api.py b/pyvulnerabilitylookup/api.py index 580fd2b..4d0b20c 100644 --- a/pyvulnerabilitylookup/api.py +++ b/pyvulnerabilitylookup/api.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging +from datetime import date, datetime from importlib.metadata import version from pathlib import PurePosixPath from typing import Any @@ -24,7 +25,7 @@ def enable_full_debug() -> None: class PyVulnerabilityLookup(): - def __init__(self, root_url: str, useragent: str | None=None, token: str | None=None, + def __init__(self, root_url: str='https://vulnerability.circl.lu', useragent: str | None=None, token: str | None=None, *, proxies: dict[str, str] | None=None) -> None: '''Query a specific instance. @@ -216,3 +217,84 @@ class PyVulnerabilityLookup(): '''Get user information''' r = self.session.get(urljoin(self.root_url, str(PurePosixPath('api', 'user', 'me')))) return r.json() + + # #### Sightings #### + + def get_sighting(self, sighting_uuid: str) -> dict[str, Any]: + '''Get a sighting + + :param sighting_uuid: The UUID of the sighting + ''' + r = self.session.get(urljoin(self.root_url, str(PurePosixPath('api', 'sighting', sighting_uuid)))) + return r.json() + + def get_sightings(self, /, *, sighting_uuid: str | None=None, + sighting_type: str | None=None, vuln_id: str | None = None, + author: str | None = None, + date_from: date | datetime | None=None, + date_to: date | datetime | None=None) -> dict[str, Any]: + '''Get sightings + + :param sighting_uuid: The UUID of a specific sighting + :param sighting_type: The type of sighting, can be one of: 'seen', 'exploided', 'not-exploited', 'confirmed', 'not-confirmed', 'patched', 'not-patched'. + :param vuln_id: The vulnerability ID to get sightings of + :param author: The author of the sighting(s) + :param date_from: The date from which to get sightings + :param date_to: The date to which to get sightings + ''' + + params = {} + if sighting_uuid: + params['uuid'] = sighting_uuid + if sighting_type: + params['type'] = sighting_type + if vuln_id: + params['vuln_id'] = vuln_id + if author: + params['author'] = author + if date_from: + if isinstance(date_from, datetime): + date_from = date_from.date() + params['date_from'] = date_from.isoformat() + if date_to: + if isinstance(date_to, datetime): + date_to = date_to.date() + params['date_to'] = date_to.isoformat() + + r = self.session.get(urljoin(self.root_url, str(PurePosixPath('api', 'sighting'))), params=params) + return r.json() + + def create_sighting(self, /, *, sighting: dict[str, Any] | None=None, + creation_timestamp: datetime | None=None, + source: str | None = None, + sighting_type: str | None=None, + vulnerability: str | None=None) -> dict[str, Any]: + '''Create a sighting. + + :param sighting: The sighting, as an object. + :param creation_timestamp: The timestamp of the sighting - set to now if not provided + :param source: The source of the sighting + :param sighting_type: The type of sighting, can be one of: 'seen', 'exploided', 'not-exploited', 'confirmed', 'not-confirmed', 'patched', 'not-patched'. + :param vulnerability: The vulnerability ID of the sighting + ''' + if not sighting: + sighting = {} + if creation_timestamp: + # This calue may or may not have a TZ at this point + sighting['creation_timestamp'] = creation_timestamp + if source: + sighting['source'] = source + if sighting_type: + sighting['type'] = sighting_type + if vulnerability: + sighting['vulnerability'] = vulnerability + + if 'creation_timestamp' in sighting: + # check if the datetime object has a TZ, if it doesn't, set it to localtime, make it a string + if sighting['creation_timestamp'].tzinfo is None: + sighting['creation_timestamp'] = sighting['creation_timestamp'].astimezone() + sighting['creation_timestamp'] = sighting['creation_timestamp'].isoformat() + + r = self.session.post(urljoin(self.root_url, str(PurePosixPath('api', 'sighting'))), + json=sighting) + return r.json()