diff --git a/changes/821.added b/changes/821.added new file mode 100644 index 00000000..65dcebce --- /dev/null +++ b/changes/821.added @@ -0,0 +1 @@ +Added the ability to provide custom driver values to get_nist_urls() diff --git a/docs/user/lib_use_cases_nist.md b/docs/user/lib_use_cases_nist.md index 4fd080fb..5eb17c49 100644 --- a/docs/user/lib_use_cases_nist.md +++ b/docs/user/lib_use_cases_nist.md @@ -21,6 +21,11 @@ For this reason, for certain Vendor/OS combinations, a custom URL needs to be bu - Custom URL Output - `['https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:juniper:junos:10.2r2:*:*:*:*:*:*:*', 'https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:juniper:junos:10.2:r2:*:*:*:*:*:*']` +## Custom NIST Drivers +There are vendor and OS that are widely used in the industry and others that are not. For the widely adopted options, the `netutils.lib_mapper` will contain the mappings and requests can be made via the repo to update them if something is missing that you believe should be supported there. + +For the options that are not widely adopted there is the ability to use your own custom NIST driver value that will be used in the creation of the query URL. + ## Examples Here are a few examples showing how to use this in your python code. @@ -32,9 +37,14 @@ from netutils.nist import get_nist_urls get_nist_urls("cisco_ios", "15.5(2)S1c") # ['https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:cisco:ios:15.5\\(2\\)s1c:*'] +# Get NIST URL for the Cisco IOS object using a custom NIST driver value +get_nist_urls("cisco_ios", "15.5(2)S1c", "cisco:not_ios") +# ['https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:cisco:not_ios:15.5\\(2\\)s1c:*'] + # Get NIST URL(s) for the Juniper JunOS object get_nist_urls("juniper_junos", "10.2R2.11") # ['https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:juniper:junos:10.2r2:*:*:*:*:*:*:*', 'https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:o:juniper:junos:10.2:r2:*:*:*:*:*:*'] + ``` Currently known OS/Other Platform types that require a custom NIST URL: diff --git a/netutils/nist.py b/netutils/nist.py index a3162805..3b6883b7 100644 --- a/netutils/nist.py +++ b/netutils/nist.py @@ -261,12 +261,13 @@ def get_nist_vendor_platform_urls(vendor: str, platform: str, version: str) -> t return _get_nist_urls_default(platform_data) -def get_nist_urls(network_driver: str, version: str) -> t.List[str]: +def get_nist_urls(network_driver: str, version: str, custom_driver_mapping: str = "") -> t.List[str]: """Generate list of possible NIST URLs for the Network Driver, and Version. Args: network_driver (str): Value of device network_driver (Ex: cisco_ios, arista_eos) version (str): OS Software Platform Version + custom_driver_mapping (str): Custom network driver to NIST platform mapping string (Ex: "cisco:something_else") (Optional, None if not provided) Returns: t.List[str]: NIST URLs to search for possible CVE matches @@ -275,14 +276,19 @@ def get_nist_urls(network_driver: str, version: str) -> t.List[str]: >>> from netutils.nist import get_nist_urls >>> get_nist_urls('cisco_ios', '15.3') ['https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:o:cisco:ios:15.3:*'] - >>> + >>> get_nist_urls('cisco_ios', '15.3', 'cisco:something_else') + ['https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:o:cisco:something_else:15.3:*'] """ - # DICTIONARY FOR VENDOR/PLATFORM TO NETWORK_DRIVER; UPDATE AS NEEDED - vendor_os: str = NIST_LIB_MAPPER_REVERSE.get(network_driver, "") + if custom_driver_mapping: + vendor_os = custom_driver_mapping + else: + vendor_os = NIST_LIB_MAPPER_REVERSE.get(network_driver, "") + if not vendor_os: raise ValueError( - f"The network driver `{network_driver}` has no associated mapping, the supported drivers are {list(NIST_LIB_MAPPER_REVERSE.keys())}." + f"The network driver `{network_driver}` has no associated mapping. The supported drivers are {list(NIST_LIB_MAPPER_REVERSE.keys())}. You may also " + f"provide a custom driver mapping string in the format of 'vendor:os_name' to use a custom NIST driver value." + f"Example: get_nist_urls('cisco_ios', '15.3', 'cisco:something_else')" ) - vendor, os_name = vendor_os.split(":") - + vendor, os_name = vendor_os.split(":", 1) return get_nist_vendor_platform_urls(vendor, os_name, version) diff --git a/tests/unit/test_nist.py b/tests/unit/test_nist.py index 2d1c8302..b0115c28 100644 --- a/tests/unit/test_nist.py +++ b/tests/unit/test_nist.py @@ -28,19 +28,25 @@ "https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:o:juniper:junos:12.3x48-d25:*:*:*:*:*:*:*", ], }, + { + "sent": {"network_driver": "cisco_ios", "version": "15.5", "custom_driver_mapping": "cisco:something_else"}, + "received": ["https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:o:cisco:something_else:15.5:*"], + }, ] # Testing the composition of the nist url(s) created for a platform @pytest.mark.parametrize("data", platform_nist_urls) def test_get_nist_urls(data): - platform_obj = nist.get_nist_urls(data["sent"]["network_driver"], data["sent"]["version"]) + platform_obj = nist.get_nist_urls( + data["sent"]["network_driver"], data["sent"]["version"], data["sent"].get("custom_driver_mapping", None) + ) assert platform_obj == data["received"] def test_get_nist_urls_failed(): with pytest.raises( - ValueError, match=r"The network driver `fakeos` has no associated mapping, the supported drivers are*" + ValueError, match=r"The network driver `fakeos` has no associated mapping. The supported drivers are*" ): nist.get_nist_urls("fakeos", "15.5")