diff --git a/README.md b/README.md index c3defe0..62d5ceb 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ CWE (Common Weakness Enumeration), ... as well as additional information (RedHat | cveid | CVE number | `CVE-2014-0160` | ```python ->>> cve.cve('CVE-2014-0160') +>>> cve.id('CVE-2014-0160') ```
@@ -189,15 +189,17 @@ Outputs the last `n` amount of vulnerabilities. If the limit is not specified, t ##### Description +**DISABLED ON cve.circl.lu** + Returns all CVEs that are linked by a given key/value pair. | Argument | Description | Example | | :-------------------| :---------------------------------- | :-------------------------- | -| key | The key to link CVEs on | `msbulletin.bulletin_id` | +| key | The key to link CVEs on | `refmap.ms` | | value | The value for the given key | `MS16-098` | ```python ->>> cve.link('msbulletin.bulletin_id/MS16-098') +>>> cve.link('refmap.ms/MS16-098') ```
diff --git a/ares/__init__.py b/ares/__init__.py index 98da63a..ea90695 100644 --- a/ares/__init__.py +++ b/ares/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- __title__ = 'ares' -__version__ = '0.6.0' +__version__ = '0.6.1' __author__ = 'Martin Simon ' __repo__ = 'https://github.com/barnumbirr/ares' __license__ = 'Apache v2.0 License' diff --git a/ares/core.py b/ares/core.py index d19133f..71d3e19 100644 --- a/ares/core.py +++ b/ares/core.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import json import requests class CVESearch(object): @@ -13,74 +12,58 @@ class CVESearch(object): def __init__(self, base_url = __DEFAULT_BASE_URL, request_timeout = __DEFAULT_TIMEOUT): self.base_url = base_url self.request_timeout = request_timeout + self.session = self._create_session() - @property - def session(self): - if not self._session: - self._session = requests.Session() - self._session.headers.update({'Content-Type': 'application/json'}) - self._session.headers.update({'User-agent': 'ares - python wrapper \ - around cve.circl.lu (github.com/barnumbirr/ares)'}) - return self._session + @staticmethod + def _create_session(): + session = requests.Session() + user_agent = 'ares - python wrapper around cve.circl.lu (github.com/barnumbirr/ares)' + session.headers.update({'Content-Type': 'application/json'}) + session.headers.update({'User-agent': user_agent}) + return session def __request(self, endpoint, query): # There is probably a more elegant way to do this ¯\_(ツ)_/¯ if query: - response_object = self.session.get(requests.compat.urljoin(self.base_url, endpoint + query), - timeout = self.request_timeout) + response = self.session.get(requests.compat.urljoin(self.base_url, endpoint + query), + timeout = self.request_timeout) else: - response_object = self.session.get(requests.compat.urljoin(self.base_url, endpoint), - timeout = self.request_timeout) + response = self.session.get(requests.compat.urljoin(self.base_url, endpoint), + timeout = self.request_timeout) - try: - response = json.loads(response_object.text) - except Exception as e: - return e - - return response + response.raise_for_status() + return response.json() def browse(self, param=None): - response = self.__request('browse/', query=param) - return response + return self.__request('browse/', query=param) def capec(self, param): - response = self.__request('capec/', query=param) - return response + return self.__request('capec/', query=param) # def cpe22(self, param): - # response = self.__request('cpe2.2/', query=param) - # return response - + # return self.__request('cpe2.2/', query=param) # def cpe23(self, param): - # response = self.__request('cpe2.3/', query=param) - # return response - - def cve(self, param): - response = self.__request('cve/', query=param) - return response + # return self.__request('cpe2.3/', query=param) # def cvefor(self, param): - # response = self.__request('cvefor/', query=param) - # return response + # return self.__request('cvefor/', query=param) def cwe(self): """ Outputs a list of all CWEs (Common Weakness Enumeration). """ - response = self.__request('cwe', query=None) - return response + return self.__request('cwe', query=None) def dbinfo(self): - response = self.__request('dbInfo', query=None) - return response + return self.__request('dbInfo', query=None) + + def id(self, param): + return self.__request('cve/', query=param) - def last(self, param): - response = self.__request('last/', query=param) - return response + def last(self, param=None): + return self.__request('last/', query=param) - def link(self, param): - response = self.__request('link/', query=param) - return response + # def link(self, param): + # return self.__request('link/', query=param) # def search(self, param): - # response = self.__request('search/', query=param) - # return response + # return self.__request('search/', query=param) diff --git a/setup.py b/setup.py deleted file mode 100644 index 4d761eb..0000000 --- a/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# To use a consistent encoding -from os import path -from codecs import open -# Always prefer setuptools over distutils -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, 'README.md'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='ares', - packages = ['ares'], - version = '0.6.0', - description = 'Python wrapper around https://cve.circl.lu.', - author = 'Martin Simon', - author_email = 'me@martinsimon.me', - url = 'https://github.com/barnumbirr/ares', - project_urls={ - 'Bug Reports': 'https://github.com/barnumbirr/ares/issues', - 'Buy me a coffee': 'https://github.com/barnumbirr/ares#buy-me-a-coffee', - }, - license = 'Apache v2.0 License', - keywords=['CVE', 'cybersecurity', 'vulnerability', 'circl.lu'], - classifiers=[ - 'License :: OSI Approved :: Apache Software License', - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.7', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - long_description = long_description, - long_description_content_type='text/markdown', -) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..4f96c15 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- diff --git a/tests/test_ares.py b/tests/test_ares.py new file mode 100644 index 0000000..8d8885a --- /dev/null +++ b/tests/test_ares.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +try: + from .core import CVESearch +except ModuleNotFoundError: + from ares import CVESearch + +class TestCVEAPI(unittest.TestCase): + + def setUp(self): + self.cve = CVESearch() + + def tearDown(self): + self.cve.session.close() + + def test_init(self): + self.assertTrue(isinstance(self.cve, CVESearch)) + + def test_session_headers(self): + user_agent = 'ares - python wrapper around cve.circl.lu (github.com/barnumbirr/ares)' + self.assertEqual(self.cve.session.headers["Content-Type"], "application/json") + self.assertEqual(self.cve.session.headers["User-agent"], user_agent) + + @unittest.skip("Test too aggressive for provider.") + def test_empty_browse(self): + response = self.cve.browse() + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertIsNone(response["product"]) + self.assertIsInstance(response["vendor"], list) + self.assertTrue(len(response["vendor"]) > 1000) + + def test_browse(self): + response = self.cve.browse(param="python-requests") + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertEqual(response["vendor"], "python-requests") + + def test_capec(self): + response = self.cve.capec(param="13") + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertEqual(response["name"], "Subverting Environment Variable Values") + + @unittest.skip("Endpoint disabled on cve.circl.lu") + def test_cpe22(self): + response = self.cve.cpe22('cpe:2.3:o:microsoft:windows_vista:6.0:sp1:-:-:home_premium:-:-:x64:-') + self.assertIsNotNone(response) + self.assertIsInstance(response, str) + self.assertEqual(response, "cpe:/o:microsoft:windows_vista:6.0:sp1:~~home_premium~~x64~") + + @unittest.skip("Endpoint disabled on cve.circl.lu") + def test_cpe23(self): + response = self.cve.cpe23('cpe:/o:microsoft:windows_vista:6.0:sp1:~-~home_premium~-~x64~-') + self.assertIsNotNone(response) + self.assertIsInstance(response, str) + self.assertEqual(response, "cpe:2.3:o:microsoft:windows_vista:6.0:sp1:-:-:home_premium:-:-:x64") + + @unittest.skip("Endpoint disabled on cve.circl.lu") + def test_cvefor(self): + response = self.cve.cvefor('cpe:/o:microsoft:windows_vista:6.0:sp1:~-~home_premium~-~x64~-') + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertEqual(response["id"], "CVE-2005-0100") + + @unittest.skip("Test too aggressive for provider.") + def test_cwe(self): + response = self.cve.cwe() + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + + def test_db_info(self): + response = self.cve.dbinfo() + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + + def test_id(self): + response = self.cve.id(param="CVE-2015-2296") + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertEqual(response["Published"], "2015-03-18T16:59:00") + + def test_bad_id(self): + response = self.cve.id(param="CVE-not-real") + self.assertIsNone(response) + + def test_last(self): + response = self.cve.last() + self.assertIsNotNone(response) + self.assertIsInstance(response, list) + self.assertEqual(len(response), 30) + + @unittest.skip("Endpoint disabled on cve.circl.lu") + def test_link(self): + response = self.cve.link(param="refmap.ms/CVE-2016-3309") + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertEqual(response["cves"]["cwe"], "CWE-264") + + @unittest.skip("Endpoint disabled on cve.circl.lu") + def test_search_vendor(self): + response = self.cve.search(param="python-requests") + self.assertIsNotNone(response) + self.assertIsInstance(response, dict) + self.assertIsInstance(response["data"], list) + +if __name__ == "__main__": + unittest.main()