diff --git a/pyproject.toml b/pyproject.toml index c0eafdb..2978eeb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ packages = [ ] [tool.poetry.scripts] +glvd = 'glvd.cli.client.__main__:main' glvd-data = 'glvd.cli.data.__main__:main' [tool.poetry.dependencies] diff --git a/src/glvd/cli/client/__init__.py b/src/glvd/cli/client/__init__.py new file mode 100644 index 0000000..6ff305b --- /dev/null +++ b/src/glvd/cli/client/__init__.py @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +from ..registry import CliRegistry + + +cli = CliRegistry() diff --git a/src/glvd/cli/client/__main__.py b/src/glvd/cli/client/__main__.py new file mode 100644 index 0000000..1529d4b --- /dev/null +++ b/src/glvd/cli/client/__main__.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +from . import cli + +# Import to register all the commands +from . import ( # noqa: F401 + cve, +) + + +def main() -> None: + cli.main() + + +if __name__ == '__main__': + main() diff --git a/src/glvd/cli/client/cve.py b/src/glvd/cli/client/cve.py new file mode 100644 index 0000000..aaf9d87 --- /dev/null +++ b/src/glvd/cli/client/cve.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +import json +import logging +import sys +import urllib.parse +from datetime import datetime, timedelta, timezone +from typing import Any + +import asyncio +from sqlalchemy import func, select +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.ext.asyncio import ( + AsyncConnection, + AsyncEngine, + create_async_engine, +) + +from ...database import Base, NvdCve +from ...util import requests +from . import cli + + +logger = logging.getLogger(__name__) + + +class ClientCve: + server: str + + @staticmethod + @cli.register( + 'cve', + arguments=[ + cli.add_argument( + 'cve', + help='the CVE to look up', + metavar='CVE', + ), + cli.add_argument( + '--server', + default='http://localhost:5000', + help='the server to use', + ), + cli.add_argument( + '--debug', + action='store_true', + help='enable debug output', + ), + ] + ) + def run(cve: str, server: str, debug: bool) -> None: + logging.basicConfig(level=debug and logging.DEBUG or logging.INFO) + ClientCve(server)(cve) + + def __init__(self, server: str) -> None: + self.server = server + + def __call__(self, cve: str) -> None: + with requests.RetrySession() as rsession: + resp = rsession.get( + urllib.parse.urljoin(self.server, f'v1/cves/{cve}'), + ) + if resp.status_code == 200: + data = resp.json() + json.dump(data, sys.stdout, indent=2) + elif resp.status_code == 404: + print(f'{self.cve} not found', file=sys.stderr) + else: + resp.raise_for_status() + + +if __name__ == '__main__': + ClientCve.run() diff --git a/tests/cli/client/test_cve.py b/tests/cli/client/test_cve.py new file mode 100644 index 0000000..0d8a2e1 --- /dev/null +++ b/tests/cli/client/test_cve.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT + +from sqlalchemy import select + +from glvd.cli.client.cve import ClientCve + + +class TestIngestNvd: + async def test_one(self, requests_mock): + requests_mock.get( + f'http://localhost/v1/cves/TEST-0', + json=[ + { + 'id': f'TEST-0', + 'lastModified': '2019-04-01T00:00:00', + }, + ], + ) + + client = ClientCve(server='http://localhost') + assert client(cve='TEST-0') is None