From fe9109f3448fe198c62437c7a3299a0beef8e27c Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Fri, 22 Dec 2023 19:01:47 +0100 Subject: [PATCH] #103: moved urlSecurityJson to its own class --- .../tools/ide/tool/ToolCommandlet.java | 8 +- .../url/model/file/UrlSecurityJsonFile.java | 266 +++--------------- .../model/file/json/UrlSecurityWarning.java | 211 ++++++++++++++ .../file/json/UrlSecurityWarningsJson.java | 30 ++ .../security/BuildSecurityJsonFiles.java | 15 +- 5 files changed, 292 insertions(+), 238 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarning.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarningsJson.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index 01803e3ca..05755cff4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.common.Tags; @@ -22,7 +23,6 @@ import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.property.StringListProperty; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; -import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile.UrlSecurityWarning; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.version.VersionIdentifier; @@ -188,7 +188,7 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); - if (!securityFile.contains(current, true, this.context)) { + if (!securityFile.contains(current, true, this.context, securityFile.getParent())) { return configuredVersion; } @@ -198,14 +198,14 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured int currentVersionIndex = allVersions.indexOf(current); VersionIdentifier nextSafe = null; for (int i = currentVersionIndex - 1; i >= 0; i--) { - if (!securityFile.contains(allVersions.get(i), true, this.context)) { + if (!securityFile.contains(allVersions.get(i), true, this.context, securityFile.getParent())) { nextSafe = allVersions.get(i); break; } } VersionIdentifier latestSafe = null; for (int i = 0; i < allVersions.size(); i++) { - if (!securityFile.contains(allVersions.get(i), true, this.context)) { + if (!securityFile.contains(allVersions.get(i), true, this.context, securityFile.getParent())) { latestSafe = allVersions.get(i); break; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java index 771a4ebc5..e84d6d56b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.HashSet; import java.util.List; @@ -12,6 +11,8 @@ import java.util.Set; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarningsJson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +21,6 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; /** @@ -28,216 +28,12 @@ */ public class UrlSecurityJsonFile extends AbstractUrlFile { - /** - * A simple container with the information about a security warning. - */ - public static class UrlSecurityWarning { - - private VersionRange versionRange; - - private String matchedCpe; - - private String interval; - - private BigDecimal severity; - - private String severityVersion; - - private String cveName; - - private String description; - - private String nistUrl; - - private List referenceUrls; - - public UrlSecurityWarning() { - - super(); - } - - /** - * The constructor. - * - * @param versionRange the version range, specifying the versions of the tool to which the security risk applies. - * @param matchedCpe the matched CPE. - * @param interval the interval of vulnerability that was used to determine the {@link VersionRange}. This is used - * to check if the mapping from CPE version to UrlVersion was correct. - * @param severity the severity of the security risk. - * @param severityVersion Indicating from which version the {@code severity} was obtained. As of December 2023, this - * is either v2 or v3. - * @param cveName the name of the CVE (Common Vulnerabilities and Exposures). - * @param description the description of the CVE. - * @param nistUrl the url to the CVE on the NIST website. - * @param referenceUrl the urls where additional information about the CVE can be found. - */ - public UrlSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, - String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { - - super(); - this.versionRange = versionRange; - this.matchedCpe = matchedCpe; - this.interval = interval; - this.severity = severity; - this.severityVersion = severityVersion; - this.cveName = cveName; - this.description = description; - this.nistUrl = nistUrl; - this.referenceUrls = referenceUrl; - } - - // these setters and getters are needed for the jackson (de)serialization - public void setVersionRange(VersionRange versionRange) { - - this.versionRange = versionRange; - } - - public void setInterval(String interval) { - - this.interval = interval; - } - - public void setMatchedCpe(String matchedCpe) { - - this.matchedCpe = matchedCpe; - } - - public void setSeverity(BigDecimal severity) { - - this.severity = severity; - } - - public void setSeverityVersion(String severityVersion) { - - this.severityVersion = severityVersion; - } - - public void setCveName(String cveName) { - - this.cveName = cveName; - } - - public void setDescription(String description) { - - this.description = description; - } - - public void setNistUrl(String nistUrl) { - - this.nistUrl = nistUrl; - } - - public void setReferenceUrl(List referenceUrl) { - - this.referenceUrls = referenceUrl; - } - - public VersionRange getVersionRange() { - - return versionRange; - } - - public String getMatchedCpe() { - - return matchedCpe; - } - - public String getInterval() { - - return interval; - } - - public BigDecimal getSeverity() { - - return severity; - } - - public String getSeverityVersion() { - - return severityVersion; - } - - public String getCveName() { - - return cveName; - } - - public String getDescription() { - - return description; - } - - public String getNistUrl() { - - return nistUrl; - } - - public List getReferenceUrl() { - - return referenceUrls; - } - - @Override - public int hashCode() { - - String versionRangeString = this.versionRange == null ? "" : this.versionRange.toString(); - String severity = this.severity == null ? "" : this.severity.toString(); - String referenceUrls = this.referenceUrls == null ? "" : this.referenceUrls.toString(); - String s = versionRangeString + severity + this.severityVersion + this.cveName + this.description + this.nistUrl - + referenceUrls; - return s.hashCode(); - - } - - @Override - public boolean equals(Object obj) { - - if (obj == this) { - return true; - } - if ((obj == null) || (obj.getClass() != getClass())) { - return false; - } - UrlSecurityWarning other = (UrlSecurityWarning) obj; - if (!this.versionRange.equals(other.versionRange)) { - return false; - } - if (this.severity.compareTo(other.severity) != 0) { - return false; - } - if (!this.severityVersion.equals(other.severityVersion)) { - return false; - } - if (!this.cveName.equals(other.cveName)) { - return false; - } - if (!this.description.equals(other.description)) { - return false; - } - if (!this.nistUrl.equals(other.nistUrl)) { - return false; - } - for (String url : this.referenceUrls) { - if (!other.referenceUrls.contains(url)) { - return false; - } - } - for (String url : other.referenceUrls) { - if (!this.referenceUrls.contains(url)) { - return false; - } - } - - return true; - } - }; - /** {@link #getName() Name} of security json file. */ public static final String FILENAME_SECURITY = "security.json"; private static final Logger LOG = LoggerFactory.getLogger(UrlSecurityJsonFile.class); - private Set warnings; + private UrlSecurityWarningsJson urlSecurityWarningsJson = new UrlSecurityWarningsJson(); /** * The constructor. @@ -247,14 +43,18 @@ public boolean equals(Object obj) { public UrlSecurityJsonFile(UrlEdition parent) { super(parent, FILENAME_SECURITY); - this.warnings = new HashSet<>(); } + /** + * A wrapper for + * {@link #addSecurityWarning(VersionRange, String, String, BigDecimal, String, String, String, String, List)} used in + * the unit tests. + */ public boolean addSecurityWarning(VersionRange versionRange) { UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null, null, null, null, null); - boolean added = warnings.add(newWarning); + boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; } @@ -280,27 +80,35 @@ public boolean addSecurityWarning(VersionRange versionRange, String matchedCpe, UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, matchedCpe, interval, severity, severityVersion, cveName, description, nistUrl, referenceUrl); - boolean added = warnings.add(newWarning); + boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; } /** - * For a given version, returns whether there is a security risk by locking at the warnings in the security json file. + * For a given version, returns whether there is a security warning in the {@link UrlSecurityWarningsJson JSON + * object}. * - * @param version the version to check for security risks. + * @param version the {@link VersionIdentifier version} to check for security risks listed in the + * {@link UrlSecurityJsonFile}. + * @param ignoreWarningsThatAffectAllVersions {@code true} if warnings that affect all versions should be ignored, + * {@code false} otherwise. + * @param context the {@link IdeContext} to use in case {@code ignoreWarningsThatAffectAllVersions} is {@code true} to + * get the sorted versions of the tool. + * @param edition the {@link UrlEdition} to use in case {@code ignoreWarningsThatAffectAllVersions} is {@code true} to + * get the sorted versions of the tool. * @return {@code true} if there is a security risk for the given version, {@code false} otherwise. */ - public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAffectAllVersions, IdeContext context) { + public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAffectAllVersions, IdeContext context, + UrlEdition edition) { List sortedVersions = null; if (ignoreWarningsThatAffectAllVersions) { - sortedVersions = Objects.requireNonNull(context).getUrls() - .getSortedVersions(this.getParent().getParent().getName(), this.getParent().getName()); + sortedVersions = Objects.requireNonNull(context).getUrls().getSortedVersions(edition.getName(), + edition.getName()); } - for (UrlSecurityWarning warning : this.warnings) { - + for (UrlSecurityWarning warning : this.urlSecurityWarningsJson.getWarnings()) { VersionRange versionRange = warning.getVersionRange(); if (ignoreWarningsThatAffectAllVersions) { boolean includesOldestVersion = versionRange.getMin() == null @@ -318,15 +126,19 @@ public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAff return false; } + /** + * For a given version, returns whether there is a security warning in the {@link UrlSecurityWarningsJson JSON + * object}. This method does not ignore warnings that affect all versions. + */ public boolean contains(VersionIdentifier version) { - return contains(version, false, null); + return contains(version, false, null, null); } public Set getMatchingSecurityWarnings(VersionIdentifier version) { Set matchedWarnings = new HashSet<>(); - for (UrlSecurityWarning warning : this.warnings) { + for (UrlSecurityWarning warning : this.urlSecurityWarningsJson.getWarnings()) { if (warning.getVersionRange().contains(version)) { matchedWarnings.add(warning); } @@ -336,7 +148,7 @@ public Set getMatchingSecurityWarnings(VersionIdentifier ver public void clearSecurityWarnings() { - this.warnings.clear(); + this.urlSecurityWarningsJson.getWarnings().clear(); this.modified = true; } @@ -348,35 +160,33 @@ protected void doLoad() { } ObjectMapper mapper = JsonMapping.create(); try { - warnings = mapper.readValue(getPath().toFile(), new TypeReference>() { - }); + this.urlSecurityWarningsJson = mapper.readValue(getPath().toFile(), UrlSecurityWarningsJson.class); } catch (IOException e) { - throw new IllegalStateException("The UrlSecurityJsonFile " + getPath() + " could not be parsed.", e); + throw new IllegalStateException("Failed to load the UrlSecurityJsonFile " + getPath(), e); } } @Override protected void doSave() { - Path path = getPath(); ObjectMapper mapper = JsonMapping.create(); - if (this.warnings.isEmpty() && !Files.exists(path)) { + if (this.urlSecurityWarningsJson.getWarnings().isEmpty() && !Files.exists(getPath())) { return; } String jsonString; try { - jsonString = mapper.writeValueAsString(warnings); + jsonString = mapper.writeValueAsString(this.urlSecurityWarningsJson.getWarnings()); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - try (BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.TRUNCATE_EXISTING, + try (BufferedWriter bw = Files.newBufferedWriter(getPath(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { bw.write(jsonString + "\n"); } catch (IOException e) { - throw new IllegalStateException("Failed to save file " + path, e); + throw new IllegalStateException("Failed to save the UrlSecurityJsonFile " + getPath(), e); } } } \ No newline at end of file diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarning.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarning.java new file mode 100644 index 000000000..b6cbf766f --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarning.java @@ -0,0 +1,211 @@ +package com.devonfw.tools.ide.url.model.file.json; + +import com.devonfw.tools.ide.version.VersionRange; + +import java.math.BigDecimal; +import java.util.List; + +/** + * A simple container with the information about a security warning. + */ +public class UrlSecurityWarning { + + private VersionRange versionRange; + + private String matchedCpe; + + private String interval; + + private BigDecimal severity; + + private String severityVersion; + + private String cveName; + + private String description; + + private String nistUrl; + + private List referenceUrls; + + public UrlSecurityWarning() { + + super(); + } + + /** + * The constructor. + * + * @param versionRange the {@link VersionRange}, specifying the versions of the tool to which the security risk + * applies. + * @param matchedCpe the matched CPE. + * @param interval the interval provided by OWASP vulnerability that was used to determine the {@link VersionRange}. + * This is used to check if the mapping from CPE version to UrlVersion was correct. + * @param severity the severity of the security risk. + * @param severityVersion Indicating from which version the {@code severity} was obtained. As of December 2023, this + * is either v2 or v3. + * @param cveName the name of the CVE (Common Vulnerabilities and Exposures). + * @param description the description of the CVE. + * @param nistUrl the url to the CVE on the NIST website. + * @param referenceUrl the urls where additional information about the CVE can be found. + */ + public UrlSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, + String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { + + super(); + this.versionRange = versionRange; + this.matchedCpe = matchedCpe; + this.interval = interval; + this.severity = severity; + this.severityVersion = severityVersion; + this.cveName = cveName; + this.description = description; + this.nistUrl = nistUrl; + this.referenceUrls = referenceUrl; + } + + // these setters and getters are needed for the jackson (de)serialization + public void setVersionRange(VersionRange versionRange) { + + this.versionRange = versionRange; + } + + public void setInterval(String interval) { + + this.interval = interval; + } + + public void setMatchedCpe(String matchedCpe) { + + this.matchedCpe = matchedCpe; + } + + public void setSeverity(BigDecimal severity) { + + this.severity = severity; + } + + public void setSeverityVersion(String severityVersion) { + + this.severityVersion = severityVersion; + } + + public void setCveName(String cveName) { + + this.cveName = cveName; + } + + public void setDescription(String description) { + + this.description = description; + } + + public void setNistUrl(String nistUrl) { + + this.nistUrl = nistUrl; + } + + public void setReferenceUrl(List referenceUrl) { + + this.referenceUrls = referenceUrl; + } + + public VersionRange getVersionRange() { + + return versionRange; + } + + public String getMatchedCpe() { + + return matchedCpe; + } + + public String getInterval() { + + return interval; + } + + public BigDecimal getSeverity() { + + return severity; + } + + public String getSeverityVersion() { + + return severityVersion; + } + + public String getCveName() { + + return cveName; + } + + public String getDescription() { + + return description; + } + + public String getNistUrl() { + + return nistUrl; + } + + public List getReferenceUrl() { + + return referenceUrls; + } + + @Override + public int hashCode() { + + String versionRangeString = this.versionRange == null ? "" : this.versionRange.toString(); + String severity = this.severity == null ? "" : this.severity.toString(); + String referenceUrls = this.referenceUrls == null ? "" : this.referenceUrls.toString(); + String s = versionRangeString + severity + this.severityVersion + this.cveName + this.description + this.nistUrl + + referenceUrls; + return s.hashCode(); + + } + + @Override + public boolean equals(Object obj) { + + if (obj == this) { + return true; + } + if ((obj == null) || (obj.getClass() != getClass())) { + return false; + } + UrlSecurityWarning other = (UrlSecurityWarning) obj; + if (!this.versionRange.equals(other.versionRange)) { + return false; + } + if (this.severity.compareTo(other.severity) != 0) { + return false; + } + if (!this.severityVersion.equals(other.severityVersion)) { + return false; + } + if (!this.cveName.equals(other.cveName)) { + return false; + } + if (!this.description.equals(other.description)) { + return false; + } + if (!this.nistUrl.equals(other.nistUrl)) { + return false; + } + for (String url : this.referenceUrls) { + if (!other.referenceUrls.contains(url)) { + return false; + } + } + for (String url : other.referenceUrls) { + if (!this.referenceUrls.contains(url)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarningsJson.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarningsJson.java new file mode 100644 index 000000000..312cb7754 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityWarningsJson.java @@ -0,0 +1,30 @@ +package com.devonfw.tools.ide.url.model.file.json; + +import java.util.HashSet; +import java.util.Set; + +/** + * Java model class representing a "security.json" file. + * + * @see com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile + */ +public class UrlSecurityWarningsJson { + + private Set warnings = new HashSet<>(); + + public UrlSecurityWarningsJson() { + + super(); + } + + public Set getWarnings() { + + return this.warnings; + } + + public void setWarnings(Set warnings) { + + this.warnings = warnings; + } + +} diff --git a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java index e9fecfa4d..e6b2e9d26 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -218,10 +218,11 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, // the fields of cpe2.3 are: // cpe2.3 part, vendor, product, version, update, edition, language, swEdition, targetSw, targetHw, other; String interval = String.format( - "start excluding = %s, start including = %s, end including = %s, end excluding = %s, is the interval provided by " - + "OWASP. Manually double check whether the VersionRange was correctly determined.", + "start excluding = %s, start including = %s, end including = %s, end excluding = %s, single version = %s, is " + + "the interval provided by OWASP. Manually double check whether the VersionRange was correctly determined.", matchedVulnerableSoftware.getVersionStartExcluding(), matchedVulnerableSoftware.getVersionStartIncluding(), - matchedVulnerableSoftware.getVersionEndIncluding(), matchedVulnerableSoftware.getVersionEndExcluding()); + matchedVulnerableSoftware.getVersionEndIncluding(), matchedVulnerableSoftware.getVersionEndExcluding(), + matchedVulnerableSoftware.getVersion()); securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, severityVersion, cveName, description, nistUrl, referenceUrls); @@ -264,15 +265,17 @@ static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability return affectedRange; } - private static String getUrlVersion(String cpeVersion, AbstractUrlUpdater urlUpdater, Map cpeToUrlVersion) { + private static String getUrlVersion(String cpeVersion, AbstractUrlUpdater urlUpdater, + Map cpeToUrlVersion) { + String urlVersion = null; if (cpeVersion != null) { if (cpeToUrlVersion.containsKey(cpeVersion)) { urlVersion = cpeToUrlVersion.get(cpeVersion); - System.out.println("tool mapped using map cpe " + cpeVersion + " -> url " +urlVersion); + System.out.println("tool mapped using map cpe " + cpeVersion + " -> url " + urlVersion); } else { urlVersion = urlUpdater.mapCpeVersionToUrlVersion(cpeVersion); - System.out.println("tool mapped using mapCpeVersionToUrlVersion cpe " + cpeVersion + " -> url " +urlVersion); + System.out.println("tool mapped using mapCpeVersionToUrlVersion cpe " + cpeVersion + " -> url " + urlVersion); } }