Repository: AppThreat/vulnerability-db (language: python)

Issue:
Upgrade to CVE json schema 5.0
https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0_schema.json


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index 442bbf7..1ed4a03 100644
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,5 @@
 [flake8]
-ignore = E203, E266, E501, W503, W605
+ignore = E203, E266, E501, W503, W605, C901
 max-line-length = 79
 max-complexity = 18
 select = B,C,E,F,W,T4,B9
diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml
index d1ac4e7..6932cee 100644
--- a/.github/workflows/pythonapp.yml
+++ b/.github/workflows/pythonapp.yml
@@ -28,16 +28,9 @@ jobs:
         pip install ".[dev]"
     - name: Lint with flake8
       run: |
-        # stop the build if there are Python syntax errors or undefined names
-        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
-        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
-        flake8 . --count --exit-zero --statistics
+        flake8 . --count --show-source --exit-zero --statistics
     - name: Test with pytest
       run: |
         pytest --cov=vdb test
       env:
         PYTHONPATH: .
-    - name: Test without orjson
-      run: |
-        pip uninstall -y orjson
-        pytest --cov=vdb test
diff --git a/data/CVE_JSON_5.0_schema.json b/data/CVE_JSON_5.0_schema.json
new file mode 100644
index 0000000..216bc3f
--- /dev/null
+++ b/data/CVE_JSON_5.0_schema.json
@@ -0,0 +1,1203 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "type": "object",
+    "title": "CVE JSON record format",
+    "description": "cve-schema specifies the CVE JSON record format. This is the blueprint for a rich set of JSON data that can be submitted by CVE Numbering Authorities (CNAs) and Authorized Data Publishers (ADPs) to describe a CVE Record. Some examples of CVE Record data include CVE ID number, affected product(s), affected version(s), and public references. While those specific items are required when assigning a CVE, there are many other optional data in the schema that can be used to enrich CVE Records for community benefit. Learn more about the CVE program at [the official website](https://cve.mitre.org). This CVE JSON record format is defined using JSON Schema. Learn more about JSON Schema [here](https://json-schema.org/).",
+    "definitions": {
+        "uriType": {
+            "description": "A universal resource identifier (URI), according to [RFC 3986](https://tools.ietf.org/html/rfc3986).",
+            "type": "string",
+            "format": "uri",
+            "minLength": 1,
+            "maxLength": 2048
+        },
+        "uuidType": {
+            "description": "A version 4 (random) universally unique identifier (UUID) as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.3).",
+            "type": "string",
+            "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$"
+        },
+        "reference": {
+            "type": "object",
+            "required": ["url"],
+            "properties": {
+                "url": {
+                    "description": "The uniform resource locator (URL), according to [RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.1.3), that can be used to retrieve the referenced resource.",
+                    "$ref": "#/definitions/uriType"
+                },
+                "name": {
+                    "description": "User created name for the reference, often the title of the page.",
+                    "type": "string",
+                    "maxLength": 512,
+                    "minLength": 1
+                },
+                "tags": {
+                    "description": "An array of one or more tags that describe the resource referenced by 'url'.",
+                    "type": "array",
+                    "minItems": 1,
+                    "uniqueItems": true,
+                    "items": {
+                        "oneOf": [
+                            {
+                                "$ref": "#/definitions/tagExtension"
+                            },
+                            {
+                                "$ref": "tags/reference-tags.json"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        "cveId": {
+            "type": "string",
+            "pattern": "^CVE-[0-9]{4}-[0-9]{4,19}$"
+        },
+        "orgId": {
+            "description": "A UUID for an organization participating in the CVE program. This UUID can be used to lookup the organization record in the user registry service.",
+            "$ref": "#/definitions/uuidType"
+        },
+        "userId": {
+            "description": "A UUID for a user participating in the CVE program. This UUID can be used to lookup the user record in the user registry service.",
+            "$ref": "#/definitions/uuidType"
+        },
+        "shortName": {
+            "description": "A 2-32 character name that can be used to complement an organization's UUID.",
+            "type": "string",
+            "minLength": 2,
+            "maxLength": 32
+        },
+        "datestamp": {
+            "description": "Date/time format based on RFC3339 and ISO ISO8601.",
+            "type": "string",
+            "format": "date",
+            "pattern": "^((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$"
+        },
+        "timestamp": {
+            "type": "string",
+            "format": "date-time",
+            "description": "Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ssZZZZ'. If timezone offset is not given, GMT (0000) is assumed.",
+            "pattern": "^(((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})?$"
+        },
+        "version": {
+            "description": "A single version of a product, as expressed in its own version numbering scheme.",
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 1024
+        },
+        "status": {
+            "description": "The vulnerability status of a given version or range of versions of a product. The statuses 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability. The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. There can be many reasons for an 'unknown' status, including that an investigation has not been undertaken or that a vendor has not disclosed the status.",
+            "type": "string",
+            "enum": ["affected", "unaffected", "unknown"]
+        },
+        "product": {
+            "type": "object",
+            "description": "Provides information about the set of products and services affected by this vulnerability.",
+            "allOf": [
+                {
+                    "anyOf": [
+                        {"required": ["vendor", "product"]},
+                        {"required": ["collectionURL", "packageName"]}
+                    ]
+                },
+                {
+                    "anyOf": [
+                        {"required": ["versions"]},
+                        {"required": ["defaultStatus"]}
+                    ]
+                }
+            ],
+            "properties": {
+                "vendor": {
+                    "type": "string",
+                    "description": "Name of the organization, project, community, individual, or user that created or maintains this product or hosted service. Can be 'N/A' if none of those apply. When collectionURL and packageName are used, this field may optionally represent the user or account within the package collection associated with the package.",
+                    "minLength": 1,
+                    "maxLength": 512
+                },
+                "product": {
+                    "type": "string",
+                    "description": "Name of the affected product.",
+                    "minLength": 1,
+                    "maxLength": 2048
+                },
+                "collectionURL": {
+                    "description": "URL identifying a package collection (determines the meaning of packageName).",
+                    "$ref": "#/definitions/uriType",
+                    "examples": [
+                        "https://access.redhat.com/downloads/content/package-browser",
+                        "https://addons.mozilla.org",
+                        "https://addons.thunderbird.net",
+                        "https://anaconda.org/anaconda/repo",
+                        "https://app.vagrantup.com/boxes/search",
+                        "https://apps.apple.com",
+                        "https://archlinux.org/packages",
+                        "https://atmospherejs.meteor.com",
+                        "https://atom.io/packages",
+                        "https://bitbucket.org",
+                        "https://bower.io",
+                        "https://brew.sh/",
+                        "https://chocolatey.org/packages",
+                        "https://chrome.google.com/webstore",
+                        "https://clojars.org",
+                        "https://cocoapods.org",
+                        "https://code.dlang.org",
+                        "https://conan.io/center",
+                        "https://cpan.org/modules",
+                        "https://cran.r-project.org",
+                        "https://crates.io",
+                        "https://ctan.org/pkg",
+                        "https://drupal.org",
+                        "https://exchange.adobe.com",
+                        "https://forge.puppet.com/modules",
+                        "https://github.com",
+                        "https://gitlab.com/explore",
+                        "https://golang.org/pkg",
+                        "https://guix.gnu.org/packages",
+                        "https://hackage.haskell.org",
+                        "https://helm.sh",
+                        "https://hub.docker.com",
+                        "https://juliahub.com",
+                        "https://lib.haxe.org",
+                        "https://luarocks.org",
+                        "https://marketplace.visualstudio.com",
+                        "https://melpa.org",
+                        "https://microsoft.com/en-us/store/apps",
+                        "https://nimble.directory",
+                        "https://nuget.org/packages",
+                        "https://opam.ocaml.org/packages",
+                        "https://openwrt.org/packages/index",
+                        "https://package.elm-lang.org",
+                        "https://packagecontrol.io",
+                        "https://packages.debian.org",
+                        "https://packages.gentoo.org",
+                        "https://packagist.org",
+                        "https://pear.php.net/packages.php",
+                        "https://pecl.php.net",
+                        "https://platformio.org/lib",
+                        "https://play.google.com/store",
+                        "https://plugins.gradle.org",
+                        "https://projects.eclipse.org",
+                        "https://pub.dev",
+                        "https://pypi.python.org",
+                        "https://registry.npmjs.org",
+                        "https://registry.terraform.io",
+                        "https://repo.hex.pm",
+                        "https://repo.maven.apache.org/maven2",
+                        "https://rubygems.org",
+                        "https://search.nixos.org/packages",
+                        "https://sourceforge.net",
+                        "https://wordpress.org/plugins"
+                    ]
+                },
+                "packageName": {
+                    "type": "string",
+                    "description": "Name or identifier of the affected software package as used in the package collection.",
+                    "minLength": 1,
+                    "maxLength": 2048
+                 },
+                "cpes": {
+                    "type": "array",
+                    "description": "Affected products defined by CPE. This is an array of CPE values (vulnerable and not), we use an array so that we can make multiple statements about the same version and they are separate (if we used a JSON object we'd essentially be keying on the CPE name and they would have to overlap). Also, this allows things like cveDataVersion or cveDescription to be applied directly to the product entry. This also allows more complex statements such as \"Product X between versions 10.2 and 10.8\" to be put in a machine-readable format. As well since multiple statements can be used multiple branches of the same product can be defined here.",
+                    "uniqueItems": true,
+                    "items": {
+                        "title": "CPE Name",
+                        "type": "string",
+                        "description":"Common Platform Enumeration (CPE) Name in either 2.2 or 2.3 format",
+                        "pattern": "([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\\-~%]*){0,6})|(cpe:2\\.3:[aho*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){4})",
+                        "minLength": 1,
+                        "maxLength": 2048
+                    }
+                },
+                "modules": {
+                    "type": "array",
+                    "description": "A list of the affected components, features, modules, sub-components, sub-products, APIs, commands, utilities, programs, or functionalities (optional).",
+                    "uniqueItems": true,
+                    "items": {
+                        "type": "string",
+                        "description": "Name of the affected component, feature, module, sub-component, sub-product, API, command, utility, program, or functionality (optional).",
+                        "minLength": 1,
+                        "maxLength": 4096
+                    }
+                },
+                "programFiles": {
+                    "type": "array",
+                    "description": "A list of the affected source code files (optional).",
+                    "uniqueItems": true,
+                    "items": {
+                        "description": "Name or path or location of the affected source code file.",
+                        "type": "string",
+                        "minLength": 1,
+                        "maxLength": 1024
+                    }
+                },
+                "programRoutines": {
+                    "type": "array",
+                    "description": "A list of the affected source code functions, methods, subroutines, or procedures (optional).",
+                    "uniqueItems": true,
+                    "items": {
+                        "type": "object",
+                        "description": "An object describing program routine.",
+                        "required": [
+                            "name"
+                        ],
+                        "properties": {
+                            "name": {
+                                "type": "string",
+                                "description": "Name of the affected source code file, function, method, subroutine, or procedure.",
+                                "minLength": 1,
+                                "maxLength": 4096
+                            }
+                        }
+                    }
+                },
+                "platforms": {
+                    "title": "Platforms",
+                    "description": "List of specific platforms if the vulnerability is only relevant in the context of these platforms (optional). Platforms may include execution environments, operating systems, virtualization technologies, hardware models, or computing architectures. The lack of this field or an empty array implies that the other fields are applicable to all relevant platforms.",
+                    "type": "array",
+                    "minItems": 1,
+                    "uniqueItems": true,
+                    "items": {
+                        "type": "string",
+                        "examples": ["iOS", "Android", "Windows", "macOS", "x86", "ARM", "64 bit", "Big Endian", "iPad", "Chromebook", "Docker", "Model T"],
+                        "maxLength": 1024
+                    }
+                },
+                "repo": {
+                    "description": "The URL of the source code repository, for informational purposes and/or to resolve git hash version ranges.",
+                    "$ref": "#/definitions/uriType"
+                },
+                "defaultStatus": {
+                    "description": "The default status for versions that are not otherwise listed in the versions list. If not specified, defaultStatus defaults to 'unknown'. Versions or defaultStatus may be omitted, but not both.",
+                    "$ref": "#/definitions/status"
+                },
+                "versions": {
+                    "type": "array",
+                    "description": "Set of product versions or version ranges related to the vulnerability. The versions satisfy the CNA Rules [8.1.2 requirement](https://cve.mitre.org/cve/cna/rules.html#section_8-1_cve_entry_information_requirements). Versions or defaultStatus may be omitted, but not both.",
+                    "minItems": 1,
+                    "uniqueItems": true,
+                    "items": {
+                        "type": "object",
+                        "description": "A single version or a range of versions, with vulnerability status.\n\nAn entry with only 'version' and 'status' indicates the status of a single version.\n\nOtherwise, an entry describes a range; it must include the 'versionType' property, to define the version numbering semantics in use, and 'limit', to indicate the non-inclusive upper limit of the range. The object describes the status for versions V such that 'version' <= V and V < 'limit', using the <= and < semantics defined for the specific kind of 'versionType'. Status changes within the range can be specified by an optional 'changes' list.\n\nThe algorithm to decide the status specified for a version V is:\n\n\tfor entry in product.versions {\n\t\tif entry.lessThan is not present and entry.lessThanOrEqual is not present and v == entry.version {\n\t\t\treturn entry.status\n\t\t}\n\t\tif (entry.lessThan is present and entry.version <= v and v < entry.lessThan) or\n\t\t   (entry.lessThanOrEqual is present and entry.version <= v and v <= entry.lessThanOrEqual) { // <= and < defined by entry.versionType\n\t\t\tstatus = entry.status\n\t\t\tfor change in entry.changes {\n\t\t\t\tif change.at <= v {\n\t\t\t\t\tstatus = change.status\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn status\n\t\t}\n\t}\n\treturn product.defaultStatus\n\n.",
+                        "oneOf": [
+                            {
+                                "required": ["version", "status"],
+                                "maxProperties": 2
+                            },
+                            {
+                                "required": ["version", "status", "versionType"],
+                                "oneOf": [
+                                    {"required": ["lessThan"]},
+                                    {"required": ["lessThanOrEqual"]}
+                                ]
+                            }
+                        ],
+                        "properties": {
+                            "version": {
+                                "description": "The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version.",
+                                "$ref": "#/definitions/version"
+                            },
+                            "status": {
+                                "description": "The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list.",
+                                "$ref": "#/definitions/status"
+                            },
+                            "versionType": {
+                                "type": "string",
+                                "description": "The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.",
+                                "minLength": 1,
+                                "maxLength": 128,
+                                "examples": [
+                                    "custom",
+                                    "git",
+                                    "maven",
+                                    "python",
+                                    "rpm",
+                                    "semver"
+                                ]
+                            },
+                            "lessThan": {
+                                "description": "The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified.",
+                                "$ref": "#/definitions/version"
+                            },
+                            "lessThanOrEqual": {
+                                "description": "The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3.",
+                                "$ref": "#/definitions/version"
+                            },
+                            "changes": {
+                                "type": "array",
+                                "description": "A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.",
+                                "minItems": 1,
+                                "uniqueItems": true,
+                                "items": {
+                                    "type": "object",
+                                    "description": "The start of a single status change during the range.",
+                                    "required": ["at", "status"],
+                                    "properties": {
+                                        "at": {
+                                            "description": "The version at which a status change occurs.",
+                                            "$ref": "#/definitions/version"
+                                        },
+                                        "status": {
+                                            "description": "The new status in the range starting at the given version.",
+                                            "$ref": "#/definitions/status"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "dataType": {
+            "description": "Indicates the type of information represented in the JSON instance.",
+            "type": "string",
+            "enum": [
+                "CVE_RECORD"
+            ]
+        },
+        "dataVersion": {
+            "description": "The version of the schema being used. Used to support multiple versions of this format.",
+            "type": "string",
+            "enum": ["5.0"]
+        },
+        "cveMetadataPublished": {
+            "description": "This is meta data about the CVE ID such as the CVE ID, who requested it, who assigned it, when it was requested, the current state (PUBLISHED, REJECTED, etc.) and so on.  These fields are controlled by the CVE Services.",
+            "type": "object",
+            "required": [
+                "cveId",
+                "assignerOrgId",
+                "state"
+            ],
+            "properties": {
+                "cveId": {
+                    "description": "The CVE identifier that this record pertains to.",
+                    "$ref": "#/definitions/cveId"
+                },
+                "assignerOrgId": {
+                    "$ref": "#/definitions/orgId",
+                    "description": "The UUID for the organization to which the CVE ID was originally assigned. This UUID can be used to lookup the organization record in the user registry service."
+                },
+                "assignerShortName": {
+                    "$ref": "#/definitions/shortName",
+                    "description": "The short name for the organization to which the CVE ID was originally assigned."
+                },
+                "requesterUserId": {
+                    "$ref": "#/definitions/userId",
+                    "description": "The user that requested the CVE identifier."
+                },
+                "dateUpdated": {
+                    "description": "The date/time the record was last updated.",
+                    "$ref": "#/definitions/timestamp"
+                },
+                "serial": {
+                    "type": "integer",
+                    "minimum": 1,
+                    "description": "The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED->REJECTED transition, and moves to the Published schema upon a REJECTED->PUBLISHED transition."
+                },
+                "dateReserved": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE."
+                },
+                "datePublished": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time the CVE Record was first published in the CVE List."
+                },
+                "state": {
+                    "description": "State of CVE - PUBLISHED, REJECTED.",
+                    "type": "string",
+                    "enum": ["PUBLISHED"]
+                }
+            },
+            "additionalProperties": false
+        },
+        "cveMetadataRejected": {
+            "type": "object",
+            "description": "This is meta data about the CVE ID such as the CVE ID, who requested it, who assigned it, when it was requested, the current state (PUBLISHED, REJECTED, etc.) and so on.  These fields are controlled by the CVE Services.",
+            "required": [
+                "cveId",
+                "assignerOrgId",
+                "state"
+            ],
+            "properties": {
+                "cveId": {
+                    "description": "The CVE identifier that this record pertains to.",
+                    "$ref": "#/definitions/cveId"
+                },
+                "assignerOrgId": {
+                    "$ref": "#/definitions/orgId",
+                    "description": "The UUID for the organization to which the CVE ID was originally assigned."
+                },
+                "assignerShortName": {
+                    "$ref": "#/definitions/shortName",
+                    "description": "The short name for the organization to which the CVE ID was originally assigned."
+                },
+                "serial": {
+                    "type": "integer",
+                    "minimum": 1,
+                    "description": "The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED->REJECTED transition, and moves to the Published schema upon a REJECTED->PUBLISHED transition."
+                },
+                "dateUpdated": {
+                    "description": "The date/time the record was last updated.",
+                    "$ref": "#/definitions/timestamp"
+                },
+                "datePublished": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time the CVE Record was first published in the CVE List."
+                },
+                "dateRejected": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time the CVE ID was rejected."
+                },
+                "state": {
+                    "type": "string",
+
+                    "description": "State of CVE - PUBLISHED, REJECTED.",
+                    "enum": ["REJECTED"]
+                },
+                "dateReserved": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE."
+                }
+            },
+            "additionalProperties": false
+        },
+        "providerMetadata": {
+            "type": "object",
+            "description": "Details related to the information container provider (CNA or ADP).",
+            "properties": {
+                "orgId": {
+                    "$ref": "#/definitions/orgId",
+                    "description": "The container provider's organizational UUID."
+                },
+                "shortName": {
+                    "$ref": "#/definitions/shortName",
+                    "description": "The container provider's organizational short name."
+                },
+                "dateUpdated": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "Timestamp to be set by the system of record at time of submission. If dateUpdated is provided to the system of record it will be replaced by the current timestamp at the time of submission."
+                }
+            },
+            "required": ["orgId"]
+        },
+        "cnaPublishedContainer": {
+            "description": "An object containing the vulnerability information provided by a CVE Numbering Authority (CNA) for a published CVE ID. There can only be one CNA container per CVE record since there can only be one assigning CNA. The CNA container must include the required information defined in the CVE Rules, which includes a product, version, problem type, prose description, and a reference.",
+            "type": "object",
+            "properties": {
+                "providerMetadata": {
+                    "$ref": "#/definitions/providerMetadata"
+                },
+                "dateAssigned": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "The date/time this CVE ID was associated with a vulnerability by a CNA."
+                },
+                "datePublic": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "If known, the date/time the vulnerability was disclosed publicly."
+                },
+                "title": {
+                    "type": "string",
+                    "description": "A title, headline, or a brief phrase summarizing the CVE record. Eg., Buffer overflow in Example Soft.",
+                    "minLength": 1,
+                    "maxLength": 256
+                },
+                "descriptions": {
+                    "$ref": "#/definitions/descriptions"
+                },
+                "affected": {
+                    "$ref": "#/definitions/affected"
+                },
+                "problemTypes": {
+                    "$ref": "#/definitions/problemTypes"
+                },
+                "references": {
+                    "$ref": "#/definitions/references"
+                },
+                "impacts": {
+                    "$ref": "#/definitions/impacts"
+                },
+                "metrics": {
+                    "$ref": "#/definitions/metrics"
+                },
+                "configurations": {
+                    "$ref": "#/definitions/configurations"
+                },
+                "workarounds": {
+                    "$ref": "#/definitions/workarounds"
+                },
+                "solutions": {
+                    "$ref": "#/definitions/solutions"
+                },
+                "exploits": {
+                    "$ref": "#/definitions/exploits"
+                },
+                "timeline": {
+                    "$ref": "#/definitions/timeline"
+                },
+                "credits": {
+                    "$ref": "#/definitions/credits"
+                },
+                "source": {
+                    "$ref": "#/definitions/source"
+                },
+                "tags": {
+                    "$ref": "#/definitions/cnaTags"
+                },
+                "taxonomyMappings": {
+                    "$ref": "#/definitions/taxonomyMappings"
+                }
+            },
+            "required": [
+                "providerMetadata",
+                "descriptions",
+                "affected",
+                "references"
+            ],
+            "patternProperties": {
+                "^x_[^.]*$": {}
+            },
+            "additionalProperties": false
+        },
+        "cnaRejectedContainer": {
+            "description": "An object containing the vulnerability information provided by a CVE Numbering Authority (CNA) for a rejected CVE ID. There can only be one CNA container per CVE record since there can only be one assigning CNA.",
+            "type": "object",
+            "properties": {
+                "providerMetadata": {
+                    "$ref": "#/definitions/providerMetadata"
+                },
+                "rejectedReasons": {
+                    "description": "Reasons for rejecting this CVE Record.",
+                    "$ref": "#/definitions/descriptions"
+                },
+                "replacedBy": {
+                    "type": "array",
+                    "description": "Contains an array of CVE IDs that this CVE ID was rejected in favor of because this CVE ID was assigned to the vulnerabilities.",
+                    "minItems": 1,
+                    "uniqueItems": true,
+                    "items": {
+                        "$ref": "#/definitions/cveId"
+                    }
+                }
+            },
+            "required": [
+                "providerMetadata",
+                "rejectedReasons"
+            ],
+            "patternProperties": {
+                "^x_[^.]*$": {}
+            },
+            "additionalProperties": false
+        },
+        "adpContainer": {
+            "description": "An object containing the vulnerability information provided by an Authorized Data Publisher (ADP). Since multiple ADPs can provide information for a CVE ID, an ADP container must indicate which ADP is the source of the information in the object.",
+            "type": "object",
+            "properties": {
+                "providerMetadata": {
+                    "$ref": "#/definitions/providerMetadata"
+                },
+                "datePublic": {
+                    "$ref": "#/definitions/timestamp",
+                    "description": "If known, the date/time the vulnerability was disclosed publicly."
+                },
+                "title": {
+                    "type": "string",
+                    "description": "A title, headline, or a brief phrase summarizing the information in an ADP container.",
+                    "minLength": 1,
+                    "maxLength": 256
+                },
+                "descriptions": {
+                    "$ref": "#/definitions/descriptions"
+                },
+                "affected": {
+                    "$ref": "#/definitions/affected"
+                },
+                "problemTypes": {
+                    "$ref": "#/definitions/problemTypes"
+                },
+                "references": {
+                    "$ref": "#/definitions/references"
+                },
+                "impacts": {
+                    "$ref": "#/definitions/impacts"
+                },
+                "metrics": {
+                    "$ref": "#/definitions/metrics"
+                },
+                "configurations": {
+                    "$ref": "#/definitions/configurations"
+                },
+                "workarounds": {
+                    "$ref": "#/definitions/workarounds"
+                },
+                "solutions": {
+                    "$ref": "#/definitions/solutions"
+                },
+                "exploits": {
+                    "$ref": "#/definitions/exploits"
+                },
+                "timeline": {
+                    "$ref": "#/definitions/timeline"
+                },
+                "credits": {
+                    "$ref": "#/definitions/credits"
+                },
+                "source": {
+                    "$ref": "#/definitions/source"
+                },
+                "tags": {
+                    "$ref": "#/definitions/adpTags"
+                },
+                "taxonomyMappings": {
+                    "$ref": "#/definitions/taxonomyMappings"
+                }
+            },
+            "required": [
+                "providerMetadata"
+            ],
+            "minProperties": 2,
+            "patternProperties": {
+                "^x_[^.]*$": {}
+            },
+            "additionalProperties": false
+        },
+        "affected": {
+            "type": "array",
+            "description": "List of affected products.",
+            "minItems": 1,
+            "items": {"$ref": "#/definitions/product"}
+        },
+        "description": {
+            "type": "object",
+            "description": "Text in a particular language with optional alternate markup or formatted representation (e.g., Markdown) or embedded media.",
+            "properties": {
+                "lang": {"$ref": "#/definitions/language"},
+                "value": {
+                    "type": "string",
+                    "description": "Plain text description.",
+                    "minLength": 1,
+                    "maxLength": 4096
+                },
+                "supportingMedia": {
+                    "type": "array",
+                    "title": "Supporting media",
+                    "description": "Supporting media data for the description such as markdown, diagrams, .. (optional). Similar to RFC 2397 each media object has three main parts: media type, media data value, and an optional boolean flag to indicate if the media data is base64 encoded.",
+                    "uniqueItems": true,
+                    "minItems": 1,
+                    "items": {
+                        "type": "object",
+                        "properties": {
+                            "type": {
+                                "type": "string",
+                                "title": "Media type",
+                                "minLength": 1,
+                                "maxLength": 256,
+                                "description": "RFC2046 compliant IANA Media type for eg., text/markdown, text/html.",
+                                "examples": [
+                                    "text/markdown",
+                                    "text/html",
+                                    "image/png",
+                                    "image/svg",
+                                    "audio/mp3"
+                                ]
+                            },
+                            "base64": {
+                                "type": "boolean",
+                                "title": "Encoding",
+                                "description": "If true then the value field contains the media data encoded in base64. If false then the value field contains the UTF-8 media content.",
+                                "default": false
+                            },
+                            "value": {
+                                "type": "string",
+                                "description": "Supporting media content, up to 16K. If base64 is true, this field stores base64 encoded data.",
+                                "minLength": 1,
+                                "maxLength": 16384
+                            }
+                        },
+                        "required": [
+                            "type",
+                            "value"
+                        ]
+                    }
+                }
+            },
+            "required": [
+                "lang",
+                "value"
+            ],
+            "additionalProperties": false
+        },
+        "englishLanguageDescription": {
+            "type": "object",
+            "description": "A description with lang set to an English language (en, en_US, en_UK, and so on).",
+            "properties": {"lang": {"$ref": "#/definitions/englishLanguage"}},
+            "required": ["lang"]
+        },
+        "descriptions": {
+            "type": "array",
+            "description": "A list of multi-lingual descriptions of the vulnerability. E.g., [PROBLEMTYPE] in [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] on [PLATFORMS] allows [ATTACKER] to [IMPACT] via [VECTOR]. OR [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] [ROOT CAUSE], which allows [ATTACKER] to [IMPACT] via [VECTOR].",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "$ref": "#/definitions/description"
+            },
+            "contains": {
+                "$ref": "#/definitions/englishLanguageDescription"
+            }
+        },
+        "problemTypes": {
+            "type": "array",
+            "description": "This is problem type information (e.g. CWE identifier). Must contain: At least one entry, can be text, OWASP, CWE, please note that while only one is required you can use more than one (or indeed all three) as long as they are correct). (CNA requirement: [PROBLEMTYPE]).",
+            "items": {
+                "type": "object",
+                "required": ["descriptions"],
+                "properties": {
+                    "descriptions": {
+                        "type": "array",
+                        "items": {
+                            "type": "object",
+                            "required": [
+                                "lang",
+                                "description"
+                            ],
+                            "properties": {
+                                "lang": {"$ref": "#/definitions/language"},
+                                "description": {
+                                    "type": "string",
+                                    "description": "Text description of problemType, or title from CWE or OWASP.",
+                                    "minLength": 1,
+                                    "maxLength": 4096
+                                },
+                                "cweId": {
+                                    "type": "string",
+                                    "description": "CWE ID of the CWE that best describes this problemType entry.",
+                                    "minLength": 5,
+                                    "maxLength": 9,
+                                    "pattern": "^CWE-[1-9][0-9]*$"
+                                },
+                                "type": {
+                                    "type": "string",
+                                    "description": "Problemtype source, text, OWASP, CWE, etc.,",
+                                    "minLength": 1,
+                                    "maxLength": 128
+                                },
+                                "references": {"$ref": "#/definitions/references"}
+                                }
+                        },
+                        "minItems": 1,
+                        "uniqueItems": true
+                    }
+                }
+            },
+            "minItems": 1,
+            "uniqueItems": true
+        },
+        "references": {
+            "type": "array",
+            "description": "This is reference data in the form of URLs or file objects (uuencoded and embedded within the JSON file, exact format to be decided, e.g. we may require a compressed format so the objects require unpacking before they are \"dangerous\").",
+            "items": {"$ref": "#/definitions/reference"},
+            "minItems": 1,
+            "maxItems": 512,
+            "uniqueItems": true
+        },
+        "impacts": {
+            "type": "array",
+            "description": "Collection of impacts of this vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "type": "object",
+                "description": "This is impact type information (e.g. a text description.",
+                "required": ["descriptions"],
+                "properties": {
+                    "capecId": {
+                        "type": "string",
+                        "description": "CAPEC ID that best relates to this impact.",
+                        "minLength": 7,
+                        "maxLength": 11,
+                        "pattern": "^CAPEC-[1-9][0-9]{0,4}$"
+                    },
+                    "descriptions": {
+                        "description": "Prose description of the impact scenario. At a minimum provide the description given by CAPEC.",
+                        "$ref": "#/definitions/descriptions"
+                    }
+                }
+            }
+        },
+        "metrics": {
+            "type": "array",
+            "description": "Collection of impact scores with attribution.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "type": "object",
+                "description": "This is impact type information (e.g. a text description, CVSSv2, CVSSv3, etc.). Must contain: At least one entry, can be text, CVSSv2, CVSSv3, others may be added.",
+                "anyOf": [
+                    {
+                        "required": ["cvssV3_1"]
+                    },
+                    {
+                        "required": ["cvssV3_0"]
+                    },
+                    {
+                        "required": ["cvssV2_0"]
+                    },
+                    {
+                        "required": ["other"]
+                    }
+                ],
+                "properties": {
+                    "format": {
+                        "type": "string",
+                        "description": "Name of the scoring format. This provides a bit of future proofing. Additional properties are not prohibited, so this will support the inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV44, format = 'cvssV44', other = cvssV4_4 JSON object. In the future, the other properties can be converted to score properties when they become part of the schema.",
+                        "minLength": 1,
+                        "maxLength": 64
+                    },
+                    "scenarios": {
+                        "type": "array",
+                        "description": "Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+                        "minItems": 1,
+                        "uniqueItems": true,
+                        "items": {
+                            "type": "object",
+                            "properties": {
+                                "lang": {"$ref": "#/definitions/language"},
+                                "value": {
+                                    "type": "string",
+                                    "default": "GENERAL",
+                                    "description": "Description of the scenario this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+                                    "minLength": 1,
+                                    "maxLength": 4096
+                                }
+                            },
+                            "required": [
+                                "lang",
+                                "value"
+                            ]
+                        }
+                    },
+                    "cvssV3_1": {"$ref": "imports/cvss/cvss-v3.1.json"},
+                    "cvssV3_0": {"$ref": "imports/cvss/cvss-v3.0.json"},
+                    "cvssV2_0": {"$ref": "imports/cvss/cvss-v2.0.json"},
+                    "other": {
+                        "type": "object",
+                        "description": "A non-standard impact description, may be prose or JSON block.",
+                        "required": [
+                            "type",
+                            "content"
+                        ],
+                        "properties": {
+                            "type": {
+                                "description": "Name of the non-standard impact metrics format used.",
+                                "type": "string",
+                                "minLength": 1,
+                                "maxLength": 128
+                            },
+                            "content": {
+                                "type": "object",
+                                "description": "JSON object not covered by another metrics format.",
+                                "minProperties": 1
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "configurations": {
+            "type": "array",
+            "description": "Configurations required for exploiting this vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "$ref": "#/definitions/description"
+            }
+        },
+        "workarounds": {
+            "type": "array",
+            "description": "Workarounds and mitigations for this vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "$ref": "#/definitions/description"
+            }
+        },
+        "solutions": {
+            "type": "array",
+            "description": "Information about solutions or remediations available for this vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "$ref": "#/definitions/description"
+            }
+        },
+        "exploits": {
+            "type": "array",
+            "description": "Information about exploits of the vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "$ref": "#/definitions/description"
+            }
+        },
+        "timeline": {
+            "type": "array",
+            "description": "This is timeline information for significant events about this vulnerability or changes to the CVE Record.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "type": "object",
+                "required": [
+                    "time",
+                    "lang",
+                    "value"
+                ],
+                "properties": {
+                    "time": {
+                        "description": "Timestamp representing when the event in the timeline occurred. The timestamp format is based on RFC3339 and ISO ISO8601, with an optional timezone. yyyy-MM-ddTHH:mm:ssZZZZ - if the timezone offset is not given, GMT (0000) is assumed.",
+                        "$ref": "#/definitions/timestamp"
+                    },
+                    "lang": {
+                        "description": "The language used in the description of the event. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code.",
+                        "$ref": "#/definitions/language"
+                    },
+                    "value": {
+                        "description": "A summary of the event.",
+                        "type": "string",
+                        "minLength": 1,
+                        "maxLength": 4096
+                    }
+                }
+            }
+        },
+        "credits": {
+            "type": "array",
+            "description": "Statements acknowledging specific people, organizations, or tools recognizing the work done in researching, discovering, remediating or helping with activities related to this CVE.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "type": "object",
+                "properties": {
+                    "lang": {
+                        "description": "The language used when describing the credits. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code.",
+                        "$ref": "#/definitions/language"
+                    },
+                    "value": {
+                        "type": "string",
+                        "minLength": 1,
+                        "maxLength": 4096
+                    },
+                    "user": {
+                        "description": "UUID of the user being credited if present in the CVE User Registry (optional). This UUID can be used to lookup the user record in the user registry service.",
+                        "$ref": "#/definitions/uuidType"
+                    },
+                    "type": {
+                        "type": "string",
+                        "description": "Type or role of the entity being credited (optional). finder: identifies the vulnerability.\nreporter: notifies the vendor of the vulnerability to a CNA.\nanalyst: validates the vulnerability to ensure accuracy or severity.\ncoordinator: facilitates the coordinated response process.\nremediation developer: prepares a code change or other remediation plans.\nremediation reviewer: reviews vulnerability remediation plans or code changes for effectiveness and completeness.\nremediation verifier: tests and verifies the vulnerability or its remediation.\ntool: names of tools used in vulnerability discovery or identification.\nsponsor: supports the vulnerability identification or remediation activities.",
+                        "default": "finder",
+                        "enum": [
+                            "finder",
+                            "reporter",
+                            "analyst",
+                            "coordinator",
+                            "remediation developer",
+                            "remediation reviewer",
+                            "remediation verifier",
+                            "tool",
+                            "sponsor",
+                            "other"
+                        ]
+                    }
+                },
+                "required": [
+                    "lang",
+                    "value"
+                ]
+            }
+        },
+        "source": {
+            "type": "object",
+            "description": "This is the source information (who discovered it, who researched it, etc.) and optionally a chain of CNA information (e.g. the originating CNA and subsequent parent CNAs who have processed it before it arrives at the MITRE root).\n Must contain: IF this is in the root level it MUST contain a CNA_chain entry, IF this source entry is NOT in the root (e.g. it is part of a vendor statement) then it must contain at least one type of data entry.",
+            "minProperties": 1
+        },
+        "language": {
+            "type": "string",
+            "description": "BCP 47 language code, language-region.",
+            "default": "en",
+            "pattern": "^[A-Za-z]{2,4}([_-][A-Za-z]{4})?([_-]([A-Za-z]{2}|[0-9]{3}))?$"
+        },
+        "englishLanguage": {
+            "type": "string",
+            "description": "BCP 47 language code, language-region, required to be English.",
+            "pattern": "^en([_-][A-Za-z]{4})?([_-]([A-Za-z]{2}|[0-9]{3}))?$"
+        },
+        "taxonomyMappings": {
+            "type": "array",
+            "description": "List of taxonomy items related to the vulnerability.",
+            "minItems": 1,
+            "uniqueItems": true,
+            "items": {
+                "type": "object",
+                "description": "",
+                "required": [
+                    "taxonomyName",
+                    "taxonomyRelations"
+                ],
+                "properties": {
+                    "taxonomyName": {
+                        "type": "string",
+                        "description": "The name of the taxonomy.",
+                        "minLength": 1,
+                        "maxLength": 128
+                    },
+                    "taxonomyVersion": {
+                        "type": "string",
+                        "description": "The version of taxonomy the identifiers come from.",
+                        "minLength": 1,
+                        "maxLength": 128
+                    },
+                    "taxonomyRelations": {
+                        "type": "array",
+                        "description": "",
+                        "minItems": 1,
+                        "uniqueItems": true,
+                        "items": {
+                            "type": "object",
+                            "description": "List of relationships to the taxonomy for the vulnerability.  Relationships can be between the taxonomy and the CVE or two taxonomy items.",
+                            "required": [
+                                "taxonomyId",
+                                "relationshipName",
+                                "relationshipValue"
+                            ],
+                            "properties": {
+                                "taxonomyId": {
+                                    "type": "string",
+                                    "description": "Identifier of the item in the taxonomy.  Used as the subject of the relationship.",
+                                    "minLength": 1,
+                                    "maxLength": 2048
+                                },
+                                "relationshipName": {
+                                    "type": "string",
+                                    "description": "A description of the relationship.",
+                                    "minLength": 1,
+                                    "maxLength": 128
+                                },
+                                "relationshipValue": {
+                                    "type": "string",
+                                    "description": "The target of the relationship.  Can be the CVE ID or another taxonomy identifier.",
+                                    "minLength": 1,
+                                    "maxLength": 2048
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "tagExtension": {
+            "type": "string",
+            "minLength": 2,
+            "maxLength": 128,
+            "pattern": "^x_.*$"
+        },
+        "cnaTags": {
+            "type": "array",
+            "description": "Tags provided by a CNA describing the CVE Record.",
+            "uniqueItems": true,
+            "minItems": 1,
+            "items": {
+                "oneOf": [
+                    {
+                        "$ref": "#/definitions/tagExtension"
+                    },
+                    {
+                        "$ref": "tags/cna-tags.json"
+                    }
+                ]
+            }
+        },
+        "adpTags": {
+            "type": "array",
+            "description": "Tags provided by an ADP describing the CVE Record.",
+            "uniqueItems": true,
+            "minItems": 1,
+            "items": {
+                "oneOf": [
+                    {
+                        "$ref": "#/definitions/tagExtension"
+                    },
+                    {
+                        "$ref": "tags/adp-tags.json"
+                    }
+                ]
+            }
+        }
+    },
+    "oneOf": [
+        {
+            "title": "Published",
+            "description": "When a CNA populates the data associated with a CVE ID as a CVE Record, the state of the CVE Record is Published.",
+            "properties": {
+                "dataType": {
+                    "$ref": "#/definitions/dataType"
+                },
+                "dataVersion": {
+                    "$ref": "#/definitions/dataVersion"
+                },
+                "cveMetadata": {
+                    "$ref": "#/definitions/cveMetadataPublished"
+                },
+                "containers": {
+                    "description": "A set of structures (called containers) used to store vulnerability information related to a specific CVE ID provided by a specific organization participating in the CVE program. Each container includes information provided by a different source.\n\nAt a minimum, a 'cna' container containing the vulnerability information provided by the CNA who initially assigned the CVE ID must be included.\n\nThere can only be one 'cna' container, as there can only be one assigning CNA. However, there can be multiple 'adp' containers, allowing multiple organizations participating in the CVE program to add additional information related to the vulnerability. For the most part, the 'cna' and 'adp' containers contain the same properties. The main differences are the source of the information. The 'cna' container requires the CNA to include certain fields, while the 'adp' container does not.",
+                    "type": "object",
+                    "properties": {
+                        "cna": {"$ref": "#/definitions/cnaPublishedContainer"},
+                        "adp": {
+                            "type": "array",
+                            "items": {"$ref": "#/definitions/adpContainer"},
+                            "minItems": 1,
+                            "uniqueItems": true
+                        }
+                    },
+                    "required": ["cna"],
+                    "additionalProperties": false
+                }
+            },
+            "required": [
+                "dataType",
+                "dataVersion",
+                "cveMetadata",
+                "containers"
+            ],
+            "additionalProperties": false
+        },
+        {
+            "title": "Rejected",
+            "description": "If the CVE ID and associated CVE Record should no longer be used, the CVE Record is placed in the Rejected state. A Rejected CVE Record remains on the CVE List so that users can know when it is invalid.",
+            "properties": {
+                "dataType": {
+                    "$ref": "#/definitions/dataType"
+                },
+                "dataVersion": {
+                    "$ref": "#/definitions/dataVersion"
+                },
+                "cveMetadata": {
+                    "$ref": "#/definitions/cveMetadataRejected"
+                },
+                "containers": {
+                    "description": "A set of structures (called containers) used to store vulnerability information related to a specific CVE ID provided by a specific organization participating in the CVE program. Each container includes information provided by a different source.\n\nAt minimum, a 'cna' container containing the vulnerability information provided by the CNA who initially assigned the CVE ID must be included.\n\nThere can only be one 'cna' container, as there can only be one assigning CNA.",
+                    "type": "object",
+                    "properties": {
+                        "cna": {"$ref": "#/definitions/cnaRejectedContainer"}
+                    },
+                    "required": ["cna"],
+                    "additionalProperties": false
+                }
+            },
+            "required": [
+                "dataType",
+                "dataVersion",
+                "cveMetadata",
+                "containers"
+            ],
+            "additionalProperties": false
+        }
+    ]
+}
diff --git a/data/README.md b/data/README.md
new file mode 100644
index 0000000..57d3b21
--- /dev/null
+++ b/data/README.md
@@ -0,0 +1,11 @@
+# Generating cve_model
+
+Modify the jsonschema to make it compatible with datamodel-code-generator which is quite buggy.
+
+- Remove $id
+- Remove tags: and imports: prefix in $ref
+
+```shell
+pip install datamodel-code-generator
+datamodel-codegen --input data/CVE_JSON_5.0_schema.json --input-file-type jsonschema --output vdb/lib/cve_model --output-model-type pydantic_v2.BaseModel --target-python-version 3.10 --use-annotated --class-name CVE
+```
diff --git a/data/imports/cvss/README.md b/data/imports/cvss/README.md
new file mode 100644
index 0000000..c191063
--- /dev/null
+++ b/data/imports/cvss/README.md
@@ -0,0 +1,1 @@
+The files in this folder are included here as a stable mirror of the CVSS JSON schemas [maintained](https://www.first.org/cvss/data-representations) by the [Forum of Incident Response and Security Teams](https://www.first.org/) (FIRST).
\ No newline at end of file
diff --git a/data/imports/cvss/cvss-v2.0.json b/data/imports/cvss/cvss-v2.0.json
new file mode 100644
index 0000000..efe9917
--- /dev/null
+++ b/data/imports/cvss/cvss-v2.0.json
@@ -0,0 +1,104 @@
+{
+    "license": [
+        "Copyright (c) 2017, FIRST.ORG, INC.",
+        "All rights reserved.",
+        "",
+        "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ",
+        "following conditions are met:",
+        "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ",
+        "   disclaimer.",
+        "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ",
+        "   following disclaimer in the documentation and/or other materials provided with the distribution.",
+        "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ",
+        "   products derived from this software without specific prior written permission.",
+        "",
+        "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ",
+        "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ",
+        "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ",
+        "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ",
+        "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ",
+        "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ",
+        "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+    ],
+
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "JSON Schema for Common Vulnerability Scoring System version 2.0",
+    "id": "https://www.first.org/cvss/cvss-v2.0.json?20170531",
+    "type": "object",
+    "definitions": {
+        "accessVectorType": {
+            "type": "string",
+            "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL" ]
+        },
+        "accessComplexityType": {
+            "type": "string",
+            "enum": [ "HIGH", "MEDIUM", "LOW" ]
+        },
+        "authenticationType": {
+            "type": "string",
+            "enum": [ "MULTIPLE", "SINGLE", "NONE" ]
+        },
+        "ciaType": {
+            "type": "string",
+            "enum": [ "NONE", "PARTIAL", "COMPLETE" ]
+        },
+        "exploitabilityType": {
+            "type": "string",
+            "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ]
+        },
+        "remediationLevelType": {
+            "type": "string",
+            "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ]
+        },
+        "reportConfidenceType": {
+            "type": "string",
+            "enum": [ "UNCONFIRMED", "UNCORROBORATED", "CONFIRMED", "NOT_DEFINED" ]
+        },
+        "collateralDamagePotentialType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "LOW_MEDIUM", "MEDIUM_HIGH", "HIGH", "NOT_DEFINED" ]
+        },
+        "targetDistributionType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ]
+        },
+        "ciaRequirementType": {
+            "type": "string",
+            "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ]
+        },
+        "scoreType": {
+            "type": "number",
+            "minimum": 0,
+            "maximum": 10
+        }
+    },
+    "properties": {
+        "version": {
+            "description": "CVSS Version",
+            "type": "string",
+            "enum": [ "2.0" ]
+        },
+        "vectorString": {
+            "type": "string",
+            "pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
+        },
+        "accessVector":                   { "$ref": "#/definitions/accessVectorType" },
+        "accessComplexity":               { "$ref": "#/definitions/accessComplexityType" },
+        "authentication":                 { "$ref": "#/definitions/authenticationType" },
+        "confidentialityImpact":          { "$ref": "#/definitions/ciaType" },
+        "integrityImpact":                { "$ref": "#/definitions/ciaType" },
+        "availabilityImpact":             { "$ref": "#/definitions/ciaType" },
+        "baseScore":                      { "$ref": "#/definitions/scoreType" },
+        "exploitability":                 { "$ref": "#/definitions/exploitabilityType" },
+        "remediationLevel":               { "$ref": "#/definitions/remediationLevelType" },
+        "reportConfidence":               { "$ref": "#/definitions/reportConfidenceType" },
+        "temporalScore":                  { "$ref": "#/definitions/scoreType" },
+        "collateralDamagePotential":      { "$ref": "#/definitions/collateralDamagePotentialType" },
+        "targetDistribution":             { "$ref": "#/definitions/targetDistributionType" },
+        "confidentialityRequirement":     { "$ref": "#/definitions/ciaRequirementType" },
+        "integrityRequirement":           { "$ref": "#/definitions/ciaRequirementType" },
+        "availabilityRequirement":        { "$ref": "#/definitions/ciaRequirementType" },
+        "environmentalScore":             { "$ref": "#/definitions/scoreType" }
+    },
+    "required": [ "version", "vectorString", "baseScore" ]
+}
diff --git a/data/imports/cvss/cvss-v3.0.json b/data/imports/cvss/cvss-v3.0.json
new file mode 100644
index 0000000..28b3c38
--- /dev/null
+++ b/data/imports/cvss/cvss-v3.0.json
@@ -0,0 +1,143 @@
+{
+    "license": [
+        "Copyright (c) 2017, FIRST.ORG, INC.",
+        "All rights reserved.",
+        "",
+        "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ",
+        "following conditions are met:",
+        "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ",
+        "   disclaimer.",
+        "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ",
+        "   following disclaimer in the documentation and/or other materials provided with the distribution.",
+        "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ",
+        "   products derived from this software without specific prior written permission.",
+        "",
+        "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ",
+        "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ",
+        "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ",
+        "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ",
+        "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ",
+        "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ",
+        "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+    ],
+
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title": "JSON Schema for Common Vulnerability Scoring System version 3.0",
+    "id": "https://www.first.org/cvss/cvss-v3.0.json?20170531",
+    "type": "object",
+    "definitions": {
+        "attackVectorType": {
+            "type": "string",
+            "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL" ]
+        },
+        "modifiedAttackVectorType": {
+            "type": "string",
+            "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL", "NOT_DEFINED" ]
+        },
+        "attackComplexityType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW" ]
+        },
+        "modifiedAttackComplexityType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NOT_DEFINED" ]
+        },
+        "privilegesRequiredType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NONE" ]
+        },
+        "modifiedPrivilegesRequiredType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NONE", "NOT_DEFINED" ]
+        },
+        "userInteractionType": {
+            "type": "string",
+            "enum": [ "NONE", "REQUIRED" ]
+        },
+        "modifiedUserInteractionType": {
+            "type": "string",
+            "enum": [ "NONE", "REQUIRED", "NOT_DEFINED" ]
+        },
+        "scopeType": {
+            "type": "string",
+            "enum": [ "UNCHANGED", "CHANGED" ]
+        },
+        "modifiedScopeType": {
+            "type": "string",
+            "enum": [ "UNCHANGED", "CHANGED", "NOT_DEFINED" ]
+        },
+        "ciaType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "HIGH" ]
+        },
+        "modifiedCiaType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "HIGH", "NOT_DEFINED" ]
+        },
+        "exploitCodeMaturityType": {
+            "type": "string",
+            "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ]
+        },
+        "remediationLevelType": {
+            "type": "string",
+            "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ]
+        },
+        "confidenceType": {
+            "type": "string",
+            "enum": [ "UNKNOWN", "REASONABLE", "CONFIRMED", "NOT_DEFINED" ]
+        },
+        "ciaRequirementType": {
+            "type": "string",
+            "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ]
+        },
+        "scoreType": {
+            "type": "number",
+            "minimum": 0,
+            "maximum": 10
+        },
+        "severityType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL" ]
+        }
+    },
+    "properties": {
+        "version": {
+            "description": "CVSS Version",
+            "type": "string",
+            "enum": [ "3.0" ]
+        },
+        "vectorString": {
+            "type": "string",
+            "pattern": "^CVSS:3[.]0/((AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
+        },
+        "attackVector":                   { "$ref": "#/definitions/attackVectorType" },
+        "attackComplexity":               { "$ref": "#/definitions/attackComplexityType" },
+        "privilegesRequired":             { "$ref": "#/definitions/privilegesRequiredType" },
+        "userInteraction":                { "$ref": "#/definitions/userInteractionType" },
+        "scope":                          { "$ref": "#/definitions/scopeType" },
+        "confidentialityImpact":          { "$ref": "#/definitions/ciaType" },
+        "integrityImpact":                { "$ref": "#/definitions/ciaType" },
+        "availabilityImpact":             { "$ref": "#/definitions/ciaType" },
+        "baseScore":                      { "$ref": "#/definitions/scoreType" },
+        "baseSeverity":                   { "$ref": "#/definitions/severityType" },
+        "exploitCodeMaturity":            { "$ref": "#/definitions/exploitCodeMaturityType" },
+        "remediationLevel":               { "$ref": "#/definitions/remediationLevelType" },
+        "reportConfidence":               { "$ref": "#/definitions/confidenceType" },
+        "temporalScore":                  { "$ref": "#/definitions/scoreType" },
+        "temporalSeverity":               { "$ref": "#/definitions/severityType" },
+        "confidentialityRequirement":     { "$ref": "#/definitions/ciaRequirementType" },
+        "integrityRequirement":           { "$ref": "#/definitions/ciaRequirementType" },
+        "availabilityRequirement":        { "$ref": "#/definitions/ciaRequirementType" },
+        "modifiedAttackVector":           { "$ref": "#/definitions/modifiedAttackVectorType" },
+        "modifiedAttackComplexity":       { "$ref": "#/definitions/modifiedAttackComplexityType" },
+        "modifiedPrivilegesRequired":     { "$ref": "#/definitions/modifiedPrivilegesRequiredType" },
+        "modifiedUserInteraction":        { "$ref": "#/definitions/modifiedUserInteractionType" },
+        "modifiedScope":                  { "$ref": "#/definitions/modifiedScopeType" },
+        "modifiedConfidentialityImpact":  { "$ref": "#/definitions/modifiedCiaType" },
+        "modifiedIntegrityImpact":        { "$ref": "#/definitions/modifiedCiaType" },
+        "modifiedAvailabilityImpact":     { "$ref": "#/definitions/modifiedCiaType" },
+        "environmentalScore":             { "$ref": "#/definitions/scoreType" },
+        "environmentalSeverity":          { "$ref": "#/definitions/severityType" }
+    },
+    "required": [ "version", "vectorString", "baseScore", "baseSeverity" ]
+}
diff --git a/data/imports/cvss/cvss-v3.1.json b/data/imports/cvss/cvss-v3.1.json
new file mode 100644
index 0000000..fdb2cd0
--- /dev/null
+++ b/data/imports/cvss/cvss-v3.1.json
@@ -0,0 +1,143 @@
+{
+    "license": [
+        "Copyright (c) 2021, FIRST.ORG, INC.",
+        "All rights reserved.",
+        "",
+        "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ",
+        "following conditions are met:",
+        "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ",
+        "   disclaimer.",
+        "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ",
+        "   following disclaimer in the documentation and/or other materials provided with the distribution.",
+        "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ",
+        "   products derived from this software without specific prior written permission.",
+        "",
+        "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ",
+        "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ",
+        "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ",
+        "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ",
+        "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ",
+        "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ",
+        "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+    ],
+
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "JSON Schema for Common Vulnerability Scoring System version 3.1",
+    "id": "https://www.first.org/cvss/cvss-v3.1.json?20210501",
+    "type": "object",
+    "definitions": {
+        "attackVectorType": {
+            "type": "string",
+            "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL" ]
+        },
+        "modifiedAttackVectorType": {
+            "type": "string",
+            "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL", "NOT_DEFINED" ]
+        },
+        "attackComplexityType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW" ]
+        },
+        "modifiedAttackComplexityType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NOT_DEFINED" ]
+        },
+        "privilegesRequiredType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NONE" ]
+        },
+        "modifiedPrivilegesRequiredType": {
+            "type": "string",
+            "enum": [ "HIGH", "LOW", "NONE", "NOT_DEFINED" ]
+        },
+        "userInteractionType": {
+            "type": "string",
+            "enum": [ "NONE", "REQUIRED" ]
+        },
+        "modifiedUserInteractionType": {
+            "type": "string",
+            "enum": [ "NONE", "REQUIRED", "NOT_DEFINED" ]
+        },
+        "scopeType": {
+            "type": "string",
+            "enum": [ "UNCHANGED", "CHANGED" ]
+        },
+        "modifiedScopeType": {
+            "type": "string",
+            "enum": [ "UNCHANGED", "CHANGED", "NOT_DEFINED" ]
+        },
+        "ciaType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "HIGH" ]
+        },
+        "modifiedCiaType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "HIGH", "NOT_DEFINED" ]
+        },
+        "exploitCodeMaturityType": {
+            "type": "string",
+            "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ]
+        },
+        "remediationLevelType": {
+            "type": "string",
+            "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ]
+        },
+        "confidenceType": {
+            "type": "string",
+            "enum": [ "UNKNOWN", "REASONABLE", "CONFIRMED", "NOT_DEFINED" ]
+        },
+        "ciaRequirementType": {
+            "type": "string",
+            "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ]
+        },
+        "scoreType": {
+            "type": "number",
+            "minimum": 0,
+            "maximum": 10
+        },
+        "severityType": {
+            "type": "string",
+            "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL" ]
+        }
+    },
+    "properties": {
+        "version": {
+            "description": "CVSS Version",
+            "type": "string",
+            "enum": [ "3.1" ]
+        },
+        "vectorString": {
+            "type": "string",
+            "pattern": "^CVSS:3[.]1/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
+        },
+        "attackVector":                   { "$ref": "#/definitions/attackVectorType" },
+        "attackComplexity":               { "$ref": "#/definitions/attackComplexityType" },
+        "privilegesRequired":             { "$ref": "#/definitions/privilegesRequiredType" },
+        "userInteraction":                { "$ref": "#/definitions/userInteractionType" },
+        "scope":                          { "$ref": "#/definitions/scopeType" },
+        "confidentialityImpact":          { "$ref": "#/definitions/ciaType" },
+        "integrityImpact":                { "$ref": "#/definitions/ciaType" },
+        "availabilityImpact":             { "$ref": "#/definitions/ciaType" },
+        "baseScore":                      { "$ref": "#/definitions/scoreType" },
+        "baseSeverity":                   { "$ref": "#/definitions/severityType" },
+        "exploitCodeMaturity":            { "$ref": "#/definitions/exploitCodeMaturityType" },
+        "remediationLevel":               { "$ref": "#/definitions/remediationLevelType" },
+        "reportConfidence":               { "$ref": "#/definitions/confidenceType" },
+        "temporalScore":                  { "$ref": "#/definitions/scoreType" },
+        "temporalSeverity":               { "$ref": "#/definitions/severityType" },
+        "confidentialityRequirement":     { "$ref": "#/definitions/ciaRequirementType" },
+        "integrityRequirement":           { "$ref": "#/definitions/ciaRequirementType" },
+        "availabilityRequirement":        { "$ref": "#/definitions/ciaRequirementType" },
+        "modifiedAttackVector":           { "$ref": "#/definitions/modifiedAttackVectorType" },
+        "modifiedAttackComplexity":       { "$ref": "#/definitions/modifiedAttackComplexityType" },
+        "modifiedPrivilegesRequired":     { "$ref": "#/definitions/modifiedPrivilegesRequiredType" },
+        "modifiedUserInteraction":        { "$ref": "#/definitions/modifiedUserInteractionType" },
+        "modifiedScope":                  { "$ref": "#/definitions/modifiedScopeType" },
+        "modifiedConfidentialityImpact":  { "$ref": "#/definitions/modifiedCiaType" },
+        "modifiedIntegrityImpact":        { "$ref": "#/definitions/modifiedCiaType" },
+        "modifiedAvailabilityImpact":     { "$ref": "#/definitions/modifiedCiaType" },
+        "environmentalScore":             { "$ref": "#/definitions/scoreType" },
+        "environmentalSeverity":          { "$ref": "#/definitions/severityType" }
+    },
+    "required": [ "version", "vectorString", "baseScore", "baseSeverity" ]
+}
diff --git a/data/tags/adp-tags.json b/data/tags/adp-tags.json
new file mode 100644
index 0000000..d16868a
--- /dev/null
+++ b/data/tags/adp-tags.json
@@ -0,0 +1,7 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "https://cve.mitre.org/cve/v5_00/tags/adp/",
+    "type": "string",
+    "description": "disputed: When one party disagrees with another party's assertion that a particular issue in software is a vulnerability, a CVE Record assigned to that issue may be tagged as being 'disputed'.",
+    "enum": ["disputed"]
+}
diff --git a/data/tags/cna-tags.json b/data/tags/cna-tags.json
new file mode 100644
index 0000000..35633c7
--- /dev/null
+++ b/data/tags/cna-tags.json
@@ -0,0 +1,7 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "https://cve.mitre.org/cve/v5_00/tags/cna/",
+    "type": "string",
+    "description": "exclusively-hosted-service: All known software and/or hardware affected by this CVE Record is known to exist only in the affected hosted service. If the vulnerability affects both hosted and on-prem software and/or hardware, then the tag should not be used.\n\nunsupported-when-assigned: Used by the assigning CNA to indicate that when a request for a CVE assignment was received, the product was already end-of-life (EOL) or a product or specific version was deemed not to be supported by the vendor. This tag should only be applied to a CVE Record when all affected products or version lines referenced in the CVE-Record are EOL.\n\ndisputed: When one party disagrees with another party's assertion that a particular issue in software is a vulnerability, a CVE Record assigned to that issue may be tagged as being 'disputed'.",
+    "enum": ["unsupported-when-assigned", "exclusively-hosted-service", "disputed"]
+}
diff --git a/data/tags/reference-tags.json b/data/tags/reference-tags.json
new file mode 100644
index 0000000..41a1aeb
--- /dev/null
+++ b/data/tags/reference-tags.json
@@ -0,0 +1,27 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "https://cve.mitre.org/cve/v5_00/tags/reference/",
+    "type": "string",
+    "description": "broken-link: The reference link is returning a 404 error, or the site is no longer online.\n\ncustomer-entitlement: Similar to Privileges Required, but specific to references that require non-public/paid access for customers of the particular vendor.\n\nexploit: Reference contains an in-depth/detailed description of steps to exploit a vulnerability OR the reference contains any legitimate Proof of Concept (PoC) code or exploit kit.\n\ngovernment-resource: All reference links that are from a government agency or organization should be given the Government Resource tag.\n\nissue-tracking: The reference is a post from a bug tracking tool such as MantisBT, Bugzilla, JIRA, Github Issues, etc...\n\nmailing-list: The reference is from a mailing list -- often specific to a product or vendor.\n\nmitigation: The reference contains information on steps to mitigate against the vulnerability in the event a patch can't be applied or is unavailable or for EOL product situations.\n\nnot-applicable: The reference link is not applicable to the vulnerability and was likely associated by MITRE accidentally (should be used sparingly).\n\npatch: The reference contains an update to the software that fixes the vulnerability.\n\npermissions-required: The reference link provided is blocked by a logon page. If credentials are required to see any information this tag must be applied.\n\nmedia-coverage: The reference is from a media outlet such as a newspaper, magazine, social media, or weblog. This tag is not intended to apply to any individual's personal social media account. It is strictly intended for public media entities.\n\nproduct: A reference appropriate for describing a product for the purpose of CPE or SWID.\n\nrelated: A reference that is for a related (but not the same) vulnerability.\n\nrelease-notes: The reference is in the format of a vendor or open source project's release notes or change log.\n\nsignature: The reference contains a method to detect or prevent the presence or exploitation of the vulnerability.\n\ntechnical-description: The reference contains in-depth technical information about a vulnerability and its exploitation process, typically in the form of a presentation or whitepaper.\n\nthird-party-advisory: Advisory is from an organization that is not the vulnerable product's vendor/publisher/maintainer.\n\nvendor-advisory: Advisory is from the vendor/publisher/maintainer of the product or the parent organization.\n\nvdb-entry: VDBs are loosely defined as sites that provide information about this vulnerability, such as advisories, with identifiers. Included VDBs are free to access, substantially public, and have broad scope and coverage (not limited to a single vendor or research organization). See: https://www.first.org/global/sigs/vrdx/vdb-catalog",
+    "enum": [
+        "broken-link",
+        "customer-entitlement",
+        "exploit",
+        "government-resource",
+        "issue-tracking",
+        "mailing-list",
+        "mitigation",
+        "not-applicable",
+        "patch",
+        "permissions-required",
+        "media-coverage",
+        "product",
+        "related",
+        "release-notes",
+        "signature",
+        "technical-description",
+        "third-party-advisory",
+        "vendor-advisory",
+        "vdb-entry"
+    ]
+}
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index a00363b..49d9ae9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,6 +14,7 @@ dependencies = [
     "semver>=3.0.0",
     "packageurl-python",
     "cvss",
+    "pydantic[email]"
 ]
 requires-python = ">=3.10"
 readme = "README.md"
@@ -56,8 +57,6 @@ packages = ["test", "vdb", "vdb.lib"]
 [tool.pytest.ini_options]
 addopts="--showlocals -v --cov-report=term-missing --no-cov-on-fail --cov vdb"
 
-[tool.flake8]
-ignore = "E203, E266, E501, W503, W605"
-max-line-length = 80
-max-complexity = 18
-select = "B,C,E,F,W,T4,B9"
+[tool.pylint]
+disable = ["broad-exception-caught", "too-many-branches", "too-many-statements", "too-many-nested-blocks", "too-many-locals", "missing-function-docstring", "too-many-lines", "missing-module-docstring"]
+ignore-paths = ["vdb/lib/cve_model/*"]
diff --git a/vdb/cli.py b/vdb/cli.py
index 109f617..855a98b 100644
--- a/vdb/cli.py
+++ b/vdb/cli.py
@@ -10,11 +10,10 @@
 from tabulate import tabulate
 
 from vdb.lib import config
-from vdb.lib import db as dbLib
+from vdb.lib import db as db_lib
 from vdb.lib.aqua import AquaSource
 from vdb.lib.gha import GitHubSource
 from vdb.lib.npm import NpmSource
-from vdb.lib.nvd import NvdSource
 from vdb.lib.osv import OSVSource
 
 logging.basicConfig(
@@ -177,21 +176,19 @@ def main():
         # AquaSource also includes NVD
         if args.cache_os:
             sources.insert(0, AquaSource())
-        elif not args.only_aqua and not args.only_osv:
-            sources.append(NvdSource())
         for s in sources:
             LOG.info("Refreshing %s", s.__class__.__name__)
             s.refresh()
     elif args.sync:
-        for s in [GitHubSource(), NvdSource()]:
+        for s in (GitHubSource(),):
             LOG.info("Syncing %s", s.__class__.__name__)
             s.download_recent()
     if args.sync_npm:
-        for s in [NpmSource()]:
+        for s in (NpmSource(),):
             LOG.info("Syncing %s", s.__class__.__name__)
             s.download_recent()
     if args.sync_github:
-        for s in [GitHubSource()]:
+        for s in (GitHubSource(),):
             LOG.info("Syncing %s", s.__class__.__name__)
             s.download_recent()
     if args.search_npm:
@@ -199,29 +196,29 @@ def main():
         results = source.bulk_search(config.NPM_APP_INFO, [args.search_npm])
         print_results(results)
     if args.list:
-        db = dbLib.get()
-        results = dbLib.list_all_occurrence(db)
+        db = db_lib.get()
+        results = db_lib.list_all_occurrence(db)
         print_results(results)
     elif args.search:
         LOG.info("Vulnerability database loaded from %s", config.VDB_BIN_FILE)
-        db = dbLib.get()
+        db = db_lib.get()
         if args.search.startswith("pkg:"):
-            index_hits = dbLib.bulk_index_search([{"purl": args.search}])
-            results = dbLib.pkg_bulk_search(db, index_hits)
+            index_hits = db_lib.bulk_index_search([{"purl": args.search}])
+            results = db_lib.pkg_bulk_search(db, index_hits)
             print_results(results)
         else:
             search_list = re.split(r"[,|;]", args.search)
             for pkg_info in search_list:
                 pstr = re.split(r"[:=@]", pkg_info)
                 if pstr:
-                    if len(pstr) == 2 and dbLib.index_search(*pstr):
-                        results = dbLib.pkg_search(db, *pstr)
+                    if len(pstr) == 2 and db_lib.index_search(*pstr):
+                        results = db_lib.pkg_search(db, *pstr)
                         print_results(results)
                     elif len(pstr) == 3:
-                        index_hits = dbLib.bulk_index_search(
+                        index_hits = db_lib.bulk_index_search(
                             [{"vendor": pstr[0], "name": pstr[1], "version": pstr[2]}]
                         )
-                        results = dbLib.pkg_bulk_search(db, index_hits)
+                        results = db_lib.pkg_bulk_search(db, index_hits)
                         print_results(results)
                     else:
                         print("No vulnerability found!")
diff --git a/vdb/lib/__init__.py b/vdb/lib/__init__.py
index 43a9307..8287ff5 100644
--- a/vdb/lib/__init__.py
+++ b/vdb/lib/__init__.py
@@ -1,4 +1,3 @@
-import json
 import os
 import re
 import tempfile
@@ -6,6 +5,8 @@
 from datetime import datetime
 from enum import Enum
 
+import orjson
+
 # Known application package types
 KNOWN_PKG_TYPES = [
     "composer",
@@ -88,7 +89,7 @@ def download_recent(cls, local_store=True):
 
     @classmethod
     @abstractmethod
-    def bulk_search(cls):
+    def bulk_search(cls, app_info, pkg_list):
         pass
 
     @classmethod
@@ -129,7 +130,7 @@ def __str__(self):
         return self.value
 
 
-def convert_time(time_str):
+def convert_time(time_str: str) -> datetime | None:
     """Converts a variety of datetime formats to datetime object
 
     :param time_str: String time to convert
@@ -160,71 +161,141 @@ def convert_time(time_str):
     return dt or time_str
 
 
-class Vulnerability(object):
-    """Vulnerability"""
+class CvssV3:
+    """CVSS v3 representation"""
+
+    base_score: float
+    exploitability_score: float
+    impact_score: float
+    attack_vector: str
+    attack_complexity: str
+    privileges_required: str
+    user_interaction: str
+    scope: str
+    confidentiality_impact: str
+    integrity_impact: str
+    availability_impact: str
+    vector_string: str
 
     def __init__(
         self,
-        vid,
-        problem_type,
-        score,
-        severity,
-        description,
-        related_urls,
-        details,
-        cvss_v3,
-        source_update_time,
-        source_orig_time,
+        base_score,
+        exploitability_score,
+        impact_score,
+        attack_vector,
+        attack_complexity,
+        privileges_required,
+        user_interaction,
+        scope,
+        confidentiality_impact,
+        integrity_impact,
+        availability_impact,
+        vector_string=None,
     ):
-        self.id = vid
-        self.problem_type = problem_type
-        self.score = score
-        self.severity = Severity.from_str(severity)
-        self.description = description
-        self.related_urls = related_urls
-        self.details = details
-        self.cvss_v3 = cvss_v3
-        self.source_update_time: datetime = convert_time(source_update_time)
-        self.source_orig_time: datetime = convert_time(source_orig_time)
+        self.base_score = base_score
+        self.exploitability_score = exploitability_score
+        self.impact_score = impact_score
+        self.attack_vector = attack_vector
+        self.attack_complexity = attack_complexity
+        self.privileges_required = privileges_required
+        self.user_interaction = user_interaction
+        self.scope = scope
+        self.confidentiality_impact = confidentiality_impact
+        self.integrity_impact = integrity_impact
+        self.availability_impact = availability_impact
+        self.vector_string = vector_string
 
-    def __repr__(self):
-        return json.dumps(
+    def to_dict(self):
+        return {
+            "base_score": self.base_score,
+            "exploitability_score": self.exploitability_score,
+            "impact_score": self.impact_score,
+            "attack_vector": self.attack_vector,
+            "attack_complexity": self.attack_complexity,
+            "privileges_required": self.privileges_required,
+            "user_interaction": self.user_interaction,
+            "scope": self.scope,
+            "confidentiality_impact": self.confidentiality_impact,
+            "integrity_impact": self.integrity_impact,
+            "availability_impact": self.availability_impact,
+            "vector_string": self.vector_string,
+        }
+
+
+class PackageIssue:
+    """Package issue class"""
+
+    def __init__(
+        self,
+        affected_location,
+        fixed_location,
+        mii=None,
+        mai=None,
+        mie=None,
+        mae=None,
+    ):
+        self.affected_location = VulnerabilityLocation.from_values(
+            affected_location,
+            mii,
+            mai,
+            mie,
+            mae,
+        )
+        # If there is no fixed_location but there is max excluded version then consider that as the fix
+        if not fixed_location and mae:
+            self.fixed_location = mae
+        else:
+            if fixed_location and fixed_location.startswith("cpe"):
+                # Extract the fixed version from fixed_location cpe uri
+                fixed_parts = (
+                    CPE_REGEX.match(fixed_location) if fixed_location else None
+                )
+                if fixed_parts:
+                    self.fixed_location = fixed_parts.group("version")
+                else:
+                    self.fixed_location = ""
+            else:
+                self.fixed_location = fixed_location
+
+    @staticmethod
+    def from_dict(package_issue):
+        return PackageIssue(
+            package_issue.get("affected_location"), package_issue.get("fixed_location")
+        )
+
+    def to_dict(self) -> dict:
+        return {
+            "affected_location": self.affected_location.to_dict(),
+            "fixed_location": self.fixed_location,
+        }
+
+    def __str__(self):
+        return orjson.dumps(
             {
-                "id": self.id,
-                "problem_type": self.problem_type,
-                "score": self.score,
-                "severity": self.severity.value,
-                "description": self.description,
-                "related_urls": self.related_urls,
-                "details": str(self.details),
-                "cvss_v3": str(self.cvss_v3),
-                "source_update_time": convert_time(self.source_update_time).strftime(
-                    "%Y-%m-%dT%H:%M:%S"
-                ),
-                "source_orig_time": convert_time(self.source_orig_time).strftime(
-                    "%Y-%m-%dT%H:%M:%S"
-                ),
-            }
+                "affected_location": str(self.affected_location),
+                "fixed_location": self.fixed_location,
+            },
+            option=orjson.OPT_NAIVE_UTC,
         )
 
 
-class VulnerabilityDetail(object):
+class VulnerabilityDetail:
     """Vulnerability detail class"""
 
     def __init__(
         self,
-        cpe_uri,
-        package,
-        min_affected_version_including,
-        max_affected_version_including,
-        min_affected_version_excluding,
-        max_affected_version_excluding,
-        severity,
-        description,
-        fixed_location,
-        package_type,
-        is_obsolete,
-        source_update_time,
+        cpe_uri: str,
+        package: str,
+        min_affected_version_including: str | None,
+        max_affected_version_including: str | None,
+        min_affected_version_excluding: str | None,
+        max_affected_version_excluding: str | None,
+        severity: str,
+        description: str | None,
+        fixed_location: str,
+        package_type: str,
+        is_obsolete: str,
+        source_update_time: str,
     ):
         parts = CPE_REGEX.match(cpe_uri)
         self.cpe_uri = cpe_uri
@@ -287,7 +358,7 @@ def get_type(cpe_uri, package_type):
             ptype = parts.group("vendor")
             if ptype in KNOWN_PKG_TYPES:
                 return ptype
-            elif all_parts and (
+            if all_parts and (
                 (all_parts.group("target_sw") and all_parts.group("target_sw") != "*")
                 or (
                     all_parts.group("sw_edition")
@@ -297,14 +368,13 @@ def get_type(cpe_uri, package_type):
                 for vk, vv in PKG_TYPES_MAP.items():
                     target_sw = all_parts.group("target_sw")
                     sw_edition = all_parts.group("sw_edition")
-                    if target_sw == vk or sw_edition == vk:
+                    if vk in (target_sw, sw_edition):
                         return vk
                     if target_sw in vv or sw_edition in vv:
                         return vk
                 return ptype
-            else:
-                # Unknown type. Just pass-through for now
-                return ptype
+            # Unknown type. Just pass-through for now
+            return ptype
         return None
 
     @staticmethod
@@ -343,124 +413,59 @@ def to_dict(self):
         }
 
 
-class PackageIssue(object):
-    """Package issue class"""
+class Vulnerability:
+    """Vulnerability"""
+
+    cvss_v3: CvssV3
 
     def __init__(
         self,
-        affected_location,
-        fixed_location,
-        mii=None,
-        mai=None,
-        mie=None,
-        mae=None,
+        vid: str,
+        assigner: str,
+        problem_type: str,
+        score: float,
+        severity: str,
+        description: str,
+        related_urls: list[str],
+        details: list[VulnerabilityDetail],
+        cvss_v3: CvssV3,
+        source_update_time: str,
+        source_orig_time: str,
     ):
-        self.affected_location = VulnerabilityLocation.from_values(
-            affected_location,
-            mii,
-            mai,
-            mie,
-            mae,
-        )
-        # If there is no fixed_location but there is max excluded version then consider that as the fix
-        if not fixed_location and mae:
-            self.fixed_location = mae
-        else:
-            if fixed_location and fixed_location.startswith("cpe"):
-                # Extract the fixed version from fixed_location cpe uri
-                fixed_parts = (
-                    CPE_REGEX.match(fixed_location) if fixed_location else None
-                )
-                if fixed_parts:
-                    self.fixed_location = fixed_parts.group("version")
-                else:
-                    self.fixed_location = ""
-            else:
-                self.fixed_location = fixed_location
-
-    @staticmethod
-    def from_dict(package_issue):
-        return PackageIssue(
-            package_issue.get("affected_location"), package_issue.get("fixed_location")
-        )
-
-    def to_dict(self):
-        return {
-            "affected_location": self.affected_location.to_dict(),
-            "fixed_location": self.fixed_location,
-        }
+        self.id = vid
+        self.assigner = assigner
+        self.problem_type = problem_type
+        self.score = score
+        self.severity = Severity.from_str(severity)
+        self.description = description
+        self.related_urls = related_urls
+        self.details = details
+        self.cvss_v3 = cvss_v3
+        self.source_update_time: datetime = convert_time(source_update_time)
+        self.source_orig_time: datetime = convert_time(source_orig_time)
 
-    def __str__(self):
-        return json.dumps(
+    def __repr__(self):
+        return orjson.dumps(
             {
-                "affected_location": str(self.affected_location),
-                "fixed_location": self.fixed_location,
-            }
+                "id": self.id,
+                "assigner": self.assigner,
+                "problem_type": self.problem_type,
+                "score": self.score,
+                "severity": self.severity.value,
+                "description": self.description,
+                "related_urls": self.related_urls,
+                "details": str(self.details),
+                "cvss_v3": str(self.cvss_v3),
+                "source_update_time": self.source_update_time.strftime(
+                    "%Y-%m-%dT%H:%M:%S"
+                ),
+                "source_orig_time": self.source_orig_time.strftime("%Y-%m-%dT%H:%M:%S"),
+            },
+            option=orjson.OPT_NAIVE_UTC,
         )
 
 
-class CvssV3(object):
-    """CVSS v3 representation"""
-
-    base_score: float
-    exploitability_score: float
-    impact_score: float
-    attack_vector: str
-    attack_complexity: str
-    privileges_required: str
-    user_interaction: str
-    scope: str
-    confidentiality_impact: str
-    integrity_impact: str
-    availability_impact: str
-    vector_string: str
-
-    def __init__(
-        self,
-        base_score,
-        exploitability_score,
-        impact_score,
-        attack_vector,
-        attack_complexity,
-        privileges_required,
-        user_interaction,
-        scope,
-        confidentiality_impact,
-        integrity_impact,
-        availability_impact,
-        vector_string=None,
-    ):
-        self.base_score = base_score
-        self.exploitability_score = exploitability_score
-        self.impact_score = impact_score
-        self.attack_vector = attack_vector
-        self.attack_complexity = attack_complexity
-        self.privileges_required = privileges_required
-        self.user_interaction = user_interaction
-        self.scope = scope
-        self.confidentiality_impact = confidentiality_impact
-        self.integrity_impact = integrity_impact
-        self.availability_impact = availability_impact
-        self.vector_string = vector_string
-
-    def to_dict(self):
-        return {
-            "base_score": self.base_score,
-            "exploitability_score": self.exploitability_score,
-            "impact_score": self.impact_score,
-            "attack_vector": self.attack_vector,
-            "attack_complexity": self.attack_complexity,
-            "privileges_required": self.privileges_required,
-            "user_interaction": self.user_interaction,
-            "scope": self.scope,
-            "confidentiality_impact": self.confidentiality_impact,
-            "integrity_impact": self.integrity_impact,
-            "availability_impact": self.availability_impact,
-            "vector_string": self.vector_string,
-        }
-
-
-class VulnerabilityLocation(object):
+class VulnerabilityLocation:
     cpe_uri: str
     vendor: str
     package: str
@@ -480,6 +485,8 @@ def from_values(
         mie=None,
         mae=None,
     ):
+        parts = None
+        version = "*"
         if not cpe_uri and not mii and not mai and not mie and not mae:
             return None
         if cpe_uri:
@@ -511,17 +518,17 @@ def from_values(
             return VulnerabilityLocation(
                 cpe_uri, parts.group("vendor"), parts.group("package"), version
             )
-        else:
-            return None
+        return None
 
     def __str__(self):
-        return json.dumps(
+        return orjson.dumps(
             {
                 "cpe_uri": str(self.cpe_uri),
                 "package": str(self.package),
                 "version": str(self.version),
                 "vendor": str(self.vendor),
-            }
+            },
+            option=orjson.OPT_NAIVE_UTC,
         )
 
     def to_dict(self):
@@ -553,9 +560,9 @@ class VulnerabilityOccurrence:
 
     def __init__(
         self,
-        id,
+        oid,
         problem_type,
-        type,
+        otype,
         severity,
         cvss_score,
         cvss_v3,
@@ -568,9 +575,9 @@ def __init__(
         source_orig_time,
         matched_by,
     ):
-        self.id = id
+        self.id = oid
         self.problem_type = problem_type
-        self.type = type
+        self.type = otype
         self.severity = severity
         self.cvss_score = cvss_score
         self.cvss_v3 = cvss_v3 if cvss_v3 else None
@@ -585,12 +592,10 @@ def __init__(
 
     def to_dict(self):
         """Convert the object to dict"""
-        source_update_time = None
-        source_orig_time = None
         if isinstance(self.source_update_time, datetime):
             source_update_time = self.source_update_time.strftime("%Y-%m-%dT%H:%M:%S")
         else:
-            new_time = convert_time(self.source_update_time)
+            new_time = self.source_update_time
             source_update_time = (
                 new_time.strftime("%Y-%m-%dT%H:%M:%S")
                 if (isinstance(new_time, datetime))
@@ -599,7 +604,7 @@ def to_dict(self):
         if isinstance(self.source_orig_time, datetime):
             source_orig_time = self.source_orig_time.strftime("%Y-%m-%dT%H:%M:%S")
         else:
-            new_time = convert_time(self.source_orig_time)
+            new_time = self.source_orig_time
             source_orig_time = (
                 new_time.strftime("%Y-%m-%dT%H:%M:%S")
                 if (isinstance(new_time, datetime))
@@ -629,7 +634,7 @@ class CustomNamedTemporaryFile:
     def __init__(self, mode="wb", delete=True):
         self._mode = mode
         self._delete = delete
-        self._tempFile = None
+        self._temp_file = None
 
     def __enter__(self):
         # Generate a random temporary file name
@@ -637,11 +642,11 @@ def __enter__(self):
         # Ensure the file is created
         open(file_name, "x").close()
         # Open the file in the given mode
-        self._tempFile = open(file_name, self._mode)
-        return self._tempFile
+        self._temp_file = open(file_name, self._mode)
+        return self._temp_file
 
     def __exit__(self, exc_type, exc_val, exc_tb):
-        if self._tempFile:
-            self._tempFile.close()
+        if self._temp_file:
+            self._temp_file.close()
             if self._delete:
-                os.remove(self._tempFile.name)
+                os.remove(self._temp_file.name)
diff --git a/vdb/lib/aqua.py b/vdb/lib/aqua.py
index 3990809..e949a1e 100644
--- a/vdb/lib/aqua.py
+++ b/vdb/lib/aqua.py
@@ -8,6 +8,7 @@
 from zipfile import ZipFile
 
 import httpx
+import orjson
 
 from vdb.lib import CustomNamedTemporaryFile, config
 from vdb.lib.nvd import NvdSource
@@ -21,16 +22,6 @@
 # Size of the stream to read and write to the file
 DOWNLOAD_CHUNK_SIZE = 4096
 
-try:
-    import orjson
-
-    ORJSON_AVAILABLE = True
-except ImportError:
-    import json
-
-    ORJSON_AVAILABLE = False
-
-json_lib = orjson if ORJSON_AVAILABLE else json
 
 threat_to_severity = {
     "unspecified": "LOW",
@@ -72,7 +63,7 @@ def _process_zip(self, zname):
                     with zipfp.open(zf) as jsonf:
                         cve_data = jsonf.read()
                         try:
-                            json_data = json_lib.loads(cve_data)
+                            json_data = orjson.loads(cve_data)
                             ret_data = self.convert(json_data)
                             self.store(ret_data, reindex=False)
                         except Exception:
@@ -119,7 +110,8 @@ def convert(self, cve_data):
             return self.nvd_api_to_vuln(cve_data)
         return []
 
-    def is_supported_source(self, zfname):
+    @staticmethod
+    def is_supported_source(zfname):
         for distro in (
             "alpine",
             "cwe",
@@ -146,7 +138,8 @@ def is_supported_source(self, zfname):
             return True
         return False
 
-    def alsa_to_vuln(self, cve_data):
+    @staticmethod
+    def alsa_to_vuln(cve_data):
         """AlmaLinux"""
         ret_data = []
         if cve_data.get("type") != "security":
@@ -162,7 +155,7 @@ def alsa_to_vuln(self, cve_data):
             references.append(
                 {"name": aref.get("title", "id"), "url": aref.get("href")}
             )
-        references = json_lib.dumps(references)
+        references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
         if isinstance(references, bytes):
             references = references.decode("utf-8", "ignore")
         description = cve_data.get("description", "")
@@ -234,14 +227,15 @@ def alsa_to_vuln(self, cve_data):
                 lastModifiedDate=last_modified_date,
             )
             try:
-                vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                 vuln.description = compress_str(description)
                 ret_data.append(vuln)
             except Exception:
                 pass
         return ret_data
 
-    def alas_rlsa_to_vuln(self, cve_data, vendor):
+    @staticmethod
+    def alas_rlsa_to_vuln(cve_data, vendor):
         """Amazon Linux"""
         ret_data = []
         packages = cve_data.get("packages", [])
@@ -253,7 +247,7 @@ def alas_rlsa_to_vuln(self, cve_data, vendor):
         references = []
         for aref in cve_references:
             references.append({"name": aref.get("id"), "url": aref.get("href")})
-        references = json_lib.dumps(references)
+        references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
         if isinstance(references, bytes):
             references = references.decode("utf-8", "ignore")
         if not cve_id.startswith("CVE") and cve_data.get("cveids"):
@@ -314,7 +308,7 @@ def alas_rlsa_to_vuln(self, cve_data, vendor):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = compress_str(description)
                     ret_data.append(vuln)
                     done_pkgs[pkg_key] = True
@@ -322,7 +316,8 @@ def alas_rlsa_to_vuln(self, cve_data, vendor):
                     pass
         return ret_data
 
-    def ubuntu_to_vuln(self, cve_data):
+    @staticmethod
+    def ubuntu_to_vuln(cve_data):
         """Ubuntu Linux"""
         ret_data = []
         packages = cve_data.get("Patches", {})
@@ -334,7 +329,7 @@ def ubuntu_to_vuln(self, cve_data):
         references = []
         for aref in cve_references:
             references.append({"name": aref, "url": aref})
-        references = json_lib.dumps(references)
+        references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
         if isinstance(references, bytes):
             references = references.decode("utf-8", "ignore")
         description = cve_data.get("Description")
@@ -426,14 +421,15 @@ def ubuntu_to_vuln(self, cve_data):
                             lastModifiedDate=last_modified_date,
                         )
                         try:
-                            vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                            vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                             vuln.description = compress_str(description)
                             ret_data.append(vuln)
                         except Exception:
                             pass
         return ret_data
 
-    def redhat_to_vuln(self, cve_data):
+    @staticmethod
+    def redhat_to_vuln(cve_data):
         """RedHat Linux"""
         ret_data = []
         cvss3 = cve_data.get("cvss3", {})
@@ -449,7 +445,7 @@ def redhat_to_vuln(self, cve_data):
         for aref in cve_references:
             for bref in aref.split("\n"):
                 references.append({"name": bref, "url": bref})
-        references = json_lib.dumps(references)
+        references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
         if isinstance(references, bytes):
             references = references.decode("utf-8", "ignore")
         description = "\n".join(cve_data.get("details", []))
@@ -482,7 +478,6 @@ def redhat_to_vuln(self, cve_data):
             pkg_name = tmp_a[0]
             if pkg_name.endswith("-0"):
                 pkg_name = pkg_name[0:-2]
-            version = ""
             if ":" in pkg_key:
                 version = pkg_key.split(":")[-1]
             else:
@@ -534,7 +529,7 @@ def redhat_to_vuln(self, cve_data):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = compress_str(description)
                     ret_data.append(vuln)
                     done_pkgs[pkg_key] = True
@@ -542,7 +537,8 @@ def redhat_to_vuln(self, cve_data):
                     pass
         return ret_data
 
-    def arch_to_vuln(self, cve_data):
+    @staticmethod
+    def arch_to_vuln(cve_data):
         """Arch Linux"""
         ret_data = []
         packages = cve_data.get("packages", [])
@@ -598,14 +594,15 @@ def arch_to_vuln(self, cve_data):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = compress_str(description)
                     ret_data.append(vuln)
                 except Exception:
                     pass
         return ret_data
 
-    def product_ref_to_name_version(self, product_ref):
+    @staticmethod
+    def product_ref_to_name_version(product_ref):
         product_parts = product_ref.split("-")
         name_parts = []
         version_parts = []
@@ -622,8 +619,6 @@ def suse_to_vuln(self, cve_data):
         packages = cve_data.get("ProductTree", {}).get("Relationships", [])
         if not packages or not len(packages) > 0:
             return ret_data
-        cve_id = ""
-        description = ""
         severity = ""
         # Package name has to be extracted from the title :(
         published_date = cve_data.get("Tracking", {}).get("InitialReleaseDate", "")
@@ -642,7 +637,7 @@ def suse_to_vuln(self, cve_data):
                     references.append(
                         {"name": aref.get("Description", "id"), "url": aref.get("URL")}
                     )
-                references = json_lib.dumps(references)
+                references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
                 if isinstance(references, bytes):
                     references = references.decode("utf-8", "ignore")
                 assigner = "suse"
@@ -694,7 +689,7 @@ def suse_to_vuln(self, cve_data):
                             lastModifiedDate=last_modified_date,
                         )
                         try:
-                            vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                            vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                             vuln.description = compress_str(description)
                             ret_data.append(vuln)
                             done_pkgs[pkg_key] = True
@@ -702,7 +697,8 @@ def suse_to_vuln(self, cve_data):
                             pass
         return ret_data
 
-    def photon_to_vuln(self, cve_data):
+    @staticmethod
+    def photon_to_vuln(cve_data):
         """Photon Linux"""
         ret_data = []
         cve_id = cve_data.get("cve_id")
@@ -764,20 +760,20 @@ def photon_to_vuln(self, cve_data):
                 lastModifiedDate=last_modified_date,
             )
             try:
-                vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                 vuln.description = compress_str(description)
                 ret_data.append(vuln)
             except Exception:
                 pass
         return ret_data
 
-    def debian_to_vuln(self, cve_data):
+    @staticmethod
+    def debian_to_vuln(cve_data):
         """Debian Linux"""
         ret_data = []
         header = cve_data.get("Header")
         annotations = cve_data.get("Annotations")
         cve_id = header.get("ID")
-        pkg_name = ""
         cwe_id = ""
         references = []
         description = header.get("Description", "").replace("(", "").replace(")", "")
@@ -892,14 +888,15 @@ def debian_to_vuln(self, cve_data):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = compress_str(description)
                     ret_data.append(vuln)
                 except Exception:
                     pass
         return ret_data
 
-    def wolfi_to_vuln(self, cve_data):
+    @staticmethod
+    def wolfi_to_vuln(cve_data):
         """Wolfi OS and Chainguard"""
         ret_data = []
         cwe_id = ""
@@ -949,14 +946,15 @@ def wolfi_to_vuln(self, cve_data):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = f"""URL Prefix: {cve_data.get("urlprefix")}. Affected arch: {", ".join(cve_data.get("archs"))}"""
                     ret_data.append(vuln)
                 except Exception:
                     pass
         return ret_data
 
-    def nvd_api_to_vuln(self, cve_data):
+    @staticmethod
+    def nvd_api_to_vuln(cve_data):
         """NVD API"""
         v = NvdSource.convert_api_vuln(cve_data)
         return [v] if v else []
diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py
new file mode 100644
index 0000000..9468714
--- /dev/null
+++ b/vdb/lib/cve.py
@@ -0,0 +1,266 @@
+import uuid
+
+from semver import VersionInfo
+
+from vdb.lib import (
+    CPE_REGEX,
+    KNOWN_PKG_TYPES,
+    Vulnerability,
+    VulnerabilityDetail,
+    VulnerabilitySource,
+)
+from vdb.lib.cve_model import (
+    CVE1,
+    Affected,
+    CnaPublishedContainer,
+    Containers,
+    Cpe,
+    CveId,
+    CveMetadataPublished,
+    DataType,
+    DataVersion,
+    Description,
+    Description1,
+    Language,
+    Metrics,
+    Metrics1,
+    OrgId,
+    ProblemType,
+    ProblemTypes,
+    Product,
+    ProviderMetadata,
+    Reference,
+    References,
+    State,
+    Status,
+    UuidType,
+    Version,
+    Versions,
+)
+from vdb.lib.cve_model.cvss_v3 import Field1
+
+ASSIGNER_UUID_MAP = {}
+
+
+def to_cve_metadata(avuln: Vulnerability):
+    # Re-use the assigner org id
+    if avuln.assigner:
+        if ASSIGNER_UUID_MAP.get(avuln.assigner):
+            assigner_org_id = ASSIGNER_UUID_MAP[avuln.assigner]
+        else:
+            assigner_org_id = OrgId(UuidType(str(uuid.uuid4())))
+            ASSIGNER_UUID_MAP[avuln.assigner] = assigner_org_id
+    else:
+        assigner_org_id = OrgId(UuidType(str(uuid.uuid4())))
+    metadata = CveMetadataPublished(
+        cveId=CveId(avuln.id),
+        state=State.PUBLISHED,
+        assignerOrgId=assigner_org_id,
+    )
+    metadata.datePublished = avuln.source_orig_time
+    metadata.dateUpdated = avuln.source_update_time
+    return metadata
+
+
+def all_semver_compatible(adetail: VulnerabilityDetail) -> bool:
+    if adetail.mii and adetail.mii != "*" and not VersionInfo.is_valid(adetail.mii):
+        return False
+    if adetail.mae and adetail.mae != "*" and not VersionInfo.is_valid(adetail.mae):
+        return False
+    if adetail.mie and adetail.mie != "*" and not VersionInfo.is_valid(adetail.mie):
+        return False
+    if adetail.mai and adetail.mai != "*" and not VersionInfo.is_valid(adetail.mai):
+        return False
+    return True
+
+
+def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]:
+    versions = []
+    lt_captured = False
+    # Try to detect the version_type
+    version_type = "custom"
+    if vendor in KNOWN_PKG_TYPES:
+        version_type = vendor
+    elif all_semver_compatible(adetail):
+        version_type = "semver"
+    # The goal is to create a single entry with either version and lessThan
+    #   or version and lessThanOrEqual
+    # If this is not possible then we create a separate version objects
+    if adetail.mii and adetail.mii not in ("*", "-"):
+        if adetail.mai and adetail.mai not in ("*", "-"):
+            versions.append(
+                Versions(
+                    version=Version(adetail.mii),
+                    lessThanOrEqual=Version(adetail.mai),
+                    versionType=version_type,
+                    status=Status.affected,
+                )
+            )
+            lt_captured = True
+        elif adetail.mae:
+            versions.append(
+                Versions(
+                    version=Version(adetail.mii),
+                    lessThan=Version(adetail.mai),
+                    versionType=version_type,
+                    status=Status.affected,
+                )
+            )
+            lt_captured = True
+        else:
+            versions.append(
+                Versions(
+                    version=Version(adetail.mii),
+                    versionType=version_type,
+                    status=Status.affected,
+                )
+            )
+    if adetail.mie and adetail.mie not in ("*", "-"):
+        versions.append(
+            Versions(
+                version=Version(adetail.mie),
+                versionType=version_type,
+                status=Status.unaffected,
+            )
+        )
+    if not lt_captured:
+        if adetail.mai and adetail.mai not in ("*", "-"):
+            versions.append(
+                Versions(
+                    version=Version(adetail.mai),
+                    lessThanOrEqual=Version(adetail.mai),
+                    versionType=version_type,
+                    status=Status.affected,
+                )
+            )
+        if adetail.mae and adetail.mae not in ("*", "-"):
+            versions.append(
+                Versions(
+                    version=Version(adetail.mae),
+                    lessThan=Version(adetail.mae),
+                    versionType=version_type,
+                    status=Status.affected,
+                )
+            )
+    return versions
+
+
+def to_cve_affected(avuln: Vulnerability) -> Affected | None:
+    products = []
+    adetail: VulnerabilityDetail
+    for adetail in avuln.details:
+        cpe_uri = adetail.cpe_uri
+        parts = CPE_REGEX.match(cpe_uri)
+        versions = to_product_versions(parts.group("vendor"), adetail)
+        if parts and versions:
+            p = Product(
+                vendor=parts.group("vendor"),
+                product=parts.group("package"),
+                packageName=parts.group("package"),
+                cpes=[Cpe(cpe_uri)],
+                defaultStatus=Status.unknown,
+                versions=versions,
+            )
+            products.append(p)
+    return Affected(products) if products else None
+
+
+def to_cve_containers(avuln: Vulnerability) -> CnaPublishedContainer | None:
+    provier_meta = ProviderMetadata(
+        orgId=ASSIGNER_UUID_MAP.get(avuln.assigner, OrgId(UuidType(str(uuid.uuid4()))))
+    )
+    provier_meta.dateUpdated = avuln.source_update_time
+    affected = to_cve_affected(avuln)
+    if not affected:
+        return None
+    cont = CnaPublishedContainer(
+        providerMetadata=provier_meta,
+        descriptions=[Description(lang=Language("en"), value=avuln.description)],
+        affected=affected,
+        metrics=Metrics(
+            [
+                Metrics1(
+                    cvssV3_1=Field1(
+                        version="3.1",
+                        baseScore=avuln.cvss_v3.base_score,
+                        baseSeverity=avuln.severity,
+                        vectorString=avuln.cvss_v3.vector_string,
+                        attackVector=avuln.cvss_v3.attack_vector,
+                        attackComplexity=avuln.cvss_v3.attack_complexity,
+                        privilegesRequired=avuln.cvss_v3.privileges_required,
+                        userInteraction=avuln.cvss_v3.user_interaction,
+                        scope=avuln.cvss_v3.scope,
+                        confidentialityImpact=avuln.cvss_v3.confidentiality_impact,
+                        integrityImpact=avuln.cvss_v3.integrity_impact,
+                        availabilityImpact=avuln.cvss_v3.availability_impact,
+                    )
+                )
+            ]
+        ),
+        references=References([Reference(url=url) for url in avuln.related_urls]),
+    )
+    cont.dateAssigned = avuln.source_orig_time
+    # CWE
+    if avuln.problem_type and "noinfo" not in avuln.problem_type:
+        problem_types = []
+        for acwe in avuln.problem_type.split(","):
+            problem_types.append(
+                ProblemTypes(
+                    [
+                        ProblemType(
+                            descriptions=[
+                                Description1(
+                                    lang=Language("en"),
+                                    description=acwe,
+                                    cweId=acwe,
+                                    type="CWE",
+                                )
+                            ]
+                        )
+                    ]
+                )
+            )
+        if problem_types:
+            cont.problemTypes = problem_types
+    return cont
+
+
+class CVESource(VulnerabilitySource):
+    """
+    Generic CVE source that uses the CVE 5.0 models
+    """
+
+    db = None
+
+    @classmethod
+    def download_all(cls, local_store=True):
+        pass
+
+    @classmethod
+    def download_recent(cls, local_store=True):
+        pass
+
+    @classmethod
+    def bulk_search(cls, app_info, pkg_list):
+        pass
+
+    @classmethod
+    def refresh(cls):
+        pass
+
+    def convert(self, data: list[Vulnerability]) -> list[CVE1]:
+        cves = []
+        for avuln in data:
+            containers = Containers(cna=to_cve_containers(avuln))
+            if containers:
+                cve_obj = CVE1(
+                    dataType=DataType.CVE_RECORD,
+                    dataVersion=DataVersion.field_5_0,
+                    cveMetadata=to_cve_metadata(avuln),
+                    containers=containers,
+                )
+                cves.append(cve_obj)
+        return cves
+
+    def store(self, data: list[CVE1]):
+        pass
diff --git a/vdb/lib/cve_model/__init__.py b/vdb/lib/cve_model/__init__.py
new file mode 100644
index 0000000..cec56af
--- /dev/null
+++ b/vdb/lib/cve_model/__init__.py
@@ -0,0 +1,1338 @@
+# generated by datamodel-codegen:
+#   filename:  CVE_JSON_5.0_schema.json
+#   timestamp: 2024-02-07T18:22:42+00:00
+
+from __future__ import annotations
+
+from enum import Enum
+from typing import Annotated, Any, Dict, List, Optional, Type, Union
+
+from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel
+
+from vdb.lib.cve_model import cvss_v2, cvss_v3
+
+
+class UriType(RootModel[AnyUrl]):
+    root: Annotated[
+        AnyUrl,
+        Field(
+            description="A universal resource identifier (URI), according to [RFC 3986](https://tools.ietf.org/html/rfc3986)."
+        ),
+    ]
+
+
+class UuidType(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="A version 4 (random) universally unique identifier (UUID) as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.3).",
+            pattern="^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$",
+        ),
+    ]
+
+
+class ReferenceTags(Enum):
+    broken_link = "broken-link"
+    customer_entitlement = "customer-entitlement"
+    exploit = "exploit"
+    government_resource = "government-resource"
+    issue_tracking = "issue-tracking"
+    mailing_list = "mailing-list"
+    mitigation = "mitigation"
+    not_applicable = "not-applicable"
+    patch = "patch"
+    permissions_required = "permissions-required"
+    media_coverage = "media-coverage"
+    product = "product"
+    related = "related"
+    release_notes = "release-notes"
+    signature = "signature"
+    technical_description = "technical-description"
+    third_party_advisory = "third-party-advisory"
+    vendor_advisory = "vendor-advisory"
+    vdb_entry = "vdb-entry"
+
+
+class CveId(RootModel[str]):
+    root: Annotated[str, Field(pattern="^CVE-[0-9]{4}-[0-9]{4,19}$")]
+
+
+class OrgId(RootModel[UuidType]):
+    root: Annotated[
+        UuidType,
+        Field(
+            description="A UUID for an organization participating in the CVE program. This UUID can be used to lookup the organization record in the user registry service."
+        ),
+    ]
+
+
+class UserId(RootModel[UuidType]):
+    root: Annotated[
+        UuidType,
+        Field(
+            description="A UUID for a user participating in the CVE program. This UUID can be used to lookup the user record in the user registry service."
+        ),
+    ]
+
+
+class ShortName(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="A 2-32 character name that can be used to complement an organization's UUID.",
+            max_length=32,
+            min_length=2,
+        ),
+    ]
+
+
+class Timestamp(RootModel[AwareDatetime]):
+    root: Annotated[
+        AwareDatetime,
+        Field(
+            description="Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ssZZZZ'. If timezone offset is not given, GMT (0000) is assumed.",
+            pattern="^(((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})?$",
+        ),
+    ]
+
+
+class Version(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="A single version of a product, as expressed in its own version numbering scheme.",
+            max_length=1024,
+            min_length=1,
+        ),
+    ]
+
+
+class Status(Enum):
+    affected = "affected"
+    unaffected = "unaffected"
+    unknown = "unknown"
+
+
+class Cpe(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="Common Platform Enumeration (CPE) Name in either 2.2 or 2.3 format",
+            max_length=2048,
+            min_length=1,
+            pattern="([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\\-~%]*){0,6})|(cpe:2\\.3:[aho*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){4})",
+            title="CPE Name",
+        ),
+    ]
+
+
+class Module(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="Name of the affected component, feature, module, sub-component, sub-product, API, command, utility, program, or functionality (optional).",
+            max_length=4096,
+            min_length=1,
+        ),
+    ]
+
+
+class ProgramFile(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="Name or path or location of the affected source code file.",
+            max_length=1024,
+            min_length=1,
+        ),
+    ]
+
+
+class ProgramRoutine(BaseModel):
+    name: Annotated[
+        str,
+        Field(
+            description="Name of the affected source code file, function, method, subroutine, or procedure.",
+            max_length=4096,
+            min_length=1,
+        ),
+    ]
+
+
+class Platform(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            examples=[
+                "iOS",
+                "Android",
+                "Windows",
+                "macOS",
+                "x86",
+                "ARM",
+                "64 bit",
+                "Big Endian",
+                "iPad",
+                "Chromebook",
+                "Docker",
+                "Model T",
+            ],
+            max_length=1024,
+        ),
+    ]
+
+
+class Change(BaseModel):
+    at: Annotated[
+        Version, Field(description="The version at which a status change occurs.")
+    ]
+    status: Annotated[
+        Status,
+        Field(description="The new status in the range starting at the given version."),
+    ]
+
+
+class Versions(BaseModel):
+    version: Annotated[
+        Version,
+        Field(
+            description="The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version."
+        ),
+    ]
+    status: Annotated[
+        Status,
+        Field(
+            description="The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list."
+        ),
+    ]
+    versionType: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.",
+            examples=["custom", "git", "maven", "python", "rpm", "semver"],
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    lessThan: Annotated[
+        Optional[Version],
+        Field(
+            None,
+            description="The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified.",
+        ),
+    ]
+    lessThanOrEqual: Annotated[
+        Optional[Version],
+        Field(
+            None,
+            description="The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3.",
+        ),
+    ]
+    changes: Annotated[
+        Optional[List[Change]],
+        Field(
+            None,
+            description="A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.",
+            min_length=1,
+        ),
+    ]
+
+
+class Versions1(BaseModel):
+    version: Annotated[
+        Version,
+        Field(
+            description="The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version."
+        ),
+    ]
+    status: Annotated[
+        Status,
+        Field(
+            description="The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list."
+        ),
+    ]
+    versionType: Annotated[
+        str,
+        Field(
+            description="The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.",
+            examples=["custom", "git", "maven", "python", "rpm", "semver"],
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    lessThan: Annotated[
+        Version,
+        Field(
+            description="The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified."
+        ),
+    ]
+    lessThanOrEqual: Annotated[
+        Optional[Version],
+        Field(
+            None,
+            description="The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3.",
+        ),
+    ]
+    changes: Annotated[
+        Optional[List[Change]],
+        Field(
+            None,
+            description="A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.",
+            min_length=1,
+        ),
+    ]
+
+
+class Versions2(BaseModel):
+    version: Annotated[
+        Version,
+        Field(
+            description="The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version."
+        ),
+    ]
+    status: Annotated[
+        Status,
+        Field(
+            description="The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list."
+        ),
+    ]
+    versionType: Annotated[
+        str,
+        Field(
+            description="The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.",
+            examples=["custom", "git", "maven", "python", "rpm", "semver"],
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    lessThan: Annotated[
+        Optional[Version],
+        Field(
+            None,
+            description="The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified.",
+        ),
+    ]
+    lessThanOrEqual: Annotated[
+        Version,
+        Field(
+            description="The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3."
+        ),
+    ]
+    changes: Annotated[
+        Optional[List[Change]],
+        Field(
+            None,
+            description="A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.",
+            min_length=1,
+        ),
+    ]
+
+
+class Product(BaseModel):
+    vendor: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the organization, project, community, individual, or user that created or maintains this product or hosted service. Can be 'N/A' if none of those apply. When collectionURL and packageName are used, this field may optionally represent the user or account within the package collection associated with the package.",
+            max_length=512,
+            min_length=1,
+        ),
+    ]
+    product: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the affected product.",
+            max_length=2048,
+            min_length=1,
+        ),
+    ]
+    collectionURL: Annotated[
+        Optional[UriType],
+        Field(
+            None,
+            description="URL identifying a package collection (determines the meaning of packageName).",
+            examples=[
+                "https://access.redhat.com/downloads/content/package-browser",
+                "https://addons.mozilla.org",
+                "https://addons.thunderbird.net",
+                "https://anaconda.org/anaconda/repo",
+                "https://app.vagrantup.com/boxes/search",
+                "https://apps.apple.com",
+                "https://archlinux.org/packages",
+                "https://atmospherejs.meteor.com",
+                "https://atom.io/packages",
+                "https://bitbucket.org",
+                "https://bower.io",
+                "https://brew.sh/",
+                "https://chocolatey.org/packages",
+                "https://chrome.google.com/webstore",
+                "https://clojars.org",
+                "https://cocoapods.org",
+                "https://code.dlang.org",
+                "https://conan.io/center",
+                "https://cpan.org/modules",
+                "https://cran.r-project.org",
+                "https://crates.io",
+                "https://ctan.org/pkg",
+                "https://drupal.org",
+                "https://exchange.adobe.com",
+                "https://forge.puppet.com/modules",
+                "https://github.com",
+                "https://gitlab.com/explore",
+                "https://golang.org/pkg",
+                "https://guix.gnu.org/packages",
+                "https://hackage.haskell.org",
+                "https://helm.sh",
+                "https://hub.docker.com",
+                "https://juliahub.com",
+                "https://lib.haxe.org",
+                "https://luarocks.org",
+                "https://marketplace.visualstudio.com",
+                "https://melpa.org",
+                "https://microsoft.com/en-us/store/apps",
+                "https://nimble.directory",
+                "https://nuget.org/packages",
+                "https://opam.ocaml.org/packages",
+                "https://openwrt.org/packages/index",
+                "https://package.elm-lang.org",
+                "https://packagecontrol.io",
+                "https://packages.debian.org",
+                "https://packages.gentoo.org",
+                "https://packagist.org",
+                "https://pear.php.net/packages.php",
+                "https://pecl.php.net",
+                "https://platformio.org/lib",
+                "https://play.google.com/store",
+                "https://plugins.gradle.org",
+                "https://projects.eclipse.org",
+                "https://pub.dev",
+                "https://pypi.python.org",
+                "https://registry.npmjs.org",
+                "https://registry.terraform.io",
+                "https://repo.hex.pm",
+                "https://repo.maven.apache.org/maven2",
+                "https://rubygems.org",
+                "https://search.nixos.org/packages",
+                "https://sourceforge.net",
+                "https://wordpress.org/plugins",
+            ],
+        ),
+    ]
+    packageName: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name or identifier of the affected software package as used in the package collection.",
+            max_length=2048,
+            min_length=1,
+        ),
+    ]
+    cpes: Annotated[
+        Optional[List[Cpe]],
+        Field(
+            None,
+            description='Affected products defined by CPE. This is an array of CPE values (vulnerable and not), we use an array so that we can make multiple statements about the same version and they are separate (if we used a JSON object we\'d essentially be keying on the CPE name and they would have to overlap). Also, this allows things like cveDataVersion or cveDescription to be applied directly to the product entry. This also allows more complex statements such as "Product X between versions 10.2 and 10.8" to be put in a machine-readable format. As well since multiple statements can be used multiple branches of the same product can be defined here.',
+        ),
+    ]
+    modules: Annotated[
+        Optional[List[Module]],
+        Field(
+            None,
+            description="A list of the affected components, features, modules, sub-components, sub-products, APIs, commands, utilities, programs, or functionalities (optional).",
+        ),
+    ]
+    programFiles: Annotated[
+        Optional[List[ProgramFile]],
+        Field(None, description="A list of the affected source code files (optional)."),
+    ]
+    programRoutines: Annotated[
+        Optional[List[ProgramRoutine]],
+        Field(
+            None,
+            description="A list of the affected source code functions, methods, subroutines, or procedures (optional).",
+        ),
+    ]
+    platforms: Annotated[
+        Optional[List[Platform]],
+        Field(
+            None,
+            description="List of specific platforms if the vulnerability is only relevant in the context of these platforms (optional). Platforms may include execution environments, operating systems, virtualization technologies, hardware models, or computing architectures. The lack of this field or an empty array implies that the other fields are applicable to all relevant platforms.",
+            min_length=1,
+            title="Platforms",
+        ),
+    ]
+    repo: Annotated[
+        Optional[UriType],
+        Field(
+            None,
+            description="The URL of the source code repository, for informational purposes and/or to resolve git hash version ranges.",
+        ),
+    ]
+    defaultStatus: Annotated[
+        Optional[Status],
+        Field(
+            None,
+            description="The default status for versions that are not otherwise listed in the versions list. If not specified, defaultStatus defaults to 'unknown'. Versions or defaultStatus may be omitted, but not both.",
+        ),
+    ]
+    versions: Annotated[
+        Optional[List[Union[Versions, Union[Versions1, Versions2]]]],
+        Field(
+            None,
+            description="Set of product versions or version ranges related to the vulnerability. The versions satisfy the CNA Rules [8.1.2 requirement](https://cve.mitre.org/cve/cna/rules.html#section_8-1_cve_entry_information_requirements). Versions or defaultStatus may be omitted, but not both.",
+            min_length=1,
+        ),
+    ]
+
+
+class DataType(Enum):
+    CVE_RECORD = "CVE_RECORD"
+
+
+class DataVersion(Enum):
+    field_5_0 = "5.0"
+
+
+class State(Enum):
+    PUBLISHED = "PUBLISHED"
+
+
+class CveMetadataPublished(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    cveId: Annotated[
+        CveId, Field(description="The CVE identifier that this record pertains to.")
+    ]
+    assignerOrgId: Annotated[
+        OrgId,
+        Field(
+            description="The UUID for the organization to which the CVE ID was originally assigned. This UUID can be used to lookup the organization record in the user registry service."
+        ),
+    ]
+    assignerShortName: Annotated[
+        Optional[ShortName],
+        Field(
+            None,
+            description="The short name for the organization to which the CVE ID was originally assigned.",
+        ),
+    ]
+    requesterUserId: Annotated[
+        Optional[UserId],
+        Field(None, description="The user that requested the CVE identifier."),
+    ]
+    dateUpdated: Annotated[
+        Optional[Timestamp],
+        Field(None, description="The date/time the record was last updated."),
+    ]
+    serial: Annotated[
+        Optional[int],
+        Field(
+            None,
+            description="The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED->REJECTED transition, and moves to the Published schema upon a REJECTED->PUBLISHED transition.",
+            ge=1,
+        ),
+    ]
+    dateReserved: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.",
+        ),
+    ]
+    datePublished: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="The date/time the CVE Record was first published in the CVE List.",
+        ),
+    ]
+    state: Annotated[State, Field(description="State of CVE - PUBLISHED, REJECTED.")]
+
+
+class State1(Enum):
+    REJECTED = "REJECTED"
+
+
+class CveMetadataRejected(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    cveId: Annotated[
+        CveId, Field(description="The CVE identifier that this record pertains to.")
+    ]
+    assignerOrgId: Annotated[
+        OrgId,
+        Field(
+            description="The UUID for the organization to which the CVE ID was originally assigned."
+        ),
+    ]
+    assignerShortName: Annotated[
+        Optional[ShortName],
+        Field(
+            None,
+            description="The short name for the organization to which the CVE ID was originally assigned.",
+        ),
+    ]
+    serial: Annotated[
+        Optional[int],
+        Field(
+            None,
+            description="The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED->REJECTED transition, and moves to the Published schema upon a REJECTED->PUBLISHED transition.",
+            ge=1,
+        ),
+    ]
+    dateUpdated: Annotated[
+        Optional[Timestamp],
+        Field(None, description="The date/time the record was last updated."),
+    ]
+    datePublished: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="The date/time the CVE Record was first published in the CVE List.",
+        ),
+    ]
+    dateRejected: Annotated[
+        Optional[Timestamp],
+        Field(None, description="The date/time the CVE ID was rejected."),
+    ]
+    state: Annotated[State1, Field(description="State of CVE - PUBLISHED, REJECTED.")]
+    dateReserved: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.",
+        ),
+    ]
+
+
+class ProviderMetadata(BaseModel):
+    orgId: Annotated[
+        OrgId, Field(description="The container provider's organizational UUID.")
+    ]
+    shortName: Annotated[
+        Optional[ShortName],
+        Field(None, description="The container provider's organizational short name."),
+    ]
+    dateUpdated: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="Timestamp to be set by the system of record at time of submission. If dateUpdated is provided to the system of record it will be replaced by the current timestamp at the time of submission.",
+        ),
+    ]
+
+
+class Affected(RootModel[List[Product]]):
+    root: Annotated[
+        List[Product], Field(description="List of affected products.", min_length=1)
+    ]
+
+
+class SupportingMediaItem(BaseModel):
+    type: Annotated[
+        str,
+        Field(
+            description="RFC2046 compliant IANA Media type for eg., text/markdown, text/html.",
+            examples=[
+                "text/markdown",
+                "text/html",
+                "image/png",
+                "image/svg",
+                "audio/mp3",
+            ],
+            max_length=256,
+            min_length=1,
+            title="Media type",
+        ),
+    ]
+    base64: Annotated[
+        Optional[bool],
+        Field(
+            False,
+            description="If true then the value field contains the media data encoded in base64. If false then the value field contains the UTF-8 media content.",
+            title="Encoding",
+        ),
+    ]
+    value: Annotated[
+        str,
+        Field(
+            description="Supporting media content, up to 16K. If base64 is true, this field stores base64 encoded data.",
+            max_length=16384,
+            min_length=1,
+        ),
+    ]
+
+
+class Other(BaseModel):
+    type: Annotated[
+        str,
+        Field(
+            description="Name of the non-standard impact metrics format used.",
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    content: Annotated[
+        Dict[str, Any],
+        Field(description="JSON object not covered by another metrics format."),
+    ]
+
+
+class Source(BaseModel):
+    pass
+
+
+class Language(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="BCP 47 language code, language-region.",
+            pattern="^[A-Za-z]{2,4}([_-][A-Za-z]{4})?([_-]([A-Za-z]{2}|[0-9]{3}))?$",
+        ),
+    ]
+
+
+class EnglishLanguage(RootModel[str]):
+    root: Annotated[
+        str,
+        Field(
+            description="BCP 47 language code, language-region, required to be English.",
+            pattern="^en([_-][A-Za-z]{4})?([_-]([A-Za-z]{2}|[0-9]{3}))?$",
+        ),
+    ]
+
+
+class TaxonomyRelation(BaseModel):
+    taxonomyId: Annotated[
+        str,
+        Field(
+            description="Identifier of the item in the taxonomy.  Used as the subject of the relationship.",
+            max_length=2048,
+            min_length=1,
+        ),
+    ]
+    relationshipName: Annotated[
+        str,
+        Field(
+            description="A description of the relationship.",
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    relationshipValue: Annotated[
+        str,
+        Field(
+            description="The target of the relationship.  Can be the CVE ID or another taxonomy identifier.",
+            max_length=2048,
+            min_length=1,
+        ),
+    ]
+
+
+class TaxonomyMapping(BaseModel):
+    taxonomyName: Annotated[
+        str,
+        Field(description="The name of the taxonomy.", max_length=128, min_length=1),
+    ]
+    taxonomyVersion: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="The version of taxonomy the identifiers come from.",
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    taxonomyRelations: Annotated[
+        List[TaxonomyRelation], Field(description="", min_length=1)
+    ]
+
+
+class TaxonomyMappings(RootModel[List[TaxonomyMapping]]):
+    root: Annotated[
+        List[TaxonomyMapping],
+        Field(
+            description="List of taxonomy items related to the vulnerability.",
+            min_length=1,
+        ),
+    ]
+
+
+class TagExtension(RootModel[str]):
+    root: Annotated[str, Field(max_length=128, min_length=2, pattern="^x_.*$")]
+
+
+class CnaTags(Enum):
+    unsupported_when_assigned = "unsupported-when-assigned"
+    exclusively_hosted_service = "exclusively-hosted-service"
+    disputed = "disputed"
+
+
+class AdpTags(Enum):
+    disputed = "disputed"
+
+
+class Reference(BaseModel):
+    url: Annotated[
+        UriType,
+        Field(
+            description="The uniform resource locator (URL), according to [RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.1.3), that can be used to retrieve the referenced resource."
+        ),
+    ]
+    name: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="User created name for the reference, often the title of the page.",
+            max_length=512,
+            min_length=1,
+        ),
+    ]
+    tags: Annotated[
+        Optional[List[Union[TagExtension, ReferenceTags]]],
+        Field(
+            None,
+            description="An array of one or more tags that describe the resource referenced by 'url'.",
+            min_length=1,
+        ),
+    ]
+
+
+class Description(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    lang: Language
+    value: Annotated[
+        str, Field(description="Plain text description.", max_length=4096, min_length=1)
+    ]
+    supportingMedia: Annotated[
+        Optional[List[SupportingMediaItem]],
+        Field(
+            None,
+            description="Supporting media data for the description such as markdown, diagrams, .. (optional). Similar to RFC 2397 each media object has three main parts: media type, media data value, and an optional boolean flag to indicate if the media data is base64 encoded.",
+            min_length=1,
+            title="Supporting media",
+        ),
+    ]
+
+
+class EnglishLanguageDescription(BaseModel):
+    lang: EnglishLanguage
+
+
+class Descriptions(RootModel[List[Description]]):
+    root: Annotated[
+        List[Description],
+        Field(
+            description="A list of multi-lingual descriptions of the vulnerability. E.g., [PROBLEMTYPE] in [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] on [PLATFORMS] allows [ATTACKER] to [IMPACT] via [VECTOR]. OR [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] [ROOT CAUSE], which allows [ATTACKER] to [IMPACT] via [VECTOR].",
+            min_length=1,
+        ),
+    ]
+
+
+class References(RootModel[List[Reference]]):
+    root: Annotated[
+        List[Reference],
+        Field(
+            description='This is reference data in the form of URLs or file objects (uuencoded and embedded within the JSON file, exact format to be decided, e.g. we may require a compressed format so the objects require unpacking before they are "dangerous").',
+            max_length=512,
+            min_length=1,
+        ),
+    ]
+
+
+class Impact(BaseModel):
+    capecId: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="CAPEC ID that best relates to this impact.",
+            max_length=11,
+            min_length=7,
+            pattern="^CAPEC-[1-9][0-9]{0,4}$",
+        ),
+    ]
+    descriptions: Annotated[
+        Descriptions,
+        Field(
+            description="Prose description of the impact scenario. At a minimum provide the description given by CAPEC."
+        ),
+    ]
+
+
+class Impacts(RootModel[List[Impact]]):
+    root: Annotated[
+        List[Impact],
+        Field(description="Collection of impacts of this vulnerability.", min_length=1),
+    ]
+
+
+class Scenario(BaseModel):
+    lang: Language
+    value: Annotated[
+        str,
+        Field(
+            description="Description of the scenario this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+            max_length=4096,
+            min_length=1,
+        ),
+    ]
+
+
+class Configurations(RootModel[List[Description]]):
+    root: Annotated[
+        List[Description],
+        Field(
+            description="Configurations required for exploiting this vulnerability.",
+            min_length=1,
+        ),
+    ]
+
+
+class Workarounds(RootModel[List[Description]]):
+    root: Annotated[
+        List[Description],
+        Field(
+            description="Workarounds and mitigations for this vulnerability.",
+            min_length=1,
+        ),
+    ]
+
+
+class Solutions(RootModel[List[Description]]):
+    root: Annotated[
+        List[Description],
+        Field(
+            description="Information about solutions or remediations available for this vulnerability.",
+            min_length=1,
+        ),
+    ]
+
+
+class Exploits(RootModel[List[Description]]):
+    root: Annotated[
+        List[Description],
+        Field(
+            description="Information about exploits of the vulnerability.", min_length=1
+        ),
+    ]
+
+
+class TimelineItem(BaseModel):
+    time: Annotated[
+        Timestamp,
+        Field(
+            description="Timestamp representing when the event in the timeline occurred. The timestamp format is based on RFC3339 and ISO ISO8601, with an optional timezone. yyyy-MM-ddTHH:mm:ssZZZZ - if the timezone offset is not given, GMT (0000) is assumed."
+        ),
+    ]
+    lang: Annotated[
+        Language,
+        Field(
+            description="The language used in the description of the event. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code."
+        ),
+    ]
+    value: Annotated[
+        str, Field(description="A summary of the event.", max_length=4096, min_length=1)
+    ]
+
+
+class Timeline(RootModel[List[TimelineItem]]):
+    root: Annotated[
+        List[TimelineItem],
+        Field(
+            description="This is timeline information for significant events about this vulnerability or changes to the CVE Record.",
+            min_length=1,
+        ),
+    ]
+
+
+class Credit(BaseModel):
+    lang: Annotated[
+        Language,
+        Field(
+            description="The language used when describing the credits. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code."
+        ),
+    ]
+    value: Annotated[str, Field(max_length=4096, min_length=1)]
+    user: Annotated[
+        Optional[UuidType],
+        Field(
+            None,
+            description="UUID of the user being credited if present in the CVE User Registry (optional). This UUID can be used to lookup the user record in the user registry service.",
+        ),
+    ]
+    type: Annotated[
+        Optional[Type],
+        Field(
+            "finder",
+            description="Type or role of the entity being credited (optional). finder: identifies the vulnerability.\nreporter: notifies the vendor of the vulnerability to a CNA.\nanalyst: validates the vulnerability to ensure accuracy or severity.\ncoordinator: facilitates the coordinated response process.\nremediation developer: prepares a code change or other remediation plans.\nremediation reviewer: reviews vulnerability remediation plans or code changes for effectiveness and completeness.\nremediation verifier: tests and verifies the vulnerability or its remediation.\ntool: names of tools used in vulnerability discovery or identification.\nsponsor: supports the vulnerability identification or remediation activities.",
+        ),
+    ]
+
+
+class Credits(RootModel[List[Credit]]):
+    root: Annotated[
+        List[Credit],
+        Field(
+            description="Statements acknowledging specific people, organizations, or tools recognizing the work done in researching, discovering, remediating or helping with activities related to this CVE.",
+            min_length=1,
+        ),
+    ]
+
+
+class CnaTagsModel(RootModel[List[Union[TagExtension, CnaTags]]]):
+    root: Annotated[
+        List[Union[TagExtension, CnaTags]],
+        Field(
+            description="Tags provided by a CNA describing the CVE Record.",
+            min_length=1,
+        ),
+    ]
+
+
+class AdpTagsModel(RootModel[List[Union[TagExtension, AdpTags]]]):
+    root: Annotated[
+        List[Union[TagExtension, AdpTags]],
+        Field(
+            description="Tags provided by an ADP describing the CVE Record.",
+            min_length=1,
+        ),
+    ]
+
+
+class CnaRejectedContainer(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    providerMetadata: ProviderMetadata
+    rejectedReasons: Annotated[
+        Descriptions, Field(description="Reasons for rejecting this CVE Record.")
+    ]
+    replacedBy: Annotated[
+        Optional[List[CveId]],
+        Field(
+            None,
+            description="Contains an array of CVE IDs that this CVE ID was rejected in favor of because this CVE ID was assigned to the vulnerabilities.",
+            min_length=1,
+        ),
+    ]
+
+
+class Description1(BaseModel):
+    lang: Language
+    description: Annotated[
+        str,
+        Field(
+            description="Text description of problemType, or title from CWE or OWASP.",
+            max_length=4096,
+            min_length=1,
+        ),
+    ]
+    cweId: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="CWE ID of the CWE that best describes this problemType entry.",
+            max_length=9,
+            min_length=5,
+            pattern="^CWE-[1-9][0-9]*$",
+        ),
+    ]
+    type: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Problemtype source, text, OWASP, CWE, etc.,",
+            max_length=128,
+            min_length=1,
+        ),
+    ]
+    references: Optional[References] = None
+
+
+class ProblemType(BaseModel):
+    descriptions: Annotated[List[Description1], Field(min_length=1)]
+
+
+class ProblemTypes(RootModel[List[ProblemType]]):
+    root: Annotated[
+        List[ProblemType],
+        Field(
+            description="This is problem type information (e.g. CWE identifier). Must contain: At least one entry, can be text, OWASP, CWE, please note that while only one is required you can use more than one (or indeed all three) as long as they are correct). (CNA requirement: [PROBLEMTYPE]).",
+            min_length=1,
+        ),
+    ]
+
+
+class Metrics1(BaseModel):
+    format: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the scoring format. This provides a bit of future proofing. Additional properties are not prohibited, so this will support the inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV44, format = 'cvssV44', other = cvssV4_4 JSON object. In the future, the other properties can be converted to score properties when they become part of the schema.",
+            max_length=64,
+            min_length=1,
+        ),
+    ]
+    scenarios: Annotated[
+        Optional[List[Scenario]],
+        Field(
+            None,
+            description="Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+            min_length=1,
+        ),
+    ]
+    cvssV3_1: cvss_v3.Field1
+    cvssV3_0: Optional[cvss_v3.Field0] = None
+    cvssV2_0: Optional[cvss_v2.Field0] = None
+    other: Annotated[
+        Optional[Other],
+        Field(
+            None,
+            description="A non-standard impact description, may be prose or JSON block.",
+        ),
+    ]
+
+
+class Metrics2(BaseModel):
+    format: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the scoring format. This provides a bit of future proofing. Additional properties are not prohibited, so this will support the inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV44, format = 'cvssV44', other = cvssV4_4 JSON object. In the future, the other properties can be converted to score properties when they become part of the schema.",
+            max_length=64,
+            min_length=1,
+        ),
+    ]
+    scenarios: Annotated[
+        Optional[List[Scenario]],
+        Field(
+            None,
+            description="Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+            min_length=1,
+        ),
+    ]
+    cvssV3_1: Optional[cvss_v3.Field1] = None
+    cvssV3_0: cvss_v3.Field0
+    cvssV2_0: Optional[cvss_v2.Field0] = None
+    other: Annotated[
+        Optional[Other],
+        Field(
+            None,
+            description="A non-standard impact description, may be prose or JSON block.",
+        ),
+    ]
+
+
+class Metrics3(BaseModel):
+    format: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the scoring format. This provides a bit of future proofing. Additional properties are not prohibited, so this will support the inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV44, format = 'cvssV44', other = cvssV4_4 JSON object. In the future, the other properties can be converted to score properties when they become part of the schema.",
+            max_length=64,
+            min_length=1,
+        ),
+    ]
+    scenarios: Annotated[
+        Optional[List[Scenario]],
+        Field(
+            None,
+            description="Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+            min_length=1,
+        ),
+    ]
+    cvssV3_1: Optional[cvss_v3.Field1] = None
+    cvssV3_0: Optional[cvss_v3.Field0] = None
+    cvssV2_0: cvss_v2.Field0
+    other: Annotated[
+        Optional[Other],
+        Field(
+            None,
+            description="A non-standard impact description, may be prose or JSON block.",
+        ),
+    ]
+
+
+class Metrics4(BaseModel):
+    format: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="Name of the scoring format. This provides a bit of future proofing. Additional properties are not prohibited, so this will support the inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV44, format = 'cvssV44', other = cvssV4_4 JSON object. In the future, the other properties can be converted to score properties when they become part of the schema.",
+            max_length=64,
+            min_length=1,
+        ),
+    ]
+    scenarios: Annotated[
+        Optional[List[Scenario]],
+        Field(
+            None,
+            description="Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
+            min_length=1,
+        ),
+    ]
+    cvssV3_1: Optional[cvss_v3.Field1] = None
+    cvssV3_0: Optional[cvss_v3.Field0] = None
+    cvssV2_0: Optional[cvss_v2.Field0] = None
+    other: Annotated[
+        Other,
+        Field(
+            description="A non-standard impact description, may be prose or JSON block."
+        ),
+    ]
+
+
+class Metrics(RootModel[List[Union[Metrics1, Metrics2, Metrics3, Metrics4]]]):
+    root: Annotated[
+        List[Union[Metrics1, Metrics2, Metrics3, Metrics4]],
+        Field(
+            description="Collection of impact scores with attribution.", min_length=1
+        ),
+    ]
+
+
+class Containers1(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    cna: CnaRejectedContainer
+
+
+class CVE2(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    dataType: DataType
+    dataVersion: DataVersion
+    cveMetadata: CveMetadataRejected
+    containers: Annotated[
+        Containers1,
+        Field(
+            description="A set of structures (called containers) used to store vulnerability information related to a specific CVE ID provided by a specific organization participating in the CVE program. Each container includes information provided by a different source.\n\nAt minimum, a 'cna' container containing the vulnerability information provided by the CNA who initially assigned the CVE ID must be included.\n\nThere can only be one 'cna' container, as there can only be one assigning CNA."
+        ),
+    ]
+
+
+class CnaPublishedContainer(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    providerMetadata: ProviderMetadata
+    dateAssigned: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="The date/time this CVE ID was associated with a vulnerability by a CNA.",
+        ),
+    ]
+    datePublic: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="If known, the date/time the vulnerability was disclosed publicly.",
+        ),
+    ]
+    title: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="A title, headline, or a brief phrase summarizing the CVE record. Eg., Buffer overflow in Example Soft.",
+            max_length=256,
+            min_length=1,
+        ),
+    ]
+    descriptions: Descriptions
+    affected: Affected
+    problemTypes: Optional[ProblemTypes] = None
+    references: References
+    impacts: Optional[Impacts] = None
+    metrics: Optional[Metrics] = None
+    configurations: Optional[Configurations] = None
+    workarounds: Optional[Workarounds] = None
+    solutions: Optional[Solutions] = None
+    exploits: Optional[Exploits] = None
+    timeline: Optional[Timeline] = None
+    credits: Optional[Credits] = None
+    source: Optional[Source] = None
+    tags: Optional[CnaTagsModel] = None
+    taxonomyMappings: Optional[TaxonomyMappings] = None
+
+
+CnaPublishedContainer.model_rebuild()
+
+
+class AdpContainer(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    providerMetadata: ProviderMetadata
+    datePublic: Annotated[
+        Optional[Timestamp],
+        Field(
+            None,
+            description="If known, the date/time the vulnerability was disclosed publicly.",
+        ),
+    ]
+    title: Annotated[
+        Optional[str],
+        Field(
+            None,
+            description="A title, headline, or a brief phrase summarizing the information in an ADP container.",
+            max_length=256,
+            min_length=1,
+        ),
+    ]
+    descriptions: Optional[Descriptions] = None
+    affected: Optional[Affected] = None
+    problemTypes: Optional[ProblemTypes] = None
+    references: Optional[References] = None
+    impacts: Optional[Impacts] = None
+    metrics: Optional[Metrics] = None
+    configurations: Optional[Configurations] = None
+    workarounds: Optional[Workarounds] = None
+    solutions: Optional[Solutions] = None
+    exploits: Optional[Exploits] = None
+    timeline: Optional[Timeline] = None
+    credits: Optional[Credits] = None
+    source: Optional[Source] = None
+    tags: Optional[AdpTagsModel] = None
+    taxonomyMappings: Optional[TaxonomyMappings] = None
+
+
+class Containers(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    cna: Optional[CnaPublishedContainer]
+    adp: Annotated[Optional[List[AdpContainer]], Field(None, min_length=1)]
+
+
+class CVE1(BaseModel):
+    model_config = ConfigDict(
+        extra="forbid",
+    )
+    dataType: DataType
+    dataVersion: DataVersion
+    cveMetadata: CveMetadataPublished
+    containers: Annotated[
+        Containers,
+        Field(
+            description="A set of structures (called containers) used to store vulnerability information related to a specific CVE ID provided by a specific organization participating in the CVE program. Each container includes information provided by a different source.\n\nAt a minimum, a 'cna' container containing the vulnerability information provided by the CNA who initially assigned the CVE ID must be included.\n\nThere can only be one 'cna' container, as there can only be one assigning CNA. However, there can be multiple 'adp' containers, allowing multiple organizations participating in the CVE program to add additional information related to the vulnerability. For the most part, the 'cna' and 'adp' containers contain the same properties. The main differences are the source of the information. The 'cna' container requires the CNA to include certain fields, while the 'adp' container does not."
+        ),
+    ]
+
+
+class CVE(RootModel[Union[CVE1, CVE2]]):
+    root: Annotated[
+        Union[CVE1, CVE2],
+        Field(
+            description="cve-schema specifies the CVE JSON record format. This is the blueprint for a rich set of JSON data that can be submitted by CVE Numbering Authorities (CNAs) and Authorized Data Publishers (ADPs) to describe a CVE Record. Some examples of CVE Record data include CVE ID number, affected product(s), affected version(s), and public references. While those specific items are required when assigning a CVE, there are many other optional data in the schema that can be used to enrich CVE Records for community benefit. Learn more about the CVE program at [the official website](https://cve.mitre.org). This CVE JSON record format is defined using JSON Schema. Learn more about JSON Schema [here](https://json-schema.org/).",
+            title="CVE JSON record format",
+        ),
+    ]
diff --git a/vdb/lib/cve_model/common.py b/vdb/lib/cve_model/common.py
new file mode 100644
index 0000000..2665ff8
--- /dev/null
+++ b/vdb/lib/cve_model/common.py
@@ -0,0 +1,189 @@
+from enum import Enum
+from typing import Annotated
+
+from pydantic import Field, RootModel
+
+
+class AttackVectorType(Enum):
+    NETWORK = "NETWORK"
+    ADJACENT_NETWORK = "ADJACENT_NETWORK"
+    LOCAL = "LOCAL"
+    PHYSICAL = "PHYSICAL"
+
+
+class ModifiedAttackVectorType(Enum):
+    NETWORK = "NETWORK"
+    ADJACENT_NETWORK = "ADJACENT_NETWORK"
+    LOCAL = "LOCAL"
+    PHYSICAL = "PHYSICAL"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class AttackComplexityType(Enum):
+    HIGH = "HIGH"
+    LOW = "LOW"
+
+
+class ModifiedAttackComplexityType(Enum):
+    HIGH = "HIGH"
+    LOW = "LOW"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class PrivilegesRequiredType(Enum):
+    HIGH = "HIGH"
+    LOW = "LOW"
+    NONE = "NONE"
+
+
+class ModifiedPrivilegesRequiredType(Enum):
+    HIGH = "HIGH"
+    LOW = "LOW"
+    NONE = "NONE"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class UserInteractionType(Enum):
+    NONE = "NONE"
+    REQUIRED = "REQUIRED"
+
+
+class ModifiedUserInteractionType(Enum):
+    NONE = "NONE"
+    REQUIRED = "REQUIRED"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class ScopeType(Enum):
+    UNCHANGED = "UNCHANGED"
+    CHANGED = "CHANGED"
+
+
+class ModifiedScopeType(Enum):
+    UNCHANGED = "UNCHANGED"
+    CHANGED = "CHANGED"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class CiaType(Enum):
+    NONE = "NONE"
+    LOW = "LOW"
+    HIGH = "HIGH"
+
+
+class ModifiedCiaType(Enum):
+    NONE = "NONE"
+    LOW = "LOW"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class ExploitCodeMaturityType(Enum):
+    UNPROVEN = "UNPROVEN"
+    PROOF_OF_CONCEPT = "PROOF_OF_CONCEPT"
+    FUNCTIONAL = "FUNCTIONAL"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class RemediationLevelType(Enum):
+    OFFICIAL_FIX = "OFFICIAL_FIX"
+    TEMPORARY_FIX = "TEMPORARY_FIX"
+    WORKAROUND = "WORKAROUND"
+    UNAVAILABLE = "UNAVAILABLE"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class ConfidenceType(Enum):
+    UNKNOWN = "UNKNOWN"
+    REASONABLE = "REASONABLE"
+    CONFIRMED = "CONFIRMED"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class CiaRequirementType(Enum):
+    LOW = "LOW"
+    MEDIUM = "MEDIUM"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class ScoreType(RootModel[float]):
+    root: Annotated[float, Field(ge=0.0, le=10.0)]
+
+
+class SeverityType(Enum):
+    NONE = "NONE"
+    LOW = "LOW"
+    MEDIUM = "MEDIUM"
+    HIGH = "HIGH"
+    CRITICAL = "CRITICAL"
+
+
+class AccessVectorType(Enum):
+    NETWORK = "NETWORK"
+    ADJACENT_NETWORK = "ADJACENT_NETWORK"
+    LOCAL = "LOCAL"
+
+
+class AccessComplexityType(Enum):
+    HIGH = "HIGH"
+    MEDIUM = "MEDIUM"
+    LOW = "LOW"
+
+
+class AuthenticationType(Enum):
+    MULTIPLE = "MULTIPLE"
+    SINGLE = "SINGLE"
+    NONE = "NONE"
+
+
+class CiaTypeModel(Enum):
+    NONE = "NONE"
+    PARTIAL = "PARTIAL"
+    COMPLETE = "COMPLETE"
+
+
+class ExploitabilityType(Enum):
+    UNPROVEN = "UNPROVEN"
+    PROOF_OF_CONCEPT = "PROOF_OF_CONCEPT"
+    FUNCTIONAL = "FUNCTIONAL"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class ReportConfidenceType(Enum):
+    UNCONFIRMED = "UNCONFIRMED"
+    UNCORROBORATED = "UNCORROBORATED"
+    CONFIRMED = "CONFIRMED"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class CollateralDamagePotentialType(Enum):
+    NONE = "NONE"
+    LOW = "LOW"
+    LOW_MEDIUM = "LOW_MEDIUM"
+    MEDIUM_HIGH = "MEDIUM_HIGH"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class TargetDistributionType(Enum):
+    NONE = "NONE"
+    LOW = "LOW"
+    MEDIUM = "MEDIUM"
+    HIGH = "HIGH"
+    NOT_DEFINED = "NOT_DEFINED"
+
+
+class Type(Enum):
+    finder = "finder"
+    reporter = "reporter"
+    analyst = "analyst"
+    coordinator = "coordinator"
+    remediation_developer = "remediation developer"
+    remediation_reviewer = "remediation reviewer"
+    remediation_verifier = "remediation verifier"
+    tool = "tool"
+    sponsor = "sponsor"
+    other = "other"
diff --git a/vdb/lib/cve_model/cvss_v2.py b/vdb/lib/cve_model/cvss_v2.py
new file mode 100644
index 0000000..6c908ec
--- /dev/null
+++ b/vdb/lib/cve_model/cvss_v2.py
@@ -0,0 +1,55 @@
+# generated by datamodel-codegen:
+#   filename:  CVE_JSON_5.0_schema.json
+#   timestamp: 2024-02-07T18:22:42+00:00
+
+from __future__ import annotations
+
+from enum import Enum
+from typing import Annotated, Optional
+
+from pydantic import BaseModel, Field
+
+from vdb.lib.cve_model.common import (
+    AccessComplexityType,
+    AccessVectorType,
+    AuthenticationType,
+    CiaRequirementType,
+    CiaTypeModel,
+    CollateralDamagePotentialType,
+    ExploitabilityType,
+    RemediationLevelType,
+    ReportConfidenceType,
+    ScoreType,
+    TargetDistributionType,
+)
+
+
+class Version(Enum):
+    field_2_0 = "2.0"
+
+
+class Field0(BaseModel):
+    version: Annotated[Version, Field(description="CVSS Version")]
+    vectorString: Annotated[
+        str,
+        Field(
+            pattern="^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
+        ),
+    ]
+    accessVector: Optional[AccessVectorType] = None
+    accessComplexity: Optional[AccessComplexityType] = None
+    authentication: Optional[AuthenticationType] = None
+    confidentialityImpact: Optional[CiaTypeModel] = None
+    integrityImpact: Optional[CiaTypeModel] = None
+    availabilityImpact: Optional[CiaTypeModel] = None
+    baseScore: ScoreType
+    exploitability: Optional[ExploitabilityType] = None
+    remediationLevel: Optional[RemediationLevelType] = None
+    reportConfidence: Optional[ReportConfidenceType] = None
+    temporalScore: Optional[ScoreType] = None
+    collateralDamagePotential: Optional[CollateralDamagePotentialType] = None
+    targetDistribution: Optional[TargetDistributionType] = None
+    confidentialityRequirement: Optional[CiaRequirementType] = None
+    integrityRequirement: Optional[CiaRequirementType] = None
+    availabilityRequirement: Optional[CiaRequirementType] = None
+    environmentalScore: Optional[ScoreType] = None
diff --git a/vdb/lib/cve_model/cvss_v3.py b/vdb/lib/cve_model/cvss_v3.py
new file mode 100644
index 0000000..6cf45a0
--- /dev/null
+++ b/vdb/lib/cve_model/cvss_v3.py
@@ -0,0 +1,115 @@
+# generated by datamodel-codegen:
+#   filename:  CVE_JSON_5.0_schema.json
+#   timestamp: 2024-02-07T18:22:42+00:00
+
+from __future__ import annotations
+
+from enum import Enum
+from typing import Annotated, Optional
+
+from pydantic import BaseModel, Field
+
+from vdb.lib.cve_model.common import (
+    AttackComplexityType,
+    AttackVectorType,
+    CiaRequirementType,
+    CiaType,
+    ConfidenceType,
+    ExploitCodeMaturityType,
+    ModifiedAttackComplexityType,
+    ModifiedAttackVectorType,
+    ModifiedCiaType,
+    ModifiedPrivilegesRequiredType,
+    ModifiedScopeType,
+    ModifiedUserInteractionType,
+    PrivilegesRequiredType,
+    RemediationLevelType,
+    ScopeType,
+    ScoreType,
+    SeverityType,
+    UserInteractionType,
+)
+
+
+class Version(Enum):
+    field_3_1 = "3.1"
+
+
+class Version1Model(Enum):
+    field_3_0 = "3.0"
+
+
+class Field1(BaseModel):
+    version: Annotated[Version, Field(description="CVSS Version")]
+    vectorString: Annotated[
+        str,
+        Field(
+            pattern="^CVSS:3[.]1/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
+        ),
+    ]
+    attackVector: Optional[AttackVectorType] = None
+    attackComplexity: Optional[AttackComplexityType] = None
+    privilegesRequired: Optional[PrivilegesRequiredType] = None
+    userInteraction: Optional[UserInteractionType] = None
+    scope: Optional[ScopeType] = None
+    confidentialityImpact: Optional[CiaType] = None
+    integrityImpact: Optional[CiaType] = None
+    availabilityImpact: Optional[CiaType] = None
+    baseScore: ScoreType
+    baseSeverity: SeverityType
+    exploitCodeMaturity: Optional[ExploitCodeMaturityType] = None
+    remediationLevel: Optional[RemediationLevelType] = None
+    reportConfidence: Optional[ConfidenceType] = None
+    temporalScore: Optional[ScoreType] = None
+    temporalSeverity: Optional[SeverityType] = None
+    confidentialityRequirement: Optional[CiaRequirementType] = None
+    integrityRequirement: Optional[CiaRequirementType] = None
+    availabilityRequirement: Optional[CiaRequirementType] = None
+    modifiedAttackVector: Optional[ModifiedAttackVectorType] = None
+    modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = None
+    modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = None
+    modifiedUserInteraction: Optional[ModifiedUserInteractionType] = None
+    modifiedScope: Optional[ModifiedScopeType] = None
+    modifiedConfidentialityImpact: Optional[ModifiedCiaType] = None
+    modifiedIntegrityImpact: Optional[ModifiedCiaType] = None
+    modifiedAvailabilityImpact: Optional[ModifiedCiaType] = None
+    environmentalScore: Optional[ScoreType] = None
+    environmentalSeverity: Optional[SeverityType] = None
+
+
+class Field0(BaseModel):
+    version: Annotated[Version1Model, Field(description="CVSS Version")]
+    vectorString: Annotated[
+        str,
+        Field(
+            pattern="^CVSS:3[.]0/((AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
+        ),
+    ]
+    attackVector: Optional[AttackVectorType] = None
+    attackComplexity: Optional[AttackComplexityType] = None
+    privilegesRequired: Optional[PrivilegesRequiredType] = None
+    userInteraction: Optional[UserInteractionType] = None
+    scope: Optional[ScopeType] = None
+    confidentialityImpact: Optional[CiaType] = None
+    integrityImpact: Optional[CiaType] = None
+    availabilityImpact: Optional[CiaType] = None
+    baseScore: ScoreType
+    baseSeverity: SeverityType
+    exploitCodeMaturity: Optional[ExploitCodeMaturityType] = None
+    remediationLevel: Optional[RemediationLevelType] = None
+    reportConfidence: Optional[ConfidenceType] = None
+    temporalScore: Optional[ScoreType] = None
+    temporalSeverity: Optional[SeverityType] = None
+    confidentialityRequirement: Optional[CiaRequirementType] = None
+    integrityRequirement: Optional[CiaRequirementType] = None
+    availabilityRequirement: Optional[CiaRequirementType] = None
+    modifiedAttackVector: Optional[ModifiedAttackVectorType] = None
+    modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = None
+    modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = None
+    modifiedUserInteraction: Optional[ModifiedUserInteractionType] = None
+    modifiedScope: Optional[ModifiedScopeType] = None
+    modifiedConfidentialityImpact: Optional[ModifiedCiaType] = None
+    modifiedIntegrityImpact: Optional[ModifiedCiaType] = None
+    modifiedAvailabilityImpact: Optional[ModifiedCiaType] = None
+    environmentalScore: Optional[ScoreType] = None
+    environmentalSeverity: Optional[SeverityType] = None
diff --git a/vdb/lib/db.py b/vdb/lib/db.py
index 59491aa..e893c6c 100644
--- a/vdb/lib/db.py
+++ b/vdb/lib/db.py
@@ -3,9 +3,9 @@
 from vdb.lib import config, storage
 from vdb.lib.utils import convert_to_occurrence, parse_cpe, parse_purl, version_compare
 
-INDEX_DATA = None
-VENDOR_INDEX_DATA = None
-POS_INDEX_DATA = None
+INDEX_DATA = {}
+VENDOR_INDEX_DATA = {}
+POS_INDEX_DATA = {}
 
 
 def build_index(index_pos_list):
@@ -41,7 +41,7 @@ def build_index(index_pos_list):
             pos_idx[d["name"]].append(store_pos_key)
             if d.get("vendor"):
                 vendor_idx_key = f'{d.get("vendor")}|{d["name"]}'
-                vendor_idx[vendor_idx_key].append((ver_range_str))
+                vendor_idx[vendor_idx_key].append(ver_range_str)
                 pos_idx[vendor_idx_key].append(store_pos_key)
     return idx, vendor_idx, pos_idx
 
@@ -63,8 +63,9 @@ def get(db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX):
 def store(db, datas, reindex=True):
     """Store data in the table
 
-    :param table: Table instance
+    :param db: Table instance
     :param datas: Data list to store
+    :param reindex: Boolean to enable re-index
     :return: Stored packed documents
     """
     if datas is None:
@@ -113,12 +114,6 @@ def _key_func(data, match_list):
     :param value: Value to compare against
     :return: True if there is a match False otherwise
     """
-    package = ""
-    min_affected_version_including = ""
-    max_affected_version_including = ""
-    min_affected_version_excluding = None
-    max_affected_version_excluding = None
-    cpe_uri = ""
     if isinstance(data, dict):
         if not data.get("details"):
             return False
@@ -200,11 +195,7 @@ def bulk_index_search(pkg_list):
     ret_list = set()
     pos_cve_cache = {}
     for pkg in pkg_list:
-        vendor = None
-        name = None
-        version = None
         # This key could be either a vendor|name or name
-        vendor_idx_key = None
         pkg_type = None
         if pkg.get("purl"):
             purl_obj = parse_purl(pkg.get("purl"))
@@ -237,7 +228,6 @@ def bulk_index_search(pkg_list):
         vendor_idx_key = (
             f"{vendor.lower()}|{name.lower()}" if vendor else f"{name.lower()}"
         )
-        version_list = None
         # If there is vendor information use it to perform strict search
         # For ecosystem such as npm, always perform such a strict search to reduce false positives
         if vendor or (pkg_type and pkg_type in ("npm",)):
diff --git a/vdb/lib/gha.py b/vdb/lib/gha.py
index a71b5ab..0c57063 100755
--- a/vdb/lib/gha.py
+++ b/vdb/lib/gha.py
@@ -12,16 +12,8 @@
 import os
 import re
 
-try:
-    import orjson
-
-    ORJSON_AVAILABLE = True
-except ImportError:
-    import json
-
-    ORJSON_AVAILABLE = False
-
 import httpx
+import orjson
 
 from vdb.lib import config
 from vdb.lib.nvd import NvdSource
@@ -35,16 +27,13 @@
 api_token = os.environ.get("GITHUB_TOKEN")
 headers = {"Authorization": f"token {api_token}"}
 
-json_lib = orjson if ORJSON_AVAILABLE else json
 
-
-def get_query(type="recent"):
+def get_query(qtype="recent"):
     """"""
-    extra_args = ""
-    if type == "recent" or not type:
+    if qtype == "recent" or not qtype:
         extra_args = "first: 100"
     else:
-        extra_args = 'first: 100, after: "' + str(type) + '"'
+        extra_args = 'first: 100, after: "' + str(qtype) + '"'
     gqljson = {
         "query": """
             query {
@@ -101,7 +90,7 @@ def download_all(self, local_store=True):
         data_list = []
         last_id = None
         for y in range(0, int(config.GHA_PAGES_COUNT)):
-            data, page_info = self.fetch(type=last_id)
+            data, page_info = self.fetch(vtype=last_id)
             if data:
                 if local_store:
                     self.store(data)
@@ -116,17 +105,20 @@ def download_recent(self, local_store=True):
             self.store(data)
         return data
 
-    def fetch(self, type):
+    def fetch(self, vtype):
         """Private method to fetch the advisory data via GraphQL api"""
         LOG.debug(
-            "Download GitHub advisory from %s with cursor %s", config.GHA_URL, type
+            "Download GitHub advisory from %s with cursor %s", config.GHA_URL, vtype
         )
         client = httpx.Client(http2=True, follow_redirects=True, timeout=180)
-        r = client.post(url=config.GHA_URL, json=get_query(type=type), headers=headers)
+        r = client.post(
+            url=config.GHA_URL, json=get_query(qtype=vtype), headers=headers
+        )
         json_data = r.json()
         return self.convert(json_data)
 
-    def get_version_range(self, version_str):
+    @staticmethod
+    def get_version_range(version_str):
         """
         Version range format - https://developer.github.com/v4/object/securityvulnerability/
         = 0.2.0 denotes a single vulnerable version.
@@ -189,13 +181,13 @@ def convert(self, cve_data):
             references = []
             for r in cve["references"]:
                 references.append({"url": r["url"], "name": r["url"]})
-            for id in cve["identifiers"]:
-                if id["type"] == "CVE":
-                    cve_id = id["value"]
+            for cid in cve["identifiers"]:
+                if cid["type"] == "CVE":
+                    cve_id = cid["value"]
             if not cve_id:
                 cve_id = cve["ghsaId"]
                 assigner = "@github"
-            references = json_lib.dumps(references)
+            references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
             if isinstance(references, bytes):
                 references = references.decode("utf-8", "ignore")
             for p in cve["vulnerabilities"]["nodes"]:
@@ -270,7 +262,7 @@ def convert(self, cve_data):
                     lastModifiedDate=cve["updatedAt"],
                 )
                 try:
-                    tdata_json = json_lib.loads(tdata)
+                    tdata_json = orjson.loads(tdata)
                     vuln = NvdSource.convert_vuln(tdata_json)
                     vuln.description = compress_str(description)
                     ret_data.append(vuln)
diff --git a/vdb/lib/npm.py b/vdb/lib/npm.py
index 9032184..7adca3d 100644
--- a/vdb/lib/npm.py
+++ b/vdb/lib/npm.py
@@ -5,16 +5,8 @@
 """
 import logging
 
-try:
-    import orjson
-
-    ORJSON_AVAILABLE = True
-except ImportError:
-    import json
-
-    ORJSON_AVAILABLE = False
-
 import httpx
+import orjson
 
 from vdb.lib import config
 from vdb.lib.nvd import NvdSource
@@ -32,8 +24,6 @@
 )
 LOG = logging.getLogger(__name__)
 
-json_lib = orjson if ORJSON_AVAILABLE else json
-
 
 class NpmSource(NvdSource):
     """
@@ -44,7 +34,6 @@ def bulk_search(self, app_info, pkg_list):
         """
         Bulk search the resource instead of downloading the information
 
-        :param payload: Data containing required metadata and dependencies
         :return: Vulnerability occurrences
         """
         payload = {**app_info}
@@ -110,7 +99,8 @@ def download_all(self, local_store=True):
                     break
         return data_list
 
-    def get_version_ranges(self, version_str):
+    @staticmethod
+    def get_version_ranges(version_str):
         """
         Version range formats used by npm
         <1.10.2
@@ -197,7 +187,7 @@ def to_vuln(self, v, ret_data):
             )
             if v.get("references"):
                 references = convert_md_references(v.get("references"))
-            references = json_lib.dumps(references)
+            references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
             if isinstance(references, bytes):
                 references = references.decode("utf-8", "ignore")
             severity = v.get("severity")
@@ -271,7 +261,7 @@ def to_vuln(self, v, ret_data):
                     lastModifiedDate=last_modified_date,
                 )
                 try:
-                    vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                    vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                     vuln.description = compress_str(description)
                     ret_data.append([vuln, f"""{v["id"]}|{product}|{used_version}"""])
                 except Exception as e:
diff --git a/vdb/lib/nvd.py b/vdb/lib/nvd.py
index f890464..6468988 100644
--- a/vdb/lib/nvd.py
+++ b/vdb/lib/nvd.py
@@ -2,16 +2,8 @@
 import gzip
 import logging
 
-try:
-    import orjson
-
-    ORJSON_AVAILABLE = True
-except ImportError:
-    import json
-
-    ORJSON_AVAILABLE = False
-
 import httpx
+import orjson
 
 from vdb.lib import (
     CustomNamedTemporaryFile,
@@ -21,7 +13,7 @@
     VulnerabilitySource,
     config,
 )
-from vdb.lib import db as dbLib
+from vdb.lib import db as db_lib
 
 logging.basicConfig(
     level=logging.INFO, format="%(levelname)s [%(asctime)s] %(message)s"
@@ -35,10 +27,8 @@
 # Size of the stream to read and write to the file
 DOWNLOAD_CHUNK_SIZE = 128
 
-json_lib = orjson if ORJSON_AVAILABLE else json
 
-
-def get_version(inc_version, exc_version):
+def get_version(inc_version: str, exc_version: str) -> str:
     """
     Method to determine whether to use including or excluding version
     :param inc_version: Including version
@@ -58,7 +48,7 @@ class NvdSource(VulnerabilitySource):
     def download_all(self, local_store=True):
         """Download all historic cve data"""
         # Create database
-        self.db = dbLib.get()
+        self.db = db_lib.get()
         data_list = []
         for y in range(now.year, int(start_year) - 1, -1):
             data = self.fetch(y)
@@ -71,13 +61,13 @@ def download_all(self, local_store=True):
     def download_recent(self, local_store=True):
         """Method which downloads the recent CVE gzip from NVD"""
         # Create database
-        self.db = dbLib.get()
+        self.db = db_lib.get()
         data = self.fetch("recent")
-        if local_store:
+        if data and local_store:
             self.store(data)
         return data
 
-    def fetch(self, year):
+    def fetch(self, year: int | str) -> list[Vulnerability] | None:
         """Private Method which downloads the given CVE gzip from NVD"""
         url = config.NVD_URL % dict(year=year)
         LOG.debug("Download NVD CVE from %s", url)
@@ -93,7 +83,7 @@ def fetch(self, year):
             with gzip.open(tf.name, "rb") as gzipjf:
                 try:
                     cve_data = gzipjf.read()
-                    json_data = json_lib.loads(cve_data)
+                    json_data = orjson.loads(cve_data)
                     return self.convert(json_data)
                 except Exception:
                     logging.warning(
@@ -102,7 +92,7 @@ def fetch(self, year):
                     )
                     return None
 
-    def convert(self, cve_data):
+    def convert(self, cve_data: dict) -> list[Vulnerability]:
         """Convert cve data to Vulnerability"""
         # If this the data from the api, use the new convert_api method
         if not cve_data.get("CVE_Items") and cve_data.get("vulnStatus"):
@@ -115,7 +105,8 @@ def convert(self, cve_data):
                 data.append(v)
         return data
 
-    def _convert_api(self, api_data):
+    @staticmethod
+    def _convert_api(api_data: dict) -> list[Vulnerability]:
         """Convert NVD API data to vulnerability"""
         v = NvdSource.convert_api_vuln(api_data)
         return [v] if v else []
@@ -124,14 +115,14 @@ def refresh(self):
         """Refresh CVE data"""
         return self.download_all()
 
-    def store(self, data, reindex=True):
+    def store(self, data: list[Vulnerability], reindex=True):
         """Store data in the database"""
         if not self.db:
-            self.db = dbLib.get()
-        docs = dbLib.store(self.db, data, reindex)
+            self.db = db_lib.get()
+        docs = db_lib.store(self.db, data, reindex)
         return docs
 
-    def bulk_search(self):
+    def bulk_search(self, app_info, pkg_list):
         """
         Bulk search the resource instead of downloading the information
         :return: Vulnerability result
@@ -139,8 +130,9 @@ def bulk_search(self):
         pass
 
     @staticmethod
-    def convert_vuln(vuln):
+    def convert_vuln(vuln: dict) -> Vulnerability | None:
         vid = vuln["cve"]["CVE_data_meta"]["ID"]
+        assigner = vuln["cve"]["CVE_data_meta"]["ASSIGNER"]
         # CWE
         problem_type = ""
         if (
@@ -186,6 +178,7 @@ def convert_vuln(vuln):
             return None
         return Vulnerability(
             vid,
+            assigner,
             problem_type,
             base_score,
             severity,
@@ -198,7 +191,7 @@ def convert_vuln(vuln):
         )
 
     @staticmethod
-    def convert_vuln_detail(vuln):
+    def convert_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None:
         nodes_list = vuln["configurations"]["nodes"]
         details = []
         for node in nodes_list:
@@ -244,7 +237,7 @@ def convert_vuln_detail(vuln):
         return details
 
     @staticmethod
-    def convert_api_vuln_detail(vuln):
+    def convert_api_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None:
         """Convert configurations section from the API into details"""
         config_list = vuln.get("configurations", [])
         details = []
@@ -266,14 +259,15 @@ def convert_api_vuln_detail(vuln):
                     )
             cpe_details_list = []
             for cpe in cpe_list:
-                detail = {}
-                detail["cpe_uri"] = cpe["criteria"]
-                detail["mii"] = cpe.get("versionStartIncluding")
-                detail["mie"] = cpe.get("versionStartExcluding")
-                detail["mai"] = cpe.get("versionEndIncluding")
-                detail["mae"] = cpe.get("versionEndExcluding")
-                detail["source_update_time"] = vuln["lastModified"]
-                detail["source_orig_time"] = vuln["published"]
+                detail = {
+                    "cpe_uri": cpe["criteria"],
+                    "mii": cpe.get("versionStartIncluding"),
+                    "mie": cpe.get("versionStartExcluding"),
+                    "mai": cpe.get("versionEndIncluding"),
+                    "mae": cpe.get("versionEndExcluding"),
+                    "source_update_time": vuln["lastModified"],
+                    "source_orig_time": vuln["published"],
+                }
                 cpe_details_list.append(detail)
             for det in cpe_details_list:
                 adetail = VulnerabilityDetail.from_dict(det)
@@ -285,7 +279,7 @@ def convert_api_vuln_detail(vuln):
         return details
 
     @staticmethod
-    def _get_value(vuln, key):
+    def _get_value(vuln: dict, key: str) -> str:
         if vuln.get(key):
             value = vuln[key]
             if isinstance(value, str):
@@ -298,12 +292,13 @@ def _get_value(vuln, key):
         return ""
 
     @staticmethod
-    def convert_api_vuln(vuln):
+    def convert_api_vuln(vuln: dict) -> Vulnerability | None:
         # Undergoing Analysis, Analyzed, Modified, Deferred are accepted.
         # Only the below 3 can be rejected
         if vuln["vulnStatus"] in ("Awaiting Analysis", "Rejected", "Received"):
             return None
         vid = vuln["id"]
+        assigner = vuln["sourceIdentifier"]
         # CWE
         problem_type = ""
         for aweakness in vuln.get("weaknesses", []):
@@ -312,15 +307,11 @@ def convert_api_vuln(vuln):
             ].startswith("CWE-"):
                 problem_type = aweakness["description"][0]["value"]
                 break
-        cvss_v3 = None
-        severity = None
-        base_score = None
         description = NvdSource._get_value(vuln, "descriptions")
         rdata = vuln.get("references", [])
         related_urls = [r["url"] for r in rdata]
         metrics = vuln.get("metrics", {})
         cvss_metrics = []
-        cvss_data = {}
         if metrics.get("cvssMetricV31"):
             cvss_metrics = metrics.get("cvssMetricV31")
         elif metrics.get("cvssMetricV30"):
@@ -347,6 +338,7 @@ def convert_api_vuln(vuln):
             return None
         return Vulnerability(
             vid,
+            assigner,
             problem_type,
             base_score,
             severity,
diff --git a/vdb/lib/osv.py b/vdb/lib/osv.py
index 8f541dd..d9e2cce 100644
--- a/vdb/lib/osv.py
+++ b/vdb/lib/osv.py
@@ -6,6 +6,7 @@
 from zipfile import ZipFile
 
 import httpx
+import orjson
 from semver import Version
 
 from vdb.lib import CustomNamedTemporaryFile, config
@@ -21,16 +22,6 @@
 # Size of the stream to read and write to the file
 DOWNLOAD_CHUNK_SIZE = 4096
 
-try:
-    import orjson
-
-    ORJSON_AVAILABLE = True
-except ImportError:
-    import json
-
-    ORJSON_AVAILABLE = False
-
-json_lib = orjson if ORJSON_AVAILABLE else json
 
 vendor_overrides = {
     "apk": "alpine",
@@ -77,7 +68,7 @@ def fetch(self, url):
                         with zipfp.open(zf) as jsonf:
                             cve_data = jsonf.read()
                             try:
-                                json_data = json_lib.loads(cve_data)
+                                json_data = orjson.loads(cve_data)
                                 ret_data += self.convert(json_data)
                             except Exception:
                                 pass
@@ -88,7 +79,8 @@ def convert(self, cve_data):
             return []
         return self.to_vuln(cve_data)
 
-    def to_vuln(self, cve_data):
+    @staticmethod
+    def to_vuln(cve_data):
         ret_data = []
         if not cve_data.get("affected"):
             return ret_data
@@ -119,7 +111,7 @@ def to_vuln(self, cve_data):
         # Change the key from type to name in references
         for aref in cve_references:
             references.append({"name": aref.get("type", "url"), "url": aref.get("url")})
-        references = json_lib.dumps(references)
+        references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
         if isinstance(references, bytes):
             references = references.decode("utf-8", "ignore")
         # Try to locate the CVE id from the aliases section
@@ -295,7 +287,7 @@ def to_vuln(self, cve_data):
                         lastModifiedDate=cve_data.get("modified"),
                     )
                     try:
-                        vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                        vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                         vuln.description = compress_str(description)
                         ret_data.append(vuln)
                     except Exception:
@@ -377,7 +369,7 @@ def to_vuln(self, cve_data):
                                 lastModifiedDate=cve_data.get("modified"),
                             )
                             try:
-                                vuln = NvdSource.convert_vuln(json_lib.loads(tdata))
+                                vuln = NvdSource.convert_vuln(orjson.loads(tdata))
                                 vuln.description = compress_str(description)
                                 ret_data.append(vuln)
                             except Exception:
diff --git a/vdb/lib/storage.py b/vdb/lib/storage.py
index e355b3a..08b5dc8 100644
--- a/vdb/lib/storage.py
+++ b/vdb/lib/storage.py
@@ -15,6 +15,7 @@ def store(datas, db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX):
 
     :param datas: Data list to store
     :param db_file: DB file to use
+    :param index_file: Index file to use
     """
     data_list = serialize_vuln_list(datas)
     packed_obj = None
@@ -50,8 +51,8 @@ def store(datas, db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX):
                 ],
                 use_bin_type=True,
             )
-            with open(index_file, mode="ab") as fp:
-                fp.write(index_obj)
+            with open(index_file, mode="ab") as ifp:
+                ifp.write(index_obj)
     return packed_obj
 
 
diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py
index b286f9a..c9681b2 100644
--- a/vdb/lib/utils.py
+++ b/vdb/lib/utils.py
@@ -1,6 +1,5 @@
 import codecs
 import importlib
-import os
 import re
 import string
 from datetime import date, datetime
@@ -24,7 +23,7 @@
         (\.
             (?P<patch>0|[1-9]\d*[a-zA-Z]?)
         )?
-        (?:[-\.](?P<prerelease>
+        (?:[-.](?P<prerelease>
             (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
             (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
         ))?
@@ -37,7 +36,6 @@
     re.VERBOSE,
 )
 
-
 KNOWN_PRERELEASE_STR = ["final", "release", "alpha", "beta", "rc", "latest"]
 
 DEBIAN_VALID_VERSION = re.compile(
@@ -59,30 +57,30 @@ def load(d):
     Arguments: Dict object; the module which contains the class, the parsed object is instance of.
     """
 
-    def _load(d):
-        if isinstance(d, list):
+    def _load(d1):
+        if isinstance(d1, list):
             li = []
-            for item in d:
+            for item in d1:
                 li.append(_load(item))
             return li
-        elif isinstance(d, str) and ("\\n" in d or "\\t" in d):
-            return decompress_str(d)
-        elif isinstance(d, dict) and "type" in d:  # object
-            t = d["type"]
+        elif isinstance(d1, str) and ("\\n" in d1 or "\\t" in d1):
+            return decompress_str(d1)
+        elif isinstance(d1, dict) and "type" in d1:  # object
+            t = d1["type"]
             if t == "datetime":
                 if hasattr(datetime, "fromisoformat"):
-                    return datetime.fromisoformat(d["value"])
+                    return datetime.fromisoformat(d1["value"])
                 else:
-                    return datetime.strptime(d["value"], DATE_FORMAT_STR)
+                    return datetime.strptime(d1["value"], DATE_FORMAT_STR)
             if t == "Severity":
-                return Severity.from_str(d["value"])
+                return Severity.from_str(d1["value"])
             try:
-                del d["type"]
+                del d1["type"]
                 clazz = getattr(importlib.import_module("vdb.lib"), t)
                 if hasattr(clazz, "from_dict"):
-                    o = clazz.from_dict(d)
+                    o = clazz.from_dict(d1)
                 else:
-                    o = clazz(**d)
+                    o = clazz(**d1)
             except KeyError:
                 raise ClassNotFoundError(
                     "Class '%s' not found in the given module!" % t
@@ -93,13 +91,13 @@ def _load(d):
                     % t
                 )
             return o
-        elif isinstance(d, dict):  # dict
+        elif isinstance(d1, dict):  # dict
             rd = {}
-            for key in d:
-                rd[key] = _load(d[key])
+            for key in d1:
+                rd[key] = _load(d1[key])
             return rd
         else:
-            return d
+            return d1
 
     return _load(d)
 
@@ -107,47 +105,43 @@ def _load(d):
 def dump(obj):
     """Dumps a python object to a JSON string. Argument: Python object"""
 
-    def _dump(obj, path):
-        if isinstance(obj, list):
+    def _dump(obj2, path):
+        if isinstance(obj2, list):
             li = []
             i = 0
-            for item in obj:
+            for item in obj2:
                 li.append(_dump(item, path + "/[" + str(i) + "]"))
                 i += 1
             return li
-        elif isinstance(obj, Enum):  # Enum
-            d = {}
-            d["type"] = obj.__class__.__name__
-            d["value"] = obj.value
+        elif isinstance(obj2, Enum):  # Enum
+            d = {"type": obj2.__class__.__name__, "value": obj2.value}
             return d
-        elif isinstance(obj, dict):  # dict
+        elif isinstance(obj2, dict):  # dict
             rd = {}
-            for key in obj:
-                rd[key] = _dump(obj[key], path + "/" + key)
+            for key in obj2:
+                rd[key] = _dump(obj2[key], path + "/" + key)
             return rd
-        elif isinstance(obj, datetime):  # datetime
-            d = {}
-            d["type"] = obj.__class__.__name__
-            if hasattr(obj, "isoformat"):
-                d["value"] = obj.isoformat()
+        elif isinstance(obj2, datetime):  # datetime
+            d = {"type": obj2.__class__.__name__}
+            if hasattr(obj2, "isoformat"):
+                d["value"] = obj2.isoformat()
             else:
-                d["value"] = obj.strftime(DATE_FORMAT_STR)
+                d["value"] = obj2.strftime(DATE_FORMAT_STR)
             return d
         elif (
-            isinstance(obj, str)
-            or isinstance(obj, bytes)
-            or isinstance(obj, int)
-            or isinstance(obj, float)
-            or isinstance(obj, complex)
-            or isinstance(obj, bool)
-            or type(obj).__name__ == "NoneType"
+            isinstance(obj2, str)
+            or isinstance(obj2, bytes)
+            or isinstance(obj2, int)
+            or isinstance(obj2, float)
+            or isinstance(obj2, complex)
+            or isinstance(obj2, bool)
+            or type(obj2).__name__ == "NoneType"
         ):
-            return obj
+            return obj2
         else:
-            d = {}
-            d["type"] = obj.__class__.__name__
-            for key in obj.__dict__:
-                d[key] = _dump(obj.__dict__[key], path + "/" + key)
+            d = {"type": obj2.__class__.__name__}
+            for key in obj2.__dict__:
+                d[key] = _dump(obj2.__dict__[key], path + "/" + key)
             return d
 
     return _dump(obj, "/")
@@ -168,7 +162,6 @@ def serialize_vuln_list(datas):
         else:
             data = datam
         ddata = data
-        details = None
         if not isinstance(data, dict):
             ddata = vars(data)
             details = data.details
@@ -267,6 +260,7 @@ def normalise_version_str(version_num, normal_len):
     >>> normalise_version_str(1.0.0, 4)
     1.0.0.0
     """
+    version_num = str(version_num)
     version_num_parts = len(version_num.split("."))
     if version_num_parts < normal_len:
         for i in range(version_num_parts, normal_len):
@@ -299,10 +293,10 @@ def convert_to_semver(version):
     :rtype: tuple(:class:`VersionInfo` | None, str)
     """
     if version and len(version) > 8 and check_hex(version):
-        return (None, None)
+        return None, None
     match = BASEVERSION.search(version)
     if not match:
-        return (None, version)
+        return None, version
     ver = {
         key: 0 if value is None else value for key, value in match.groupdict().items()
     }
@@ -731,7 +725,7 @@ def version_compare(
         and len(str(compare_ver_num)) == len(str(min_version_num))
         and len(str(compare_ver_num)) == len(str(max_version_num))
     ):
-        if compare_ver_num >= min_version_num and compare_ver_num <= max_version_num:
+        if min_version_num <= compare_ver_num <= max_version_num:
             if (compare_ver_build == min_version_build and not is_min_exclude) or (
                 compare_ver_build == max_version_build and not is_max_exclude
             ):
@@ -881,12 +875,8 @@ def convert_to_occurrence(datas):
             d = dm
         vobj = load(d)
         vdetails = vobj["details"]
-        package_type = ""
-        package = ""
-        cpe_uri = ""
         if isinstance(vdetails, dict):
             package_type = vdetails["package_type"]
-            package = vdetails["package"]
             cpe_uri = vdetails["cpe_uri"]
             fixed_location = vdetails["fixed_location"]
             mii = vdetails["mii"]
@@ -895,7 +885,6 @@ def convert_to_occurrence(datas):
             mae = vdetails["mae"]
         else:
             package_type = vdetails.package_type
-            package = vdetails.package
             cpe_uri = vdetails.cpe_uri
             fixed_location = vdetails.fixed_location
             mii = vdetails.mii
@@ -906,9 +895,9 @@ def convert_to_occurrence(datas):
         # Filter duplicates for the same package with the same id
         if unique_key not in id_list:
             occ = VulnerabilityOccurrence(
-                id=vobj["id"],
+                oid=vobj["id"],
                 problem_type=vobj["problem_type"],
-                type=package_type,
+                otype=package_type,
                 severity=vobj["severity"],
                 cvss_score=vobj["score"],
                 cvss_v3=vobj["cvss_v3"],


Test patch:
diff --git a/test/test_source.py b/test/test_source.py
index 7b5d9a7..0d0cfd2 100644
--- a/test/test_source.py
+++ b/test/test_source.py
@@ -4,6 +4,7 @@
 import pytest
 
 from vdb.lib.aqua import AquaSource
+from vdb.lib.cve import CVESource
 from vdb.lib.gha import GitHubSource
 from vdb.lib.nvd import NvdSource
 from vdb.lib.osv import OSVSource
@@ -364,6 +365,9 @@ def test_convert(test_cve_json):
             assert detail.severity
             assert detail.package
             assert detail.package_type
+    cvesource = CVESource()
+    data = cvesource.convert(data)
+    assert len(data) == 384
 
 
 def test_convert2(test_cve_wconfig_json):
@@ -378,6 +382,9 @@ def test_convert2(test_cve_wconfig_json):
             assert detail.package
             assert detail.package_type
             assert not detail.fixed_location
+    cvesource = CVESource()
+    data = cvesource.convert(data)
+    assert len(data) == 1
 
 
 def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_json3):
@@ -394,8 +401,13 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
             assert not detail.fixed_location
     data = nvdlatest.convert(test_nvd_api_json2)
     assert len(data) == 1
+    cvesource = CVESource()
+    data = cvesource.convert(data)
+    assert len(data) == 1
     data = nvdlatest.convert(test_nvd_api_json3)
     assert len(data) == 0
+    data = cvesource.convert(data)
+    assert len(data) == 0
 
 
 @pytest.mark.skip(reason="This downloads and tests with live data")