nvd-client 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
nvd_client/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ from .exceptions import InvalidParametersError, InvalidCVEIDError, InvalidDateFormatError
2
+ from .api import NvdApi
3
+
4
+ __version__ = '0.1.0'
5
+ __author__ = 'Ahur4'
6
+ __author_email__ = 'ahur4.rahmani@gmail.com'
7
+ __license__ = 'MIT'
8
+ __copyright__ = 'Copyright 2024, Ahur4'
9
+
10
+ __all__ = [
11
+ "InvalidDateFormatError",
12
+ "InvalidCVEIDError",
13
+ "InvalidParametersError",
14
+ "NvdApi",
15
+ ]
nvd_client/api.py ADDED
@@ -0,0 +1,305 @@
1
+ from typing import Dict, List, Optional, Union
2
+
3
+ import requests
4
+
5
+ from nvd_client.utils import validate_and_convert_dates, validate_cve_id
6
+
7
+
8
+ class NvdApi:
9
+ """
10
+ A class to interact with the NVD API for fetching CVE data.
11
+
12
+ Attributes:
13
+ api_key (Optional[str]): The API key for authenticating requests.
14
+ api_key (Optional[str]): The Socks5 Proxy.
15
+ """
16
+
17
+ def __init__(self, api_key: Optional[str] = None, proxy: Optional[str] = None) -> None:
18
+ """
19
+ Initialize the NvdApi class.
20
+
21
+ Args:
22
+ api_key (Optional[str]): The API key for authenticating requests. Default is None.
23
+ api_key (Optional[str]): The Socks5 Proxy.
24
+ """
25
+ self.proxy: Optional[str] = proxy
26
+ self.api_key: Optional[str] = api_key
27
+ self.base_url_cve: str = 'https://services.nvd.nist.gov/rest/json/cves/2.0'
28
+ self.base_url_cpe: str = 'https://services.nvd.nist.gov/rest/json/cpematch/2.0'
29
+
30
+ def __str__(self) -> str:
31
+ """
32
+ Return a string representation of the NvdApi class.
33
+
34
+ Returns:
35
+ str: A string representation of the class.
36
+ """
37
+ return "What are you looking for?"
38
+
39
+ @staticmethod
40
+ def _verifier(params: Dict[str, Union[str, int]]) -> str:
41
+ """
42
+ Construct query string from parameters dictionary.
43
+
44
+ Args:
45
+ params (Dict[str, Union[str, int]]): The parameters' dictionary.
46
+
47
+ Returns:
48
+ str: The query string constructed from the parameters.
49
+ """
50
+ return "&".join([f"{k}={v}" for k, v in params.items()])
51
+
52
+ def _make_request(
53
+ self,
54
+ params: Optional[Dict[str, Union[str, int]]] = None,
55
+ type_of: str = "cve",
56
+ proxy: Optional[str] = None,
57
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
58
+ """
59
+ Make a request to the NVD API with the provided parameters.
60
+
61
+ Args:
62
+ params (Optional[Dict[str, Union[str, int]]]): The parameters for the API request. Default is None.
63
+ type_of (str): The parameters for the type of used api. default is cve.
64
+
65
+ Returns: Optional[Dict[str, Union[str, int, List, Dict]]]: The JSON response from the API, or None if an
66
+ error occurred.
67
+ """
68
+ try:
69
+ parameters = self._verifier(params or {})
70
+ if type_of == "cve":
71
+ link = f"{self.base_url_cve}?{parameters}"
72
+ else:
73
+ link = f"{self.base_url_cpe}?{parameters}"
74
+ headers = {'apiKey': self.api_key} if self.api_key else {}
75
+ if self.proxy:
76
+ response = requests.get(link, headers=headers, proxies={"http": self.proxy, "https": self.proxy})
77
+ elif proxy:
78
+ response = requests.get(link, headers=headers, proxies={"http": proxy, "https": proxy})
79
+ else:
80
+ response = requests.get(link, headers=headers)
81
+ response.raise_for_status()
82
+ return response.json()
83
+ except Exception as e:
84
+ return None
85
+
86
+ def get_all_cves(self, per_page: int = 2000, offset: int = 0, proxy: Optional[str] = None) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
87
+ """
88
+ Fetch all CVEs with pagination.
89
+
90
+ Args:
91
+ per_page (int): The number of results per page. Default is 2000.
92
+ offset (int): The starting index for the results. Default is 0.
93
+ proxy (str): The proxy to use. Default is None.
94
+
95
+ Returns: Optional[Dict[str, Union[str, int, List, Dict]]]: The JSON response from the API, or None if an
96
+ error occurred.
97
+ """
98
+ parameters = {
99
+ 'resultsPerPage': per_page,
100
+ 'startIndex': offset
101
+ }
102
+ if proxy:
103
+ return self._make_request(params=parameters, proxy=proxy)
104
+ return self._make_request(params=parameters)
105
+
106
+ def get_cve_by_id(self, cve_id: str, proxy: Optional[str] = None) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
107
+ """
108
+ Fetch a CVE by its ID.
109
+
110
+ Args:
111
+ cve_id (str): The CVE ID to fetch.
112
+ proxy (str): The proxy to use. Default is None.
113
+
114
+ Returns: Optional[Dict[str, Union[str, int, List, Dict]]]: The JSON response from the API, or None if an
115
+ error occurred.
116
+ """
117
+ if validate_cve_id(cve_id):
118
+ parameters = {"cveId": cve_id}
119
+ if proxy:
120
+ return self._make_request(params=parameters, proxy=proxy)
121
+ return self._make_request(params=parameters)
122
+
123
+ def get_cve_by_date(
124
+ self,
125
+ per_page: int = 2000,
126
+ offset: int = 0,
127
+ publish_start_date: Optional[str] = None,
128
+ publish_end_date: Optional[str] = None,
129
+ modify_start_date: Optional[str] = None,
130
+ modify_end_date: Optional[str] = None,
131
+ proxy: Optional[str] = None,
132
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
133
+ """
134
+ Fetch CVEs by publication or modification date.
135
+
136
+ Args:
137
+ per_page (int): The number of results per page. Default is 2000.
138
+ offset (int): The starting index for the results. Default is 0.
139
+ publish_start_date (Optional[str]): The start date for the publication date range. Default is None.
140
+ publish_end_date (Optional[str]): The end date for the publication date range. Default is None.
141
+ modify_start_date (Optional[str]): The start date for the modification date range. Default is None.
142
+ modify_end_date (Optional[str]): The end date for the modification date range. Default is None.
143
+ proxy (str): The proxy to use. Default is None.
144
+
145
+ Returns: Optional[Dict[str, Union[str, int, List, Dict]]]: The JSON response from the API, or None if an
146
+ error occurred.
147
+ """
148
+ parameters: Dict[str, Union[str, int, List, Dict]] = {}
149
+
150
+ publish_start_date, publish_end_date = validate_and_convert_dates(
151
+ start_date=publish_start_date,
152
+ end_date=publish_end_date,
153
+ param_names=["publish_start_date", "publish_end_date"]
154
+ )
155
+
156
+ modify_start_date, modify_end_date = validate_and_convert_dates(
157
+ start_date=modify_start_date,
158
+ end_date=modify_end_date,
159
+ param_names=["modify_start_date", "modify_end_date"]
160
+ )
161
+
162
+ if publish_start_date and publish_end_date:
163
+ parameters.update(pubStartDate=publish_start_date, pubEndDate=publish_end_date)
164
+
165
+ if modify_start_date and modify_end_date:
166
+ parameters.update(lastModStartDate=modify_start_date, lastModEndDate=modify_end_date)
167
+
168
+ # Add more parameters as needed
169
+ parameters.update(resultsPerPage=per_page, startIndex=offset)
170
+
171
+ if proxy:
172
+ return self._make_request(params=parameters, proxy=proxy)
173
+ return self._make_request(params=parameters)
174
+
175
+ def get_cve_by_cpe(
176
+ self,
177
+ cpe_name: str,
178
+ per_page: int = 2000,
179
+ offset: int = 0,
180
+ publish_start_date: Optional[str] = None,
181
+ publish_end_date: Optional[str] = None,
182
+ modify_start_date: Optional[str] = None,
183
+ modify_end_date: Optional[str] = None,
184
+ proxy: Optional[str] = None,
185
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
186
+ """
187
+ Fetch CVEs by CpeId.
188
+
189
+ Args:
190
+ cpe_name (str): The CPE name to fetch.
191
+ per_page (int): The number of results per page. Default is 2000.
192
+ offset (int): The starting index for the results. Default is 0.
193
+ publish_start_date (Optional[str]): The start date for the publication date range. Default is None.
194
+ publish_end_date (Optional[str]): The end date for the publication date range. Default is None.
195
+ modify_start_date (Optional[str]): The start date for the modification date range. Default is None.
196
+ modify_end_date (Optional[str]): The end date for the modification date range. Default is None.
197
+ proxy (str): The proxy to use. Default is None.
198
+
199
+ Returns: Optional[Dict[str, Union[str, int, List, Dict]]]: The JSON response from the API, or None if an
200
+ error occurred.
201
+ """
202
+ parameters = {}
203
+
204
+ publish_start_date, publish_end_date = validate_and_convert_dates(
205
+ start_date=publish_start_date,
206
+ end_date=publish_end_date,
207
+ param_names=["publish_start_date", "publish_end_date"]
208
+ )
209
+
210
+ modify_start_date, modify_end_date = validate_and_convert_dates(
211
+ start_date=modify_start_date,
212
+ end_date=modify_end_date,
213
+ param_names=["modify_start_date", "modify_end_date"]
214
+ )
215
+
216
+ if publish_start_date and publish_end_date:
217
+ parameters.update(pubStartDate=publish_start_date, pubEndDate=publish_end_date)
218
+
219
+ if modify_start_date and modify_end_date:
220
+ parameters.update(lastModStartDate=modify_start_date, lastModEndDate=modify_end_date)
221
+
222
+ parameters.update(resultsPerPage=per_page, startIndex=offset, cpeName=cpe_name)
223
+
224
+ if proxy:
225
+ return self._make_request(params=parameters, proxy=proxy)
226
+ return self._make_request(params=parameters)
227
+
228
+ def get_cpes_by_cve(
229
+ self,
230
+ cve_id: str,
231
+ per_page: int = 500,
232
+ offset: int = 0,
233
+ proxy: Optional[str] = None,
234
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
235
+ """
236
+ Fetch CPEs by CveId.
237
+
238
+ Args:
239
+ cve_id (str): The CVE ID to fetch.
240
+ per_page (int): The number of results per page. Default and Maximum is 500.
241
+ offset (int): The starting index for the results. Default is 0.
242
+ proxy (str): The proxy to use. Default is None.
243
+
244
+ Returns: List[Union[Dict[str, Any], Any]]: The JSON response from the API, or None if an
245
+ error occurred.
246
+ """
247
+ if validate_cve_id(cve_id):
248
+ parameters = {}
249
+ parameters.update(resultsPerPage=per_page, startIndex=offset, cveId=cve_id)
250
+
251
+ if proxy:
252
+ return self._make_request(params=parameters, type_of="cpe", proxy=proxy)
253
+ return self._make_request(params=parameters, type_of="cpe")
254
+
255
+ def get_cpes_by_criteria(
256
+ self,
257
+ criteria: str,
258
+ per_page: int = 500,
259
+ offset: int = 0,
260
+ proxy: Optional[str] = None,
261
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
262
+ """
263
+ Fetch CPEs by CveId.
264
+
265
+ Args:
266
+ criteria (str): The Criteria to fetch.
267
+ per_page (int): The number of results per page. Default and Maximum is 500.
268
+ offset (int): The starting index for the results. Default is 0.
269
+ proxy (str): The proxy to use. Default is None.
270
+
271
+ Returns: List[Union[Dict[str, Any], Any]]: The JSON response from the API, or None if an
272
+ error occurred.
273
+ """
274
+ parameters = {}
275
+ parameters.update(resultsPerPage=per_page, startIndex=offset, matchStringSearch=criteria)
276
+
277
+ if proxy:
278
+ return self._make_request(params=parameters, type_of="cpe", proxy=proxy)
279
+ return self._make_request(params=parameters, type_of="cpe")
280
+
281
+ def get_cpes_by_criteria_name_id(
282
+ self,
283
+ criteria_name_id: str,
284
+ per_page: int = 500,
285
+ offset: int = 0,
286
+ proxy: Optional[str] = None,
287
+ ) -> Optional[Dict[str, Union[str, int, List, Dict]]]:
288
+ """
289
+ Fetch CPEs by CveId.
290
+
291
+ Args:
292
+ criteria_name_id (str): The Criteria Name ID to fetch.
293
+ per_page (int): The number of results per page. Default and Maximum is 500.
294
+ offset (int): The starting index for the results. Default is 0.
295
+ proxy (str): The proxy to use. Default is None.
296
+
297
+ Returns: List[Union[Dict[str, Any], Any]]: The JSON response from the API, or None if an
298
+ error occurred.
299
+ """
300
+ parameters = {}
301
+ parameters.update(resultsPerPage=per_page, startIndex=offset, matchCriteriaId=criteria_name_id)
302
+
303
+ if proxy:
304
+ return self._make_request(params=parameters, type_of="cpe", proxy=proxy)
305
+ return self._make_request(params=parameters, type_of="cpe")
@@ -0,0 +1,40 @@
1
+ from typing import List
2
+
3
+
4
+ class InvalidDateFormatError(Exception):
5
+ """Exception raised for errors in the input date format."""
6
+
7
+ def __init__(self, date_str, message="Invalid date format. Please use YYYY-MM-DD."):
8
+ self.date_str = date_str
9
+ self.message = message
10
+ super().__init__(self.message)
11
+
12
+ def __str__(self):
13
+ return f'{self.date_str} -> {self.message}'
14
+
15
+
16
+ class InvalidParametersError(Exception):
17
+ """Exception raised for invalid parameters."""
18
+
19
+ def __init__(
20
+ self,
21
+ missing_params: List[str],
22
+ message="Invalid parameter combination. Both parameters are required."
23
+ ):
24
+ self.missing_params = missing_params
25
+ self.message = message
26
+ super().__init__(self.message)
27
+
28
+ def __str__(self):
29
+ return f'Missing or incomplete parameters: {", ".join(self.missing_params)} -> {self.message}'
30
+
31
+
32
+ class InvalidCVEIDError(Exception):
33
+ """Exception raised for errors in the input CVE ID."""
34
+ def __init__(self, cve_id: str, message="Invalid CVE ID format. Please use CVE-YYYY-NNNN format."):
35
+ self.cve_id = cve_id
36
+ self.message = message
37
+ super().__init__(self.message)
38
+
39
+ def __str__(self):
40
+ return f'{self.cve_id} -> {self.message}'
nvd_client/utils.py ADDED
@@ -0,0 +1,48 @@
1
+ import re
2
+ from datetime import datetime
3
+ from .exceptions import InvalidDateFormatError, InvalidParametersError, InvalidCVEIDError
4
+ from typing import Optional, List
5
+
6
+
7
+ def validate_date_format(date_str):
8
+ # Define the regex pattern for YYYY-MM-DD format
9
+ pattern = r'^\d{4}-\d{2}-\d{2}$'
10
+ # Check if the date_str matches the pattern
11
+ if re.match(pattern, date_str):
12
+ try:
13
+ # Try to parse the date to ensure it's a valid date
14
+ datetime.strptime(date_str, '%Y-%m-%d')
15
+ return True
16
+ except ValueError:
17
+ return False
18
+ else:
19
+ return False
20
+
21
+
22
+ def convert_date_to_iso(date_str):
23
+ if validate_date_format(date_str):
24
+ # Parse the input date
25
+ date = datetime.strptime(date_str, '%Y-%m-%d')
26
+ # Convert to the desired format
27
+ iso_format_date = date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]
28
+ return iso_format_date
29
+ else:
30
+ raise InvalidDateFormatError(date_str)
31
+
32
+
33
+ def validate_and_convert_dates(start_date: Optional[str], end_date: Optional[str], param_names: List[str]):
34
+ if start_date and end_date:
35
+ return convert_date_to_iso(start_date), convert_date_to_iso(end_date)
36
+ elif start_date or end_date:
37
+ raise InvalidParametersError(missing_params=param_names)
38
+ return None, None
39
+
40
+
41
+ def validate_cve_id(cve_id: str) -> bool:
42
+ # Define the regex pattern for CVE-YYYY-NNNN format
43
+ pattern = r'^CVE-\d{4}-\d{4,}$'
44
+ # Check if the cve_id matches the pattern
45
+ if re.match(pattern, cve_id):
46
+ return True
47
+ else:
48
+ raise InvalidCVEIDError(cve_id)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ahur4
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.1
2
+ Name: nvd-client
3
+ Version: 0.1.1
4
+ Summary: A Python client for interacting with the National Vulnerability Database (NVD) API to fetch CVE data.
5
+ Home-page: https://github.com/ahur4/nvd-client
6
+ Author: Ahur4
7
+ Author-email: ahur4.rahmani@gmail.com
8
+ Keywords: vulnerability,nvd,national vulnerability database,nvd_client,nvd_client-api,ahur4,ahura rahmani,redteam,soc,vulnerability assessment,penetration testing
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: certifi ==2024.6.2
16
+ Requires-Dist: charset-normalizer ==3.3.2
17
+ Requires-Dist: idna ==3.7
18
+ Requires-Dist: requests[socks] ==2.32.3
19
+ Requires-Dist: urllib3 ==2.2.1
20
+
21
+
22
+ # NVD API Client
23
+
24
+ ![PyPI](https://img.shields.io/pypi/v/nvd-client)
25
+ ![Daily Downloads](https://img.shields.io/pypi/dd/nvd-client)
26
+ ![Monthly Downloads](https://img.shields.io/pypi/dm/nvd-client)
27
+ [![Telegram-Channel](https://img.shields.io/badge/Telegram--Channel-Ahura_Rahmani-blue)](https://t.me/Ahur4_Rahmani)
28
+
29
+
30
+ A Python client for interacting with the National Vulnerability Database (NVD) API to fetch CVE data.
31
+
32
+ ## Features
33
+
34
+ - Fetch all CVEs with pagination
35
+ - Fetch CVE by its ID
36
+ - Fetch CVEs by publication or modification date
37
+ - Fetch CVEs by CPE name
38
+ - Other features coming soon...
39
+
40
+ ## Installation
41
+
42
+ You can install the package using pip:
43
+
44
+ ```sh
45
+ pip install nvd_client
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from nvd_client import NvdApi
52
+
53
+ # Initialize the API client
54
+ api_key = "your_api_key_here"
55
+ nvd_api = NvdApi(api_key)
56
+
57
+ # Fetch all CVEs
58
+ cves = nvd_api.get_all_cves(per_page=100, offset=0)
59
+ print(cves)
60
+
61
+ # Fetch a CVE by ID
62
+ cve = nvd_api.get_cve_by_id(cve_id="CVE-2024-30078")
63
+ print(cve)
64
+
65
+ # Fetch CVEs by publish or modify date range
66
+ cves_by_date = nvd_api.get_cve_by_date(
67
+ per_page=100,
68
+ offset=0,
69
+ publish_start_date="2021-01-01",
70
+ publish_end_date="2021-12-31"
71
+ )
72
+ print(cves_by_date)
73
+
74
+ # Fetch CVEs by CPE name
75
+ cves_by_cpe = nvd_api.get_cve_by_cpe(
76
+ cpe_name="cpe:2.3:o:microsoft:windows_10:1607:*:*:*:*:*:*:*",
77
+ per_page=100,
78
+ offset=0
79
+ )
80
+ print(cves_by_cpe)
81
+ ```
82
+
83
+ ## Author
84
+
85
+ - Author: Ahur4
86
+
87
+ - Telegram: [@Ahura_rahmani](https://t.me/Ahura_rahmani)
88
+
89
+ ## License
90
+
91
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
92
+
93
+
94
+ ### Explanation:
95
+ 1. **Features**: Lists the main functionalities provided by the module.
96
+ 2. **Installation**: Provides instructions for installing the package using pip from PyPI.
97
+ 3. **Usage**: Gives examples of how to initialize the client and use its methods.
98
+ 4. **Author**: Provides your name and a link to your Telegram channel.
99
+ 5. **License**: States that the project is licensed under the MIT License.
@@ -0,0 +1,9 @@
1
+ nvd_client/__init__.py,sha256=JBBwBR214nMGV_ZG5dr06gNuL6Wu6EzneYLTLcjWTgo,392
2
+ nvd_client/api.py,sha256=aOh19xUXzg08FtyXxJCEZsbkmwieqakVII2FxfKIzCM,12667
3
+ nvd_client/exceptions.py,sha256=jah5qsid089419Cwq1fLY3SIgkdeGorj1daG3Z6qsOo,1314
4
+ nvd_client/utils.py,sha256=GpN4v4ONQAijtxJL49sxpmkJs3SZMziCxMT9r7uwrEY,1645
5
+ nvd_client-0.1.1.dist-info/LICENSE,sha256=sH516rZoWuam08PIkKJ7N9q8AR_KZZ67CttgY9Q5n7U,1081
6
+ nvd_client-0.1.1.dist-info/METADATA,sha256=rRIJ_SLq-K0pMAAYGZuEPuVxULkKfL224mtMmCPxHqQ,2906
7
+ nvd_client-0.1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
8
+ nvd_client-0.1.1.dist-info/top_level.txt,sha256=Bt0KpXk1HXgHIzDg70YvgQaBzhBsBVwZHSAbTtsrOk4,11
9
+ nvd_client-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ nvd_client