diff --git a/open-vulnerability-clients/src/main/java/io/github/jeremylong/openvulnerability/client/nvd/CvssV4Data.java b/open-vulnerability-clients/src/main/java/io/github/jeremylong/openvulnerability/client/nvd/CvssV4Data.java index 542f8702..aab08f77 100644 --- a/open-vulnerability-clients/src/main/java/io/github/jeremylong/openvulnerability/client/nvd/CvssV4Data.java +++ b/open-vulnerability-clients/src/main/java/io/github/jeremylong/openvulnerability/client/nvd/CvssV4Data.java @@ -71,8 +71,8 @@ public CvssV4Data(Version version, String vectorString, AttackVectorType attackV ModifiedPrivilegesRequiredType modifiedPrivilegesRequired, ModifiedUserInteractionType modifiedUserInteraction, ModifiedCiaType modifiedVulnConfidentialityImpact, ModifiedCiaType modifiedVulnIntegrityImpact, ModifiedCiaType modifiedVulnAvailabilityImpact, - ModifiedCiaType modifiedSubConfidentialityImpact, ModifiedCiaType modifiedSubIntegrityImpact, - ModifiedCiaType modifiedSubAvailabilityImpact, SafetyType safety, AutomatableType automatable, + ModifiedSubCType modifiedSubConfidentialityImpact, ModifiedSubIaType modifiedSubIntegrityImpact, + ModifiedSubIaType modifiedSubAvailabilityImpact, SafetyType safety, AutomatableType automatable, RecoveryType recovery, ValueDensityType valueDensity, VulnerabilityResponseEffortType vulnerabilityResponseEffort, ProviderUrgencyType providerUrgency, Double baseScore, SeverityType baseSeverity, Double threatScore, SeverityType threatSeverity, @@ -200,15 +200,15 @@ public CvssV4Data(Version version, String vectorString, AttackVectorType attackV @JsonProperty("modifiedSubsequentSystemConfidentiality") @JsonAlias("modifiedSubConfidentialityImpact") @JsonSetter(nulls = Nulls.SKIP) - private ModifiedCiaType modifiedSubConfidentialityImpact = ModifiedCiaType.NOT_DEFINED; + private ModifiedSubCType modifiedSubConfidentialityImpact = ModifiedSubCType.NOT_DEFINED; @JsonProperty("modifiedSubsequentSystemIntegrity") @JsonAlias("modifiedSubIntegrityImpact") @JsonSetter(nulls = Nulls.SKIP) - private ModifiedCiaType modifiedSubIntegrityImpact = ModifiedCiaType.NOT_DEFINED; + private ModifiedSubIaType modifiedSubIntegrityImpact = ModifiedSubIaType.NOT_DEFINED; @JsonProperty("modifiedSubsequentSystemAvailability") @JsonAlias("modifiedSubAvailabilityImpact") @JsonSetter(nulls = Nulls.SKIP) - private ModifiedCiaType modifiedSubAvailabilityImpact = ModifiedCiaType.NOT_DEFINED; + private ModifiedSubIaType modifiedSubAvailabilityImpact = ModifiedSubIaType.NOT_DEFINED; @JsonProperty("safety") @JsonAlias("Safety") @JsonSetter(nulls = Nulls.SKIP) @@ -477,7 +477,7 @@ public ModifiedCiaType getModifiedVulnAvailabilityImpact() { * @return modifiedSubConfidentialityImpact */ @JsonProperty("modifiedSubConfidentialityImpact") - public ModifiedCiaType getModifiedSubConfidentialityImpact() { + public ModifiedSubCType getModifiedSubConfidentialityImpact() { return modifiedSubConfidentialityImpact; } @@ -485,7 +485,7 @@ public ModifiedCiaType getModifiedSubConfidentialityImpact() { * @return modifiedSubIntegrityImpact */ @JsonProperty("modifiedSubIntegrityImpact") - public ModifiedCiaType getModifiedSubIntegrityImpact() { + public ModifiedSubIaType getModifiedSubIntegrityImpact() { return modifiedSubIntegrityImpact; } @@ -493,7 +493,7 @@ public ModifiedCiaType getModifiedSubIntegrityImpact() { * @return modifiedSubAvailabilityImpact */ @JsonProperty("modifiedSubAvailabilityImpact") - public ModifiedCiaType getModifiedSubAvailabilityImpact() { + public ModifiedSubIaType getModifiedSubAvailabilityImpact() { return modifiedSubAvailabilityImpact; } @@ -650,15 +650,15 @@ public String toString() { } // (\/MSC:[XNLH])?(\/MSI:[XNLHS])?(\/MSA:[XNLHS])?(\/S:[XNP])?(\/AU:[XNY])?(\/R:[XAUI])? if (modifiedSubConfidentialityImpact != null) { - v.append("/MSC:").append(modifiedSubConfidentialityImpact == ModifiedCiaType.NOT_DEFINED ? "X" + v.append("/MSC:").append(modifiedSubConfidentialityImpact == ModifiedSubCType.NOT_DEFINED ? "X" : modifiedSubConfidentialityImpact.value().charAt(0)); } if (modifiedSubIntegrityImpact != null) { - v.append("/MSI:").append(modifiedSubIntegrityImpact == ModifiedCiaType.NOT_DEFINED ? "X" + v.append("/MSI:").append(modifiedSubIntegrityImpact == ModifiedSubIaType.NOT_DEFINED ? "X" : modifiedSubIntegrityImpact.value().charAt(0)); } if (modifiedSubAvailabilityImpact != null) { - v.append("/MSA:").append(modifiedSubAvailabilityImpact == ModifiedCiaType.NOT_DEFINED ? "X" + v.append("/MSA:").append(modifiedSubAvailabilityImpact == ModifiedSubIaType.NOT_DEFINED ? "X" : modifiedSubAvailabilityImpact.value().charAt(0)); } if (safety != null) { @@ -1239,6 +1239,106 @@ public String value() { } + public enum ModifiedSubCType { + + NEGLIGIBLE("NEGLIGIBLE"), LOW("LOW"), HIGH("HIGH"), NOT_DEFINED("NOT_DEFINED"); + + private final static Map CONSTANTS = new HashMap<>(); + + static { + for (ModifiedSubCType c : values()) { + CONSTANTS.put(c.value, c); + } + } + + private final String value; + + ModifiedSubCType(String value) { + this.value = value; + } + + @JsonCreator + public static ModifiedSubCType fromValue(String value) { + // allow conversion from vector string + if (value != null && value.length() == 1) { + if ("x".equalsIgnoreCase(value)) { + return NOT_DEFINED; + } + for (ModifiedSubCType t : values()) { + if (t.value.startsWith(value.toUpperCase())) { + return t; + } + } + } + ModifiedSubCType constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + } + + public enum ModifiedSubIaType { + + NEGLIGIBLE("NEGLIGIBLE"), LOW("LOW"), HIGH("HIGH"), SAFETY("SAFETY"), NOT_DEFINED("NOT_DEFINED"); + + private final static Map CONSTANTS = new HashMap<>(); + + static { + for (ModifiedSubIaType c : values()) { + CONSTANTS.put(c.value, c); + } + } + + private final String value; + + ModifiedSubIaType(String value) { + this.value = value; + } + + @JsonCreator + public static ModifiedSubIaType fromValue(String value) { + // allow conversion from vector string + if (value != null && value.length() == 1) { + if ("x".equalsIgnoreCase(value)) { + return NOT_DEFINED; + } + for (ModifiedSubIaType t : values()) { + if (t.value.startsWith(value.toUpperCase())) { + return t; + } + } + } + ModifiedSubIaType constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + } + public enum SafetyType { NEGLIGIBLE("NEGLIGIBLE"), PRESENT("PRESENT"), NOT_DEFINED("NOT_DEFINED"); diff --git a/open-vulnerability-clients/src/test/java/io/github/jeremylong/openvulnerability/client/nvd/SerializationTest.java b/open-vulnerability-clients/src/test/java/io/github/jeremylong/openvulnerability/client/nvd/SerializationTest.java index 6291a6ed..55e1e655 100644 --- a/open-vulnerability-clients/src/test/java/io/github/jeremylong/openvulnerability/client/nvd/SerializationTest.java +++ b/open-vulnerability-clients/src/test/java/io/github/jeremylong/openvulnerability/client/nvd/SerializationTest.java @@ -41,7 +41,7 @@ void thereAndBackAgain() throws IOException { json = r.lines().collect(Collectors.joining("\n")); } CveApiJson20 current = objectMapper.readValue(json, CveApiJson20.class); - assertEquals(2000, current.getVulnerabilities().size()); + assertEquals(2001, current.getVulnerabilities().size()); String serialized = objectMapper.writeValueAsString(current); CveApiJson20 hydrated = objectMapper.readValue(serialized, CveApiJson20.class); diff --git a/open-vulnerability-clients/src/test/resources/nvd.json b/open-vulnerability-clients/src/test/resources/nvd.json index 6265443d..3d7887f1 100644 --- a/open-vulnerability-clients/src/test/resources/nvd.json +++ b/open-vulnerability-clients/src/test/resources/nvd.json @@ -323476,6 +323476,90 @@ } ] } + }, + { + "cve": { + "id": "CVE-2025-26793", + "sourceIdentifier": "cve@mitre.org", + "published": "2025-02-15T15:15:23.587", + "lastModified": "2025-02-15T15:15:23.587", + "vulnStatus": "Received", + "cveTags": [], + "descriptions": [ + { + "lang": "en", + "value": "The Web GUI configuration panel of Hirsch (formerly Identiv and Viscount) Enterphone MESH through 2024 ships with default credentials (username freedom, password viscount). The administrator is not prompted to change these credentials on initial configuration, and changing the credentials requires many steps. Attackers can use the credentials over the Internet via mesh.webadmin.MESHAdminServlet to gain access to dozens of Canadian and U.S. apartment buildings and obtain building residents' PII. NOTE: the Supplier's perspective is that the \"vulnerable systems are not following manufacturers' recommendations to change the default password.\"" + } + ], + "metrics": { + "cvssMetricV40": [ + { + "source": "cve@mitre.org", + "type": "Secondary", + "cvssData": { + "version": "4.0", + "vectorString": "CVSS:4.0\/AV:N\/AC:L\/AT:N\/PR:N\/UI:N\/VC:H\/VI:H\/VA:N\/SC:N\/SI:N\/SA:N\/E:X\/CR:X\/IR:X\/AR:X\/MAV:X\/MAC:X\/MAT:X\/MPR:X\/MUI:X\/MVC:X\/MVI:X\/MVA:X\/MSC:X\/MSI:S\/MSA:X\/S:P\/AU:X\/R:X\/V:X\/RE:X\/U:X", + "baseScore": 10.0, + "baseSeverity": "CRITICAL", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "attackRequirements": "NONE", + "privilegesRequired": "NONE", + "userInteraction": "NONE", + "vulnerableSystemConfidentiality": "HIGH", + "vulnerableSystemIntegrity": "HIGH", + "vulnerableSystemAvailability": "NONE", + "subsequentSystemConfidentiality": "NONE", + "subsequentSystemIntegrity": "NONE", + "subsequentSystemAvailability": "NONE", + "exploitMaturity": "NOT_DEFINED", + "confidentialityRequirements": "NOT_DEFINED", + "integrityRequirements": "NOT_DEFINED", + "availabilityRequirements": "NOT_DEFINED", + "modifiedAttackVector": "NOT_DEFINED", + "modifiedAttackComplexity": "NOT_DEFINED", + "modifiedAttackRequirements": "NOT_DEFINED", + "modifiedPrivilegesRequired": "NOT_DEFINED", + "modifiedUserInteraction": "NOT_DEFINED", + "modifiedVulnerableSystemConfidentiality": "NOT_DEFINED", + "modifiedVulnerableSystemIntegrity": "NOT_DEFINED", + "modifiedVulnerableSystemAvailability": "NOT_DEFINED", + "modifiedSubsequentSystemConfidentiality": "NOT_DEFINED", + "modifiedSubsequentSystemIntegrity": "SAFETY", + "modifiedSubsequentSystemAvailability": "NOT_DEFINED", + "safety": "PRESENT", + "automatable": "NOT_DEFINED", + "recovery": "NOT_DEFINED", + "valueDensity": "NOT_DEFINED", + "vulnerabilityResponseEffort": "NOT_DEFINED", + "providerUrgency": "NOT_DEFINED" + } + } + ] + }, + "weaknesses": [ + { + "source": "cve@mitre.org", + "type": "Secondary", + "description": [ + { + "lang": "en", + "value": "CWE-1393" + } + ] + } + ], + "references": [ + { + "url": "https:\/\/support.identiv.com\/products\/physical-access\/hirsch\/", + "source": "cve@mitre.org" + }, + { + "url": "https:\/\/www.ericdaigle.ca\/posts\/breaking-into-dozens-of-apartments-in-five-minutes\/", + "source": "cve@mitre.org" + } + ] + } } ] } \ No newline at end of file