diff --git a/INTEGRATION.md b/INTEGRATION.md
index 24801d0..1333afc 100644
--- a/INTEGRATION.md
+++ b/INTEGRATION.md
@@ -14,7 +14,7 @@ The vulnerability database comprises two SQLite database files.
![Index schema](./docs/vdb-index-schema.png)
-- data.vdb6 - Full CVE source database containing normalized data in CVE 5.0 specification formation and purl prefix.
+- data.vdb6 - Full CVE source database containing normalized data in CVE 5.1 specification formation and purl prefix.
![Data schema](./docs/vdb-schema.png)
diff --git a/README.md b/README.md
index c8a7a35..b1e2b97 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ A good vulnerability database must have the following properties:
- Easy to [download](#download-pre-built-database-recommended), [integrate](./INTEGRATION.md), and use
- Performance
-Multiple upstream sources are used by vdb to improve accuracy and reduce false negatives. SQLite database containing data in CVE 5.0 schema format is precompiled and distributed as files via ghcr to simplify download. With automatic purl prefix generation even for git repos, searches on the database can be performed with purl, cpe, or even http git url string. Every row in the database uses an open specification such as CVE 5.0 or Package URL (purl and vers) thus preventing the possibility of vendor lock-in.
+Multiple upstream sources are used by vdb to improve accuracy and reduce false negatives. SQLite database containing data in CVE 5.1 schema format is precompiled and distributed as files via ghcr to simplify download. With automatic purl prefix generation even for git repos, searches on the database can be performed with purl, cpe, or even http git url string. Every row in the database uses an open specification such as CVE 5.0 or Package URL (purl and vers) thus preventing the possibility of vendor lock-in.
## Vulnerability Data sources
@@ -40,7 +40,7 @@ Multiple upstream sources are used by vdb to improve accuracy and reduce false n
## Installation
```shell
-pip install appthreat-vulnerability-db>=6.0.1
+pip install appthreat-vulnerability-db>=6.2.0
```
To install vdb with optional dependencies such as `oras` use the `[oras]` or `[all]` dependency group.
@@ -49,10 +49,10 @@ To install vdb with optional dependencies such as `oras` use the `[oras]` or `[a
pip install appthreat-vulnerability-db[all]
```
-**NOTE:** VDB v6 is a major rewrite to use SQLite database. Current users of depscan v5 must continue using version 5.6.x
+**NOTE:** VDB v6 is a major rewrite to use SQLite database. Current users of depscan v5 must continue using version 5.8.x
```shell
-pip install appthreat-vulnerability-db==5.6.7
+pip install appthreat-vulnerability-db==5.8.0
```
## Usage
@@ -93,7 +93,7 @@ Use any sqlite browser or cli tools to load and query the two databases.
-**data.vdb6** - Contains source data in CVE 5.0 format stored as a jsonb blob.
+**data.vdb6** - Contains source data in CVE 5.1 format stored as a jsonb blob.
diff --git a/data/CVE_JSON_5.0_schema.json b/data/CVE_Record_Format.json
similarity index 94%
rename from data/CVE_JSON_5.0_schema.json
rename to data/CVE_Record_Format.json
index 216bc3f..104e25d 100644
--- a/data/CVE_JSON_5.0_schema.json
+++ b/data/CVE_Record_Format.json
@@ -46,7 +46,8 @@
]
}
}
- }
+ },
+ "additionalProperties": false
},
"cveId": {
"type": "string",
@@ -74,8 +75,7 @@
},
"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.",
+ "description": "Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ss[+-]ZH:ZM'. If timezone offset is not given, GMT (+00:00) 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": {
@@ -246,7 +246,8 @@
"minLength": 1,
"maxLength": 4096
}
- }
+ },
+ "additionalProperties": false
}
},
"platforms": {
@@ -284,10 +285,13 @@
},
{
"required": ["version", "status", "versionType"],
- "oneOf": [
- {"required": ["lessThan"]},
- {"required": ["lessThanOrEqual"]}
- ]
+ "maxProperties": 3
+ },
+ {
+ "required": ["version", "status", "versionType", "lessThan"]
+ },
+ {
+ "required": ["version", "status", "versionType", "lessThanOrEqual"]
}
],
"properties": {
@@ -330,6 +334,7 @@
"type": "object",
"description": "The start of a single status change during the range.",
"required": ["at", "status"],
+ "additionalProperties": false,
"properties": {
"at": {
"description": "The version at which a status change occurs.",
@@ -342,7 +347,8 @@
}
}
}
- }
+ },
+ "additionalProperties": false
}
}
}
@@ -355,9 +361,10 @@
]
},
"dataVersion": {
- "description": "The version of the schema being used. Used to support multiple versions of this format.",
+ "description": "The version of the CVE schema used for validating this record. Used to support multiple versions of this format.",
"type": "string",
- "enum": ["5.0"]
+ "pattern": "^5\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?$",
+ "default": "5.1.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.",
@@ -477,7 +484,8 @@
"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"]
+ "required": ["orgId"],
+ "additionalProperties": false
},
"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.",
@@ -555,6 +563,7 @@
"patternProperties": {
"^x_[^.]*$": {}
},
+ "$comment": "The character . is restricted in names allowed by patternProperties to work-around naming limitations in some common implementations.",
"additionalProperties": false
},
"cnaRejectedContainer": {
@@ -585,6 +594,7 @@
"patternProperties": {
"^x_[^.]*$": {}
},
+ "$comment": "The character . is restricted in names allowed by patternProperties to work-around naming limitations in some common implementations.",
"additionalProperties": false
},
"adpContainer": {
@@ -657,6 +667,7 @@
"patternProperties": {
"^x_[^.]*$": {}
},
+ "$comment": "The character . is restricted in names allowed by patternProperties to work-around naming limitations in some common implementations.",
"additionalProperties": false
},
"affected": {
@@ -715,7 +726,8 @@
"required": [
"type",
"value"
- ]
+ ],
+ "additionalProperties": false
}
}
},
@@ -729,7 +741,8 @@
"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"]
+ "required": ["lang"],
+ "$comment": "Cannot use additionalProperties: false here, as this prevents the other properties used by /definitions/description."
},
"descriptions": {
"type": "array",
@@ -780,12 +793,14 @@
"maxLength": 128
},
"references": {"$ref": "#/definitions/references"}
- }
+ },
+ "additionalProperties": false
},
"minItems": 1,
"uniqueItems": true
}
- }
+ },
+ "additionalProperties": false
},
"minItems": 1,
"uniqueItems": true
@@ -819,7 +834,8 @@
"description": "Prose description of the impact scenario. At a minimum provide the description given by CAPEC.",
"$ref": "#/definitions/descriptions"
}
- }
+ },
+ "additionalProperties": false
}
},
"metrics": {
@@ -829,8 +845,11 @@
"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.",
+ "description": "This is impact type information (e.g. a text description, CVSSv2, CVSSv3, CVSSV4, etc.). Must contain: At least one entry, can be text, CVSSv2, CVSSv3, others may be added.",
"anyOf": [
+ {
+ "required": ["cvssV4_0"]
+ },
{
"required": ["cvssV3_1"]
},
@@ -871,9 +890,11 @@
"required": [
"lang",
"value"
- ]
+ ],
+ "additionalProperties": false
}
},
+ "cvssV4_0": {"$ref": "imports/cvss/cvss-v4.0.json"},
"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"},
@@ -893,12 +914,15 @@
},
"content": {
"type": "object",
+ "$comment": "additionalProperties are allowed here, since this construct supports arbitrary JSON.",
"description": "JSON object not covered by another metrics format.",
"minProperties": 1
}
- }
+ },
+ "additionalProperties": false
}
- }
+ },
+ "additionalProperties": false
}
},
"configurations": {
@@ -951,7 +975,7 @@
],
"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.",
+ "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:ss[+-]ZH:ZM - if the timezone offset is not given, GMT (+00:00) is assumed.",
"$ref": "#/definitions/timestamp"
},
"lang": {
@@ -964,7 +988,8 @@
"minLength": 1,
"maxLength": 4096
}
- }
+ },
+ "additionalProperties": false
}
},
"credits": {
@@ -1006,6 +1031,7 @@
]
}
},
+ "additionalProperties": false,
"required": [
"lang",
"value"
@@ -1035,7 +1061,7 @@
"uniqueItems": true,
"items": {
"type": "object",
- "description": "",
+ "description": "A taxonomy mapping object identifies the taxonomy by a name and version (eg., ATT&CK v13.1, CVSS 3.1, CWE 4.12) along with a list of relations relevant to this CVE.",
"required": [
"taxonomyName",
"taxonomyRelations"
@@ -1043,7 +1069,7 @@
"properties": {
"taxonomyName": {
"type": "string",
- "description": "The name of the taxonomy.",
+ "description": "The name of the taxonomy, eg., ATT&CK, D3FEND, CWE, CVSS",
"minLength": 1,
"maxLength": 128
},
@@ -1055,12 +1081,12 @@
},
"taxonomyRelations": {
"type": "array",
- "description": "",
+ "description": "List of relationships to the taxonomy for the vulnerability.",
"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.",
+ "description": "A relationship between the taxonomy and the CVE or two taxonomy items.",
"required": [
"taxonomyId",
"relationshipName",
@@ -1085,17 +1111,20 @@
"minLength": 1,
"maxLength": 2048
}
- }
+ },
+ "additionalProperties": false
}
}
- }
+ },
+ "additionalProperties": false
}
},
"tagExtension": {
"type": "string",
"minLength": 2,
"maxLength": 128,
- "pattern": "^x_.*$"
+ "pattern": "^x_.*$",
+ "$comment": "These values are not used as JSON property names, so there is not a need to work-around property naming limitations in some common implementations."
},
"cnaTags": {
"type": "array",
@@ -1134,6 +1163,7 @@
{
"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.",
+ "type": "object",
"properties": {
"dataType": {
"$ref": "#/definitions/dataType"
@@ -1171,6 +1201,7 @@
{
"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.",
+ "type": "object",
"properties": {
"dataType": {
"$ref": "#/definitions/dataType"
@@ -1200,4 +1231,4 @@
"additionalProperties": false
}
]
-}
+}
\ No newline at end of file
diff --git a/data/README.md b/data/README.md
index 57d3b21..fe0795b 100644
--- a/data/README.md
+++ b/data/README.md
@@ -7,5 +7,5 @@ Modify the jsonschema to make it compatible with datamodel-code-generator which
```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
+datamodel-codegen --input data/CVE_Record_Format.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/cvss-v4.0.json b/data/imports/cvss/cvss-v4.0.json
new file mode 100644
index 0000000..262616b
--- /dev/null
+++ b/data/imports/cvss/cvss-v4.0.json
@@ -0,0 +1,784 @@
+{
+ "license": [
+ "Copyright (c) 2023, 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 4.0",
+ "$id": "https://www.first.org/cvss/cvss-v4.0.json?20231011",
+ "type": "object",
+ "definitions": {
+ "attackVectorType": {
+ "type": "string",
+ "enum": [
+ "NETWORK",
+ "ADJACENT",
+ "LOCAL",
+ "PHYSICAL"
+ ]
+ },
+ "modifiedAttackVectorType": {
+ "type": "string",
+ "enum": [
+ "NETWORK",
+ "ADJACENT",
+ "LOCAL",
+ "PHYSICAL",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "attackComplexityType": {
+ "type": "string",
+ "enum": [
+ "HIGH",
+ "LOW"
+ ]
+ },
+ "modifiedAttackComplexityType": {
+ "type": "string",
+ "enum": [
+ "HIGH",
+ "LOW",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "attackRequirementsType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "PRESENT"
+ ]
+ },
+ "modifiedAttackRequirementsType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "PRESENT",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "privilegesRequiredType": {
+ "type": "string",
+ "enum": [
+ "HIGH",
+ "LOW",
+ "NONE"
+ ]
+ },
+ "modifiedPrivilegesRequiredType": {
+ "type": "string",
+ "enum": [
+ "HIGH",
+ "LOW",
+ "NONE",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "userInteractionType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "PASSIVE",
+ "ACTIVE"
+ ]
+ },
+ "modifiedUserInteractionType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "PASSIVE",
+ "ACTIVE",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "vulnCiaType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "HIGH"
+ ]
+ },
+ "modifiedVulnCiaType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "HIGH",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "subCiaType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "HIGH"
+ ]
+ },
+ "modifiedSubCType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "HIGH",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "modifiedSubIaType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "HIGH",
+ "SAFETY",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "exploitMaturityType": {
+ "type": "string",
+ "enum": [
+ "UNREPORTED",
+ "PROOF_OF_CONCEPT",
+ "ATTACKED",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "ciaRequirementType": {
+ "type": "string",
+ "enum": [
+ "LOW",
+ "MEDIUM",
+ "HIGH",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "safetyType": {
+ "type": "string",
+ "enum": [
+ "NEGLIGIBLE",
+ "PRESENT",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "automatableType": {
+ "type": "string",
+ "enum": [
+ "NO",
+ "YES",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "recoveryType": {
+ "type": "string",
+ "enum": [
+ "AUTOMATIC",
+ "USER",
+ "IRRECOVERABLE",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "valueDensityType": {
+ "type": "string",
+ "enum": [
+ "DIFFUSE",
+ "CONCENTRATED",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "vulnerabilityResponseEffortType": {
+ "type": "string",
+ "enum": [
+ "LOW",
+ "MODERATE",
+ "HIGH",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "providerUrgencyType": {
+ "type": "string",
+ "enum": [
+ "CLEAR",
+ "GREEN",
+ "AMBER",
+ "RED",
+ "NOT_DEFINED"
+ ],
+ "default": "NOT_DEFINED"
+ },
+ "scoreType": {
+ "type": "number",
+ "enum": [
+ 0.0,
+ 0.1,
+ 0.2,
+ 0.3,
+ 0.4,
+ 0.5,
+ 0.6,
+ 0.7,
+ 0.8,
+ 0.9,
+ 1.0,
+ 1.1,
+ 1.2,
+ 1.3,
+ 1.4,
+ 1.5,
+ 1.6,
+ 1.7,
+ 1.8,
+ 1.9,
+ 2.0,
+ 2.1,
+ 2.2,
+ 2.3,
+ 2.4,
+ 2.5,
+ 2.6,
+ 2.7,
+ 2.8,
+ 2.9,
+ 3.0,
+ 3.1,
+ 3.2,
+ 3.3,
+ 3.4,
+ 3.5,
+ 3.6,
+ 3.7,
+ 3.8,
+ 3.9,
+ 4.0,
+ 4.1,
+ 4.2,
+ 4.3,
+ 4.4,
+ 4.5,
+ 4.6,
+ 4.7,
+ 4.8,
+ 4.9,
+ 5.0,
+ 5.1,
+ 5.2,
+ 5.3,
+ 5.4,
+ 5.5,
+ 5.6,
+ 5.7,
+ 5.8,
+ 5.9,
+ 6.0,
+ 6.1,
+ 6.2,
+ 6.3,
+ 6.4,
+ 6.5,
+ 6.6,
+ 6.7,
+ 6.8,
+ 6.9,
+ 7.0,
+ 7.1,
+ 7.2,
+ 7.3,
+ 7.4,
+ 7.5,
+ 7.6,
+ 7.7,
+ 7.8,
+ 7.9,
+ 8.0,
+ 8.1,
+ 8.2,
+ 8.3,
+ 8.4,
+ 8.5,
+ 8.6,
+ 8.7,
+ 8.8,
+ 8.9,
+ 9.0,
+ 9.1,
+ 9.2,
+ 9.3,
+ 9.4,
+ 9.5,
+ 9.6,
+ 9.7,
+ 9.8,
+ 9.9,
+ 10.0
+ ]
+ },
+ "noneScoreType": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 0
+ },
+ "lowScoreType": {
+ "type": "number",
+ "enum": [
+ 0.1,
+ 0.2,
+ 0.3,
+ 0.4,
+ 0.5,
+ 0.6,
+ 0.7,
+ 0.8,
+ 0.9,
+ 1.0,
+ 1.1,
+ 1.2,
+ 1.3,
+ 1.4,
+ 1.5,
+ 1.6,
+ 1.7,
+ 1.8,
+ 1.9,
+ 2.0,
+ 2.1,
+ 2.2,
+ 2.3,
+ 2.4,
+ 2.5,
+ 2.6,
+ 2.7,
+ 2.8,
+ 2.9,
+ 3.0,
+ 3.1,
+ 3.2,
+ 3.3,
+ 3.4,
+ 3.5,
+ 3.6,
+ 3.7,
+ 3.8,
+ 3.9
+ ]
+ },
+ "mediumScoreType": {
+ "type": "number",
+ "enum": [
+ 4.0,
+ 4.1,
+ 4.2,
+ 4.3,
+ 4.4,
+ 4.5,
+ 4.6,
+ 4.7,
+ 4.8,
+ 4.9,
+ 5.0,
+ 5.1,
+ 5.2,
+ 5.3,
+ 5.4,
+ 5.5,
+ 5.6,
+ 5.7,
+ 5.8,
+ 5.9,
+ 6.0,
+ 6.1,
+ 6.2,
+ 6.3,
+ 6.4,
+ 6.5,
+ 6.6,
+ 6.7,
+ 6.8,
+ 6.9
+ ]
+ },
+ "highScoreType": {
+ "type": "number",
+ "enum": [
+ 7.0,
+ 7.1,
+ 7.2,
+ 7.3,
+ 7.4,
+ 7.5,
+ 7.6,
+ 7.7,
+ 7.8,
+ 7.9,
+ 8.0,
+ 8.1,
+ 8.2,
+ 8.3,
+ 8.4,
+ 8.5,
+ 8.6,
+ 8.7,
+ 8.8,
+ 8.9
+ ]
+ },
+ "criticalScoreType": {
+ "type": "number",
+ "enum": [
+ 9.0,
+ 9.1,
+ 9.2,
+ 9.3,
+ 9.4,
+ 9.5,
+ 9.6,
+ 9.7,
+ 9.8,
+ 9.9,
+ 10.0
+ ]
+ },
+ "severityType": {
+ "type": "string",
+ "enum": [
+ "NONE",
+ "LOW",
+ "MEDIUM",
+ "HIGH",
+ "CRITICAL"
+ ]
+ },
+ "noneSeverityType": {
+ "const": "NONE"
+ },
+ "lowSeverityType": {
+ "const": "LOW"
+ },
+ "mediumSeverityType": {
+ "const": "MEDIUM"
+ },
+ "highSeverityType": {
+ "const": "HIGH"
+ },
+ "criticalSeverityType": {
+ "const": "CRITICAL"
+ }
+ },
+ "properties": {
+ "version": {
+ "description": "CVSS Version",
+ "type": "string",
+ "enum": [
+ "4.0"
+ ]
+ },
+ "vectorString": {
+ "type": "string",
+ "pattern": "^CVSS:4[.]0/AV:[NALP]/AC:[LH]/AT:[NP]/PR:[NLH]/UI:[NPA]/VC:[HLN]/VI:[HLN]/VA:[HLN]/SC:[HLN]/SI:[HLN]/SA:[HLN](/E:[XAPU])?(/CR:[XHML])?(/IR:[XHML])?(/AR:[XHML])?(/MAV:[XNALP])?(/MAC:[XLH])?(/MAT:[XNP])?(/MPR:[XNLH])?(/MUI:[XNPA])?(/MVC:[XNLH])?(/MVI:[XNLH])?(/MVA:[XNLH])?(/MSC:[XNLH])?(/MSI:[XNLHS])?(/MSA:[XNLHS])?(/S:[XNP])?(/AU:[XNY])?(/R:[XAUI])?(/V:[XDC])?(/RE:[XLMH])?(/U:(X|Clear|Green|Amber|Red))?$"
+ },
+ "baseScore": {
+ "$ref": "#/definitions/scoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/severityType"
+ },
+ "attackVector": {
+ "$ref": "#/definitions/attackVectorType"
+ },
+ "attackComplexity": {
+ "$ref": "#/definitions/attackComplexityType"
+ },
+ "attackRequirements": {
+ "$ref": "#/definitions/attackRequirementsType"
+ },
+ "privilegesRequired": {
+ "$ref": "#/definitions/privilegesRequiredType"
+ },
+ "userInteraction": {
+ "$ref": "#/definitions/userInteractionType"
+ },
+ "vulnConfidentialityImpact": {
+ "$ref": "#/definitions/vulnCiaType"
+ },
+ "vulnIntegrityImpact": {
+ "$ref": "#/definitions/vulnCiaType"
+ },
+ "vulnAvailabilityImpact": {
+ "$ref": "#/definitions/vulnCiaType"
+ },
+ "subConfidentialityImpact": {
+ "$ref": "#/definitions/subCiaType"
+ },
+ "subIntegrityImpact": {
+ "$ref": "#/definitions/subCiaType"
+ },
+ "subAvailabilityImpact": {
+ "$ref": "#/definitions/subCiaType"
+ },
+ "exploitMaturity": {
+ "$ref": "#/definitions/exploitMaturityType"
+ },
+ "confidentialityRequirement": {
+ "$ref": "#/definitions/ciaRequirementType"
+ },
+ "integrityRequirement": {
+ "$ref": "#/definitions/ciaRequirementType"
+ },
+ "availabilityRequirement": {
+ "$ref": "#/definitions/ciaRequirementType"
+ },
+ "modifiedAttackVector": {
+ "$ref": "#/definitions/modifiedAttackVectorType"
+ },
+ "modifiedAttackComplexity": {
+ "$ref": "#/definitions/modifiedAttackComplexityType"
+ },
+ "modifiedAttackRequirements": {
+ "$ref": "#/definitions/modifiedAttackRequirementsType"
+ },
+ "modifiedPrivilegesRequired": {
+ "$ref": "#/definitions/modifiedPrivilegesRequiredType"
+ },
+ "modifiedUserInteraction": {
+ "$ref": "#/definitions/modifiedUserInteractionType"
+ },
+ "modifiedVulnConfidentialityImpact": {
+ "$ref": "#/definitions/modifiedVulnCiaType"
+ },
+ "modifiedVulnIntegrityImpact": {
+ "$ref": "#/definitions/modifiedVulnCiaType"
+ },
+ "modifiedVulnAvailabilityImpact": {
+ "$ref": "#/definitions/modifiedVulnCiaType"
+ },
+ "modifiedSubConfidentialityImpact": {
+ "$ref": "#/definitions/modifiedSubCType"
+ },
+ "modifiedSubIntegrityImpact": {
+ "$ref": "#/definitions/modifiedSubIaType"
+ },
+ "modifiedSubAvailabilityImpact": {
+ "$ref": "#/definitions/modifiedSubIaType"
+ },
+ "Safety": {
+ "$ref": "#/definitions/safetyType"
+ },
+ "Automatable": {
+ "$ref": "#/definitions/automatableType"
+ },
+ "Recovery": {
+ "$ref": "#/definitions/recoveryType"
+ },
+ "valueDensity": {
+ "$ref": "#/definitions/valueDensityType"
+ },
+ "vulnerabilityResponseEffort": {
+ "$ref": "#/definitions/vulnerabilityResponseEffortType"
+ },
+ "providerUrgency": {
+ "$ref": "#/definitions/providerUrgencyType"
+ }
+ },
+ "allOf": [
+ {
+ "anyOf": [
+ {
+ "properties": {
+ "baseScore": {
+ "$ref": "#/definitions/noneScoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/noneSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "baseScore": {
+ "$ref": "#/definitions/lowScoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/lowSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "baseScore": {
+ "$ref": "#/definitions/mediumScoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/mediumSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "baseScore": {
+ "$ref": "#/definitions/highScoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/highSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "baseScore": {
+ "$ref": "#/definitions/criticalScoreType"
+ },
+ "baseSeverity": {
+ "$ref": "#/definitions/criticalSeverityType"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "anyOf": [
+ {
+ "properties": {
+ "threatScore": {
+ "$ref": "#/definitions/noneScoreType"
+ },
+ "threatSeverity": {
+ "$ref": "#/definitions/noneSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "threatScore": {
+ "$ref": "#/definitions/lowScoreType"
+ },
+ "threatSeverity": {
+ "$ref": "#/definitions/lowSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "threatScore": {
+ "$ref": "#/definitions/mediumScoreType"
+ },
+ "threatSeverity": {
+ "$ref": "#/definitions/mediumSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "threatScore": {
+ "$ref": "#/definitions/highScoreType"
+ },
+ "threatSeverity": {
+ "$ref": "#/definitions/highSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "threatScore": {
+ "$ref": "#/definitions/criticalScoreType"
+ },
+ "threatSeverity": {
+ "$ref": "#/definitions/criticalSeverityType"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "anyOf": [
+ {
+ "properties": {
+ "environmentalScore": {
+ "$ref": "#/definitions/noneScoreType"
+ },
+ "environmentalSeverity": {
+ "$ref": "#/definitions/noneSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "environmentalScore": {
+ "$ref": "#/definitions/lowScoreType"
+ },
+ "environmentalSeverity": {
+ "$ref": "#/definitions/lowSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "environmentalScore": {
+ "$ref": "#/definitions/mediumScoreType"
+ },
+ "environmentalSeverity": {
+ "$ref": "#/definitions/mediumSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "environmentalScore": {
+ "$ref": "#/definitions/highScoreType"
+ },
+ "environmentalSeverity": {
+ "$ref": "#/definitions/highSeverityType"
+ }
+ }
+ },
+ {
+ "properties": {
+ "environmentalScore": {
+ "$ref": "#/definitions/criticalScoreType"
+ },
+ "environmentalSeverity": {
+ "$ref": "#/definitions/criticalSeverityType"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "required": [
+ "version",
+ "vectorString",
+ "baseScore",
+ "baseSeverity"
+ ],
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index c1dc599..d78376e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "appthreat-vulnerability-db"
-version = "6.1.2"
+version = "6.2.0"
description = "AppThreat's vulnerability database and package search library with a built-in sqlite based storage. OSV, CVE, GitHub, npm are the primary sources of vulnerabilities."
authors = [
{name = "Team AppThreat", email = "cloud@appthreat.com"},
diff --git a/test/data/osv-maven-cvss4.json b/test/data/osv-maven-cvss4.json
new file mode 100644
index 0000000..4d3309b
--- /dev/null
+++ b/test/data/osv-maven-cvss4.json
@@ -0,0 +1,156 @@
+{
+ "schema_version": "1.4.0",
+ "id": "GHSA-gr3c-q7xf-47vh",
+ "modified": "2024-11-08T18:49:15Z",
+ "published": "2024-11-08T18:49:15Z",
+ "aliases": [
+ "CVE-2024-52007"
+ ],
+ "summary": "XXE vulnerability in XSLT parsing in `org.hl7.fhir.core`",
+ "details": "### Summary\nXSLT parsing performed by various components are vulnerable to XML external entity injections. A processed XML file with a malicious DTD tag ( ]> could produce XML containing data from the host system. This impacts use cases where org.hl7.fhir.core is being used to within a host where external clients can submit XML.\n\n### Details\nThis is related to https://github.com/hapifhir/org.hl7.fhir.core/security/advisories/GHSA-6cr6-ph3p-f5rf, in which its fix ( https://github.com/hapifhir/org.hl7.fhir.core/issues/1571, https://github.com/hapifhir/org.hl7.fhir.core/pull/1717) was incomplete. \n\n### References\nhttps://cwe.mitre.org/data/definitions/611.html\nhttps://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#jaxp-documentbuilderfactory-saxparserfactory-and-dom4j",
+ "severity": [
+ {
+ "type": "CVSS_V3",
+ "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"
+ },
+ {
+ "type": "CVSS_V4",
+ "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:N/SA:N"
+ }
+ ],
+ "affected": [
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.dstu3"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.r4"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.r4b"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.r5"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.utilities"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "package": {
+ "ecosystem": "Maven",
+ "name": "ca.uhn.hapi.fhir:org.hl7.fhir.dstu2016may"
+ },
+ "ranges": [
+ {
+ "type": "ECOSYSTEM",
+ "events": [
+ {
+ "introduced": "0"
+ },
+ {
+ "fixed": "6.4.0"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "references": [
+ {
+ "type": "WEB",
+ "url": "https://github.com/hapifhir/org.hl7.fhir.core/security/advisories/GHSA-gr3c-q7xf-47vh"
+ },
+ {
+ "type": "PACKAGE",
+ "url": "https://github.com/hapifhir/org.hl7.fhir.core"
+ }
+ ],
+ "database_specific": {
+ "cwe_ids": [
+ "CWE-611"
+ ],
+ "severity": "HIGH",
+ "github_reviewed": true,
+ "github_reviewed_at": "2024-11-08T18:49:15Z",
+ "nvd_published_at": null
+ }
+}
\ No newline at end of file
diff --git a/test/test_source.py b/test/test_source.py
index 2094cdd..82ec1c2 100644
--- a/test/test_source.py
+++ b/test/test_source.py
@@ -253,6 +253,15 @@ def test_osv_npm_star_json():
return json.loads(fp.read())
+@pytest.fixture
+def test_osv_maven_cvss4_json():
+ test_cve_data = os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), "data", "osv-maven-cvss4.json"
+ )
+ with open(test_cve_data, mode="r", encoding="utf-8") as fp:
+ return json.loads(fp.read())
+
+
@pytest.fixture
def test_osv_mal_json():
test_cve_data = os.path.join(
@@ -1065,6 +1074,14 @@ def test_osv_convert3(test_osv_mvn_single_json):
assert len(cve_data) == 2
+def test_osv_convert4(test_osv_maven_cvss4_json):
+ osvlatest = OSVSource()
+
+ # maven cvss4 version
+ cve_data = osvlatest.convert(test_osv_maven_cvss4_json)
+ assert len(cve_data) == 6
+
+
def test_osv_mal_convert(test_osv_mal_json, test_osv_mal2_json):
osvlatest = OSVSource()
cve_data = osvlatest.convert(test_osv_mal2_json)
diff --git a/vdb/lib/__init__.py b/vdb/lib/__init__.py
index 3aad79d..026101c 100644
--- a/vdb/lib/__init__.py
+++ b/vdb/lib/__init__.py
@@ -496,6 +496,7 @@ class Vulnerability:
"""Vulnerability"""
cvss_v3: CvssV3
+ cvss4_vector_string: str | None
def __init__(
self,
@@ -524,6 +525,7 @@ def __init__(
self.source_update_time: datetime = convert_time(source_update_time)
self.source_orig_time: datetime = convert_time(source_orig_time)
self.affects = affects
+ self.cvss4_vector_string = None
def __repr__(self):
return orjson.dumps(
@@ -541,7 +543,8 @@ def __repr__(self):
"%Y-%m-%dT%H:%M:%S"
),
"source_orig_time": self.source_orig_time.strftime("%Y-%m-%dT%H:%M:%S"),
- "affects": self.affects
+ "affects": self.affects,
+ "cvss4_vector_string": self.cvss4_vector_string
},
option=orjson.OPT_NAIVE_UTC,
).decode("utf-8", "ignore")
diff --git a/vdb/lib/config.py b/vdb/lib/config.py
index 269459c..5b56fbf 100644
--- a/vdb/lib/config.py
+++ b/vdb/lib/config.py
@@ -112,6 +112,7 @@
THREAT_TO_SEVERITY = {
"unspecified": "LOW",
"": "LOW",
+ "none": "LOW",
"negligible": "LOW",
"low": "LOW",
"unimportant": "LOW",
diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py
index 99caf3f..dd5bd4e 100644
--- a/vdb/lib/cve.py
+++ b/vdb/lib/cve.py
@@ -34,6 +34,7 @@
Metrics,
Metrics1,
Metrics2,
+ Metrics3,
Module,
OrgId,
ProblemType,
@@ -50,9 +51,10 @@
Version,
Versions,
)
-from vdb.lib.cve_model.common import CiaType
+from vdb.lib.cve_model.common import CiaType, SubCiaType
from vdb.lib.cve_model.cvss_v3 import Field0, Field1
-from vdb.lib.utils import calculate_hash, to_purl_vers
+from vdb.lib.cve_model.cvss_v4 import Field0Model6
+from vdb.lib.utils import calculate_hash, get_cvss4_from_vector, to_purl_vers
# Our DB creation process could result in duplicates. By tracking these keys we reduce them
source_completed_keys = {}
@@ -296,13 +298,78 @@ def severity_to_impact(severity: str) -> CiaType:
return CiaType.LOW
+def severity_to_impact4(severity: str) -> str:
+ """Method to convert severity string to a valid impact enum string"""
+ if not severity:
+ return "NONE"
+ if severity.lower() in ("critical", "high", "medium", "moderate"):
+ return "HIGH"
+ return "LOW"
+
+
+def fix_maturity(maturity: str) -> str:
+ if not maturity:
+ return "NOT_DEFINED"
+ if maturity == "POC":
+ return "PROOF_OF_CONCEPT"
+ return maturity.upper()
+
+
+def fix_attack_vector(attack_vector: str) -> str:
+ if attack_vector == "ADJACENT_NETWORK":
+ return "NETWORK"
+ return attack_vector.upper()
+
+
def to_metrics(avuln: Vulnerability) -> Metrics:
metrics_list = []
+ if avuln.cvss4_vector_string:
+ cvss4_obj = get_cvss4_from_vector(avuln.cvss4_vector_string)
+ metrics_list.append(Metrics1(
+ cvssV4_0=Field0Model6(
+ version="4.0",
+ vectorString=avuln.cvss4_vector_string,
+ baseScore=cvss4_obj.get("baseScore"),
+ baseSeverity=config.THREAT_TO_SEVERITY.get(cvss4_obj.get("baseSeverity").lower()) if cvss4_obj.get("baseSeverity") else "LOW",
+ attackVector=fix_attack_vector(cvss4_obj.get("attackVector")),
+ attackComplexity=to_complexity_type(cvss4_obj.get("attackComplexity")),
+ attackRequirements=cvss4_obj.get("attackRequirements"),
+ privilegesRequired=cvss4_obj.get("privilegesRequired"),
+ userInteraction=cvss4_obj.get("userInteraction"),
+ vulnConfidentialityImpact=severity_to_impact4(cvss4_obj.get("vulnConfidentialityImpact")),
+ vulnIntegrityImpact=severity_to_impact4(cvss4_obj.get("vulnIntegrityImpact")),
+ vulnAvailabilityImpact=severity_to_impact4(cvss4_obj.get("vulnAvailabilityImpact")),
+ subConfidentialityImpact=severity_to_impact4(cvss4_obj.get("subConfidentialityImpact")),
+ subIntegrityImpact=severity_to_impact4(cvss4_obj.get("subIntegrityImpact")),
+ subAvailabilityImpact=severity_to_impact4(cvss4_obj.get("subAvailabilityImpact")),
+ exploitMaturity=fix_maturity(cvss4_obj.get("exploitMaturity")),
+ confidentialityRequirement=cvss4_obj.get("confidentialityRequirement"),
+ integrityRequirement=cvss4_obj.get("integrityRequirement"),
+ availabilityRequirement=cvss4_obj.get("availabilityRequirement"),
+ modifiedAttackVector=fix_attack_vector(cvss4_obj.get("modifiedAttackVector")),
+ modifiedAttackComplexity=cvss4_obj.get("modifiedAttackComplexity"),
+ modifiedAttackRequirements=cvss4_obj.get("modifiedAttackRequirements"),
+ modifiedPrivilegesRequired=cvss4_obj.get("modifiedPrivilegesRequired"),
+ modifiedUserInteraction=cvss4_obj.get("modifiedUserInteraction"),
+ modifiedVulnConfidentialityImpact=severity_to_impact4(cvss4_obj.get("modifiedVulnConfidentialityImpact")),
+ modifiedVulnIntegrityImpact=severity_to_impact4(cvss4_obj.get("modifiedVulnIntegrityImpact")),
+ modifiedVulnAvailabilityImpact=severity_to_impact4(cvss4_obj.get("modifiedVulnAvailabilityImpact")),
+ modifiedSubConfidentialityImpact=severity_to_impact4(cvss4_obj.get("modifiedSubConfidentialityImpact")),
+ modifiedSubIntegrityImpact=severity_to_impact4(cvss4_obj.get("modifiedSubIntegrityImpact")),
+ modifiedSubAvailabilityImpact=severity_to_impact4(cvss4_obj.get("modifiedSubAvailabilityImpact")),
+ Safety=cvss4_obj.get("safety"),
+ Automatable=cvss4_obj.get("automatable"),
+ Recovery=cvss4_obj.get("recovery"),
+ valueDensity=cvss4_obj.get("valueDensity"),
+ vulnerabilityResponseEffort=cvss4_obj.get("vulnerabilityResponseEffort"),
+ providerUrgency=cvss4_obj.get("providerUrgency"),
+ )
+ ))
if avuln.cvss_v3.vector_string and avuln.cvss_v3.vector_string.startswith(
"CVSS:3.1"
):
metrics_list.append(
- Metrics1(
+ Metrics2(
cvssV3_1=Field1(
version="3.1",
baseScore=avuln.cvss_v3.base_score,
@@ -325,11 +392,11 @@ def to_metrics(avuln: Vulnerability) -> Metrics:
)
)
)
- elif avuln.cvss_v3.vector_string and avuln.cvss_v3.vector_string.startswith(
+ if avuln.cvss_v3.vector_string and avuln.cvss_v3.vector_string.startswith(
"CVSS:3.0"
):
metrics_list.append(
- Metrics2(
+ Metrics3(
cvssV3_0=Field0(
version="3.0",
baseScore=avuln.cvss_v3.base_score,
@@ -426,7 +493,7 @@ def to_cve_containers(avuln: Vulnerability) -> CnaPublishedContainer | None:
class CVESource(VulnerabilitySource):
"""
- Generic CVE source that uses the CVE 5.0 models
+ Generic CVE source that uses the CVE 5.1 models
"""
db_conn = None
@@ -463,7 +530,7 @@ def convert5(self, data: list[Vulnerability]) -> list[CVE]:
if containers:
cve_obj = CVE1(
dataType=DataType.CVE_RECORD,
- dataVersion=DataVersion.field_5_0,
+ dataVersion=DataVersion.field_5_1,
cveMetadata=to_cve_metadata(avuln),
containers=containers,
)
diff --git a/vdb/lib/cve_model/__init__.py b/vdb/lib/cve_model/__init__.py
index 66024aa..db66a86 100644
--- a/vdb/lib/cve_model/__init__.py
+++ b/vdb/lib/cve_model/__init__.py
@@ -3,11 +3,11 @@
from __future__ import annotations
from enum import Enum
-from typing import Annotated, Any, Dict, List, Optional, Type, Union
+from typing import Annotated, Any, Dict, List, Optional, Union
from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, NaiveDatetime, RootModel
-from vdb.lib.cve_model import cvss_v2, cvss_v3
+from vdb.lib.cve_model import cvss_v2, cvss_v3, cvss_v4
class UriType(RootModel[AnyUrl]):
@@ -51,6 +51,19 @@ class ReferenceTags(Enum):
vdb_entry = "vdb-entry"
+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"
+
+
# Removed patten validation to support a wide range of CVE id
class CveId(RootModel[str]):
root: Annotated[str, Field(min_length=4, max_length=128)]
@@ -195,35 +208,31 @@ class Versions(BaseModel):
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,
),
- ]
+ ] = None
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.",
+ 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."
),
- ]
+ ] = None
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.",
+ 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."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
class Versions1(BaseModel):
@@ -249,26 +258,24 @@ class Versions1(BaseModel):
),
]
lessThan: Annotated[
- Version,
+ Optional[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."
),
- ]
+ ] = None
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.",
+ 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."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
class Versions2(BaseModel):
@@ -294,12 +301,54 @@ class Versions2(BaseModel):
),
]
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 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.",
+ 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."
+ ),
+ ] = None
+ changes: Annotated[
+ Optional[List[Change]],
+ Field(
+ 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,
+ ),
+ ] = None
+
+
+class Versions3(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(
+ 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."
+ ),
+ ] = None
lessThanOrEqual: Annotated[
Version,
Field(
@@ -309,36 +358,30 @@ class Versions2(BaseModel):
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,
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
product: Annotated[
Optional[str],
Field(
- None,
- description="Name of the affected product.",
- max_length=2048,
- min_length=1,
+ description="Name of the affected product.", max_length=2048, min_length=1
),
- ]
+ ] = None
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",
@@ -406,72 +449,64 @@ class Product(BaseModel):
"https://wordpress.org/plugins",
],
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
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.',
+ 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.'
),
- ]
+ ] = None
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).",
+ description="A list of the affected components, features, modules, sub-components, sub-products, APIs, commands, utilities, programs, or functionalities (optional)."
),
- ]
+ ] = None
programFiles: Annotated[
Optional[List[ProgramFile]],
- Field(None, description="A list of the affected source code files (optional)."),
- ]
+ Field(description="A list of the affected source code files (optional)."),
+ ] = None
programRoutines: Annotated[
Optional[List[ProgramRoutine]],
Field(
- None,
- description="A list of the affected source code functions, methods, subroutines, or procedures (optional).",
+ description="A list of the affected source code functions, methods, subroutines, or procedures (optional)."
),
- ]
+ ] = None
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",
),
- ]
+ ] = None
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.",
+ description="The URL of the source code repository, for informational purposes and/or to resolve git hash version ranges."
),
- ]
+ ] = None
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.",
+ 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."
),
- ]
+ ] = None
versions: Annotated[
- Optional[List[Union[Versions, Union[Versions1, Versions2]]]],
+ Optional[List[Union[Versions, Versions1, Versions2, Versions3]]],
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,
),
- ]
+ ] = None
class DataType(Enum):
@@ -480,6 +515,7 @@ class DataType(Enum):
class DataVersion(Enum):
field_5_0 = "5.0"
+ field_5_1 = "5.1"
class State(Enum):
@@ -502,40 +538,36 @@ class CveMetadataPublished(BaseModel):
assignerShortName: Annotated[
Optional[ShortName],
Field(
- None,
- description="The short name for the organization to which the CVE ID was originally assigned.",
+ description="The short name for the organization to which the CVE ID was originally assigned."
),
- ]
+ ] = None
requesterUserId: Annotated[
Optional[UserId],
- Field(None, description="The user that requested the CVE identifier."),
- ]
+ Field(description="The user that requested the CVE identifier."),
+ ] = None
dateUpdated: Annotated[
Optional[AwareDatetime | NaiveDatetime],
- Field(None, description="The date/time the record was last updated."),
- ]
+ Field(description="The date/time the record was last updated."),
+ ] = None
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,
),
- ]
+ ] = None
dateReserved: Annotated[
Optional[AwareDatetime | NaiveDatetime],
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.",
+ 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."
),
- ]
+ ] = None
datePublished: Annotated[
Optional[AwareDatetime | NaiveDatetime],
Field(
- None,
- description="The date/time the CVE Record was first published in the CVE List.",
+ description="The date/time the CVE Record was first published in the CVE List."
),
- ]
+ ] = None
state: Annotated[State, Field(description="State of CVE - PUBLISHED, REJECTED.")]
@@ -559,41 +591,36 @@ class CveMetadataRejected(BaseModel):
assignerShortName: Annotated[
Optional[ShortName],
Field(
- None,
- description="The short name for the organization to which the CVE ID was originally assigned.",
+ description="The short name for the organization to which the CVE ID was originally assigned."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
dateUpdated: Annotated[
Optional[AwareDatetime | NaiveDatetime],
- Field(None, description="The date/time the record was last updated."),
- ]
+ Field(description="The date/time the record was last updated."),
+ ] = None
datePublished: Annotated[
Optional[AwareDatetime | NaiveDatetime],
Field(
- None,
- description="The date/time the CVE Record was first published in the CVE List.",
+ description="The date/time the CVE Record was first published in the CVE List."
),
- ]
+ ] = None
dateRejected: Annotated[
- Optional[AwareDatetime | NaiveDatetime],
- Field(None, description="The date/time the CVE ID was rejected."),
- ]
+ Optional[AwareDatetime | NaiveDatetime], Field(description="The date/time the CVE ID was rejected.")
+ ] = None
state: Annotated[State1, Field(description="State of CVE - PUBLISHED, REJECTED.")]
dateReserved: Annotated[
Optional[AwareDatetime | NaiveDatetime],
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.",
+ 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."
),
- ]
+ ] = None
class ProviderMetadata(BaseModel):
@@ -602,15 +629,14 @@ class ProviderMetadata(BaseModel):
]
shortName: Annotated[
Optional[ShortName],
- Field(None, description="The container provider's organizational short name."),
- ]
+ Field(description="The container provider's organizational short name."),
+ ] = None
dateUpdated: Annotated[
Optional[AwareDatetime | NaiveDatetime],
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.",
+ 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."
),
- ]
+ ] = None
class Affected(RootModel[List[Product]]):
@@ -639,11 +665,10 @@ class SupportingMediaItem(BaseModel):
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",
),
- ]
+ ] = False
# Removed max_length
value: Annotated[
str,
@@ -723,19 +748,26 @@ class TaxonomyRelation(BaseModel):
class TaxonomyMapping(BaseModel):
taxonomyName: Annotated[
str,
- Field(description="The name of the taxonomy.", max_length=128, min_length=1),
+ Field(
+ description="The name of the taxonomy, eg., ATT&CK, D3FEND, CWE, CVSS",
+ 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,
),
- ]
+ ] = None
taxonomyRelations: Annotated[
- List[TaxonomyRelation], Field(description="", min_length=1)
+ List[TaxonomyRelation],
+ Field(
+ description="List of relationships to the taxonomy for the vulnerability.",
+ min_length=1,
+ ),
]
@@ -773,20 +805,18 @@ class Reference(BaseModel):
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,
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
class Description(BaseModel):
@@ -795,17 +825,16 @@ class Description(BaseModel):
)
lang: Language
value: Annotated[
- str, Field(description="Plain text description.", min_length=1, max_length=4096)
+ 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",
),
- ]
+ ] = None
class EnglishLanguageDescription(BaseModel):
@@ -837,13 +866,12 @@ 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}$",
),
- ]
+ ] = None
descriptions: Annotated[
Descriptions,
Field(
@@ -913,7 +941,7 @@ class TimelineItem(BaseModel):
time: Annotated[
AwareDatetime,
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."
+ 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:ss[+-]ZH:ZM - if the timezone offset is not given, GMT (+00:00) is assumed."
),
]
lang: Annotated[
@@ -948,17 +976,15 @@ class Credit(BaseModel):
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.",
+ 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."
),
- ]
+ ] = None
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.",
+ 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."
),
- ]
+ ] = "finder"
class Credits(RootModel[List[Credit]]):
@@ -1002,11 +1028,10 @@ class CnaRejectedContainer(BaseModel):
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,
),
- ]
+ ] = None
class Description1(BaseModel):
@@ -1021,22 +1046,20 @@ class Description1(BaseModel):
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]*$",
),
- ]
+ ] = None
type: Annotated[
Optional[str],
Field(
- None,
description="Problemtype source, text, OWASP, CWE, etc.,",
max_length=128,
min_length=1,
),
- ]
+ ] = None
references: Optional[References] = None
@@ -1058,110 +1081,131 @@ 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,
),
- ]
+ ] = None
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
+ ] = None
+ cvssV4_0: cvss_v4.Field0Model6
+ cvssV3_1: Optional[cvss_v3.Field1] = None
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.",
+ description="A non-standard impact description, may be prose or JSON block."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
+ cvssV4_0: Optional[cvss_v4.Field0Model6] = None
+ cvssV3_1: cvss_v3.Field1
+ cvssV3_0: Optional[cvss_v3.Field0] = None
+ cvssV2_0: Optional[cvss_v2.Field0] = None
+ other: Annotated[
+ Optional[Other],
+ Field(
+ description="A non-standard impact description, may be prose or JSON block."
+ ),
+ ] = None
+
+
+class Metrics3(BaseModel):
+ format: Annotated[
+ Optional[str],
+ Field(
+ 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,
+ ),
+ ] = None
+ scenarios: Annotated[
+ Optional[List[Scenario]],
+ Field(
+ 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,
+ ),
+ ] = None
+ cvssV4_0: Optional[cvss_v4.Field0Model6] = None
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.",
+ description="A non-standard impact description, may be prose or JSON block."
),
- ]
+ ] = None
-class Metrics3(BaseModel):
+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,
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
+ cvssV4_0: Optional[cvss_v4.Field0Model6] = None
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.",
+ description="A non-standard impact description, may be prose or JSON block."
),
- ]
+ ] = None
-class Metrics4(BaseModel):
+class Metrics5(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,
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
+ cvssV4_0: Optional[cvss_v4.Field0Model6] = None
cvssV3_1: Optional[cvss_v3.Field1] = None
cvssV3_0: Optional[cvss_v3.Field0] = None
cvssV2_0: Optional[cvss_v2.Field0] = None
@@ -1173,9 +1217,9 @@ class Metrics4(BaseModel):
]
-class Metrics(RootModel[List[Union[Metrics1, Metrics2, Metrics3, Metrics4]]]):
+class Metrics(RootModel[List[Union[Metrics1, Metrics2, Metrics3, Metrics4, Metrics5]]]):
root: Annotated[
- List[Union[Metrics1, Metrics2, Metrics3, Metrics4]],
+ List[Union[Metrics1, Metrics2, Metrics3, Metrics4, Metrics5]],
Field(
description="Collection of impact scores with attribution.", min_length=1
),
@@ -1212,26 +1256,23 @@ class CnaPublishedContainer(BaseModel):
dateAssigned: Annotated[
Optional[AwareDatetime | NaiveDatetime],
Field(
- None,
- description="The date/time this CVE ID was associated with a vulnerability by a CNA.",
+ description="The date/time this CVE ID was associated with a vulnerability by a CNA."
),
- ]
+ ] = None
datePublic: Annotated[
Optional[AwareDatetime | NaiveDatetime],
Field(
- None,
- description="If known, the date/time the vulnerability was disclosed publicly.",
+ description="If known, the date/time the vulnerability was disclosed publicly."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
descriptions: Descriptions
affected: Affected
problemTypes: Optional[ProblemTypes] = None
@@ -1260,19 +1301,17 @@ class AdpContainer(BaseModel):
datePublic: Annotated[
Optional[AwareDatetime | NaiveDatetime],
Field(
- None,
- description="If known, the date/time the vulnerability was disclosed publicly.",
+ description="If known, the date/time the vulnerability was disclosed publicly."
),
- ]
+ ] = None
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,
),
- ]
+ ] = None
descriptions: Optional[Descriptions] = None
affected: Optional[Affected] = None
problemTypes: Optional[ProblemTypes] = None
@@ -1295,7 +1334,7 @@ class Containers(BaseModel):
extra="forbid",
)
cna: Optional[CnaPublishedContainer]
- adp: Annotated[Optional[List[AdpContainer]], Field(None, min_length=1)]
+ adp: Annotated[Optional[List[AdpContainer]], Field(min_length=1)] = None
class CVE1(BaseModel):
diff --git a/vdb/lib/cve_model/common.py b/vdb/lib/cve_model/common.py
index 2e0b64d..6b52c7b 100644
--- a/vdb/lib/cve_model/common.py
+++ b/vdb/lib/cve_model/common.py
@@ -1,21 +1,21 @@
# pylint: disable=C0115, C0103, C0301
from enum import Enum
-from typing import Annotated
+from typing import Annotated, Literal
from pydantic import Field, RootModel
class AttackVectorType(Enum):
NETWORK = "NETWORK"
- ADJACENT_NETWORK = "ADJACENT_NETWORK"
+ ADJACENT = "ADJACENT"
LOCAL = "LOCAL"
PHYSICAL = "PHYSICAL"
class ModifiedAttackVectorType(Enum):
NETWORK = "NETWORK"
- ADJACENT_NETWORK = "ADJACENT_NETWORK"
+ ADJACENT = "ADJACENT"
LOCAL = "LOCAL"
PHYSICAL = "PHYSICAL"
NOT_DEFINED = "NOT_DEFINED"
@@ -32,6 +32,17 @@ class ModifiedAttackComplexityType(Enum):
NOT_DEFINED = "NOT_DEFINED"
+class AttackRequirementsType(Enum):
+ NONE = "NONE"
+ PRESENT = "PRESENT"
+
+
+class ModifiedAttackRequirementsType(Enum):
+ NONE = "NONE"
+ PRESENT = "PRESENT"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
class PrivilegesRequiredType(Enum):
HIGH = "HIGH"
LOW = "LOW"
@@ -47,10 +58,374 @@ class ModifiedPrivilegesRequiredType(Enum):
class UserInteractionType(Enum):
NONE = "NONE"
- REQUIRED = "REQUIRED"
+ PASSIVE = "PASSIVE"
+ ACTIVE = "ACTIVE"
class ModifiedUserInteractionType(Enum):
+ NONE = "NONE"
+ PASSIVE = "PASSIVE"
+ ACTIVE = "ACTIVE"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class VulnCiaType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ HIGH = "HIGH"
+
+
+class ModifiedVulnCiaType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ HIGH = "HIGH"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class SubCiaType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ HIGH = "HIGH"
+
+
+class ModifiedSubCType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ HIGH = "HIGH"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class ModifiedSubIaType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ HIGH = "HIGH"
+ SAFETY = "SAFETY"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class ExploitMaturityType(Enum):
+ UNREPORTED = "UNREPORTED"
+ PROOF_OF_CONCEPT = "PROOF_OF_CONCEPT"
+ ATTACKED = "ATTACKED"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class CiaRequirementType(Enum):
+ LOW = "LOW"
+ MEDIUM = "MEDIUM"
+ HIGH = "HIGH"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class SafetyType(Enum):
+ NEGLIGIBLE = "NEGLIGIBLE"
+ PRESENT = "PRESENT"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class AutomatableType(Enum):
+ NO = "NO"
+ YES = "YES"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class RecoveryType(Enum):
+ AUTOMATIC = "AUTOMATIC"
+ USER = "USER"
+ IRRECOVERABLE = "IRRECOVERABLE"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class ValueDensityType(Enum):
+ DIFFUSE = "DIFFUSE"
+ CONCENTRATED = "CONCENTRATED"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class VulnerabilityResponseEffortType(Enum):
+ LOW = "LOW"
+ MODERATE = "MODERATE"
+ HIGH = "HIGH"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class ProviderUrgencyType(Enum):
+ CLEAR = "CLEAR"
+ GREEN = "GREEN"
+ AMBER = "AMBER"
+ RED = "RED"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class ScoreType(Enum):
+ number_0_0 = 0.0
+ number_0_1 = 0.1
+ number_0_2 = 0.2
+ number_0_3 = 0.3
+ number_0_4 = 0.4
+ number_0_5 = 0.5
+ number_0_6 = 0.6
+ number_0_7 = 0.7
+ number_0_8 = 0.8
+ number_0_9 = 0.9
+ number_1_0 = 1.0
+ number_1_1 = 1.1
+ number_1_2 = 1.2
+ number_1_3 = 1.3
+ number_1_4 = 1.4
+ number_1_5 = 1.5
+ number_1_6 = 1.6
+ number_1_7 = 1.7
+ number_1_8 = 1.8
+ number_1_9 = 1.9
+ number_2_0 = 2.0
+ number_2_1 = 2.1
+ number_2_2 = 2.2
+ number_2_3 = 2.3
+ number_2_4 = 2.4
+ number_2_5 = 2.5
+ number_2_6 = 2.6
+ number_2_7 = 2.7
+ number_2_8 = 2.8
+ number_2_9 = 2.9
+ number_3_0 = 3.0
+ number_3_1 = 3.1
+ number_3_2 = 3.2
+ number_3_3 = 3.3
+ number_3_4 = 3.4
+ number_3_5 = 3.5
+ number_3_6 = 3.6
+ number_3_7 = 3.7
+ number_3_8 = 3.8
+ number_3_9 = 3.9
+ number_4_0 = 4.0
+ number_4_1 = 4.1
+ number_4_2 = 4.2
+ number_4_3 = 4.3
+ number_4_4 = 4.4
+ number_4_5 = 4.5
+ number_4_6 = 4.6
+ number_4_7 = 4.7
+ number_4_8 = 4.8
+ number_4_9 = 4.9
+ number_5_0 = 5.0
+ number_5_1 = 5.1
+ number_5_2 = 5.2
+ number_5_3 = 5.3
+ number_5_4 = 5.4
+ number_5_5 = 5.5
+ number_5_6 = 5.6
+ number_5_7 = 5.7
+ number_5_8 = 5.8
+ number_5_9 = 5.9
+ number_6_0 = 6.0
+ number_6_1 = 6.1
+ number_6_2 = 6.2
+ number_6_3 = 6.3
+ number_6_4 = 6.4
+ number_6_5 = 6.5
+ number_6_6 = 6.6
+ number_6_7 = 6.7
+ number_6_8 = 6.8
+ number_6_9 = 6.9
+ number_7_0 = 7.0
+ number_7_1 = 7.1
+ number_7_2 = 7.2
+ number_7_3 = 7.3
+ number_7_4 = 7.4
+ number_7_5 = 7.5
+ number_7_6 = 7.6
+ number_7_7 = 7.7
+ number_7_8 = 7.8
+ number_7_9 = 7.9
+ number_8_0 = 8.0
+ number_8_1 = 8.1
+ number_8_2 = 8.2
+ number_8_3 = 8.3
+ number_8_4 = 8.4
+ number_8_5 = 8.5
+ number_8_6 = 8.6
+ number_8_7 = 8.7
+ number_8_8 = 8.8
+ number_8_9 = 8.9
+ number_9_0 = 9.0
+ number_9_1 = 9.1
+ number_9_2 = 9.2
+ number_9_3 = 9.3
+ number_9_4 = 9.4
+ number_9_5 = 9.5
+ number_9_6 = 9.6
+ number_9_7 = 9.7
+ number_9_8 = 9.8
+ number_9_9 = 9.9
+ number_10_0 = 10.0
+
+
+class NoneScoreType(RootModel[float]):
+ root: Annotated[float, Field(ge=0.0, le=0.0)]
+
+
+class LowScoreType(Enum):
+ number_0_1 = 0.1
+ number_0_2 = 0.2
+ number_0_3 = 0.3
+ number_0_4 = 0.4
+ number_0_5 = 0.5
+ number_0_6 = 0.6
+ number_0_7 = 0.7
+ number_0_8 = 0.8
+ number_0_9 = 0.9
+ number_1_0 = 1.0
+ number_1_1 = 1.1
+ number_1_2 = 1.2
+ number_1_3 = 1.3
+ number_1_4 = 1.4
+ number_1_5 = 1.5
+ number_1_6 = 1.6
+ number_1_7 = 1.7
+ number_1_8 = 1.8
+ number_1_9 = 1.9
+ number_2_0 = 2.0
+ number_2_1 = 2.1
+ number_2_2 = 2.2
+ number_2_3 = 2.3
+ number_2_4 = 2.4
+ number_2_5 = 2.5
+ number_2_6 = 2.6
+ number_2_7 = 2.7
+ number_2_8 = 2.8
+ number_2_9 = 2.9
+ number_3_0 = 3.0
+ number_3_1 = 3.1
+ number_3_2 = 3.2
+ number_3_3 = 3.3
+ number_3_4 = 3.4
+ number_3_5 = 3.5
+ number_3_6 = 3.6
+ number_3_7 = 3.7
+ number_3_8 = 3.8
+ number_3_9 = 3.9
+
+
+class MediumScoreType(Enum):
+ number_4_0 = 4.0
+ number_4_1 = 4.1
+ number_4_2 = 4.2
+ number_4_3 = 4.3
+ number_4_4 = 4.4
+ number_4_5 = 4.5
+ number_4_6 = 4.6
+ number_4_7 = 4.7
+ number_4_8 = 4.8
+ number_4_9 = 4.9
+ number_5_0 = 5.0
+ number_5_1 = 5.1
+ number_5_2 = 5.2
+ number_5_3 = 5.3
+ number_5_4 = 5.4
+ number_5_5 = 5.5
+ number_5_6 = 5.6
+ number_5_7 = 5.7
+ number_5_8 = 5.8
+ number_5_9 = 5.9
+ number_6_0 = 6.0
+ number_6_1 = 6.1
+ number_6_2 = 6.2
+ number_6_3 = 6.3
+ number_6_4 = 6.4
+ number_6_5 = 6.5
+ number_6_6 = 6.6
+ number_6_7 = 6.7
+ number_6_8 = 6.8
+ number_6_9 = 6.9
+
+
+class HighScoreType(Enum):
+ number_7_0 = 7.0
+ number_7_1 = 7.1
+ number_7_2 = 7.2
+ number_7_3 = 7.3
+ number_7_4 = 7.4
+ number_7_5 = 7.5
+ number_7_6 = 7.6
+ number_7_7 = 7.7
+ number_7_8 = 7.8
+ number_7_9 = 7.9
+ number_8_0 = 8.0
+ number_8_1 = 8.1
+ number_8_2 = 8.2
+ number_8_3 = 8.3
+ number_8_4 = 8.4
+ number_8_5 = 8.5
+ number_8_6 = 8.6
+ number_8_7 = 8.7
+ number_8_8 = 8.8
+ number_8_9 = 8.9
+
+
+class CriticalScoreType(Enum):
+ number_9_0 = 9.0
+ number_9_1 = 9.1
+ number_9_2 = 9.2
+ number_9_3 = 9.3
+ number_9_4 = 9.4
+ number_9_5 = 9.5
+ number_9_6 = 9.6
+ number_9_7 = 9.7
+ number_9_8 = 9.8
+ number_9_9 = 9.9
+ number_10_0 = 10.0
+
+
+class SeverityType(Enum):
+ NONE = "NONE"
+ LOW = "LOW"
+ MEDIUM = "MEDIUM"
+ HIGH = "HIGH"
+ CRITICAL = "CRITICAL"
+
+
+class NoneSeverityType(RootModel[Literal["NONE"]]):
+ root: Literal["NONE"]
+
+
+class LowSeverityType(RootModel[Literal["LOW"]]):
+ root: Literal["LOW"]
+
+
+class MediumSeverityType(RootModel[Literal["MEDIUM"]]):
+ root: Literal["MEDIUM"]
+
+
+class HighSeverityType(RootModel[Literal["HIGH"]]):
+ root: Literal["HIGH"]
+
+
+class CriticalSeverityType(RootModel[Literal["CRITICAL"]]):
+ root: Literal["CRITICAL"]
+
+
+class AttackVectorTypeModel(Enum):
+ NETWORK = "NETWORK"
+ ADJACENT_NETWORK = "ADJACENT_NETWORK"
+ LOCAL = "LOCAL"
+ PHYSICAL = "PHYSICAL"
+
+
+class ModifiedAttackVectorTypeModel(Enum):
+ NETWORK = "NETWORK"
+ ADJACENT_NETWORK = "ADJACENT_NETWORK"
+ LOCAL = "LOCAL"
+ PHYSICAL = "PHYSICAL"
+ NOT_DEFINED = "NOT_DEFINED"
+
+
+class UserInteractionTypeModel(Enum):
+ NONE = "NONE"
+ REQUIRED = "REQUIRED"
+
+
+class ModifiedUserInteractionTypeModel(Enum):
NONE = "NONE"
REQUIRED = "REQUIRED"
NOT_DEFINED = "NOT_DEFINED"
@@ -103,25 +478,10 @@ class ConfidenceType(Enum):
NOT_DEFINED = "NOT_DEFINED"
-class CiaRequirementType(Enum):
- LOW = "LOW"
- MEDIUM = "MEDIUM"
- HIGH = "HIGH"
- NOT_DEFINED = "NOT_DEFINED"
-
-
-class ScoreType(RootModel[float]):
+class ScoreTypeModel(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"
@@ -176,16 +536,3 @@ class TargetDistributionType(Enum):
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
index 458ef86..358da12 100644
--- a/vdb/lib/cve_model/cvss_v2.py
+++ b/vdb/lib/cve_model/cvss_v2.py
@@ -17,7 +17,7 @@
ExploitabilityType,
RemediationLevelType,
ReportConfidenceType,
- ScoreType,
+ ScoreTypeModel,
TargetDistributionType,
)
@@ -40,14 +40,14 @@ class Field0(BaseModel):
confidentialityImpact: Optional[CiaTypeModel] = None
integrityImpact: Optional[CiaTypeModel] = None
availabilityImpact: Optional[CiaTypeModel] = None
- baseScore: ScoreType
+ baseScore: ScoreTypeModel
exploitability: Optional[ExploitabilityType] = None
remediationLevel: Optional[RemediationLevelType] = None
reportConfidence: Optional[ReportConfidenceType] = None
- temporalScore: Optional[ScoreType] = None
+ temporalScore: Optional[ScoreTypeModel] = 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
+ confidentialityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ integrityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ availabilityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ environmentalScore: Optional[ScoreTypeModel] = None
diff --git a/vdb/lib/cve_model/cvss_v3.py b/vdb/lib/cve_model/cvss_v3.py
index 4ce8047..ea6c73b 100644
--- a/vdb/lib/cve_model/cvss_v3.py
+++ b/vdb/lib/cve_model/cvss_v3.py
@@ -9,23 +9,23 @@
from vdb.lib.cve_model.common import (
AttackComplexityType,
- AttackVectorType,
+ AttackVectorTypeModel,
CiaRequirementType,
CiaType,
ConfidenceType,
ExploitCodeMaturityType,
ModifiedAttackComplexityType,
- ModifiedAttackVectorType,
+ ModifiedAttackVectorTypeModel,
ModifiedCiaType,
ModifiedPrivilegesRequiredType,
ModifiedScopeType,
- ModifiedUserInteractionType,
+ ModifiedUserInteractionTypeModel,
PrivilegesRequiredType,
RemediationLevelType,
ScopeType,
- ScoreType,
+ ScoreTypeModel,
SeverityType,
- UserInteractionType,
+ UserInteractionTypeModel,
)
@@ -45,33 +45,33 @@ class Field1(BaseModel):
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
+ attackVector: Optional[AttackVectorTypeModel] = None
attackComplexity: Optional[AttackComplexityType] = None
privilegesRequired: Optional[PrivilegesRequiredType] = None
- userInteraction: Optional[UserInteractionType] = None
+ userInteraction: Optional[UserInteractionTypeModel] = None
scope: Optional[ScopeType] = None
confidentialityImpact: Optional[CiaType] = None
integrityImpact: Optional[CiaType] = None
availabilityImpact: Optional[CiaType] = None
- baseScore: ScoreType
+ baseScore: ScoreTypeModel
baseSeverity: SeverityType
exploitCodeMaturity: Optional[ExploitCodeMaturityType] = None
remediationLevel: Optional[RemediationLevelType] = None
reportConfidence: Optional[ConfidenceType] = None
- temporalScore: Optional[ScoreType] = None
+ temporalScore: Optional[ScoreTypeModel] = 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
+ confidentialityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ integrityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ availabilityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ modifiedAttackVector: Optional[ModifiedAttackVectorTypeModel] = None
+ modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = "NOT_DEFINED"
+ modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = "NOT_DEFINED"
+ modifiedUserInteraction: Optional[ModifiedUserInteractionTypeModel] = None
modifiedScope: Optional[ModifiedScopeType] = None
modifiedConfidentialityImpact: Optional[ModifiedCiaType] = None
modifiedIntegrityImpact: Optional[ModifiedCiaType] = None
modifiedAvailabilityImpact: Optional[ModifiedCiaType] = None
- environmentalScore: Optional[ScoreType] = None
+ environmentalScore: Optional[ScoreTypeModel] = None
environmentalSeverity: Optional[SeverityType] = None
@@ -83,31 +83,31 @@ class Field0(BaseModel):
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
+ attackVector: Optional[AttackVectorTypeModel] = None
attackComplexity: Optional[AttackComplexityType] = None
privilegesRequired: Optional[PrivilegesRequiredType] = None
- userInteraction: Optional[UserInteractionType] = None
+ userInteraction: Optional[UserInteractionTypeModel] = None
scope: Optional[ScopeType] = None
confidentialityImpact: Optional[CiaType] = None
integrityImpact: Optional[CiaType] = None
availabilityImpact: Optional[CiaType] = None
- baseScore: ScoreType
+ baseScore: ScoreTypeModel
baseSeverity: SeverityType
exploitCodeMaturity: Optional[ExploitCodeMaturityType] = None
remediationLevel: Optional[RemediationLevelType] = None
reportConfidence: Optional[ConfidenceType] = None
- temporalScore: Optional[ScoreType] = None
+ temporalScore: Optional[ScoreTypeModel] = 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
+ confidentialityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ integrityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ availabilityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ modifiedAttackVector: Optional[ModifiedAttackVectorTypeModel] = None
+ modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = "NOT_DEFINED"
+ modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = "NOT_DEFINED"
+ modifiedUserInteraction: Optional[ModifiedUserInteractionTypeModel] = None
modifiedScope: Optional[ModifiedScopeType] = None
modifiedConfidentialityImpact: Optional[ModifiedCiaType] = None
modifiedIntegrityImpact: Optional[ModifiedCiaType] = None
modifiedAvailabilityImpact: Optional[ModifiedCiaType] = None
- environmentalScore: Optional[ScoreType] = None
+ environmentalScore: Optional[ScoreTypeModel] = None
environmentalSeverity: Optional[SeverityType] = None
diff --git a/vdb/lib/cve_model/cvss_v4.py b/vdb/lib/cve_model/cvss_v4.py
new file mode 100644
index 0000000..cf6b22f
--- /dev/null
+++ b/vdb/lib/cve_model/cvss_v4.py
@@ -0,0 +1,174 @@
+# pylint: disable=C0115, C0103, C0301
+
+from __future__ import annotations
+
+from enum import Enum
+from typing import Annotated, Optional
+
+from pydantic import BaseModel, Field, RootModel
+
+from vdb.lib.cve_model.common import (
+ AttackComplexityType,
+ AttackRequirementsType,
+ AttackVectorType,
+ AutomatableType,
+ CiaRequirementType,
+ CriticalScoreType,
+ CriticalSeverityType,
+ ExploitMaturityType,
+ HighScoreType,
+ HighSeverityType,
+ LowScoreType,
+ LowSeverityType,
+ MediumScoreType,
+ MediumSeverityType,
+ ModifiedAttackComplexityType,
+ ModifiedAttackRequirementsType,
+ ModifiedAttackVectorType,
+ ModifiedPrivilegesRequiredType,
+ ModifiedSubCType,
+ ModifiedSubIaType,
+ ModifiedUserInteractionType,
+ ModifiedVulnCiaType,
+ NoneScoreType,
+ NoneSeverityType,
+ PrivilegesRequiredType,
+ ProviderUrgencyType,
+ RecoveryType,
+ SafetyType,
+ ScoreType,
+ SeverityType,
+ SubCiaType,
+ UserInteractionType,
+ ValueDensityType,
+ VulnCiaType,
+ VulnerabilityResponseEffortType,
+)
+
+
+class Version(Enum):
+ field_4_0 = "4.0"
+
+
+class Field0(BaseModel):
+ environmentalScore: Optional[NoneScoreType] = None
+ environmentalSeverity: Optional[NoneSeverityType] = None
+
+
+class Field0Model(BaseModel):
+ environmentalScore: Optional[LowScoreType] = None
+ environmentalSeverity: Optional[LowSeverityType] = None
+
+
+class Field0Model1(BaseModel):
+ environmentalScore: Optional[MediumScoreType] = None
+ environmentalSeverity: Optional[MediumSeverityType] = None
+
+
+class Field0Model2(BaseModel):
+ environmentalScore: Optional[HighScoreType] = None
+ environmentalSeverity: Optional[HighSeverityType] = None
+
+
+class Field0Model3(BaseModel):
+ environmentalScore: Optional[CriticalScoreType] = None
+ environmentalSeverity: Optional[CriticalSeverityType] = None
+
+
+class Field0Model4(BaseModel):
+ version: Annotated[Version, Field(description="CVSS Version")]
+ vectorString: Annotated[
+ str,
+ Field(
+ pattern="^CVSS:4[.]0/AV:[NALP]/AC:[LH]/AT:[NP]/PR:[NLH]/UI:[NPA]/VC:[HLN]/VI:[HLN]/VA:[HLN]/SC:[HLN]/SI:[HLN]/SA:[HLN](/E:[XAPU])?(/CR:[XHML])?(/IR:[XHML])?(/AR:[XHML])?(/MAV:[XNALP])?(/MAC:[XLH])?(/MAT:[XNP])?(/MPR:[XNLH])?(/MUI:[XNPA])?(/MVC:[XNLH])?(/MVI:[XNLH])?(/MVA:[XNLH])?(/MSC:[XNLH])?(/MSI:[XNLHS])?(/MSA:[XNLHS])?(/S:[XNP])?(/AU:[XNY])?(/R:[XAUI])?(/V:[XDC])?(/RE:[XLMH])?(/U:(X|Clear|Green|Amber|Red))?$"
+ ),
+ ]
+ baseScore: ScoreType
+ baseSeverity: SeverityType
+ attackVector: Optional[AttackVectorType] = None
+ attackComplexity: Optional[AttackComplexityType] = None
+ attackRequirements: Optional[AttackRequirementsType] = None
+ privilegesRequired: Optional[PrivilegesRequiredType] = None
+ userInteraction: Optional[UserInteractionType] = None
+ vulnConfidentialityImpact: Optional[VulnCiaType] = None
+ vulnIntegrityImpact: Optional[VulnCiaType] = None
+ vulnAvailabilityImpact: Optional[VulnCiaType] = None
+ subConfidentialityImpact: Optional[SubCiaType] = None
+ subIntegrityImpact: Optional[SubCiaType] = None
+ subAvailabilityImpact: Optional[SubCiaType] = None
+ exploitMaturity: Optional[ExploitMaturityType] = "NOT_DEFINED"
+ confidentialityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ integrityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ availabilityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ modifiedAttackVector: Optional[ModifiedAttackVectorType] = "NOT_DEFINED"
+ modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = "NOT_DEFINED"
+ modifiedAttackRequirements: Optional[ModifiedAttackRequirementsType] = "NOT_DEFINED"
+ modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = "NOT_DEFINED"
+ modifiedUserInteraction: Optional[ModifiedUserInteractionType] = "NOT_DEFINED"
+ modifiedVulnConfidentialityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedVulnIntegrityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedVulnAvailabilityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedSubConfidentialityImpact: Optional[ModifiedSubCType] = "NOT_DEFINED"
+ modifiedSubIntegrityImpact: Optional[ModifiedSubIaType] = "NOT_DEFINED"
+ modifiedSubAvailabilityImpact: Optional[ModifiedSubIaType] = "NOT_DEFINED"
+ Safety: Optional[SafetyType] = "NOT_DEFINED"
+ Automatable: Optional[AutomatableType] = "NOT_DEFINED"
+ Recovery: Optional[RecoveryType] = "NOT_DEFINED"
+ valueDensity: Optional[ValueDensityType] = "NOT_DEFINED"
+ vulnerabilityResponseEffort: Optional[VulnerabilityResponseEffortType] = (
+ "NOT_DEFINED"
+ )
+ providerUrgency: Optional[ProviderUrgencyType] = "NOT_DEFINED"
+
+
+class Field0Model5(Field0, Field0Model4):
+ version: Annotated[Version, Field(description="CVSS Version")]
+ vectorString: Annotated[
+ str,
+ Field(
+ pattern="^CVSS:4[.]0/AV:[NALP]/AC:[LH]/AT:[NP]/PR:[NLH]/UI:[NPA]/VC:[HLN]/VI:[HLN]/VA:[HLN]/SC:[HLN]/SI:[HLN]/SA:[HLN](/E:[XAPU])?(/CR:[XHML])?(/IR:[XHML])?(/AR:[XHML])?(/MAV:[XNALP])?(/MAC:[XLH])?(/MAT:[XNP])?(/MPR:[XNLH])?(/MUI:[XNPA])?(/MVC:[XNLH])?(/MVI:[XNLH])?(/MVA:[XNLH])?(/MSC:[XNLH])?(/MSI:[XNLHS])?(/MSA:[XNLHS])?(/S:[XNP])?(/AU:[XNY])?(/R:[XAUI])?(/V:[XDC])?(/RE:[XLMH])?(/U:(X|Clear|Green|Amber|Red))?$"
+ ),
+ ]
+ baseScore: ScoreType
+ baseSeverity: SeverityType
+ attackVector: Optional[AttackVectorType] = None
+ attackComplexity: Optional[AttackComplexityType] = None
+ attackRequirements: Optional[AttackRequirementsType] = None
+ privilegesRequired: Optional[PrivilegesRequiredType] = None
+ userInteraction: Optional[UserInteractionType] = None
+ vulnConfidentialityImpact: Optional[VulnCiaType] = None
+ vulnIntegrityImpact: Optional[VulnCiaType] = None
+ vulnAvailabilityImpact: Optional[VulnCiaType] = None
+ subConfidentialityImpact: Optional[SubCiaType] = None
+ subIntegrityImpact: Optional[SubCiaType] = None
+ subAvailabilityImpact: Optional[SubCiaType] = None
+ exploitMaturity: Optional[ExploitMaturityType] = "NOT_DEFINED"
+ confidentialityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ integrityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ availabilityRequirement: Optional[CiaRequirementType] = "NOT_DEFINED"
+ modifiedAttackVector: Optional[ModifiedAttackVectorType] = "NOT_DEFINED"
+ modifiedAttackComplexity: Optional[ModifiedAttackComplexityType] = "NOT_DEFINED"
+ modifiedAttackRequirements: Optional[ModifiedAttackRequirementsType] = "NOT_DEFINED"
+ modifiedPrivilegesRequired: Optional[ModifiedPrivilegesRequiredType] = "NOT_DEFINED"
+ modifiedUserInteraction: Optional[ModifiedUserInteractionType] = "NOT_DEFINED"
+ modifiedVulnConfidentialityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedVulnIntegrityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedVulnAvailabilityImpact: Optional[ModifiedVulnCiaType] = "NOT_DEFINED"
+ modifiedSubConfidentialityImpact: Optional[ModifiedSubCType] = "NOT_DEFINED"
+ modifiedSubIntegrityImpact: Optional[ModifiedSubIaType] = "NOT_DEFINED"
+ modifiedSubAvailabilityImpact: Optional[ModifiedSubIaType] = "NOT_DEFINED"
+ Safety: Optional[SafetyType] = "NOT_DEFINED"
+ Automatable: Optional[AutomatableType] = "NOT_DEFINED"
+ Recovery: Optional[RecoveryType] = "NOT_DEFINED"
+ valueDensity: Optional[ValueDensityType] = "NOT_DEFINED"
+ vulnerabilityResponseEffort: Optional[VulnerabilityResponseEffortType] = (
+ "NOT_DEFINED"
+ )
+ providerUrgency: Optional[ProviderUrgencyType] = "NOT_DEFINED"
+
+
+class Field0Model6(RootModel[Field0Model5]):
+ root: Annotated[
+ Field0Model5,
+ Field(title="JSON Schema for Common Vulnerability Scoring System version 4.0"),
+ ]
diff --git a/vdb/lib/cve_model/tagger.py b/vdb/lib/cve_model/tagger.py
index 7250caf..e1177d8 100644
--- a/vdb/lib/cve_model/tagger.py
+++ b/vdb/lib/cve_model/tagger.py
@@ -18,7 +18,7 @@
"zerodayinitiative.com",
"www.samba.org/samba/security/",
"www.synology.com/support/security/",
- "us-cert.gov/advisories"
+ "us-cert.gov/advisories",
],
"government-resource": [
".gov",
@@ -73,12 +73,7 @@
"medium.com",
"twitter.com",
],
- "release-notes": [
- "/release",
- ".md",
- "/changeset",
- "releases/"
- ],
+ "release-notes": ["/release", ".md", "/changeset", "releases/"],
"technical-description": [
"poc",
"hackerone",
@@ -100,7 +95,7 @@
"disclosure",
"rapid7",
"reference",
- ".me/"
+ ".me/",
],
"vendor-advisory": [
"oracle.com",
@@ -148,7 +143,7 @@
"vulncheck",
"glsa",
"rhsa-",
- ]
+ ],
}
diff --git a/vdb/lib/npm.py b/vdb/lib/npm.py
index 3290b82..5d9a362 100644
--- a/vdb/lib/npm.py
+++ b/vdb/lib/npm.py
@@ -211,6 +211,10 @@ def to_vuln(self, v, ret_data):
if vector_string:
cvss3_obj = get_cvss3_from_vector(vector_string)
if cvss3_obj:
+ # For some CVEs such as CVE-2024-47875, severity and score are not aligned
+ # By utilising the vector string, we make them consistent
+ score = cvss3_obj.get("baseScore")
+ severity = cvss3_obj.get("baseSeverity")
exploitability_score = cvss3_obj.get("temporalScore")
attack_complexity = cvss3_obj.get("attackComplexity")
user_interaction = cvss3_obj.get("userInteraction")
diff --git a/vdb/lib/osv.py b/vdb/lib/osv.py
index e4f196f..f504c2c 100644
--- a/vdb/lib/osv.py
+++ b/vdb/lib/osv.py
@@ -16,6 +16,7 @@
compress_str,
convert_score_severity,
get_cvss3_from_vector,
+ get_cvss4_from_vector,
get_default_cve_data,
parse_purl,
)
@@ -139,6 +140,7 @@ def to_vuln(cve_data):
break
assigner = "google"
vector_string = ""
+ cvss4_vector_string = ""
if cve_id.startswith("GHSA"):
assigner = "github_m"
elif cve_id.startswith("CVE"):
@@ -152,6 +154,8 @@ def to_vuln(cve_data):
for sv in severity_list:
if sv["type"] == "CVSS_V3":
vector_string = sv["score"]
+ elif sv["type"] == "CVSS_V4":
+ cvss4_vector_string = sv["score"]
# Issue 58
cve_database_specific = cve_data.get("database_specific")
cve_ecosystem_specific = cve_data.get("ecosystem_specific")
@@ -198,14 +202,24 @@ def to_vuln(cve_data):
score = cvss.get("score")
severity = convert_score_severity(score)
user_interaction = "REQUIRED"
+ # We give importance to cvss4 which always seems to be lower compared to cvss3
+ if cvss4_vector_string:
+ cvss4_obj = get_cvss4_from_vector(cvss4_vector_string)
+ score = cvss4_obj.get("baseScore")
+ severity = cvss4_obj.get("baseSeverity")
+ exploitability_score = score
+ attack_complexity = cvss4_obj.get("attackComplexity")
+ user_interaction = cvss4_obj.get("userInteraction")
if vector_string:
cvss3_obj = get_cvss3_from_vector(vector_string)
- score = cvss3_obj.get("baseScore")
- severity = cvss3_obj.get("baseSeverity")
- exploitability_score = cvss3_obj.get("temporalScore")
- attack_complexity = cvss3_obj.get("attackComplexity")
- user_interaction = cvss3_obj.get("userInteraction")
- else:
+ # Fallback to CVSS 3.1 only if CVSS 4 is absent
+ if not cvss4_vector_string:
+ score = cvss3_obj.get("baseScore")
+ severity = cvss3_obj.get("baseSeverity")
+ exploitability_score = cvss3_obj.get("temporalScore")
+ attack_complexity = cvss3_obj.get("attackComplexity")
+ user_interaction = cvss3_obj.get("userInteraction")
+ if not vector_string and not cvss4_vector_string:
(
score,
severity,
@@ -364,6 +378,8 @@ def to_vuln(cve_data):
)
),
}
+ if cvss4_vector_string:
+ vuln.cvss4_vector_string = cvss4_vector_string
ret_data.append(vuln)
except Exception:
pass
@@ -480,6 +496,8 @@ def to_vuln(cve_data):
)
),
}
+ if cvss4_vector_string:
+ vuln.cvss4_vector_string = cvss4_vector_string
ret_data.append(vuln)
except Exception:
pass
diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py
index 6c2b30c..e2b56fb 100644
--- a/vdb/lib/utils.py
+++ b/vdb/lib/utils.py
@@ -9,7 +9,7 @@
from urllib.parse import parse_qs, urlparse
import orjson
-from cvss import CVSS3
+from cvss import CVSS3, CVSS4
from packageurl import PackageURL
from semver import VersionInfo
@@ -909,6 +909,16 @@ def get_cvss3_from_vector(vector: str) -> Dict:
return c.as_json()
+def get_cvss4_from_vector(vector: str) -> Dict:
+ """
+ Return CVE metadata for the given vector
+ :param vector: Vector
+ :return: CVSS4 parsed and converted to json
+ """
+ c = CVSS4(vector)
+ return c.as_json()
+
+
def convert_to_occurrence(datas):
"""Method to parse raw search result and convert to Vulnerability occurence