From 2237158b89377fe621691183cac908358c4c70ab Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Wed, 25 Oct 2023 12:24:54 +0200 Subject: [PATCH 01/47] #103: security warning for CVEs in file tool/edition/security --- .../com/devonfw/tools/ide/tool/ToolCommandlet.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 43cb012f1..09a8c9637 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 @@ -21,6 +21,7 @@ import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.property.StringListProperty; import com.devonfw.tools.ide.repo.ToolRepository; +import com.devonfw.tools.ide.url.model.file.UrlSecurityFile; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.version.VersionIdentifier; @@ -260,6 +261,15 @@ protected boolean doInstall(boolean silent) { // check if we already have this version installed (linked) locally in IDE_HOME/software VersionIdentifier installedVersion = getInstalledVersion(); + + UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile(); + // I do not want to use the installed version here, as I want to warn the user whether the tool is installed or not. + VersionIdentifier currentVersion = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); + if (securityFile.contains(currentVersion)) { + this.context.warning("Version {} of tool {} is known to have security issues!", currentVersion, + getToolWithEdition()); + } + VersionIdentifier resolvedVersion = installation.resolvedVersion(); if (isInstalledVersion(resolvedVersion, installedVersion, silent)) { return false; From 897b8007cc2699b0bedad6920ffdeaeeeb06440e Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Wed, 8 Nov 2023 18:27:43 +0100 Subject: [PATCH 02/47] promting the user for vulnerabilities and start of Main for OWASP check --- cli/pom.xml | 6 + .../tools/ide/context/AbstractIdeContext.java | 5 +- .../tools/ide/tool/ToolCommandlet.java | 107 +++++++++++- pom.xml | 1 + security/pom.xml | 29 ++++ .../java/com/devonfw/tools/security/Main.java | 158 ++++++++++++++++++ .../devonfw/tools/security/UrlAnalyzer.java | 55 ++++++ .../devonfw/tools/security/UrlFileFilter.java | 23 +++ 8 files changed, 372 insertions(+), 12 deletions(-) create mode 100644 security/pom.xml create mode 100644 security/src/main/java/com/devonfw/tools/security/Main.java create mode 100644 security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java create mode 100644 security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java diff --git a/cli/pom.xml b/cli/pom.xml index a938e7082..ffbd3895b 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -20,6 +20,12 @@ + + + org.owasp + dependency-check-core + 8.4.2 + org.apache.commons diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index d1f99bf49..337dd4e14 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -657,8 +657,9 @@ public O question(String question, O... options) { assert (options.length >= 2); interaction(question); Map mapping = new HashMap<>(options.length); - int i = 1; + int i = 0; for (O option : options) { + i++; String key = "" + option; addMapping(mapping, key, option); String numericKey = Integer.toString(i); @@ -700,6 +701,4 @@ private static void addMapping(Map mapping, String key, O option) } } - - } 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 09a8c9637..57ebd1b73 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 @@ -246,6 +246,98 @@ public boolean install(boolean silent) { return doInstall(silent); } + protected String question(String question, String ... options) { + question += " Do you want to"; + for (int i = 0; i < options.length - 1; i++) { + options[i] += " or"; + } + options[options.length - 1] += "?"; + return this.context.question(question, options); + } + protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) { + + // TODO vielleicht security file auch neu als json file wenn 1.2 > 2.9 nicht ausreicht + // TODO vielleicht auch zusätzlich das tool scannen sobald es installiert ist, + + // TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n"; + + UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile(); + VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), + configuredVersion); + if (!securityFile.contains(current)) { + return configuredVersion; + } + + List allVersions = this.context.getUrls().getSortedVersions(this.tool, this.getEdition()); + VersionIdentifier latest = allVersions.get(0); + + // currentVersion = VersionIdentifier.of("4.9.52"); + + int currentVersionIndex = allVersions.indexOf(current); + + // VersionIdentifier nextVersion = currentVersionIndex == 0 ? null : allVersions.get(allVersions.indexOf(currentVersion) - 1); + VersionIdentifier nextSafe = null; + for (int i = currentVersionIndex - 1; i >= 0; i--) { + if (!securityFile.contains(allVersions.get(i))) { + nextSafe = allVersions.get(i); + break; + } + } + VersionIdentifier latestSafe = null; + for (int i = 0; i < allVersions.size(); i++) { + if (!securityFile.contains(allVersions.get(i))) { + latestSafe = allVersions.get(i); + break; + } + } + VersionIdentifier previousSafe = null; + for (int i = currentVersionIndex + 1; i < allVersions.size(); i++) { + if (!securityFile.contains(allVersions.get(i))) { + previousSafe = allVersions.get(i); + break; + } + } + + String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is installed, " + + "which is has a vulnerability."; + String stay = "stay with the current unsafe version (" + current + ")"; + String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; + String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")"; + String installNextSafe = "install the next safe version (" + nextSafe+ ")"; + + if (current.equals(latest)) { + String answer = question(currentIsUnsafe, stay, installLatestSafe); + return answer.startsWith(stay) ? current : latestSafe; + + } else if (nextSafe == null) { + // TODO also allow selection of next or previous version, even if they are unsafe? + String answer = question(currentIsUnsafe + " All newer versions are also not safe.", + stay, installLatestSafe); + return answer.startsWith(stay) ? current : latestSafe; + + } else if (nextSafe.equals(latest)) { + String answer = question( currentIsUnsafe + " Of the newer versions, only the latest is safe.", + stay, installSafeLatest); + return answer.startsWith(stay) ? current : latestSafe; + + } else if (nextSafe.equals(latestSafe)) { + String answer = question(currentIsUnsafe +" Of the newer versions, only the version " + + nextSafe + " is safe.", stay, "Install the safe version (" + nextSafe + ")"); + return answer.startsWith(stay) ? current : nextSafe; + + } else { + if (latest.equals(latestSafe)) { + String answer = question(currentIsUnsafe, stay, installNextSafe, installSafeLatest); + return answer.startsWith(stay) ? current + : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; + + } else { + String answer = question(currentIsUnsafe, stay, installNextSafe, installLatestSafe); + return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; + } + } + } + /** * Installs or updates the managed {@link #getName() tool}. * @@ -256,20 +348,17 @@ public boolean install(boolean silent) { protected boolean doInstall(boolean silent) { VersionIdentifier configuredVersion = getConfiguredVersion(); + + VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion); + setVersion(chosenVersion, silent); + // configuredVersion = chosenVersion; + // install configured version of our tool in the software repository if not already installed ToolInstallation installation = installInRepo(configuredVersion); // check if we already have this version installed (linked) locally in IDE_HOME/software VersionIdentifier installedVersion = getInstalledVersion(); - UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile(); - // I do not want to use the installed version here, as I want to warn the user whether the tool is installed or not. - VersionIdentifier currentVersion = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); - if (securityFile.contains(currentVersion)) { - this.context.warning("Version {} of tool {} is known to have security issues!", currentVersion, - getToolWithEdition()); - } - VersionIdentifier resolvedVersion = installation.resolvedVersion(); if (isInstalledVersion(resolvedVersion, installedVersion, silent)) { return false; @@ -294,7 +383,7 @@ protected boolean doInstall(boolean silent) { /** * This method is called after the tool has been newly installed or updated to a new version. Override it to add - * custom post intallation logic. + * custom post installation logic. */ protected void postInstall() { diff --git a/pom.xml b/pom.xml index 6fbd3dd2e..a432552af 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ documentation cli + security diff --git a/security/pom.xml b/security/pom.xml new file mode 100644 index 000000000..a1da8b649 --- /dev/null +++ b/security/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.devonfw.tools.IDEasy.dev + ide + dev-SNAPSHOT + + + org.example + security + + + 17 + 17 + UTF-8 + + + + com.devonfw.tools.IDEasy + ide-cli + 2024.01.001-SNAPSHOT + compile + + + + \ No newline at end of file diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/Main.java new file mode 100644 index 000000000..1e1680beb --- /dev/null +++ b/security/src/main/java/com/devonfw/tools/security/Main.java @@ -0,0 +1,158 @@ +package com.devonfw.tools.security; + +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; +import org.owasp.dependencycheck.dependency.*; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; +import org.owasp.dependencycheck.utils.Settings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class Main { + public static void main(String[] args) throws ReportException { + + //TODO, wenn eine cve gefunden wird. dann in ide cli prompen und auch die cve sagen, damit der user selbst entschienden kann ob es vielleicht doch nicht eine false positive is. weil zb der vendor nicht so richtig gemached worden ist + + // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock + // why is this not in projects dir but in user dir? + + Settings settings = new Settings(); + Engine engine = new Engine(settings); + + // das brauche ich um die file endung zu akzeptieren + FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(); + // engine.getAnalyzers().add(myAnalyzer); + engine.getFileTypeAnalyzers().add(myAnalyzer); + List dependencyList = engine.scan("C:\\projects\\_ide\\myUrls"); + System.out.println("size of dependencylist is " + dependencyList.size()); + + for (Dependency dependency : dependencyList) { + // TODO soll ich auch noch die ulr splitten und die zu evidence machen? + String filePath = dependency.getFilePath(); + Path parent = Paths.get(filePath).getParent(); + String tool = parent.getParent().getParent().getFileName().toString(); + String edition = parent.getParent().getFileName().toString(); + String version = parent.getFileName().toString(); + + String vendor = ""; // maybe split url and take domain or second and third after / + + // TODO is versions od dependency updated when adding evidence? + + Evidence productEvidence = new Evidence("mysoure", "myname", tool, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, productEvidence); + + Evidence editionEvidence = new Evidence("mysoure", "myname", edition, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, editionEvidence); + + Evidence versionEvidence = new Evidence("mysoure", "myname", version, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, versionEvidence); + + Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); + + // Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); + // dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); + // dependency.getAvailableVersions(); + } + + // TODO oder kann ich doch manche analyzer weg machen? + // welche sollen weg? + try { + engine.analyzeDependencies();// needed for db stuff which is private + for (Dependency dependency : engine.getDependencies()) { + engine.removeDependency(dependency); + for (EvidenceType type : EvidenceType.values()) { + for (Evidence evidence : dependency.getEvidence(type)) { + if (!evidence.getName().equals("myname")) { + dependency.removeEvidence(type, evidence); + } + } + } + engine.addDependency(dependency); + } + + } catch (ExceptionCollection e) { + throw new RuntimeException(e); + } + + // TODO dont do this with this method but try to do it by hand, since i cant seem to add my URL analyzer to the map of engine + // look at path and them extract name and version and vendor maybe from url + List exceptionsList = new ArrayList<>(); + ExceptionCollection exceptions = new ExceptionCollection(exceptionsList); + + File dir = new File("C:\\projects\\devonfw\\report"); + engine.writeReports("applicationName", "groupId", "artifactId", "version", dir, "JSON", exceptions); + + + String filename = dir.toString() + "\\dependency-check-report.json"; + Path filepath = Paths.get(filename); + // Read all lines from the file into a List + String formatted = ""; + try { + List lines = Files.readAllLines(filepath); + assert (lines.size() == 1); + formatted = formatJsonString(lines.get(0)); + + } catch (IOException e) { + throw new RuntimeException(e); + } + + Path newfilepath = filepath.getParent().resolve("dependency-check-report2.json"); + try { + Files.delete(filepath); + } catch (IOException e) { + throw new RuntimeException(e); + } + try { + if (Files.exists(newfilepath)) { + Files.delete(newfilepath); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + try { + Files.write(newfilepath, formatted.getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static String formatJsonString(String jsonString) { + int level = 0; + StringBuilder formattedJson = new StringBuilder(); + int stringLength = jsonString.length(); + + for (int i = 0; i < stringLength; i++) { + char ch = jsonString.charAt(i); + + if (ch == '{' || ch == '[') { + formattedJson.append(ch).append("\n").append(getIndent(++level)); + } else if (ch == '}' || ch == ']') { + formattedJson.append("\n").append(getIndent(--level)).append(ch); + } else if (ch == ',') { + formattedJson.append(ch).append("\n").append(getIndent(level)); + } else { + formattedJson.append(ch); + } + } + + return formattedJson.toString(); + } + + private static String getIndent(int level) { + StringBuilder indent = new StringBuilder(); + for (int i = 0; i < level; i++) { + indent.append("\t"); + } + return indent.toString(); + } +} \ No newline at end of file diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java new file mode 100644 index 000000000..7f79c0047 --- /dev/null +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -0,0 +1,55 @@ +package com.devonfw.tools.security; + +import com.devonfw.tools.ide.os.SystemInfo; +import com.devonfw.tools.ide.os.SystemInfoImpl; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; +import org.owasp.dependencycheck.analyzer.AnalysisPhase; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.exception.InitializationException; + +import java.io.FileFilter; + +public class UrlAnalyzer extends AbstractFileTypeAnalyzer { + + // The file filter used to filter supported files. + private FileFilter fileFilter = null; + + public UrlAnalyzer() { + + fileFilter = new UrlFileFilter(); + + } + + @Override + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + System.out.println("analyzeDependency"); + System.out.println("analyzeDependency"); + } + + @Override + protected String getAnalyzerEnabledSettingKey() { + return null; + } + + @Override + protected FileFilter getFileFilter() { + return fileFilter; + } + + @Override + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { + + } + + @Override + public String getName() { + return null; + } + + @Override + public AnalysisPhase getAnalysisPhase() { + return null; + } +} diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java new file mode 100644 index 000000000..d02fc418b --- /dev/null +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -0,0 +1,23 @@ +package com.devonfw.tools.security; + +import com.devonfw.tools.ide.os.SystemInfo; +import com.devonfw.tools.ide.os.SystemInfoImpl; + +import java.io.FileFilter; +public class UrlFileFilter implements FileFilter { + + final private SystemInfo systemInfo; + private final String os; + + public UrlFileFilter() { + this.systemInfo = new SystemInfoImpl(); + this.os = this.systemInfo.getOs().toString(); + } + + @Override + public boolean accept(java.io.File pathname) { + boolean isUrlFile = pathname.toString().endsWith(".urls"); + boolean isCorrectOs = pathname.toString().contains(this.os); + return isUrlFile && isCorrectOs; + } +} From 5f162e257d7534e0c5578ccf41a23c2f48e9cbeb Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 14 Nov 2023 15:42:05 +0100 Subject: [PATCH 03/47] moved owasp dep from idecli pom to security pom, mor work in Main --- cli/pom.xml | 6 - .../tools/ide/tool/java/JavaUrlUpdater.java | 12 ++ .../ide/url/updater/AbstractUrlUpdater.java | 12 ++ .../tools/ide/url/updater/UpdateManager.java | 6 + security/pom.xml | 6 + .../java/com/devonfw/tools/security/Main.java | 125 +++++++++++------- 6 files changed, 111 insertions(+), 56 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index ffbd3895b..a938e7082 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -20,12 +20,6 @@ - - - org.owasp - dependency-check-core - 8.4.2 - org.apache.commons diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index e1b2f972c..c6a8714c7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -26,6 +26,18 @@ protected String mapVersion(String version) { return super.mapVersion(version); } + protected String getCPEVendor() { + return "eclipse"; + } + + protected String getCPEProduct() { + return "temurin"; + } + + + + + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index cb248d1cb..4258c6a5e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -97,6 +97,18 @@ protected final String getToolWithEdition() { return tool + "/" + edition; } + protected String getCPEVendor() { + return ""; + } + + protected String getCPEProduct() { + return ""; + } + + protected String mapUrlVersionToCPEVersion(String version) { + return version; + } + /** * Retrieves the response body from a given URL. * diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 14642205b..22d6b0a9c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -95,4 +95,10 @@ public void updateAll() { } } + public String getVendor(String tool) { + AbstractUrlUpdater matchedUpdater = (AbstractUrlUpdater) updaters.stream().filter(updater -> updater.getTool().equals(tool)).toArray()[0]; + return matchedUpdater.getCPEVendor(); +// updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst().ifPresent(AbstractUrlUpdater::getVendor); + } + } diff --git a/security/pom.xml b/security/pom.xml index a1da8b649..4838fe849 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -18,6 +18,12 @@ UTF-8 + + + org.owasp + dependency-check-core + 8.4.2 + com.devonfw.tools.IDEasy ide-cli diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/Main.java index 1e1680beb..090247325 100644 --- a/security/src/main/java/com/devonfw/tools/security/Main.java +++ b/security/src/main/java/com/devonfw/tools/security/Main.java @@ -1,12 +1,22 @@ package com.devonfw.tools.security; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.url.model.folder.UrlVersion; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.url.updater.UrlUpdater; import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.AnalysisPhase; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.dependency.*; import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.Settings; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -17,79 +27,94 @@ import java.util.List; public class Main { + // TODO is owasp dependence i main pomxlm correc tor should i move it to security pomxml + public static void main(String[] args) throws ReportException { - //TODO, wenn eine cve gefunden wird. dann in ide cli prompen und auch die cve sagen, damit der user selbst entschienden kann ob es vielleicht doch nicht eine false positive is. weil zb der vendor nicht so richtig gemached worden ist + // TODO edit depedency check properties file to switch off analysers, this file is currently read only + // TODO maybe this can be done in pom.xml + + //TODO, wenn eine cve gefunden wird. dann in ide cli prompten und auch die cve sagen, damit der user selbst entschienden kann ob es vielleicht doch nicht eine false positive is. weil zb der vendor nicht so richtig gemached worden ist // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock // why is this not in projects dir but in user dir? Settings settings = new Settings(); - Engine engine = new Engine(settings); + File dir; - // das brauche ich um die file endung zu akzeptieren - FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(); - // engine.getAnalyzers().add(myAnalyzer); - engine.getFileTypeAnalyzers().add(myAnalyzer); - List dependencyList = engine.scan("C:\\projects\\_ide\\myUrls"); - System.out.println("size of dependencylist is " + dependencyList.size()); - for (Dependency dependency : dependencyList) { - // TODO soll ich auch noch die ulr splitten und die zu evidence machen? - String filePath = dependency.getFilePath(); - Path parent = Paths.get(filePath).getParent(); - String tool = parent.getParent().getParent().getFileName().toString(); - String edition = parent.getParent().getFileName().toString(); - String version = parent.getFileName().toString(); + settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); - String vendor = ""; // maybe split url and take domain or second and third after / - // TODO is versions od dependency updated when adding evidence? + try (Engine engine = new Engine(settings)) { - Evidence productEvidence = new Evidence("mysoure", "myname", tool, Confidence.HIGH); - dependency.addEvidence(EvidenceType.PRODUCT, productEvidence); + // das brauche ich um die file endung zu akzeptieren + FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(); + // engine.getAnalyzers().add(myAnalyzer); + engine.getFileTypeAnalyzers().add(myAnalyzer); +// engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(new UrlAnalyzer()); + List dependencyList = engine.scan("C:\\projects\\_ide\\myUrls"); + System.out.println("size of dependencyList is " + dependencyList.size()); - Evidence editionEvidence = new Evidence("mysoure", "myname", edition, Confidence.HIGH); - dependency.addEvidence(EvidenceType.PRODUCT, editionEvidence); + // add my infos to dependency + for (Dependency dependency : dependencyList) { + // TODO soll ich auch noch die ulr splitten und die zu evidence machen? + String filePath = dependency.getFilePath(); + Path parent = Paths.get(filePath).getParent(); + String tool = parent.getParent().getParent().getFileName().toString(); + String edition = parent.getParent().getFileName().toString(); + String version = parent.getFileName().toString(); - Evidence versionEvidence = new Evidence("mysoure", "myname", version, Confidence.HIGH); - dependency.addEvidence(EvidenceType.VERSION, versionEvidence); - Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); - dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); + // TODO is versions od dependency updated when adding evidence? - // Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); - // dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); - // dependency.getAvailableVersions(); - } + // from the context I want to get the JavaUrlUpdater + // UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); + // String vendor = updateManager.getVendor("java"); - // TODO oder kann ich doch manche analyzer weg machen? - // welche sollen weg? - try { - engine.analyzeDependencies();// needed for db stuff which is private - for (Dependency dependency : engine.getDependencies()) { - engine.removeDependency(dependency); - for (EvidenceType type : EvidenceType.values()) { - for (Evidence evidence : dependency.getEvidence(type)) { - if (!evidence.getName().equals("myname")) { - dependency.removeEvidence(type, evidence); + Evidence productEvidence = new Evidence("mysoure", "myname", tool, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, productEvidence); + + Evidence editionEvidence = new Evidence("mysoure", "myname", edition, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, editionEvidence); + + Evidence versionEvidence = new Evidence("mysoure", "myname", version, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, versionEvidence); + + Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); + + + } + + // TODO oder kann ich doch manche analyzer weg machen? + // welche sollen weg? + try { + engine.analyzeDependencies();// needed for db stuff which is private + for (Dependency dependency : engine.getDependencies()) { + engine.removeDependency(dependency); + for (EvidenceType type : EvidenceType.values()) { + for (Evidence evidence : dependency.getEvidence(type)) { + if (!evidence.getName().equals("myname")) { + dependency.removeEvidence(type, evidence); + } } } + engine.addDependency(dependency); } - engine.addDependency(dependency); - } - } catch (ExceptionCollection e) { - throw new RuntimeException(e); - } + } catch (ExceptionCollection e) { + throw new RuntimeException(e); + } - // TODO dont do this with this method but try to do it by hand, since i cant seem to add my URL analyzer to the map of engine - // look at path and them extract name and version and vendor maybe from url - List exceptionsList = new ArrayList<>(); - ExceptionCollection exceptions = new ExceptionCollection(exceptionsList); + // TODO dont do this with this method but try to do it by hand, since i cant seem to add my URL analyzer to the map of engine + // look at path and them extract name and version and vendor maybe from url + List exceptionsList = new ArrayList<>(); + ExceptionCollection exceptions = new ExceptionCollection(exceptionsList); - File dir = new File("C:\\projects\\devonfw\\report"); - engine.writeReports("applicationName", "groupId", "artifactId", "version", dir, "JSON", exceptions); + dir = new File("C:\\projects\\devonfw\\report"); + engine.writeReports("applicationName", "groupId", "artifactId", "version", dir, "JSON", exceptions); + } String filename = dir.toString() + "\\dependency-check-report.json"; From e5c70e7a63ee379a9aeb0a4daf0eefce56c8bc63 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Wed, 15 Nov 2023 17:54:48 +0100 Subject: [PATCH 04/47] #103: url analyzer works and vulnerabilities are retrieved --- .../tools/ide/tool/java/JavaUrlUpdater.java | 17 +- .../ide/url/updater/AbstractUrlUpdater.java | 9 +- .../tools/ide/url/updater/UpdateManager.java | 35 +++- .../java/com/devonfw/tools/security/Main.java | 192 ++++-------------- .../devonfw/tools/security/UrlAnalyzer.java | 108 +++++++--- 5 files changed, 164 insertions(+), 197 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index c6a8714c7..ecad6ce41 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -26,17 +26,26 @@ protected String mapVersion(String version) { return super.mapVersion(version); } - protected String getCPEVendor() { + @Override + protected String getCpeVendor() { + + // return "vikwp"; return "eclipse"; } - protected String getCPEProduct() { + @Override + protected String getCpeProduct() { + + // return "vik_booking"; return "temurin"; } + @Override + protected String mapUrlVersionToCpeVersion(String version) { - - + // return "1.5.8"; + return version; + } @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 4258c6a5e..ded51f75c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -97,15 +97,18 @@ protected final String getToolWithEdition() { return tool + "/" + edition; } - protected String getCPEVendor() { + protected String getCpeVendor() { + return ""; } - protected String getCPEProduct() { + protected String getCpeProduct() { + return ""; } - protected String mapUrlVersionToCPEVersion(String version) { + protected String mapUrlVersionToCpeVersion(String version) { + return version; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 22d6b0a9c..70788f5e9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -57,13 +57,14 @@ public class UpdateManager extends AbstractProcessorWithTimeout { private final UrlRepository urlRepository; private final List updaters = Arrays.asList(new AndroidStudioUrlUpdater(), new AwsUrlUpdater(), - new AzureUrlUpdater(), new CobigenUrlUpdater(), new DockerDesktopUrlUpdater() , new DotNetUrlUpdater(), new EclipseCppUrlUpdater(), - new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(), new GcViewerUrlUpdater(), new GhUrlUpdater(), - new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijUrlUpdater(), - new JavaUrlUpdater(), new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), new KotlincNativeUrlUpdater(), - new LazyDockerUrlUpdater(), new MvnUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), - new PipUrlUpdater(), new PythonUrlUpdater(), new QuarkusUrlUpdater(), new DockerRancherDesktopUrlUpdater(), - new SonarUrlUpdater(), new TerraformUrlUpdater(), new TomcatUrlUpdater(), new VsCodeUrlUpdater()); + new AzureUrlUpdater(), new CobigenUrlUpdater(), new DockerDesktopUrlUpdater(), new DotNetUrlUpdater(), + new EclipseCppUrlUpdater(), new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(), new GcViewerUrlUpdater(), + new GhUrlUpdater(), new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), new GradleUrlUpdater(), + new HelmUrlUpdater(), new IntellijUrlUpdater(), new JavaUrlUpdater(), new JenkinsUrlUpdater(), + new JmcUrlUpdater(), new KotlincUrlUpdater(), new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), + new MvnUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), new PipUrlUpdater(), + new PythonUrlUpdater(), new QuarkusUrlUpdater(), new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(), + new TerraformUrlUpdater(), new TomcatUrlUpdater(), new VsCodeUrlUpdater()); /** * The constructor. @@ -95,10 +96,22 @@ public void updateAll() { } } - public String getVendor(String tool) { - AbstractUrlUpdater matchedUpdater = (AbstractUrlUpdater) updaters.stream().filter(updater -> updater.getTool().equals(tool)).toArray()[0]; - return matchedUpdater.getCPEVendor(); -// updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst().ifPresent(AbstractUrlUpdater::getVendor); + public String getCpeVendor(String tool) { + + return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() + .map(AbstractUrlUpdater::getCpeVendor).orElse(null); + } + + public String getCpeProduct(String tool) { + + return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() + .map(AbstractUrlUpdater::getCpeProduct).orElse(null); + } + + public String mapUrlVersionToCpeVersion(String tool, String urlVersion) { + + return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() + .map(updater -> updater.mapUrlVersionToCpeVersion(urlVersion)).orElse(null); } } diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/Main.java index 090247325..1f5ef29e0 100644 --- a/security/src/main/java/com/devonfw/tools/security/Main.java +++ b/security/src/main/java/com/devonfw/tools/security/Main.java @@ -1,183 +1,79 @@ package com.devonfw.tools.security; +import java.util.List; +import java.util.stream.Collectors; + import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.context.IdeContextConsole; import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.tool.ToolCommandlet; -import com.devonfw.tools.ide.url.model.folder.UrlVersion; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; -import com.devonfw.tools.ide.url.updater.UrlUpdater; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AnalysisPhase; +import org.owasp.dependencycheck.analyzer.FileNameAnalyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.dependency.*; import org.owasp.dependencycheck.exception.ExceptionCollection; -import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.Settings; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - public class Main { - // TODO is owasp dependence i main pomxlm correc tor should i move it to security pomxml - - public static void main(String[] args) throws ReportException { - - // TODO edit depedency check properties file to switch off analysers, this file is currently read only + public static void main(String[] args) { + // TODO edit dependency check properties file to switch off analysers, this file is currently read only // TODO maybe this can be done in pom.xml + // or simply remove it like FileNameAnalyzer was removed - //TODO, wenn eine cve gefunden wird. dann in ide cli prompten und auch die cve sagen, damit der user selbst entschienden kann ob es vielleicht doch nicht eine false positive is. weil zb der vendor nicht so richtig gemached worden ist + // TODO: note settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); - // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock + // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock // why is this not in projects dir but in user dir? Settings settings = new Settings(); - File dir; - - - settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); - - - try (Engine engine = new Engine(settings)) { - - // das brauche ich um die file endung zu akzeptieren - FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(); - // engine.getAnalyzers().add(myAnalyzer); - engine.getFileTypeAnalyzers().add(myAnalyzer); -// engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(new UrlAnalyzer()); - List dependencyList = engine.scan("C:\\projects\\_ide\\myUrls"); - System.out.println("size of dependencyList is " + dependencyList.size()); - - // add my infos to dependency - for (Dependency dependency : dependencyList) { - // TODO soll ich auch noch die ulr splitten und die zu evidence machen? - String filePath = dependency.getFilePath(); - Path parent = Paths.get(filePath).getParent(); - String tool = parent.getParent().getParent().getFileName().toString(); - String edition = parent.getParent().getFileName().toString(); - String version = parent.getFileName().toString(); - - - // TODO is versions od dependency updated when adding evidence? - - // from the context I want to get the JavaUrlUpdater - // UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); - // String vendor = updateManager.getVendor("java"); - - Evidence productEvidence = new Evidence("mysoure", "myname", tool, Confidence.HIGH); - dependency.addEvidence(EvidenceType.PRODUCT, productEvidence); - - Evidence editionEvidence = new Evidence("mysoure", "myname", edition, Confidence.HIGH); - dependency.addEvidence(EvidenceType.PRODUCT, editionEvidence); - - Evidence versionEvidence = new Evidence("mysoure", "myname", version, Confidence.HIGH); - dependency.addEvidence(EvidenceType.VERSION, versionEvidence); - - Evidence vendorEvidence = new Evidence("mysoure", "myname", "oracle", Confidence.HIGH); - dependency.addEvidence(EvidenceType.VENDOR, vendorEvidence); + Engine engine = new Engine(settings); // doesn't work with "try with resource" + IdeContext ideContext = new IdeContextConsole(IdeLogLevel.INFO, null, false); + UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); - } - - // TODO oder kann ich doch manche analyzer weg machen? - // welche sollen weg? - try { - engine.analyzeDependencies();// needed for db stuff which is private - for (Dependency dependency : engine.getDependencies()) { - engine.removeDependency(dependency); - for (EvidenceType type : EvidenceType.values()) { - for (Evidence evidence : dependency.getEvidence(type)) { - if (!evidence.getName().equals("myname")) { - dependency.removeEvidence(type, evidence); - } - } - } - engine.addDependency(dependency); - } - - } catch (ExceptionCollection e) { - throw new RuntimeException(e); - } - - // TODO dont do this with this method but try to do it by hand, since i cant seem to add my URL analyzer to the map of engine - // look at path and them extract name and version and vendor maybe from url - List exceptionsList = new ArrayList<>(); - ExceptionCollection exceptions = new ExceptionCollection(exceptionsList); - - dir = new File("C:\\projects\\devonfw\\report"); - engine.writeReports("applicationName", "groupId", "artifactId", "version", dir, "JSON", exceptions); - } + FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); + engine.getFileTypeAnalyzers().add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION) + .removeIf(analyzer -> analyzer instanceof FileNameAnalyzer); + engine.scan("C:\\projects\\_ide\\myUrls"); - String filename = dir.toString() + "\\dependency-check-report.json"; - Path filepath = Paths.get(filename); - // Read all lines from the file into a List - String formatted = ""; - try { - List lines = Files.readAllLines(filepath); - assert (lines.size() == 1); - formatted = formatJsonString(lines.get(0)); - - } catch (IOException e) { - throw new RuntimeException(e); - } - - Path newfilepath = filepath.getParent().resolve("dependency-check-report2.json"); try { - Files.delete(filepath); - } catch (IOException e) { + engine.analyzeDependencies();// needed for db stuff which is private + } catch (ExceptionCollection e) { throw new RuntimeException(e); } - try { - if (Files.exists(newfilepath)) { - Files.delete(newfilepath); + float minV2Severity = 0.0f; + float minV3Severity = 0.0f; + + for (Dependency dependency : engine.getDependencies()) { + List noSeverity = dependency.getVulnerabilities(true).stream() + .filter(v -> v.getCvssV2() == null && v.getCvssV3() == null).collect(Collectors.toList()); + List onlyV2Severity = dependency.getVulnerabilities(true).stream() + .filter(v -> v.getCvssV2() != null && v.getCvssV3() == null).collect(Collectors.toList()); + List hasV3Severity = dependency.getVulnerabilities(true).stream() + .filter(v -> v.getCvssV3() != null).collect(Collectors.toList()); + + if (!noSeverity.isEmpty()) { + System.out.println("no severity is not empty: " + dependency.getFileName()); + System.exit(1); } - } catch (IOException e) { - throw new RuntimeException(e); - } - try { - Files.write(newfilepath, formatted.getBytes()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + onlyV2Severity.removeIf(v -> v.getCvssV2().getScore() < minV3Severity); + hasV3Severity.removeIf(v -> v.getCvssV3().getBaseScore() < minV2Severity); - public static String formatJsonString(String jsonString) { - int level = 0; - StringBuilder formattedJson = new StringBuilder(); - int stringLength = jsonString.length(); - - for (int i = 0; i < stringLength; i++) { - char ch = jsonString.charAt(i); - - if (ch == '{' || ch == '[') { - formattedJson.append(ch).append("\n").append(getIndent(++level)); - } else if (ch == '}' || ch == ']') { - formattedJson.append("\n").append(getIndent(--level)).append(ch); - } else if (ch == ',') { - formattedJson.append(ch).append("\n").append(getIndent(level)); - } else { - formattedJson.append(ch); - } - } + System.out.println("There were vulnerabilities found in: " + dependency.getFileName()); + onlyV2Severity.forEach(v -> System.out.println("V2: " + v.getName() + " " + v.getCvssV2().getScore())); + hasV3Severity.forEach(v -> System.out.println("V3: " + v.getName() + " " + v.getCvssV3().getBaseScore())); - return formattedJson.toString(); - } + // TODO read min levels from console + // TODO list all vulnerabilities, so maybe description, all fields of cvssv3 and cvssv2, cve name, source, + // url of vulnerabilityIds, and vulnerableSoftware + // TODO take all vulnerabilities, or ask for another min level und update the numbers of vulnerabilities + // TODO write vulnerabilities to file -> new format? that includes CVE name? - private static String getIndent(int level) { - StringBuilder indent = new StringBuilder(); - for (int i = 0; i < level; i++) { - indent.append("\t"); } - return indent.toString(); } } \ No newline at end of file diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 7f79c0047..dc384d822 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -1,55 +1,101 @@ package com.devonfw.tools.security; -import com.devonfw.tools.ide.os.SystemInfo; -import com.devonfw.tools.ide.os.SystemInfoImpl; +import java.io.FileFilter; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.log.IdeLogLevel; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; -import java.io.FileFilter; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.url.updater.UpdateManager; public class UrlAnalyzer extends AbstractFileTypeAnalyzer { - // The file filter used to filter supported files. - private FileFilter fileFilter = null; + // The file filter is used to filter supported files. + private FileFilter fileFilter = null; - public UrlAnalyzer() { + private static final String ANALYZER_NAME = "UrlAnalyzer"; - fileFilter = new UrlFileFilter(); + private final UpdateManager updateManager; + public UrlAnalyzer(UpdateManager updateManager) { - } + fileFilter = new UrlFileFilter(); + this.updateManager = updateManager; + } - @Override - protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { - System.out.println("analyzeDependency"); - System.out.println("analyzeDependency"); - } + @Override + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { - @Override - protected String getAnalyzerEnabledSettingKey() { - return null; - } - @Override - protected FileFilter getFileFilter() { - return fileFilter; - } - @Override - protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { + String filePath = dependency.getFilePath(); + Path parent = Paths.get(filePath).getParent(); + String tool = parent.getParent().getParent().getFileName().toString(); + String edition = parent.getParent().getFileName().toString(); - } + String source = "UrlAnalyzer"; - @Override - public String getName() { - return null; - } + // adding vendor evidence + String vendor = updateManager.getCpeVendor(tool); + Evidence evidence = new Evidence(source, "CpeVendor", vendor, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, evidence); - @Override - public AnalysisPhase getAnalysisPhase() { - return null; + // adding product evidence + String product = updateManager.getCpeProduct(tool); + if (product == null) { + product = tool; } + evidence = new Evidence(source, "CpeProduct", product, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, evidence); + + // adding version evidence + String version = updateManager.mapUrlVersionToCpeVersion(tool, parent.getFileName().toString()); + evidence = new Evidence(source, "CpeVersion", version, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, evidence); + } + + @Override + public boolean isEnabled() { + + return true; + } + + @Override + protected String getAnalyzerEnabledSettingKey() { + // whether this Analyzer is enabled or not is not configurable but fixed by isEnabled() + return null; + } + + @Override + protected FileFilter getFileFilter() { + + return fileFilter; + } + + @Override + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { + // nothing to prepare here + } + + @Override + public String getName() { + + return ANALYZER_NAME; + } + + @Override + public AnalysisPhase getAnalysisPhase() { + + return AnalysisPhase.INFORMATION_COLLECTION; + } } From 34febf5bc04f312cd9d3b14f80c8127dcb6ddfd4 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 16 Nov 2023 18:09:26 +0100 Subject: [PATCH 05/47] #103: Security Entry and determination of version Range for vulnerability --- .../ide/url/model/file/SecurityEntry.java | 17 +++ .../java/com/devonfw/tools/security/Main.java | 118 +++++++++++++++--- 2 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java new file mode 100644 index 000000000..dba1847dc --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java @@ -0,0 +1,17 @@ +package com.devonfw.tools.ide.url.model.file; + +import com.devonfw.tools.ide.version.VersionRange; + +/** + * A simple container with the information about a security entry. + * + * @param versionRange The version range of affected versions. + * @param severity The severity of the security issue (0.0 - 10.0). + * @param severityVersion The version of the severity. As of November 2023 its either v2 or v3. + * @param cveName The CVE name. + * @param Description The description of the security issue. + * @param url The url to the security issue. + */ +public record SecurityEntry(VersionRange versionRange, double severity, String severityVersion, String cveName, + String Description, String url) { +} diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/Main.java index 1f5ef29e0..22c6daaa9 100644 --- a/security/src/main/java/com/devonfw/tools/security/Main.java +++ b/security/src/main/java/com/devonfw/tools/security/Main.java @@ -1,12 +1,20 @@ package com.devonfw.tools.security; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.json.mapping.JsonMapping; import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.url.model.file.SecurityEntry; import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.version.VersionIdentifier; +import com.devonfw.tools.ide.version.VersionRange; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AnalysisPhase; import org.owasp.dependencycheck.analyzer.FileNameAnalyzer; @@ -49,31 +57,101 @@ public static void main(String[] args) { float minV3Severity = 0.0f; for (Dependency dependency : engine.getDependencies()) { - List noSeverity = dependency.getVulnerabilities(true).stream() - .filter(v -> v.getCvssV2() == null && v.getCvssV3() == null).collect(Collectors.toList()); - List onlyV2Severity = dependency.getVulnerabilities(true).stream() - .filter(v -> v.getCvssV2() != null && v.getCvssV3() == null).collect(Collectors.toList()); - List hasV3Severity = dependency.getVulnerabilities(true).stream() - .filter(v -> v.getCvssV3() != null).collect(Collectors.toList()); - - if (!noSeverity.isEmpty()) { - System.out.println("no severity is not empty: " + dependency.getFileName()); - System.exit(1); - } - - onlyV2Severity.removeIf(v -> v.getCvssV2().getScore() < minV3Severity); - hasV3Severity.removeIf(v -> v.getCvssV3().getBaseScore() < minV2Severity); + Set vulnerabilities = dependency.getVulnerabilities(true); - System.out.println("There were vulnerabilities found in: " + dependency.getFileName()); - onlyV2Severity.forEach(v -> System.out.println("V2: " + v.getName() + " " + v.getCvssV2().getScore())); - hasV3Severity.forEach(v -> System.out.println("V3: " + v.getName() + " " + v.getCvssV3().getBaseScore())); + String filePath = dependency.getFilePath(); + Path parent = Paths.get(filePath).getParent(); + String tool = parent.getParent().getParent().getFileName().toString(); + String edition = parent.getParent().getFileName().toString(); + List sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); - // TODO read min levels from console + // TODO read min levels from console or args[] // TODO list all vulnerabilities, so maybe description, all fields of cvssv3 and cvssv2, cve name, source, // url of vulnerabilityIds, and vulnerableSoftware // TODO take all vulnerabilities, or ask for another min level und update the numbers of vulnerabilities // TODO write vulnerabilities to file -> new format? that includes CVE name? + // List foundVulnerabilities = new ArrayList<>(); + + for (Vulnerability vulnerability : vulnerabilities) { + + if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { + throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); + } + boolean hasV3Severity = vulnerability.getCvssV3() != null; + double severity = hasV3Severity + ? vulnerability.getCvssV3().getBaseScore() + : vulnerability.getCvssV2().getScore(); + String severityVersion = hasV3Severity ? "v3" : "v2"; + String cveName = vulnerability.getName(); + String description = vulnerability.getDescription(); + + boolean toLowSeverity = hasV3Severity ? severity < minV3Severity : severity < minV2Severity; + if (toLowSeverity) { + continue; + } + + VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, vulnerability); + SecurityEntry securityEntry = new SecurityEntry(versionRange, severity, severityVersion, cveName, description, + null); + ObjectMapper mapper = JsonMapping.create(); + try { + String jsonString = mapper.writeValueAsString(securityEntry); + System.out.println(jsonString); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } } + engine.close(); } -} \ No newline at end of file + + static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, String vStartIncluding, + String vEndIncluding, String vEndExcluding) { + + VersionIdentifier max = null; + if (vEndIncluding != null) { + max = VersionIdentifier.of(vEndIncluding); // this allows that max is not part of the available versions, this has no impact on the contains method but maybe confusing + } else if (vEndExcluding != null) { + VersionIdentifier end = VersionIdentifier.of(vEndExcluding); + for (VersionIdentifier version : sortedVersions) { + if (version.isLess(end)) { + max = version; + break; + } + } + } + + VersionIdentifier min = null; + if (vStartIncluding != null) { + min = VersionIdentifier.of(vStartIncluding); + } else if (vStartExcluding != null) { + for (int i = sortedVersions.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedVersions.get(i); + if (version.isGreater(VersionIdentifier.of(vStartExcluding))) { + min = version; + break; + } + } + } + return new VersionRange(min, max); + } + + static VersionRange getVersionRangeFromVulnerability(List sortedVersions, + Vulnerability vulnerability) { + + VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); + String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); + String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); + String vStartExcluding = matchedVulnerableSoftware.getVersionStartExcluding(); + String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); + + if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { + throw new RuntimeException("Vulnerability without version range found: " + vulnerability.getName()); + } + + return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, + vEndExcluding); + } +} + From ae0558b3e87f6e6183780c71538553cac6dee6ef Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Fri, 24 Nov 2023 21:11:26 +0100 Subject: [PATCH 06/47] #103: writing security json file --- .../tools/ide/tool/LocalToolCommandlet.java | 4 + .../tools/ide/tool/ToolCommandlet.java | 42 ++-- .../ide/url/model/file/SecurityEntry.java | 17 -- .../model/file/json/UrlSecurityJsonFile.java | 181 ++++++++++++++++ .../ide/url/model/folder/UrlEdition.java | 12 + .../ide/url/updater/AbstractUrlUpdater.java | 9 +- .../tools/ide/url/updater/UpdateManager.java | 6 + .../tools/ide/version/VersionRange.java | 34 ++- .../tools/ide/version/VersionRangeTest.java | 28 +++ .../java/com/devonfw/tools/security/Main.java | 205 +++++++++++------- .../devonfw/tools/security/UrlAnalyzer.java | 23 +- 11 files changed, 440 insertions(+), 121 deletions(-) delete mode 100644 cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 8505dead7..36e6c8566 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -63,6 +63,10 @@ protected boolean doInstall(boolean silent) { // install configured version of our tool in the software repository if not already installed ToolInstallation installation = installInRepo(configuredVersion); + VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion); + + System.out.println("Selected version: " + selectedVersion); + // check if we already have this version installed (linked) locally in IDE_HOME/software VersionIdentifier installedVersion = getInstalledVersion(); VersionIdentifier resolvedVersion = installation.resolvedVersion(); 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 cd7636099..abb5b1028 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 @@ -16,6 +16,7 @@ import com.devonfw.tools.ide.environment.EnvironmentVariablesType; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.TarCompression; +import com.devonfw.tools.ide.json.mapping.JsonMapping; import com.devonfw.tools.ide.os.MacOsHelper; import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessErrorHandling; @@ -23,6 +24,7 @@ import com.devonfw.tools.ide.url.model.file.UrlSecurityFile; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.version.VersionIdentifier; +import com.fasterxml.jackson.databind.ObjectMapper; /** * {@link Commandlet} for a tool integrated into the IDE. @@ -172,7 +174,8 @@ public boolean install(boolean silent) { return doInstall(silent); } - protected String question(String question, String ... options) { + protected String question(String question, String... options) { + question += " Do you want to"; for (int i = 0; i < options.length - 1; i++) { options[i] += " or"; @@ -180,16 +183,19 @@ protected String question(String question, String ... options) { options[options.length - 1] += "?"; return this.context.question(question, options); } + protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) { // TODO vielleicht security file auch neu als json file wenn 1.2 > 2.9 nicht ausreicht - // TODO vielleicht auch zusätzlich das tool scannen sobald es installiert ist, - // TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n"; + // TODO if no version is save, find a version that has lowest security risk, or suggest multiple ones, such that the + // user can choose + UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile(); - VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), - configuredVersion); + ObjectMapper mapper = JsonMapping.create(); + + VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); if (!securityFile.contains(current)) { return configuredVersion; } @@ -201,7 +207,8 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured int currentVersionIndex = allVersions.indexOf(current); - // VersionIdentifier nextVersion = currentVersionIndex == 0 ? null : allVersions.get(allVersions.indexOf(currentVersion) - 1); + // VersionIdentifier nextVersion = currentVersionIndex == 0 ? null : + // allVersions.get(allVersions.indexOf(currentVersion) - 1); VersionIdentifier nextSafe = null; for (int i = currentVersionIndex - 1; i >= 0; i--) { if (!securityFile.contains(allVersions.get(i))) { @@ -225,11 +232,12 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured } String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is installed, " - + "which is has a vulnerability."; + + "which is has a vulnerability:\n" + " TODOODODO" + "\n\n"; + String stay = "stay with the current unsafe version (" + current + ")"; String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")"; - String installNextSafe = "install the next safe version (" + nextSafe+ ")"; + String installNextSafe = "install the next safe version (" + nextSafe + ")"; if (current.equals(latest)) { String answer = question(currentIsUnsafe, stay, installLatestSafe); @@ -237,25 +245,23 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured } else if (nextSafe == null) { // TODO also allow selection of next or previous version, even if they are unsafe? - String answer = question(currentIsUnsafe + " All newer versions are also not safe.", - stay, installLatestSafe); + String answer = question(currentIsUnsafe + " All newer versions are also not safe.", stay, installLatestSafe); return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe.equals(latest)) { - String answer = question( currentIsUnsafe + " Of the newer versions, only the latest is safe.", - stay, installSafeLatest); + String answer = question(currentIsUnsafe + " Of the newer versions, only the latest is safe.", stay, + installSafeLatest); return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe.equals(latestSafe)) { - String answer = question(currentIsUnsafe +" Of the newer versions, only the version " - + nextSafe + " is safe.", stay, "Install the safe version (" + nextSafe + ")"); + String answer = question(currentIsUnsafe + " Of the newer versions, only the version " + nextSafe + " is safe.", + stay, "Install the safe version (" + nextSafe + ")"); return answer.startsWith(stay) ? current : nextSafe; } else { if (latest.equals(latestSafe)) { String answer = question(currentIsUnsafe, stay, installNextSafe, installSafeLatest); - return answer.startsWith(stay) ? current - : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; + return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; } else { String answer = question(currentIsUnsafe, stay, installNextSafe, installLatestSafe); @@ -263,8 +269,8 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured } } - // VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion); - // setVersion(chosenVersion, silent); + // VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion); + // setVersion(chosenVersion, silent); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java deleted file mode 100644 index dba1847dc..000000000 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.devonfw.tools.ide.url.model.file; - -import com.devonfw.tools.ide.version.VersionRange; - -/** - * A simple container with the information about a security entry. - * - * @param versionRange The version range of affected versions. - * @param severity The severity of the security issue (0.0 - 10.0). - * @param severityVersion The version of the severity. As of November 2023 its either v2 or v3. - * @param cveName The CVE name. - * @param Description The description of the security issue. - * @param url The url to the security issue. - */ -public record SecurityEntry(VersionRange versionRange, double severity, String severityVersion, String cveName, - String Description, String url) { -} diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java new file mode 100644 index 000000000..d7305ca4a --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java @@ -0,0 +1,181 @@ +package com.devonfw.tools.ide.url.model.file.json; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.ide.json.mapping.JsonMapping; +import com.devonfw.tools.ide.url.model.file.AbstractUrlFile; +import com.devonfw.tools.ide.url.model.folder.UrlEdition; +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; + +public class UrlSecurityJsonFile extends AbstractUrlFile { + + /** {@link #getName() Name} of security json file. */ + public static final String FILENAME_SECURITY = "security.json"; + + private static final Logger LOG = LoggerFactory.getLogger(UrlSecurityJsonFile.class); + + List matches; + + /** + * The constructor. + * + * @param parent the {@link #getParent() parent folder}. + */ + public UrlSecurityJsonFile(UrlEdition parent) { + + super(parent, FILENAME_SECURITY); + this.matches = new ArrayList<>(); + } + + public boolean addSecurityMatch(VersionRange versionRange, double severity, String severityVersion, String cveName, + String description, String nistUrl, List referenceUrl) { + + UrlSecurityWarning newWarning = new UrlSecurityWarning(severity, severityVersion, cveName, description, nistUrl, + referenceUrl); + for (UrlSecurityMatch match : matches) { + if (match.getVersionRange().equals(versionRange)) { + boolean added = match.addWarning(newWarning); + this.modified = this.modified || added; + return added; + } + } + UrlSecurityMatch newMatch = new UrlSecurityMatch(versionRange); + newMatch.addWarning(newWarning); + this.modified = true; + return matches.add(newMatch); + } + + public boolean removeSecurityMatch(VersionRange versionRange) { + + for (UrlSecurityMatch match : matches) { + if (match.getVersionRange().equals(versionRange)) { + boolean removed = matches.remove(match); + this.modified = this.modified || removed; + return removed; + } + } + return false; + } + + public boolean contains(VersionIdentifier version) { + + for (UrlSecurityMatch match : matches) { + if (match.getVersionRange().contains(version)) { + return true; + } + } + return false; + } + + public Set getSecurityWarnings(VersionIdentifier version) { + + Set warnings = new HashSet<>(); + for (UrlSecurityMatch match : matches) { + if (match.getVersionRange().contains(version)) { + warnings.addAll(match.getWarnings()); + } + } + return warnings; + } + + public void clearSecurityMatches() { + + this.matches.clear(); + } + + @Override + protected void doLoad() { + + if (!Files.exists(getPath())) { + return; + } + ObjectMapper mapper = JsonMapping.create(); + try { + matches = mapper.readValue(getPath().toFile(), new TypeReference>() { + }); + } catch (IOException e) { + throw new IllegalStateException("The UrlSecurityJsonFile " + getPath() + " could not be parsed.", e); + } + } + + @Override + protected void doSave() { + + Path path = getPath(); + ObjectMapper mapper = JsonMapping.create(); + + if (this.matches.isEmpty() && !Files.exists(path)) { + return; + } + + String jsonString; + try { + jsonString = mapper.writeValueAsString(matches); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + try (BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { + bw.write(jsonString + "\n"); + } catch (IOException e) { + throw new IllegalStateException("Failed to save file " + path, e); + } + } +} + +class UrlSecurityMatch { + private final VersionRange versionRange; + + private final Set warnings; + + public UrlSecurityMatch() { + + // this constructor is needed for jackson deserialization + this.versionRange = null; + this.warnings = new HashSet<>(); + } + + public UrlSecurityMatch(VersionRange versionRange) { + + this.versionRange = versionRange; + this.warnings = new HashSet<>(); + } + + public VersionRange getVersionRange() { + + return versionRange; + } + + public Set getWarnings() { + + return warnings; + } + + public boolean addWarning(UrlSecurityWarning warning) { + + return this.warnings.add(warning); + } + +} + +// severity could be java.math.BigDecimal; instead of double (unsing BigDecimal("123.4").setScale(1, +// BigDecimal.ROUND_HALF_UP);) +record UrlSecurityWarning(double severity, String severityVersion, String cveName, String description, String nistUrl, + List referenceUrl) { +}; \ No newline at end of file diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java index a9390e232..b3f0fdea3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java @@ -2,6 +2,7 @@ import com.devonfw.tools.ide.url.model.AbstractUrlFolderWithParent; import com.devonfw.tools.ide.url.model.file.UrlSecurityFile; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; /** * An {@link UrlFolder} representing the actual edition of a {@link UrlTool}. The default edition may have the same @@ -12,6 +13,8 @@ public class UrlEdition extends AbstractUrlFolderWithParent private UrlSecurityFile securityFile; + private UrlSecurityJsonFile securityJsonFile; + /** * The constructor. * @@ -48,6 +51,15 @@ public UrlSecurityFile getSecurityFile() { return this.securityFile; } + public UrlSecurityJsonFile getSecurityJsonFile() { + + if (this.securityJsonFile == null) { + this.securityJsonFile = new UrlSecurityJsonFile(this); + this.securityJsonFile.load(false); + } + return this.securityJsonFile; + } + @Override public void save() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index ded51f75c..7874bc47f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -99,12 +99,17 @@ protected final String getToolWithEdition() { protected String getCpeVendor() { - return ""; + return null; } protected String getCpeProduct() { - return ""; + return null; + } + + protected String getCpeEdition() { + + return null; } protected String mapUrlVersionToCpeVersion(String version) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 70788f5e9..708e63304 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -108,6 +108,12 @@ public String getCpeProduct(String tool) { .map(AbstractUrlUpdater::getCpeProduct).orElse(null); } + public String getCpeEdition(String tool) { + + return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() + .map(AbstractUrlUpdater::getCpeEdition).orElse(null); + } + public String mapUrlVersionToCpeVersion(String tool, String urlVersion) { return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index d09021a2b..656117531 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -1,5 +1,8 @@ package com.devonfw.tools.ide.version; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + /** * Container for a range of versions. */ @@ -13,8 +16,9 @@ public final class VersionRange implements Comparable { * The constructor. * * @param min the {@link #getMin() minimum}. - * @param max the {@link #getMax() maximum}. + * @param max the {@link #getMax() maximum} (including). */ + public VersionRange(VersionIdentifier min, VersionIdentifier max) { super(); @@ -25,6 +29,7 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) { /** * @return the minimum {@link VersionIdentifier} or {@code null} for no lower bound. */ + // @JsonBackReference public VersionIdentifier getMin() { return this.min; @@ -33,6 +38,7 @@ public VersionIdentifier getMin() { /** * @return the maximum {@link VersionIdentifier} or {@code null} for no upper bound. */ + // @JsonBackReference public VersionIdentifier getMax() { return this.max; @@ -73,6 +79,31 @@ public int compareTo(VersionRange o) { } @Override + public boolean equals(Object obj) { + + if (this == obj) + return true; + + if (obj == null || getClass() != obj.getClass()) + return false; + + VersionRange o = (VersionRange) obj; + + if (this.min == null && this.max == null) { + return o.min == null && o.max == null; + } + if (this.min == null) { + return o.min == null && this.max.equals(o.max); + } + if (this.max == null) { + return this.min.equals(o.min) && o.max == null; + } + return this.min.equals(o.min) && this.max.equals(o.max); + + } + + @Override + @JsonValue public String toString() { StringBuilder sb = new StringBuilder(); @@ -90,6 +121,7 @@ public String toString() { * @param value the {@link #toString() string representation} of a {@link VersionRange} to parse. * @return the parsed {@link VersionRange}. */ + @JsonCreator public static VersionRange of(String value) { int index = value.indexOf('>'); diff --git a/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java new file mode 100644 index 000000000..83f9209b8 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java @@ -0,0 +1,28 @@ +package com.devonfw.tools.ide.version; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class VersionRangeTest extends Assertions { + @Test + void testEquals() { + + assertThat(VersionRange.of("1.2>")).isEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("1.2>3")).isEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of(">3")).isEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of(">")).isEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isEqualTo(VersionRange.of("8u302b08>11.0.14_9")); + + assertThat(VersionRange.of("1>")).isNotEqualTo(null); + assertThat(VersionRange.of("1.2>")).isNotEqualTo(VersionRange.of("1>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("1.2>")).isNotEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of(">")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.15_9")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.14_0")); + + } +} diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/Main.java index 22c6daaa9..d78fff2e5 100644 --- a/security/src/main/java/com/devonfw/tools/security/Main.java +++ b/security/src/main/java/com/devonfw/tools/security/Main.java @@ -4,122 +4,170 @@ import java.nio.file.Paths; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.context.IdeContextConsole; -import com.devonfw.tools.ide.json.mapping.JsonMapping; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.url.model.file.SecurityEntry; -import com.devonfw.tools.ide.url.updater.UpdateManager; -import com.devonfw.tools.ide.version.VersionIdentifier; -import com.devonfw.tools.ide.version.VersionRange; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AnalysisPhase; import org.owasp.dependencycheck.analyzer.FileNameAnalyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; -import org.owasp.dependencycheck.dependency.*; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Reference; +import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; +import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.version.VersionIdentifier; +import com.devonfw.tools.ide.version.VersionRange; public class Main { + + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + private static final String CVE_BASE_URL = "https://nvd.nist.gov/vuln/detail/"; + + private static double minV2Severity; + + private static double minV3Severity; + public static void main(String[] args) { - // TODO edit dependency check properties file to switch off analysers, this file is currently read only - // TODO maybe this can be done in pom.xml - // or simply remove it like FileNameAnalyzer was removed - // TODO: note settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); + if (args.length != 2) { + throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); + } + try { + minV2Severity = Double.parseDouble(args[0]); + minV3Severity = Double.parseDouble(args[1]); + } catch (NumberFormatException e) { + throw new RuntimeException("These two args could not be parsed as double"); + } + run(minV2Severity, minV3Severity); - // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock - // why is this not in projects dir but in user dir? + } - Settings settings = new Settings(); - Engine engine = new Engine(settings); // doesn't work with "try with resource" + private static void run(double minV2Severity, double minV3Severity) { IdeContext ideContext = new IdeContextConsole(IdeLogLevel.INFO, null, false); - UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); - FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); - engine.getFileTypeAnalyzers().add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION) - .removeIf(analyzer -> analyzer instanceof FileNameAnalyzer); + // TODO edit dependency check properties file to switch off analysers, this file is currently read only + // TODO maybe this can be done in pom.xml + // or simply remove it like FileNameAnalyzer was removed - engine.scan("C:\\projects\\_ide\\myUrls"); + // note: settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); - try { - engine.analyzeDependencies();// needed for db stuff which is private - } catch (ExceptionCollection e) { - throw new RuntimeException(e); - } - float minV2Severity = 0.0f; - float minV3Severity = 0.0f; + // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock + // why is this not in projects dir but in user dir? - for (Dependency dependency : engine.getDependencies()) { - Set vulnerabilities = dependency.getVulnerabilities(true); + Dependency[] dependencies = getDependenciesWithVulnerabilities(ideContext); + + for (Dependency dependency : dependencies) { String filePath = dependency.getFilePath(); Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); - List sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); - // TODO read min levels from console or args[] - // TODO list all vulnerabilities, so maybe description, all fields of cvssv3 and cvssv2, cve name, source, - // url of vulnerabilityIds, and vulnerableSoftware - // TODO take all vulnerabilities, or ask for another min level und update the numbers of vulnerabilities - // TODO write vulnerabilities to file -> new format? that includes CVE name? + UrlSecurityJsonFile securityFile = ideContext.getUrls().getEdition(tool, edition).getSecurityJsonFile(); + securityFile.clearSecurityMatches(); - // List foundVulnerabilities = new ArrayList<>(); + List sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); + Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { + addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedVersions); + } + securityFile.save(); + } + } - if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { - throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); - } - boolean hasV3Severity = vulnerability.getCvssV3() != null; - double severity = hasV3Severity - ? vulnerability.getCvssV3().getBaseScore() - : vulnerability.getCvssV2().getScore(); - String severityVersion = hasV3Severity ? "v3" : "v2"; - String cveName = vulnerability.getName(); - String description = vulnerability.getDescription(); - - boolean toLowSeverity = hasV3Severity ? severity < minV3Severity : severity < minV2Severity; - if (toLowSeverity) { - continue; - } + private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, + List sortedVersions) { - VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, vulnerability); - SecurityEntry securityEntry = new SecurityEntry(versionRange, severity, severityVersion, cveName, description, - null); - ObjectMapper mapper = JsonMapping.create(); - try { - String jsonString = mapper.writeValueAsString(securityEntry); - System.out.println(jsonString); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } + if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { + throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); } - engine.close(); + boolean hasV3Severity = vulnerability.getCvssV3() != null; + double severity = hasV3Severity ? vulnerability.getCvssV3().getBaseScore() : vulnerability.getCvssV2().getScore(); + String severityVersion = hasV3Severity ? "v3" : "v2"; + String cveName = vulnerability.getName(); + String description = vulnerability.getDescription(); + String nistUrl = CVE_BASE_URL + cveName; + List referenceUrls = vulnerability.getReferences().stream().map(Reference::getUrl) + .collect(Collectors.toList()); + if (referenceUrls.isEmpty()) { + referenceUrls.add("No references found, try searching for the CVE name (" + cveName + ") on the web."); + } + boolean toLowSeverity = hasV3Severity ? severity < minV3Severity : severity < minV2Severity; + if (toLowSeverity) { + return; + } + VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, vulnerability); + if (versionRange == null) { + logger.info( + "Vulnerability {} is not relevant because its affected versions have no overlap with the versions available " + + "through IDEasy.", + vulnerability.getName()); + return; + } + + securityFile.addSecurityMatch(versionRange, severity, severityVersion, cveName, description, nistUrl, + referenceUrls); + } - static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, String vStartIncluding, - String vEndIncluding, String vEndExcluding) { + private static Dependency[] getDependenciesWithVulnerabilities(IdeContext ideContext) { + + Settings settings = new Settings(); + // Using try with resource or engine.close at the end resulted in SEVERE warning by owasp + Engine engine = new Engine(settings); + UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); + FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); + engine.getFileTypeAnalyzers().add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).removeIf(analyze -> analyze instanceof FileNameAnalyzer); + + // engine.scan(ideContext.getUrlsPath().toString()); + engine.scan("C:\\projects\\_ide\\myUrls"); + + try { + engine.analyzeDependencies(); + } catch (ExceptionCollection e) { + throw new RuntimeException(e); + } + return engine.getDependencies(); + } + + static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, + String vStartIncluding, String vEndIncluding, String vEndExcluding) { VersionIdentifier max = null; if (vEndIncluding != null) { - max = VersionIdentifier.of(vEndIncluding); // this allows that max is not part of the available versions, this has no impact on the contains method but maybe confusing + max = VersionIdentifier.of(vEndIncluding); // this allows that max is not part of the available versions, this has + // no impact on the contains method but maybe confusing } else if (vEndExcluding != null) { VersionIdentifier end = VersionIdentifier.of(vEndExcluding); for (VersionIdentifier version : sortedVersions) { if (version.isLess(end)) { + + // TODO here the version from the name in url dir is v.2.7.0 for example and end is 2.7.2 which should be + // smaller but is not + // sinvce the v is there, i either have to map the sorted versions and remove the v or add the v to "end" max = version; break; } } + if (max == null) { // vEndExcluding is smaller or equal than all available versions -> this vulnerability is not + // relevant and just leaving max to be null could result in a version range like ">" meaning all versions are + // effected, which is wrong. + return null; + } } VersionIdentifier min = null; @@ -133,6 +181,11 @@ static VersionRange getVersionRangeFromInterval(List sortedVe break; } } + if (min == null) { // vStartExcluding is greater or equal than all available versions -> this vulnerability is not + // relevant and just leaving min to be null could result in a version range like ">" meaning all versions are + // effected, which is wrong. + return null; + } } return new VersionRange(min, max); } @@ -147,11 +200,11 @@ static VersionRange getVersionRangeFromVulnerability(List sor String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { - throw new RuntimeException("Vulnerability without version range found: " + vulnerability.getName()); + // maybe instead all versions are vulnerable in this case + return VersionRange.of(">"); + // throw new RuntimeException("Vulnerability without version range found: " + vulnerability.getName()); } - return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, - vEndExcluding); + return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, vEndExcluding); } } - diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index dc384d822..a85985846 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -4,8 +4,6 @@ import java.nio.file.Path; import java.nio.file.Paths; -import com.devonfw.tools.ide.context.IdeContextConsole; -import com.devonfw.tools.ide.log.IdeLogLevel; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -16,7 +14,6 @@ import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; -import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.url.updater.UpdateManager; public class UrlAnalyzer extends AbstractFileTypeAnalyzer { @@ -27,6 +24,7 @@ public class UrlAnalyzer extends AbstractFileTypeAnalyzer { private static final String ANALYZER_NAME = "UrlAnalyzer"; private final UpdateManager updateManager; + public UrlAnalyzer(UpdateManager updateManager) { fileFilter = new UrlFileFilter(); @@ -36,8 +34,6 @@ public UrlAnalyzer(UpdateManager updateManager) { @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { - - String filePath = dependency.getFilePath(); Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); @@ -47,17 +43,28 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An // adding vendor evidence String vendor = updateManager.getCpeVendor(tool); - Evidence evidence = new Evidence(source, "CpeVendor", vendor, Confidence.HIGH); + Evidence evidence; + if (vendor == null) { + vendor = tool; + } + evidence = new Evidence(source, "CpeVendor", vendor, Confidence.HIGH); dependency.addEvidence(EvidenceType.VENDOR, evidence); // adding product evidence String product = updateManager.getCpeProduct(tool); - if (product == null) { + if (product == null) { // for the product it is reasonable to assume that "tool" is the product in most cases product = tool; } evidence = new Evidence(source, "CpeProduct", product, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); + // adding edition evidence + String editionEvidence = updateManager.getCpeEdition(tool); + if (editionEvidence != null) { + evidence = new Evidence(source, "CpeEdition", editionEvidence, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, evidence); + } + // adding version evidence String version = updateManager.mapUrlVersionToCpeVersion(tool, parent.getFileName().toString()); evidence = new Evidence(source, "CpeVersion", version, Confidence.HIGH); @@ -72,6 +79,7 @@ public boolean isEnabled() { @Override protected String getAnalyzerEnabledSettingKey() { + // whether this Analyzer is enabled or not is not configurable but fixed by isEnabled() return null; } @@ -84,6 +92,7 @@ protected FileFilter getFileFilter() { @Override protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { + // nothing to prepare here } From ba87b952e236cd6e2c32e50d2c4d2bf6d0c564ce Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 5 Dec 2023 15:09:43 +0100 Subject: [PATCH 07/47] #103: test interaction and getVersionRangeFromInterval --- .../tools/ide/tool/GlobalToolCommandlet.java | 6 +- .../tools/ide/tool/LocalToolCommandlet.java | 6 +- .../tools/ide/tool/ToolCommandlet.java | 74 +++--- .../model/file/json/UrlSecurityJsonFile.java | 120 +++------ .../ide/url/updater/AbstractUrlUpdater.java | 23 +- .../tools/ide/url/updater/UpdateManager.java | 23 +- .../ide/context/AbstractIdeContextTest.java | 6 +- .../tools/ide/tool/ToolCommandletTest.java | 238 ++++++++++++++++++ pom.xml | 2 +- .../{Main.java => BuildSecurityJsonFile.java} | 217 ++++++++++------ .../devonfw/tools/security/UrlAnalyzer.java | 12 +- .../security/BuildSecurityJsonFileTest.java | 107 ++++++++ 12 files changed, 606 insertions(+), 228 deletions(-) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java rename security/src/main/java/com/devonfw/tools/security/{Main.java => BuildSecurityJsonFile.java} (56%) create mode 100644 security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java index 8c83a16a1..b4ae9822c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java @@ -47,7 +47,11 @@ protected boolean doInstall(boolean silent) { String edition = getEdition(); ToolRepository toolRepository = this.context.getDefaultToolRepository(); VersionIdentifier configuredVersion = getConfiguredVersion(); - VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, configuredVersion); + + VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion); + System.out.println("Selected version: " + selectedVersion); + + VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, selectedVersion); // download and install the global tool FileAccess fileAccess = this.context.getFileAccess(); Path target = toolRepository.download(this.tool, edition, resolvedVersion); diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 36e6c8566..a77363e49 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -60,13 +60,15 @@ public Path getToolBinPath() { protected boolean doInstall(boolean silent) { VersionIdentifier configuredVersion = getConfiguredVersion(); - // install configured version of our tool in the software repository if not already installed - ToolInstallation installation = installInRepo(configuredVersion); VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion); System.out.println("Selected version: " + selectedVersion); + // install configured version of our tool in the software repository if not already installed + ToolInstallation installation = installInRepo(selectedVersion); + + // check if we already have this version installed (linked) locally in IDE_HOME/software VersionIdentifier installedVersion = getInstalledVersion(); VersionIdentifier resolvedVersion = installation.resolvedVersion(); 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 abb5b1028..b64636914 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 @@ -16,15 +16,13 @@ import com.devonfw.tools.ide.environment.EnvironmentVariablesType; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.TarCompression; -import com.devonfw.tools.ide.json.mapping.JsonMapping; import com.devonfw.tools.ide.os.MacOsHelper; import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.property.StringListProperty; -import com.devonfw.tools.ide.url.model.file.UrlSecurityFile; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.version.VersionIdentifier; -import com.fasterxml.jackson.databind.ObjectMapper; /** * {@link Commandlet} for a tool integrated into the IDE. @@ -68,7 +66,6 @@ public String getName() { } /** - * * @return the name of the binary */ protected String getBinaryName() { @@ -92,7 +89,7 @@ public void run() { * Ensures the tool is installed and then runs this tool with the given arguments. * * @param toolVersion the explicit version (pattern) to run. Typically {@code null} to ensure the configured version - * is installed and use that one. Otherwise the specified version will be installed in the software repository + * is installed and use that one. Otherwise, the specified version will be installed in the software repository * without touching and IDE installation and used to run. * @param args the commandline arguments to run the tool. */ @@ -174,7 +171,7 @@ public boolean install(boolean silent) { return doInstall(silent); } - protected String question(String question, String... options) { + protected String securityRiskInteractionQuestion(String question, String... options) { question += " Do you want to"; for (int i = 0; i < options.length - 1; i++) { @@ -184,18 +181,27 @@ protected String question(String question, String... options) { return this.context.question(question, options); } + /** + * Checks if the given {@link VersionIdentifier} has a matching security warning in the {@link UrlSecurityJsonFile}. + * + * @param configuredVersion the {@link VersionIdentifier} to be checked. + * @return the {@link VersionIdentifier} to be used for installation. If the configured version is safe or there are + * no save versions the potentially unresolved configured version is simply returned. Otherwise, a resolved version is + * returned. + */ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) { - // TODO vielleicht security file auch neu als json file wenn 1.2 > 2.9 nicht ausreicht - // TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n"; + // TODO maybe instead of returning current return configuredVersion if the users chooses "stay" - // TODO if no version is save, find a version that has lowest security risk, or suggest multiple ones, such that the - // user can choose + // TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n"; - UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile(); - ObjectMapper mapper = JsonMapping.create(); + UrlSecurityJsonFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()) + .getSecurityJsonFile(); VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); + // TODO oder doch eher sowas wie VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, + // edition, selectedVersion); sollte immer das selbe ergeben + if (!securityFile.contains(current)) { return configuredVersion; } @@ -203,12 +209,8 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured List allVersions = this.context.getUrls().getSortedVersions(this.tool, this.getEdition()); VersionIdentifier latest = allVersions.get(0); - // currentVersion = VersionIdentifier.of("4.9.52"); - int currentVersionIndex = allVersions.indexOf(current); - // VersionIdentifier nextVersion = currentVersionIndex == 0 ? null : - // allVersions.get(allVersions.indexOf(currentVersion) - 1); VersionIdentifier nextSafe = null; for (int i = currentVersionIndex - 1; i >= 0; i--) { if (!securityFile.contains(allVersions.get(i))) { @@ -223,48 +225,50 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured break; } } - VersionIdentifier previousSafe = null; - for (int i = currentVersionIndex + 1; i < allVersions.size(); i++) { - if (!securityFile.contains(allVersions.get(i))) { - previousSafe = allVersions.get(i); - break; - } - } String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is installed, " - + "which is has a vulnerability:\n" + " TODOODODO" + "\n\n"; + + "which is has a vulnerability:\n" + " TODO list vulnerability" + "\n\n (See also " + securityFile.getPath() + + ")"; - String stay = "stay with the current unsafe version (" + current + ")"; + String stay = "stay with the current unsafe version (" + current + ")"; String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")"; String installNextSafe = "install the next safe version (" + nextSafe + ")"; + // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose "stay" + + if (latestSafe == null) { + this.context.warning(currentIsUnsafe + "There is no safe version available."); + return configuredVersion; + } if (current.equals(latest)) { - String answer = question(currentIsUnsafe, stay, installLatestSafe); + String answer = securityRiskInteractionQuestion(currentIsUnsafe + "There are no updates available.", stay, + installLatestSafe); return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe == null) { - // TODO also allow selection of next or previous version, even if they are unsafe? - String answer = question(currentIsUnsafe + " All newer versions are also not safe.", stay, installLatestSafe); + String answer = securityRiskInteractionQuestion(currentIsUnsafe + " All newer versions are also not safe.", stay, + installLatestSafe); return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe.equals(latest)) { - String answer = question(currentIsUnsafe + " Of the newer versions, only the latest is safe.", stay, - installSafeLatest); + String answer = securityRiskInteractionQuestion( + currentIsUnsafe + " Of the newer versions, only the latest is safe.", stay, installSafeLatest); return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe.equals(latestSafe)) { - String answer = question(currentIsUnsafe + " Of the newer versions, only the version " + nextSafe + " is safe.", - stay, "Install the safe version (" + nextSafe + ")"); + String answer = securityRiskInteractionQuestion( + currentIsUnsafe + " Of the newer versions, only the version " + nextSafe + + " is safe, Which is not the latest.", stay, "Install the safe version (" + nextSafe + ")"); return answer.startsWith(stay) ? current : nextSafe; } else { - if (latest.equals(latestSafe)) { - String answer = question(currentIsUnsafe, stay, installNextSafe, installSafeLatest); + if (latestSafe.equals(latest)) { + String answer = securityRiskInteractionQuestion(currentIsUnsafe, stay, installNextSafe, installSafeLatest); return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; } else { - String answer = question(currentIsUnsafe, stay, installNextSafe, installLatestSafe); + String answer = securityRiskInteractionQuestion(currentIsUnsafe, stay, installNextSafe, installLatestSafe); return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java index d7305ca4a..d400e9f1f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java @@ -2,10 +2,10 @@ import java.io.BufferedWriter; 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.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -29,7 +29,7 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { private static final Logger LOG = LoggerFactory.getLogger(UrlSecurityJsonFile.class); - List matches; + Set warnings; /** * The constructor. @@ -39,63 +39,62 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { public UrlSecurityJsonFile(UrlEdition parent) { super(parent, FILENAME_SECURITY); - this.matches = new ArrayList<>(); + this.warnings = new HashSet<>(); } - public boolean addSecurityMatch(VersionRange versionRange, double severity, String severityVersion, String cveName, + /*** + * Adds a new security warning to the security json file. + * + * @param versionRange the version range, specifying the versions of the tool to which the security risk applies + * @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. + * @return {@code true} if the security match was added, {@code false} if it was already present. + */ + public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(severity, severityVersion, cveName, description, nistUrl, + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, severityVersion, cveName, description, nistUrl, referenceUrl); - for (UrlSecurityMatch match : matches) { - if (match.getVersionRange().equals(versionRange)) { - boolean added = match.addWarning(newWarning); - this.modified = this.modified || added; - return added; - } - } - UrlSecurityMatch newMatch = new UrlSecurityMatch(versionRange); - newMatch.addWarning(newWarning); - this.modified = true; - return matches.add(newMatch); - } - - public boolean removeSecurityMatch(VersionRange versionRange) { - - for (UrlSecurityMatch match : matches) { - if (match.getVersionRange().equals(versionRange)) { - boolean removed = matches.remove(match); - this.modified = this.modified || removed; - return removed; - } - } - return false; + boolean added = warnings.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. + * + * @param version the version to check for security risks. + * @return {@code true} if there is a security risk for the given version, {@code false} otherwise. + */ public boolean contains(VersionIdentifier version) { - for (UrlSecurityMatch match : matches) { - if (match.getVersionRange().contains(version)) { + for (UrlSecurityWarning warning : this.warnings) { + if (warning.versionRange().contains(version)) { return true; } } return false; } - public Set getSecurityWarnings(VersionIdentifier version) { + public Set getMatchingSecurityWarnings(VersionIdentifier version) { - Set warnings = new HashSet<>(); - for (UrlSecurityMatch match : matches) { - if (match.getVersionRange().contains(version)) { - warnings.addAll(match.getWarnings()); + Set matchedWarnings = new HashSet<>(); + for (UrlSecurityWarning warning : this.warnings) { + if (warning.versionRange().contains(version)) { + matchedWarnings.add(warning); } } - return warnings; + return matchedWarnings; } - public void clearSecurityMatches() { + public void clearSecurityWarnings() { - this.matches.clear(); + this.warnings.clear(); } @Override @@ -106,7 +105,7 @@ protected void doLoad() { } ObjectMapper mapper = JsonMapping.create(); try { - matches = mapper.readValue(getPath().toFile(), new TypeReference>() { + warnings = mapper.readValue(getPath().toFile(), new TypeReference>() { }); } catch (IOException e) { throw new IllegalStateException("The UrlSecurityJsonFile " + getPath() + " could not be parsed.", e); @@ -119,13 +118,13 @@ protected void doSave() { Path path = getPath(); ObjectMapper mapper = JsonMapping.create(); - if (this.matches.isEmpty() && !Files.exists(path)) { + if (this.warnings.isEmpty() && !Files.exists(path)) { return; } String jsonString; try { - jsonString = mapper.writeValueAsString(matches); + jsonString = mapper.writeValueAsString(warnings); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -139,43 +138,6 @@ protected void doSave() { } } -class UrlSecurityMatch { - private final VersionRange versionRange; - - private final Set warnings; - - public UrlSecurityMatch() { - - // this constructor is needed for jackson deserialization - this.versionRange = null; - this.warnings = new HashSet<>(); - } - - public UrlSecurityMatch(VersionRange versionRange) { - - this.versionRange = versionRange; - this.warnings = new HashSet<>(); - } - - public VersionRange getVersionRange() { - - return versionRange; - } - - public Set getWarnings() { - - return warnings; - } - - public boolean addWarning(UrlSecurityWarning warning) { - - return this.warnings.add(warning); - } - -} - -// severity could be java.math.BigDecimal; instead of double (unsing BigDecimal("123.4").setScale(1, -// BigDecimal.ROUND_HALF_UP);) -record UrlSecurityWarning(double severity, String severityVersion, String cveName, String description, String nistUrl, +record UrlSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { }; \ No newline at end of file diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 7874bc47f..1fdac6c71 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -97,22 +97,35 @@ protected final String getToolWithEdition() { return tool + "/" + edition; } - protected String getCpeVendor() { + /*** + * + * @return the vendor of the tool as specified in the CPE (Common Platform Enumeration) + */ + public String getCpeVendor() { return null; } - - protected String getCpeProduct() { + /*** + * @return the product name of the tool as specified in the CPE (Common Platform Enumeration) + */ + public String getCpeProduct() { return null; } - protected String getCpeEdition() { + /*** + * @return the edition of the tool as specified in the CPE (Common Platform Enumeration) + */ + public String getCpeEdition() { return null; } - protected String mapUrlVersionToCpeVersion(String version) { + /*** + * @return maps the version as specified by the directory name in the url repository to the version as specified in + * the CPE (Common Platform Enumeration). + */ + public String mapUrlVersionToCpeVersion(String version) { return version; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 708e63304..7d7429f8b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -96,28 +96,9 @@ public void updateAll() { } } - public String getCpeVendor(String tool) { + public AbstractUrlUpdater getUrlUpdater(String tool) { - return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() - .map(AbstractUrlUpdater::getCpeVendor).orElse(null); - } - - public String getCpeProduct(String tool) { - - return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() - .map(AbstractUrlUpdater::getCpeProduct).orElse(null); - } - - public String getCpeEdition(String tool) { - - return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() - .map(AbstractUrlUpdater::getCpeEdition).orElse(null); - } - - public String mapUrlVersionToCpeVersion(String tool, String urlVersion) { - - return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst() - .map(updater -> updater.mapUrlVersionToCpeVersion(urlVersion)).orElse(null); + return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst().orElse(null); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index ba671cff3..d92658ac7 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -60,7 +60,7 @@ protected static IdeTestContext newContext(String projectName, String projectPat * in that project. * @return the {@link IdeTestContext} pointing to that project. */ - protected static IdeTestContext newContext(String projectName, String projectPath, boolean copyForMutation) { + protected static IdeTestContext newContext(String projectName, String projectPath, boolean copyForMutation, String ... answers) { Path sourceDir = PATH_PROJECTS.resolve(projectName); Path userDir = sourceDir; @@ -77,9 +77,9 @@ protected static IdeTestContext newContext(String projectName, String projectPat fileAccess.copy(sourceDir, projectDir, FileCopyMode.COPY_TREE_OVERRIDE_TREE); fileAccess.copy(PATH_PROJECTS.resolve(IdeContext.FOLDER_IDE), PATH_PROJECTS_COPY.resolve(IdeContext.FOLDER_IDE), FileCopyMode.COPY_TREE_OVERRIDE_TREE); - context = new IdeTestContext(projectDir.resolve(projectPath)); + context = new IdeTestContext(projectDir.resolve(projectPath), answers); } else { - context = new IdeTestContext(userDir); + context = new IdeTestContext(userDir, answers); } return context; } diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java new file mode 100644 index 000000000..da3da87e7 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -0,0 +1,238 @@ +package com.devonfw.tools.ide.tool; + +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; +import com.devonfw.tools.ide.version.VersionRange; +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.tool.az.Azure; +import com.devonfw.tools.ide.version.VersionIdentifier; + +import java.nio.file.Path; + +/*** + * Test of {@link ToolCommandlet}. + */ +public class ToolCommandletTest extends AbstractIdeContextTest { + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the set version is the latest but + * vulnerable. + */ + @Test + public void testSecurityRiskInteractionCurrentIsLatest() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("2>5"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("7>9"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("9")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("6")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where there are no newer versions that + * are safe, but there is a previous version that is safe. + */ + @Test + public void testSecurityRiskInteractionNextSafeIsNull() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("6>7"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("8>"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("5")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version is also the + * latest. + */ + @Test + public void testSecurityRiskInteractionNextSafeIsLatest() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("6>7"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("8>8"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("7")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("9")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version is also the + * latest safe version, and the overall latest version is not safe. + */ + @Test + public void testSecurityRiskInteractionNextSafeIsLatestSafe() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("5>6"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("8>9"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version differs from + * the latest safe, which is also the overall latest version. + */ + @Test + public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2", "3" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("5>6"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("8>8"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); + // answer to the interaction is 3 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("9")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version differs from + * the latest safe, and the overall latest version is not safe. + */ + @Test + public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = { "1", "2", "3" }; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("6>6"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("8>9"), null, null, null, null, null, null); + + // act & assert + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("3")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("4")); + // answer to the interaction is 3 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("7")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where set version is safe. + */ + @Test + public void testSecurityRiskInteractionCurrentVersionIsSafe() { + + // arrange + Class dummyTool = Azure.class; + IdeContext context = getContextForSecurityJsonTests(dummyTool); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("1>5"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("7>8"), null, null, null, null, null, null); + + // act & assert + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); + } + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. + */ + @Test + public void testSecurityRiskInteractionNoSafeVersionFound() { + + // arrange + Class dummyTool = Azure.class; + IdeContext context = getContextForSecurityJsonTests(dummyTool); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("1>5"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("6>"), null, null, null, null, null, null); + + // act & assert + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); + } + + /*** + * Creates the context and data for the tests of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. + * + * @param dummyTool the dummy tool to be used for the tests. The {@link com.devonfw.tools.ide.url.model.folder.UrlVersion folders} + * representing the versions of the dummy tool are created here. + * @param answers the answers to be used for the interaction in {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. + * @return the {@link IdeTestContext} to be used for the tests. + */ + private IdeContext getContextForSecurityJsonTests(Class dummyTool, String... answers) { + + String path = "workspaces/foo-test/my-git-repo"; + // if I don't pass answers here I get: End of answers reached! + IdeContext context = newContext("basic", path, true, answers); + ToolCommandlet toolCommandlet = context.getCommandletManager().getCommandlet(dummyTool); + Path eitionPath = context.getUrlsPath().resolve(toolCommandlet.getName()).resolve(toolCommandlet.getEdition()); + context.getFileAccess().delete(eitionPath); // I want to define my own versions for simplicity + for (int i = 1; i < 10; i++) { + context.getFileAccess().mkdirs(eitionPath.resolve(String.valueOf(i))); + } + return context; + } +} + + diff --git a/pom.xml b/pom.xml index a432552af..36ed794ef 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ documentation cli - security + security diff --git a/security/src/main/java/com/devonfw/tools/security/Main.java b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java similarity index 56% rename from security/src/main/java/com/devonfw/tools/security/Main.java rename to security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java index d78fff2e5..345960e74 100644 --- a/security/src/main/java/com/devonfw/tools/security/Main.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java @@ -1,11 +1,15 @@ package com.devonfw.tools.security; +import java.math.BigDecimal; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; +import java.util.ArrayList; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AnalysisPhase; import org.owasp.dependencycheck.analyzer.FileNameAnalyzer; @@ -27,15 +31,15 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -public class Main { +public class BuildSecurityJsonFile { - private static final Logger logger = LoggerFactory.getLogger(Main.class); + private static final Logger logger = LoggerFactory.getLogger(BuildSecurityJsonFile.class); private static final String CVE_BASE_URL = "https://nvd.nist.gov/vuln/detail/"; - private static double minV2Severity; + private static BigDecimal minV2Severity; - private static double minV3Severity; + private static BigDecimal minV3Severity; public static void main(String[] args) { @@ -43,18 +47,19 @@ public static void main(String[] args) { throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); } try { - minV2Severity = Double.parseDouble(args[0]); - minV3Severity = Double.parseDouble(args[1]); + minV2Severity = new BigDecimal(String.format(args[0])); + minV3Severity = new BigDecimal(String.format(args[1])); } catch (NumberFormatException e) { - throw new RuntimeException("These two args could not be parsed as double"); + throw new RuntimeException("These two args could not be parsed as BigDecimal"); } - run(minV2Severity, minV3Severity); + run(); } - private static void run(double minV2Severity, double minV3Severity) { + private static void run() { IdeContext ideContext = new IdeContextConsole(IdeLogLevel.INFO, null, false); + UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); // TODO edit dependency check properties file to switch off analysers, this file is currently read only // TODO maybe this can be done in pom.xml @@ -65,7 +70,7 @@ private static void run(double minV2Severity, double minV3Severity) { // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock // why is this not in projects dir but in user dir? - Dependency[] dependencies = getDependenciesWithVulnerabilities(ideContext); + Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); for (Dependency dependency : dependencies) { @@ -73,20 +78,50 @@ private static void run(double minV2Severity, double minV3Severity) { Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); + AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); UrlSecurityJsonFile securityFile = ideContext.getUrls().getEdition(tool, edition).getSecurityJsonFile(); - securityFile.clearSecurityMatches(); + + // TODO maybe instead of clear check cve name and add only if cve name is not already present + // TODO if new min security is higher than the severity in the loaded file, then remove the old one? + + // TODO wenn dieses repo auch als nightly laufen soll, wo sollen dann die min severity werte herkommen? + securityFile.clearSecurityWarnings(); List sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); + List sortedCpeVersions = sortedVersions.stream().map(VersionIdentifier::toString) + .map(urlUpdater::mapUrlVersionToCpeVersion).map(VersionIdentifier::of) + .collect(Collectors.toCollection(ArrayList::new)); Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { - addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedVersions); + addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedCpeVersions); } securityFile.save(); } } + private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { + + Settings settings = new Settings(); + // Using "try with resource" or engine.close() at the end resulted in SEVERE warning by owasp + Engine engine = new Engine(settings); + FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); + engine.getFileTypeAnalyzers().add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).removeIf(analyze -> analyze instanceof FileNameAnalyzer); + + // engine.scan(ideContext.getUrlsPath().toString()); + engine.scan("C:\\projects\\_ide\\myUrls"); + + try { + engine.analyzeDependencies(); + } catch (ExceptionCollection e) { + throw new RuntimeException(e); + } + return engine.getDependencies(); + } + private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, List sortedVersions) { @@ -94,7 +129,11 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); } boolean hasV3Severity = vulnerability.getCvssV3() != null; - double severity = hasV3Severity ? vulnerability.getCvssV3().getBaseScore() : vulnerability.getCvssV2().getScore(); + double severityDouble = hasV3Severity + ? vulnerability.getCvssV3().getBaseScore() + : vulnerability.getCvssV2().getScore(); + String formatted = String.format(Locale.US, "%.1f", severityDouble); + BigDecimal severity = new BigDecimal(formatted); String severityVersion = hasV3Severity ? "v3" : "v2"; String cveName = vulnerability.getName(); String description = vulnerability.getDescription(); @@ -104,7 +143,10 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (referenceUrls.isEmpty()) { referenceUrls.add("No references found, try searching for the CVE name (" + cveName + ") on the web."); } - boolean toLowSeverity = hasV3Severity ? severity < minV3Severity : severity < minV2Severity; + boolean toLowSeverity = hasV3Severity + ? severity.compareTo(minV3Severity) < 0 + : severity.compareTo(minV2Severity) < 0; + if (toLowSeverity) { return; } @@ -112,99 +154,120 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (versionRange == null) { logger.info( "Vulnerability {} is not relevant because its affected versions have no overlap with the versions available " - + "through IDEasy.", - vulnerability.getName()); + + "through IDEasy.", vulnerability.getName()); return; } - securityFile.addSecurityMatch(versionRange, severity, severityVersion, cveName, description, nistUrl, + securityFile.addSecurityWarning(versionRange, severity, severityVersion, cveName, description, nistUrl, referenceUrls); } - private static Dependency[] getDependenciesWithVulnerabilities(IdeContext ideContext) { - - Settings settings = new Settings(); - // Using try with resource or engine.close at the end resulted in SEVERE warning by owasp - Engine engine = new Engine(settings); - UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); - FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); - engine.getFileTypeAnalyzers().add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).removeIf(analyze -> analyze instanceof FileNameAnalyzer); + /*** + * From the vulnerability determine the {@link VersionRange versionRange} to which the vulnerability applies. + * + * @param sortedVersions sorted versions of the tool available through IDEasy. Must match the format of the versions + * in the vulnerability. See {@link AbstractUrlUpdater#mapUrlVersionToCpeVersion(String)}. + * @param vulnerability the vulnerability determined by OWASP dependency check. + * @return the {@link VersionRange versionRange} to which the vulnerability applies. + */ + static VersionRange getVersionRangeFromVulnerability(List sortedVersions, + Vulnerability vulnerability) { - // engine.scan(ideContext.getUrlsPath().toString()); - engine.scan("C:\\projects\\_ide\\myUrls"); + VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); + String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); + String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); + String vStartExcluding = matchedVulnerableSoftware.getVersionStartExcluding(); + String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); - try { - engine.analyzeDependencies(); - } catch (ExceptionCollection e) { - throw new RuntimeException(e); + if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { + return VersionRange.of(">"); } - return engine.getDependencies(); + + return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, vEndExcluding); } + /*** + * From the interval determine the {@link VersionRange versionRange} to which the vulnerability applies. Since the + * versions as specified in the vulnerability might not be in the {@code sortedVersions} list, the {@link VersionRange} + * is determined by finding the versions in the {@code sortedVersions} list that, when selected, cover all affected + * versions correctly. + */ static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, String vStartIncluding, String vEndIncluding, String vEndExcluding) { - VersionIdentifier max = null; - if (vEndIncluding != null) { - max = VersionIdentifier.of(vEndIncluding); // this allows that max is not part of the available versions, this has - // no impact on the contains method but maybe confusing - } else if (vEndExcluding != null) { - VersionIdentifier end = VersionIdentifier.of(vEndExcluding); - for (VersionIdentifier version : sortedVersions) { - if (version.isLess(end)) { - - // TODO here the version from the name in url dir is v.2.7.0 for example and end is 2.7.2 which should be - // smaller but is not - // sinvce the v is there, i either have to map the sorted versions and remove the v or add the v to "end" - max = version; - break; - } + VersionIdentifier min = null; + if (vStartExcluding != null) { + min = findMinFromStartExcluding(sortedVersions, vStartExcluding); + if (min == null) { + return null; } - if (max == null) { // vEndExcluding is smaller or equal than all available versions -> this vulnerability is not - // relevant and just leaving max to be null could result in a version range like ">" meaning all versions are - // effected, which is wrong. + } else if (vStartIncluding != null) { + min = findMinFromStartIncluding(sortedVersions, vStartIncluding); + if (min == null) { return null; } } - VersionIdentifier min = null; - if (vStartIncluding != null) { - min = VersionIdentifier.of(vStartIncluding); - } else if (vStartExcluding != null) { - for (int i = sortedVersions.size() - 1; i >= 0; i--) { - VersionIdentifier version = sortedVersions.get(i); - if (version.isGreater(VersionIdentifier.of(vStartExcluding))) { - min = version; - break; - } + VersionIdentifier max = null; + if (vEndIncluding != null) { + max = findMaxFromEndIncluding(sortedVersions, vEndIncluding); + if (max == null) { + return null; } - if (min == null) { // vStartExcluding is greater or equal than all available versions -> this vulnerability is not - // relevant and just leaving min to be null could result in a version range like ">" meaning all versions are - // effected, which is wrong. + } else if (vEndExcluding != null) { + max = findMaxFromEndExcluding(sortedVersions, vEndExcluding); + if (max == null) { return null; } } return new VersionRange(min, max); } - static VersionRange getVersionRangeFromVulnerability(List sortedVersions, - Vulnerability vulnerability) { + private static VersionIdentifier findMinFromStartExcluding(List sortedVs, String vStartExcluding) { - VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); - String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); - String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); - String vStartExcluding = matchedVulnerableSoftware.getVersionStartExcluding(); - String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); + VersionIdentifier startExcl = VersionIdentifier.of(vStartExcluding); + for (int i = sortedVs.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedVs.get(i); + if (version.isGreater(startExcl)) { + return version; + } + } + return null; + } - if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { - // maybe instead all versions are vulnerable in this case - return VersionRange.of(">"); - // throw new RuntimeException("Vulnerability without version range found: " + vulnerability.getName()); + private static VersionIdentifier findMinFromStartIncluding(List sortedVs, String vStartIncluding) { + + VersionIdentifier startIncl = VersionIdentifier.of(vStartIncluding); + for (int i = sortedVs.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedVs.get(i); + if (version.compareTo(startIncl) >= 0) { + return version; + } } + return null; + } - return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, vEndExcluding); + private static VersionIdentifier findMaxFromEndIncluding(List sortedVs, String vEndIncluding) { + + VersionIdentifier endIncl = VersionIdentifier.of(vEndIncluding); + for (VersionIdentifier version : sortedVs) { + if (version.compareTo(endIncl) <= 0) { + return version; + } + } + return null; + } + + private static VersionIdentifier findMaxFromEndExcluding(List sortedVs, String vEndExcluding) { + + VersionIdentifier endExl = VersionIdentifier.of(vEndExcluding); + for (VersionIdentifier version : sortedVs) { + if (version.isLess(endExl)) { + return version; + } + } + return null; } + } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index a85985846..09ffca6a5 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -4,6 +4,8 @@ import java.nio.file.Path; import java.nio.file.Paths; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UrlUpdater; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -39,10 +41,12 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); + AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); + String source = "UrlAnalyzer"; // adding vendor evidence - String vendor = updateManager.getCpeVendor(tool); + String vendor = urlUpdater.getCpeVendor(); Evidence evidence; if (vendor == null) { vendor = tool; @@ -51,7 +55,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An dependency.addEvidence(EvidenceType.VENDOR, evidence); // adding product evidence - String product = updateManager.getCpeProduct(tool); + String product = urlUpdater.getCpeProduct(); if (product == null) { // for the product it is reasonable to assume that "tool" is the product in most cases product = tool; } @@ -59,14 +63,14 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An dependency.addEvidence(EvidenceType.PRODUCT, evidence); // adding edition evidence - String editionEvidence = updateManager.getCpeEdition(tool); + String editionEvidence = urlUpdater.getCpeEdition(); if (editionEvidence != null) { evidence = new Evidence(source, "CpeEdition", editionEvidence, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); } // adding version evidence - String version = updateManager.mapUrlVersionToCpeVersion(tool, parent.getFileName().toString()); + String version = urlUpdater.mapUrlVersionToCpeVersion(parent.getFileName().toString()); evidence = new Evidence(source, "CpeVersion", version, Confidence.HIGH); dependency.addEvidence(EvidenceType.VERSION, evidence); } diff --git a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java new file mode 100644 index 000000000..d8f926483 --- /dev/null +++ b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java @@ -0,0 +1,107 @@ +package com.devonfw.tools.security; + +import com.devonfw.tools.ide.version.VersionIdentifier; +import com.devonfw.tools.ide.version.VersionRange; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static com.devonfw.tools.security.BuildSecurityJsonFile.getVersionRangeFromInterval; + +public class BuildSecurityJsonFileTest extends Assertions { + + /*** + * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing + * vStartExcluding and null for the other parameters . + */ + @Test + public void testGetVersionRangeFromIntervalStartExcluding() { + + // arrange + List v = getSortedVersions(); + + // act & assert + assertThat(getVersionRangeFromInterval(v, null, null, null, null)).isEqualTo(VersionRange.of(">")); + assertThat(getVersionRangeFromInterval(v, "1", null, null, null)).isEqualTo(VersionRange.of("1.2.3>")); + assertThat(getVersionRangeFromInterval(v, "1.2.3", null, null, null)).isEqualTo(VersionRange.of("1.2.4>")); + assertThat(getVersionRangeFromInterval(v, "1.4", null, null, null)).isEqualTo(VersionRange.of("2.0>")); + assertThat(getVersionRangeFromInterval(v, "1.5", null, null, null)).isEqualTo(VersionRange.of("2.0>")); + assertThat(getVersionRangeFromInterval(v, "2.1", null, null, null)).isNull(); + assertThat(getVersionRangeFromInterval(v, "2.2", null, null, null)).isNull(); + + } + /*** + * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing + * vStartIncluding and null for the other parameters . + */ + @Test + public void testGetVersionRangeFromIntervalStartIncluding() { + + // arrange + List v = getSortedVersions(); + + // act & assert + assertThat(getVersionRangeFromInterval(v, null, "1", null, null)).isEqualTo(VersionRange.of("1.2.3>")); + assertThat(getVersionRangeFromInterval(v, null, "1.2.3", null, null)).isEqualTo(VersionRange.of("1.2.3>")); + assertThat(getVersionRangeFromInterval(v, null, "1.4", null, null)).isEqualTo(VersionRange.of("1.4>")); + assertThat(getVersionRangeFromInterval(v, null, "1.5", null, null)).isEqualTo(VersionRange.of("2.0>")); + assertThat(getVersionRangeFromInterval(v, null, "2.1", null, null)).isEqualTo(VersionRange.of("2.1>")); + assertThat(getVersionRangeFromInterval(v, null, "2.2", null, null)).isNull(); + + } + + /*** + * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing + * vEndIncluding and null for the other parameters . + */ + @Test + public void testGetVersionRangeFromIntervalEndIncluding() { + + // arrange + List v = getSortedVersions(); + + // act & assert + assertThat(getVersionRangeFromInterval(v, null, null, "1", null)).isNull(); + assertThat(getVersionRangeFromInterval(v, null, null, "1.2.3", null)).isEqualTo(VersionRange.of(">1.2.3")); + assertThat(getVersionRangeFromInterval(v, null, null, "1.4", null)).isEqualTo(VersionRange.of(">1.4")); + assertThat(getVersionRangeFromInterval(v, null, null, "1.5", null)).isEqualTo(VersionRange.of(">1.4")); + assertThat(getVersionRangeFromInterval(v, null, null, "2.1", null)).isEqualTo(VersionRange.of(">2.1")); + assertThat(getVersionRangeFromInterval(v, null, null, "2.2", null)).isEqualTo(VersionRange.of(">2.1")); + + } + + /*** + * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing + * vEndExcluding and null for the other parameters . + */ + @Test + public void testGetVersionRangeFromIntervalEndExcluding() { + + // arrange + List v = getSortedVersions(); + + // act & assert + assertThat(getVersionRangeFromInterval(v, null, null, null, " 1")).isNull(); + assertThat(getVersionRangeFromInterval(v, null, null, null, "1.2.3")).isNull(); + assertThat(getVersionRangeFromInterval(v, null, null, null, "1.4")).isEqualTo(VersionRange.of(">1.3")); + assertThat(getVersionRangeFromInterval(v, null, null, null, "1.5")).isEqualTo(VersionRange.of(">1.4")); + assertThat(getVersionRangeFromInterval(v, null, null, null, "2.1")).isEqualTo(VersionRange.of(">2.0")); + assertThat(getVersionRangeFromInterval(v, null, null, null, "2.2")).isEqualTo(VersionRange.of(">2.1")); + + } + + private static List getSortedVersions() { + + List sortedVersions = new ArrayList<>(); + sortedVersions.add(VersionIdentifier.of("2.1")); + sortedVersions.add(VersionIdentifier.of("2.0")); + sortedVersions.add(VersionIdentifier.of("1.4")); + sortedVersions.add(VersionIdentifier.of("1.3")); + sortedVersions.add(VersionIdentifier.of("1.2.5")); + sortedVersions.add(VersionIdentifier.of("1.2.4")); + sortedVersions.add(VersionIdentifier.of("1.2.3")); + return sortedVersions; + } +} From ba694ab7f4027c51f9ad2b931cb6de2459998a96 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Wed, 6 Dec 2023 11:31:55 +0100 Subject: [PATCH 08/47] #103: refinements --- .../devonfw/tools/ide/common/SystemPath.java | 1 + .../tools/ide/tool/GlobalToolCommandlet.java | 4 +- .../tools/ide/tool/LocalToolCommandlet.java | 7 +- .../tools/ide/tool/ToolCommandlet.java | 26 ++--- .../tools/ide/tool/helm/HelmUrlUpdater.java | 6 ++ .../tools/ide/tool/java/JavaUrlUpdater.java | 17 +--- .../file/{json => }/UrlSecurityJsonFile.java | 42 +++++--- .../ide/url/model/folder/UrlEdition.java | 2 +- .../tools/ide/tool/ToolCommandletTest.java | 20 ++-- .../tools/security/BuildSecurityJsonFile.java | 97 +++++++++++-------- .../devonfw/tools/security/UrlAnalyzer.java | 1 - .../devonfw/tools/security/UrlFileFilter.java | 27 +++--- 12 files changed, 135 insertions(+), 115 deletions(-) rename cli/src/main/java/com/devonfw/tools/ide/url/model/file/{json => }/UrlSecurityJsonFile.java (75%) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index ac24d3a99..9219eea93 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -166,6 +166,7 @@ public Path getPath(String tool) { */ public void setPath(String tool, Path path) { + this.paths.add(path); this.tool2pathMap.put(tool, path); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java index b4ae9822c..981dceff2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java @@ -47,10 +47,8 @@ protected boolean doInstall(boolean silent) { String edition = getEdition(); ToolRepository toolRepository = this.context.getDefaultToolRepository(); VersionIdentifier configuredVersion = getConfiguredVersion(); - VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion); - System.out.println("Selected version: " + selectedVersion); - + setVersion(selectedVersion, silent); VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, selectedVersion); // download and install the global tool FileAccess fileAccess = this.context.getFileAccess(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index a77363e49..cbb5e57f0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -60,15 +60,10 @@ public Path getToolBinPath() { protected boolean doInstall(boolean silent) { VersionIdentifier configuredVersion = getConfiguredVersion(); - VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion); - - System.out.println("Selected version: " + selectedVersion); - + setVersion(selectedVersion, silent); // install configured version of our tool in the software repository if not already installed ToolInstallation installation = installInRepo(selectedVersion); - - // check if we already have this version installed (linked) locally in IDE_HOME/software VersionIdentifier installedVersion = getInstalledVersion(); VersionIdentifier resolvedVersion = installation.resolvedVersion(); 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 b64636914..fdf081731 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 @@ -6,6 +6,7 @@ import java.nio.file.Paths; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.devonfw.tools.ide.cli.CliException; @@ -20,7 +21,8 @@ import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.property.StringListProperty; -import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; +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; @@ -173,7 +175,7 @@ public boolean install(boolean silent) { protected String securityRiskInteractionQuestion(String question, String... options) { - question += " Do you want to"; + question += "Do you want to"; for (int i = 0; i < options.length - 1; i++) { options[i] += " or"; } @@ -186,8 +188,8 @@ protected String securityRiskInteractionQuestion(String question, String... opti * * @param configuredVersion the {@link VersionIdentifier} to be checked. * @return the {@link VersionIdentifier} to be used for installation. If the configured version is safe or there are - * no save versions the potentially unresolved configured version is simply returned. Otherwise, a resolved version is - * returned. + * no save versions the potentially unresolved configured version is simply returned. Otherwise, a resolved + * version is returned. */ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) { @@ -225,16 +227,17 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured break; } } - - String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is installed, " - + "which is has a vulnerability:\n" + " TODO list vulnerability" + "\n\n (See also " + securityFile.getPath() - + ")"; + String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::cveName) + .collect(Collectors.joining(", ")); + String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " + + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; String stay = "stay with the current unsafe version (" + current + ")"; String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")"; String installNextSafe = "install the next safe version (" + nextSafe + ")"; - // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose "stay" + // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose + // "stay" if (latestSafe == null) { this.context.warning(currentIsUnsafe + "There is no safe version available."); @@ -257,9 +260,8 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured return answer.startsWith(stay) ? current : latestSafe; } else if (nextSafe.equals(latestSafe)) { - String answer = securityRiskInteractionQuestion( - currentIsUnsafe + " Of the newer versions, only the version " + nextSafe - + " is safe, Which is not the latest.", stay, "Install the safe version (" + nextSafe + ")"); + String answer = securityRiskInteractionQuestion(currentIsUnsafe + " Of the newer versions, only the version " + + nextSafe + " is safe, Which is not the latest.", stay, "Install the safe version (" + nextSafe + ")"); return answer.startsWith(stay) ? current : nextSafe; } else { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index 6515a3fe2..c819d67ea 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -29,6 +29,12 @@ protected String getGithubOrganization() { return "helm"; } + @Override + public String mapUrlVersionToCpeVersion(String version) { + + return version.substring(getVersionPrefixToRemove().length()); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index ecad6ce41..e4d1c0e73 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -27,24 +27,15 @@ protected String mapVersion(String version) { } @Override - protected String getCpeVendor() { + public String getCpeVendor() { - // return "vikwp"; - return "eclipse"; + return "eclipse"; } @Override - protected String getCpeProduct() { + public String getCpeProduct() { - // return "vik_booking"; - return "temurin"; - } - - @Override - protected String mapUrlVersionToCpeVersion(String version) { - - // return "1.5.8"; - return version; + return "temurin"; } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java similarity index 75% rename from cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java rename to cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java index d400e9f1f..18b1e543f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlSecurityJsonFile.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java @@ -1,4 +1,4 @@ -package com.devonfw.tools.ide.url.model.file.json; +package com.devonfw.tools.ide.url.model.file; import java.io.BufferedWriter; import java.io.IOException; @@ -14,7 +14,6 @@ import org.slf4j.LoggerFactory; import com.devonfw.tools.ide.json.mapping.JsonMapping; -import com.devonfw.tools.ide.url.model.file.AbstractUrlFile; import com.devonfw.tools.ide.url.model.folder.UrlEdition; import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; @@ -22,14 +21,33 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +/** + * {@link UrlFile} for the "security.json" file. + */ public class UrlSecurityJsonFile extends AbstractUrlFile { + /*** + * A simple container with the information about a security warning. + * + * @param versionRange the version range, specifying the versions of the tool to which the security risk applies. + * @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 record UrlSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, + String cveName, String description, String nistUrl, List referenceUrl) { + }; + /** {@link #getName() Name} of security json file. */ public static final String FILENAME_SECURITY = "security.json"; private static final Logger LOG = LoggerFactory.getLogger(UrlSecurityJsonFile.class); - Set warnings; + private Set warnings; /** * The constructor. @@ -45,21 +63,21 @@ public UrlSecurityJsonFile(UrlEdition parent) { /*** * Adds a new security warning to the security json file. * - * @param versionRange the version range, specifying the versions of the tool to which the security risk applies + * @param versionRange the version range, specifying the versions of the tool to which the security risk applies. * @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. + * 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. * @return {@code true} if the security match was added, {@code false} if it was already present. */ - public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, String cveName, - String description, String nistUrl, List referenceUrl) { + public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, + String cveName, String description, String nistUrl, List referenceUrl) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, severityVersion, cveName, description, nistUrl, - referenceUrl); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, severityVersion, cveName, + description, nistUrl, referenceUrl); boolean added = warnings.add(newWarning); this.modified = this.modified || added; return added; @@ -136,8 +154,4 @@ protected void doSave() { throw new IllegalStateException("Failed to save file " + path, e); } } -} - -record UrlSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, String cveName, String description, String nistUrl, - List referenceUrl) { -}; \ No newline at end of file +} \ No newline at end of file diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java index b3f0fdea3..9ee4f71e3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java @@ -2,7 +2,7 @@ import com.devonfw.tools.ide.url.model.AbstractUrlFolderWithParent; import com.devonfw.tools.ide.url.model.file.UrlSecurityFile; -import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; +import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; /** * An {@link UrlFolder} representing the actual edition of a {@link UrlTool}. The default edition may have the same diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index da3da87e7..c70bfcbd8 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -1,16 +1,16 @@ package com.devonfw.tools.ide.tool; -import com.devonfw.tools.ide.context.IdeTestContext; -import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile; -import com.devonfw.tools.ide.version.VersionRange; +import java.nio.file.Path; + import org.junit.jupiter.api.Test; import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.tool.az.Azure; +import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.version.VersionIdentifier; - -import java.nio.file.Path; +import com.devonfw.tools.ide.version.VersionRange; /*** * Test of {@link ToolCommandlet}. @@ -215,9 +215,11 @@ public void testSecurityRiskInteractionNoSafeVersionFound() { /*** * Creates the context and data for the tests of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. * - * @param dummyTool the dummy tool to be used for the tests. The {@link com.devonfw.tools.ide.url.model.folder.UrlVersion folders} - * representing the versions of the dummy tool are created here. - * @param answers the answers to be used for the interaction in {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. + * @param dummyTool the dummy tool to be used for the tests. The + * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion folders} representing the versions of the dummy + * tool are created here. + * @param answers the answers to be used for the interaction in + * {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. * @return the {@link IdeTestContext} to be used for the tests. */ private IdeContext getContextForSecurityJsonTests(Class dummyTool, String... answers) { @@ -234,5 +236,3 @@ private IdeContext getContextForSecurityJsonTests(Class sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); @@ -95,7 +96,7 @@ private static void run() { Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { - addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedCpeVersions); + addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedVersions, sortedCpeVersions); } securityFile.save(); } @@ -123,14 +124,13 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd } private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, - List sortedVersions) { + List sortedVersions, List sortedCpeVersions) { if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); } boolean hasV3Severity = vulnerability.getCvssV3() != null; - double severityDouble = hasV3Severity - ? vulnerability.getCvssV3().getBaseScore() + double severityDouble = hasV3Severity ? vulnerability.getCvssV3().getBaseScore() : vulnerability.getCvssV2().getScore(); String formatted = String.format(Locale.US, "%.1f", severityDouble); BigDecimal severity = new BigDecimal(formatted); @@ -143,18 +143,19 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (referenceUrls.isEmpty()) { referenceUrls.add("No references found, try searching for the CVE name (" + cveName + ") on the web."); } - boolean toLowSeverity = hasV3Severity - ? severity.compareTo(minV3Severity) < 0 + boolean toLowSeverity = hasV3Severity ? severity.compareTo(minV3Severity) < 0 : severity.compareTo(minV2Severity) < 0; if (toLowSeverity) { return; } - VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, vulnerability); + VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, sortedCpeVersions, vulnerability); if (versionRange == null) { logger.info( - "Vulnerability {} is not relevant because its affected versions have no overlap with the versions available " - + "through IDEasy.", vulnerability.getName()); + "Vulnerability {} seems to be irrelevant because its affected versions have no overlap with the versions " + + "available through IDEasy. If you think the versions should match, see the methode " + + "mapUrlVersionToCpeVersion() in the UrlUpdater of the tool.", + vulnerability.getName()); return; } @@ -166,13 +167,14 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, /*** * From the vulnerability determine the {@link VersionRange versionRange} to which the vulnerability applies. * - * @param sortedVersions sorted versions of the tool available through IDEasy. Must match the format of the versions - * in the vulnerability. See {@link AbstractUrlUpdater#mapUrlVersionToCpeVersion(String)}. + * @param sortedVersions sorted versions of the tool available through IDEasy. + * @param sortedCpeVersions sorted versions of the tool. Must match the format of the CPE versions. See + * {@link AbstractUrlUpdater#mapUrlVersionToCpeVersion(String)}. * @param vulnerability the vulnerability determined by OWASP dependency check. * @return the {@link VersionRange versionRange} to which the vulnerability applies. */ static VersionRange getVersionRangeFromVulnerability(List sortedVersions, - Vulnerability vulnerability) { + List sortedCpeVersions, Vulnerability vulnerability) { VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); @@ -184,26 +186,35 @@ static VersionRange getVersionRangeFromVulnerability(List sor return VersionRange.of(">"); } - return getVersionRangeFromInterval(sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, vEndExcluding); + return getVersionRangeFromInterval(sortedVersions, sortedCpeVersions, vStartExcluding, vStartIncluding, + vEndIncluding, vEndExcluding); + } + + static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, + String vStartIncluding, String vEndIncluding, String vEndExcluding) { + + return getVersionRangeFromInterval(sortedVersions, sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, + vEndExcluding); } /*** * From the interval determine the {@link VersionRange versionRange} to which the vulnerability applies. Since the - * versions as specified in the vulnerability might not be in the {@code sortedVersions} list, the {@link VersionRange} - * is determined by finding the versions in the {@code sortedVersions} list that, when selected, cover all affected - * versions correctly. + * versions as specified in the vulnerability might not be in the {@code sortedVersions} list, the + * {@link VersionRange} is determined by finding the versions in the {@code sortedVersions} list that, when selected, + * cover all affected versions correctly. */ - static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, - String vStartIncluding, String vEndIncluding, String vEndExcluding) { + static VersionRange getVersionRangeFromInterval(List sortedVersions, + List sortedCpeVersions, String vStartExcluding, String vStartIncluding, String vEndIncluding, + String vEndExcluding) { VersionIdentifier min = null; if (vStartExcluding != null) { - min = findMinFromStartExcluding(sortedVersions, vStartExcluding); + min = findMinFromStartExcluding(sortedVersions, sortedCpeVersions, vStartExcluding); if (min == null) { return null; } } else if (vStartIncluding != null) { - min = findMinFromStartIncluding(sortedVersions, vStartIncluding); + min = findMinFromStartIncluding(sortedVersions, sortedCpeVersions, vStartIncluding); if (min == null) { return null; } @@ -211,12 +222,12 @@ static VersionRange getVersionRangeFromInterval(List sortedVe VersionIdentifier max = null; if (vEndIncluding != null) { - max = findMaxFromEndIncluding(sortedVersions, vEndIncluding); + max = findMaxFromEndIncluding(sortedVersions, sortedCpeVersions, vEndIncluding); if (max == null) { return null; } } else if (vEndExcluding != null) { - max = findMaxFromEndExcluding(sortedVersions, vEndExcluding); + max = findMaxFromEndExcluding(sortedVersions, sortedCpeVersions, vEndExcluding); if (max == null) { return null; } @@ -224,47 +235,53 @@ static VersionRange getVersionRangeFromInterval(List sortedVe return new VersionRange(min, max); } - private static VersionIdentifier findMinFromStartExcluding(List sortedVs, String vStartExcluding) { + private static VersionIdentifier findMinFromStartExcluding(List sortedVs, + List sortedCpeVs, String vStartExcluding) { VersionIdentifier startExcl = VersionIdentifier.of(vStartExcluding); - for (int i = sortedVs.size() - 1; i >= 0; i--) { - VersionIdentifier version = sortedVs.get(i); + for (int i = sortedCpeVs.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedCpeVs.get(i); if (version.isGreater(startExcl)) { - return version; + return sortedVs.get(i); } } return null; } - private static VersionIdentifier findMinFromStartIncluding(List sortedVs, String vStartIncluding) { + private static VersionIdentifier findMinFromStartIncluding(List sortedVs, + List sortedCpeVs, String vStartIncluding) { VersionIdentifier startIncl = VersionIdentifier.of(vStartIncluding); - for (int i = sortedVs.size() - 1; i >= 0; i--) { - VersionIdentifier version = sortedVs.get(i); + for (int i = sortedCpeVs.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedCpeVs.get(i); if (version.compareTo(startIncl) >= 0) { - return version; + return sortedVs.get(i); } } return null; } - private static VersionIdentifier findMaxFromEndIncluding(List sortedVs, String vEndIncluding) { + private static VersionIdentifier findMaxFromEndIncluding(List sortedVs, + List sortedCpeVs, String vEndIncluding) { VersionIdentifier endIncl = VersionIdentifier.of(vEndIncluding); - for (VersionIdentifier version : sortedVs) { + for (int i = 0; i < sortedCpeVs.size(); i++) { + VersionIdentifier version = sortedCpeVs.get(i); if (version.compareTo(endIncl) <= 0) { - return version; + return sortedVs.get(i); } } return null; } - private static VersionIdentifier findMaxFromEndExcluding(List sortedVs, String vEndExcluding) { + private static VersionIdentifier findMaxFromEndExcluding(List sortedVs, + List sortedCpeVs, String vEndExcluding) { VersionIdentifier endExl = VersionIdentifier.of(vEndExcluding); - for (VersionIdentifier version : sortedVs) { + for (int i = 0; i < sortedCpeVs.size(); i++) { + VersionIdentifier version = sortedCpeVs.get(i); if (version.isLess(endExl)) { - return version; + return sortedVs.get(i); } } return null; diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 09ffca6a5..7ec2df8c8 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -5,7 +5,6 @@ import java.nio.file.Paths; import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.UrlUpdater; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index d02fc418b..ca6b5a4fa 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -1,23 +1,20 @@ package com.devonfw.tools.security; -import com.devonfw.tools.ide.os.SystemInfo; -import com.devonfw.tools.ide.os.SystemInfoImpl; - import java.io.FileFilter; + +import static com.devonfw.tools.ide.url.model.file.UrlStatusFile.STATUS_JSON; + public class UrlFileFilter implements FileFilter { - final private SystemInfo systemInfo; - private final String os; + public UrlFileFilter() { + + } - public UrlFileFilter() { - this.systemInfo = new SystemInfoImpl(); - this.os = this.systemInfo.getOs().toString(); - } + @Override + public boolean accept(java.io.File pathname) { - @Override - public boolean accept(java.io.File pathname) { - boolean isUrlFile = pathname.toString().endsWith(".urls"); - boolean isCorrectOs = pathname.toString().contains(this.os); - return isUrlFile && isCorrectOs; - } +// System.out.println("UrlFileFilter.accept()" + pathname.getName().equals(STATUS_JSON)); +// return pathname.getName().endsWith("urls") && pathname.getName().startsWith("windows"); + return pathname.getName().equals(STATUS_JSON); + } } From 81b8586e802811e07adc018fa383973dcd975ca5 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 14 Dec 2023 20:57:48 +0100 Subject: [PATCH 09/47] #103: more refinement ignore cves list, remove some analyzers, more test for version ranges like >, some cpe vendors and products to updaters --- .../tools/ide/tool/ToolCommandlet.java | 10 +- .../tools/ide/tool/helm/HelmUrlUpdater.java | 13 +- .../tools/ide/tool/java/JavaUrlUpdater.java | 4 +- .../tools/ide/tool/mvn/MvnUrlUpdater.java | 13 + .../ide/tool/quarkus/QuarkusUrlUpdater.java | 18 ++ .../tool/terraform/TerraformUrlUpdater.java | 16 +- .../url/model/file/UrlSecurityJsonFile.java | 257 ++++++++++++++++-- .../ide/url/updater/AbstractUrlUpdater.java | 2 +- .../tools/ide/url/updater/UpdateManager.java | 5 + .../tools/ide/tool/ToolCommandletTest.java | 110 +++++--- .../tools/security/BuildSecurityJsonFile.java | 137 +++++++--- .../devonfw/tools/security/UrlAnalyzer.java | 39 ++- .../devonfw/tools/security/UrlFileFilter.java | 2 - 13 files changed, 502 insertions(+), 124 deletions(-) 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 fdf081731..f4e1a2fe4 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 @@ -204,7 +204,7 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured // TODO oder doch eher sowas wie VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, // edition, selectedVersion); sollte immer das selbe ergeben - if (!securityFile.contains(current)) { + if (!securityFile.contains(current, true, this.context)) { return configuredVersion; } @@ -215,26 +215,26 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured VersionIdentifier nextSafe = null; for (int i = currentVersionIndex - 1; i >= 0; i--) { - if (!securityFile.contains(allVersions.get(i))) { + if (!securityFile.contains(allVersions.get(i), true, this.context)) { nextSafe = allVersions.get(i); break; } } VersionIdentifier latestSafe = null; for (int i = 0; i < allVersions.size(); i++) { - if (!securityFile.contains(allVersions.get(i))) { + if (!securityFile.contains(allVersions.get(i), true, this.context)) { latestSafe = allVersions.get(i); break; } } - String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::cveName) + String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::getCveName) .collect(Collectors.joining(", ")); String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; String stay = "stay with the current unsafe version (" + current + ")"; String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; - String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")"; + String installSafeLatest = "install the (safe) latest version (" + latest + ")"; String installNextSafe = "install the next safe version (" + nextSafe + ")"; // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose // "stay" diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index c819d67ea..7bdcbf6fa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -29,6 +29,18 @@ protected String getGithubOrganization() { return "helm"; } + @Override + public String getCpeVendor() { + + return "helm"; + } + + @Override + public String getCpeProduct() { + + return "helm"; + } + @Override public String mapUrlVersionToCpeVersion(String version) { @@ -55,5 +67,4 @@ protected String mapVersion(String version) { return super.mapVersion("v" + version); } - } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index e4d1c0e73..4ab982093 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -29,13 +29,13 @@ protected String mapVersion(String version) { @Override public String getCpeVendor() { - return "eclipse"; + return "eclipse"; } @Override public String getCpeProduct() { - return "temurin"; + return "temurin"; } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java index eed6a91b6..b22e9f2ae 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java @@ -33,4 +33,17 @@ protected Pattern getVersionPattern() { return Pattern.compile("(\\d\\.\\d\\.\\d)"); } + + @Override + public String getCpeVendor() { + + return "apache"; + } + + @Override + public String getCpeProduct() { + + return "maven"; + } + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java index f79129d33..86faa1442 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java @@ -40,4 +40,22 @@ protected void addVersion(UrlVersion urlVersion) { } } + @Override + public String getCpeVendor() { + + return "quarkus"; + } + + @Override + public String getCpeProduct() { + + return "quarkus"; + } + + @Override + public String mapUrlVersionToCpeVersion(String version) { + + return version.replaceAll("[^\\d.]", ""); + } + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java index 206cb630f..37f182e21 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java @@ -8,7 +8,7 @@ * {@link GithubUrlUpdater} for terraform. */ public class TerraformUrlUpdater extends GithubUrlUpdater { - + private static final VersionIdentifier MIN_MAC_ARM_VID = VersionIdentifier.of("1.1.0"); @Override @@ -42,4 +42,18 @@ protected void addVersion(UrlVersion urlVersion) { } } + @Override + public String getCpeVendor() { + + return "hashicorp"; + } + + @Override + public String getCpeProduct() { + + return "terraform"; + } + // add matche cpe the the warning and print it in ide, to to wether the vul maybe oinly applies to the enterprise + // edition + // or can I filter this enterpsrise version by adding overriding the eidtion methiod with the normal edition string? } 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 18b1e543f..9ae9475ee 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 @@ -8,8 +8,10 @@ import java.nio.file.StandardOpenOption; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; +import com.devonfw.tools.ide.context.IdeContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,17 +31,204 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { /*** * A simple container with the information about a security warning. * - * @param versionRange the version range, specifying the versions of the tool to which the security risk applies. - * @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 record UrlSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, - String cveName, String description, String nistUrl, List referenceUrl) { + 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 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. */ @@ -60,10 +249,21 @@ public UrlSecurityJsonFile(UrlEdition parent) { this.warnings = new HashSet<>(); } + public boolean addSecurityWarning(VersionRange versionRange) { + + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null, null, null, null, + null); + boolean added = warnings.add(newWarning); + this.modified = this.modified || added; + return added; + } + /*** * Adds a new security warning to the security json file. * * @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 version range. * @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. @@ -73,11 +273,11 @@ public UrlSecurityJsonFile(UrlEdition parent) { * @param referenceUrl the urls where additional information about the CVE can be found. * @return {@code true} if the security match was added, {@code false} if it was already present. */ - public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity, String severityVersion, - String cveName, String description, String nistUrl, List referenceUrl) { + public boolean addSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, + String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, severityVersion, cveName, - description, nistUrl, referenceUrl); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, matchedCpe, interval, severity, + severityVersion, cveName, description, nistUrl, referenceUrl); boolean added = warnings.add(newWarning); this.modified = this.modified || added; return added; @@ -89,21 +289,43 @@ public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity * @param version the version to check for security risks. * @return {@code true} if there is a security risk for the given version, {@code false} otherwise. */ - public boolean contains(VersionIdentifier version) { + public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAffectAllVersions, IdeContext context) { + + List sortedVersions = null; + if (ignoreWarningsThatAffectAllVersions) { + sortedVersions = Objects.requireNonNull(context).getUrls() + .getSortedVersions(this.getParent().getParent().getName(), this.getParent().getName()); + } for (UrlSecurityWarning warning : this.warnings) { - if (warning.versionRange().contains(version)) { + + VersionRange versionRange = warning.getVersionRange(); + if (ignoreWarningsThatAffectAllVersions) { + boolean includesOldestVersion = versionRange.getMin() == null + || sortedVersions.get(sortedVersions.size() - 1).equals(versionRange.getMin()); + boolean includesNewestVersion = versionRange.getMax() == null + || sortedVersions.get(0).equals(versionRange.getMax()); + if (includesOldestVersion && includesNewestVersion) { + continue; + } + } + if (warning.getVersionRange().contains(version)) { return true; } } return false; } + public boolean contains(VersionIdentifier version) { + + return contains(version, false, null); + } + public Set getMatchingSecurityWarnings(VersionIdentifier version) { Set matchedWarnings = new HashSet<>(); for (UrlSecurityWarning warning : this.warnings) { - if (warning.versionRange().contains(version)) { + if (warning.getVersionRange().contains(version)) { matchedWarnings.add(warning); } } @@ -113,6 +335,7 @@ public Set getMatchingSecurityWarnings(VersionIdentifier ver public void clearSecurityWarnings() { this.warnings.clear(); + this.modified = true; } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 1fdac6c71..5803708fa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -351,7 +351,7 @@ private boolean checkDownloadUrl(String url, UrlVersion urlVersion, OperatingSys if (success) { if (checksum == null || checksum.isEmpty()) { String contentType = response.headers().firstValue("content-type").orElse("undefined"); - checksum = doGenerateChecksum(doGetResponseAsStream(url), url, version, contentType); + checksum = doGenerateChecksum(doGetResponseAsStream( url), url, version, contentType); } success = isChecksumStillValid(url, urlVersion, os, architecture, checksum, tool, version); diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 7d7429f8b..0fe20d176 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -101,4 +101,9 @@ public AbstractUrlUpdater getUrlUpdater(String tool) { return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst().orElse(null); } + public UrlRepository getUrlRepository() { + + return this.urlRepository; + } + } diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index c70bfcbd8..8d8a846de 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -17,6 +17,58 @@ */ public class ToolCommandletTest extends AbstractIdeContextTest { + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. But + * there is a warning that affects all versions. This warning is then ignored, but the other warnings are considered. + */ + @Test + public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { + + // arrange + Class dummyTool = Azure.class; + String[] answers = {"1", "2", "3"}; + IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + + securityFile.addSecurityWarning(VersionRange.of(">")); + securityFile.addSecurityWarning(VersionRange.of("2>5")); + + // act & assert + // no answer required + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); + // answer to the interaction is 1 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("2")); + // answer to the interaction is 2 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("6")); + // answer to the interaction is 3 + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("9")); + } + + + /*** + * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. Only + * the warnings all considered together cover all versions and there is no single warning that affects all versions. + */ + @Test + public void testSecurityRiskInteractionAllVersionAffectedByMultipleWarning() { + + // arrange + Class dummyTool = Azure.class; + IdeContext context = getContextForSecurityJsonTests(dummyTool); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) + .getSecurityJsonFile(); + securityFile.addSecurityWarning(VersionRange.of("1>5")); + securityFile.addSecurityWarning(VersionRange.of("6>")); + + // act & assert + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); + } /*** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the set version is the latest but * vulnerable. @@ -31,8 +83,8 @@ public void testSecurityRiskInteractionCurrentIsLatest() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("2>5"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("7>9"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("2>5")); + securityFile.addSecurityWarning(VersionRange.of("7>9")); // act & assert // answer to the interaction is 1 @@ -55,9 +107,9 @@ public void testSecurityRiskInteractionNextSafeIsNull() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("6>7"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("8>"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("3>3")); + securityFile.addSecurityWarning(VersionRange.of("6>7")); + securityFile.addSecurityWarning(VersionRange.of("8>")); // act & assert // answer to the interaction is 1 @@ -80,9 +132,9 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("6>7"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("8>8"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("3>3")); + securityFile.addSecurityWarning(VersionRange.of("6>7")); + securityFile.addSecurityWarning(VersionRange.of("8>8")); // act & assert // answer to the interaction is 1 @@ -105,9 +157,9 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("5>6"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("8>9"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("3>3")); + securityFile.addSecurityWarning(VersionRange.of("5>6")); + securityFile.addSecurityWarning(VersionRange.of("8>9")); // act & assert // answer to the interaction is 1 @@ -130,9 +182,9 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("5>6"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("8>8"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("3>3")); + securityFile.addSecurityWarning(VersionRange.of("5>6")); + securityFile.addSecurityWarning(VersionRange.of("8>8")); // act & assert // answer to the interaction is 1 @@ -157,9 +209,9 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("6>6"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("8>9"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("3>3")); + securityFile.addSecurityWarning(VersionRange.of("6>6")); + securityFile.addSecurityWarning(VersionRange.of("8>9")); // act & assert // answer to the interaction is 1 @@ -182,35 +234,15 @@ public void testSecurityRiskInteractionCurrentVersionIsSafe() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("1>5"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("7>8"), null, null, null, null, null, null); + securityFile.addSecurityWarning(VersionRange.of("1>5")); + securityFile.addSecurityWarning(VersionRange.of("7>8")); // act & assert assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); } - /*** - * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. - */ - @Test - public void testSecurityRiskInteractionNoSafeVersionFound() { - - // arrange - Class dummyTool = Azure.class; - IdeContext context = getContextForSecurityJsonTests(dummyTool); - ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); - UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) - .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("1>5"), null, null, null, null, null, null); - securityFile.addSecurityWarning(VersionRange.of("6>"), null, null, null, null, null, null); - // act & assert - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); - } /*** * Creates the context and data for the tests of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. diff --git a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java index 308bdd155..6ed39c476 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java @@ -4,24 +4,46 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; +import org.owasp.dependencycheck.analyzer.ArchiveAnalyzer; +import org.owasp.dependencycheck.analyzer.ArtifactoryAnalyzer; +import org.owasp.dependencycheck.analyzer.AssemblyAnalyzer; +import org.owasp.dependencycheck.analyzer.CentralAnalyzer; +import org.owasp.dependencycheck.analyzer.FalsePositiveAnalyzer; import org.owasp.dependencycheck.analyzer.FileNameAnalyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; +import org.owasp.dependencycheck.analyzer.JarAnalyzer; +import org.owasp.dependencycheck.analyzer.LibmanAnalyzer; +import org.owasp.dependencycheck.analyzer.MSBuildProjectAnalyzer; +import org.owasp.dependencycheck.analyzer.NexusAnalyzer; +import org.owasp.dependencycheck.analyzer.NodeAuditAnalyzer; +import org.owasp.dependencycheck.analyzer.NodePackageAnalyzer; +import org.owasp.dependencycheck.analyzer.NugetconfAnalyzer; +import org.owasp.dependencycheck.analyzer.NuspecAnalyzer; +import org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer; +import org.owasp.dependencycheck.analyzer.PnpmAuditAnalyzer; +import org.owasp.dependencycheck.analyzer.RetireJsAnalyzer; +import org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer; +import org.owasp.dependencycheck.analyzer.YarnAuditAnalyzer; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Reference; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.utils.Pair; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.devonfw.tools.ide.context.AbstractIdeContext; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.context.IdeContextConsole; import com.devonfw.tools.ide.log.IdeLogLevel; @@ -31,16 +53,43 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; +// TODO Doesn't yet work with versions defined like this ///latest + +/*** + * This class is used to build the {@link UrlSecurityJsonFile} file for IDEasy. It scans the + * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for + * vulnerabilities by using the OWASP package. For this the + * {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the + * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion}. If a vulnerability is found, it is added to the + * {@link UrlSecurityJsonFile} of the corresponding tool and edition. The previous content of the file is overwritten. + */ public class BuildSecurityJsonFile { private static final Logger logger = LoggerFactory.getLogger(BuildSecurityJsonFile.class); private static final String CVE_BASE_URL = "https://nvd.nist.gov/vuln/detail/"; + private static final Set CVES_TO_IGNORE = new HashSet<>(); + + private static final Set> ANALYZERS_TO_IGNORE = Set.of(ArchiveAnalyzer.class, + RubyBundlerAnalyzer.class, FileNameAnalyzer.class, JarAnalyzer.class, CentralAnalyzer.class, NexusAnalyzer.class, + ArtifactoryAnalyzer.class, NuspecAnalyzer.class, NugetconfAnalyzer.class, MSBuildProjectAnalyzer.class, + AssemblyAnalyzer.class, OpenSSLAnalyzer.class, NodePackageAnalyzer.class, LibmanAnalyzer.class, + NodeAuditAnalyzer.class, YarnAuditAnalyzer.class, PnpmAuditAnalyzer.class, RetireJsAnalyzer.class, + FalsePositiveAnalyzer.class); + private static BigDecimal minV2Severity; private static BigDecimal minV3Severity; + private static final Set actuallyIgnoredCves = new HashSet<>(); + + private static IdeContext context; + + /** + * @param args Set {@code minV2Severity} with {@code args[0]} and {@code minV3Severity} with {@code args[1]}. + * Vulnerabilities with severity lower than these values will not be added to {@link UrlSecurityJsonFile}. + */ public static void main(String[] args) { if (args.length != 2) { @@ -58,38 +107,26 @@ public static void main(String[] args) { private static void run() { - IdeContext ideContext = new IdeContextConsole(IdeLogLevel.INFO, null, false); - UpdateManager updateManager = new UpdateManager(ideContext.getUrlsPath(), null); - - // TODO edit dependency check properties file to switch off analysers, this file is currently read only - // TODO maybe this can be done in pom.xml - // or simply remove it like FileNameAnalyzer was removed + initCvesToIgnore(); + context = new IdeContextConsole(IdeLogLevel.INFO, null, false); + UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); - // note: settings.setBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_USE_CACHE, false); - - // TODO ~/.m2/repository/org/owasp/dependency-check-utils/8.4.2/data/7.0/odc.update.lock - // why is this not in projects dir but in user dir? - - // requires the STATUS_JSON file to be inside the version folder Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); - + Set> foundToolsAndEditions = new HashSet<>(); for (Dependency dependency : dependencies) { - String filePath = dependency.getFilePath(); Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); - UrlSecurityJsonFile securityFile = ideContext.getUrls().getEdition(tool, edition).getSecurityJsonFile(); - - // TODO maybe instead of clear check cve name and add only if cve name is not already present - // TODO if new min security is higher than the severity in the loaded file, then remove the old one? - - // TODO wenn dieses repo auch als nightly laufen soll, wo sollen dann die min severity werte herkommen? - securityFile.clearSecurityWarnings(); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool, edition).getSecurityJsonFile(); + boolean newlyAdded = foundToolsAndEditions.add(new Pair<>(tool, edition)); + if (newlyAdded) { // to assure that the file is cleared only once per tool and edition + securityFile.clearSecurityWarnings(); + } - List sortedVersions = ideContext.getUrls().getSortedVersions(tool, edition); + List sortedVersions = context.getUrls().getSortedVersions(tool, edition); List sortedCpeVersions = sortedVersions.stream().map(VersionIdentifier::toString) .map(urlUpdater::mapUrlVersionToCpeVersion).map(VersionIdentifier::of) .collect(Collectors.toCollection(ArrayList::new)); @@ -100,20 +137,24 @@ private static void run() { } securityFile.save(); } + actuallyIgnoredCves.forEach(cve -> context.info("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); } private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { Settings settings = new Settings(); - // Using "try with resource" or engine.close() at the end resulted in SEVERE warning by owasp + // Using "try with resource" or engine.close() at the end resulted in SEVERE warning by OWASP. Engine engine = new Engine(settings); - FileTypeAnalyzer myAnalyzer = new UrlAnalyzer(updateManager); - engine.getFileTypeAnalyzers().add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(myAnalyzer); - engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).removeIf(analyze -> analyze instanceof FileNameAnalyzer); - // engine.scan(ideContext.getUrlsPath().toString()); - engine.scan("C:\\projects\\_ide\\myUrls"); + FileTypeAnalyzer urlAnalyzer = new UrlAnalyzer(updateManager); + engine.getFileTypeAnalyzers().add(urlAnalyzer); + engine.getAnalyzers(AnalysisPhase.INFORMATION_COLLECTION).add(urlAnalyzer); + + // remove all analyzers that are not needed + engine.getMode().getPhases().forEach( + phase -> engine.getAnalyzers(phase).removeIf(analyzer -> ANALYZERS_TO_IGNORE.contains(analyzer.getClass()))); + + engine.scan(updateManager.getUrlRepository().getPath().toString()); try { engine.analyzeDependencies(); @@ -127,6 +168,7 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, List sortedVersions, List sortedCpeVersions) { if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { + // if this ever happens, add a case that handles this throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); } boolean hasV3Severity = vulnerability.getCvssV3() != null; @@ -136,6 +178,11 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, BigDecimal severity = new BigDecimal(formatted); String severityVersion = hasV3Severity ? "v3" : "v2"; String cveName = vulnerability.getName(); + if (CVES_TO_IGNORE.contains(cveName)) { + actuallyIgnoredCves.add(cveName); + return; + } + String description = vulnerability.getDescription(); String nistUrl = CVE_BASE_URL + cveName; List referenceUrls = vulnerability.getReferences().stream().map(Reference::getUrl) @@ -158,9 +205,18 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, vulnerability.getName()); return; } + // for manual detection of false positives: check of the interval of affected versions is correctly transformed to + // the versionRange + VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); + String matchedCpe = matchedVulnerableSoftware.toCpe23FS(); + // the fields of cpe2.3 are: + // cpe2.3 part, vendor, product, version, update, edition, language, swEdition, targetSw, targetHw, other; + String interval = String.format(" ( %s, [ %s, ] %s, ) %s", matchedVulnerableSoftware.getVersionStartExcluding(), + matchedVulnerableSoftware.getVersionStartIncluding(), matchedVulnerableSoftware.getVersionEndIncluding(), + matchedVulnerableSoftware.getVersionEndExcluding()); - securityFile.addSecurityWarning(versionRange, severity, severityVersion, cveName, description, nistUrl, - referenceUrls); + securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, severityVersion, cveName, description, + nistUrl, referenceUrls); } @@ -183,7 +239,8 @@ static VersionRange getVersionRangeFromVulnerability(List sor String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { - return VersionRange.of(">"); + String singleAffectedVersion = vulnerability.getMatchedVulnerableSoftware().getVersion(); + return VersionRange.of(singleAffectedVersion + ">" + singleAffectedVersion); } return getVersionRangeFromInterval(sortedVersions, sortedCpeVersions, vStartExcluding, vStartIncluding, @@ -241,7 +298,7 @@ private static VersionIdentifier findMinFromStartExcluding(List= 0; i--) { VersionIdentifier version = sortedCpeVs.get(i); - if (version.isGreater(startExcl)) { + if (version.isGreater(startExcl) && !version.compareVersion(startExcl).isUnsafe()) { return sortedVs.get(i); } } @@ -287,4 +344,18 @@ private static VersionIdentifier findMaxFromEndExcluding(List return null; } + private static void printAllOwaspAnalyzers(Engine engine) { + + engine.getMode().getPhases().forEach(phase -> engine.getAnalyzers(phase) + .forEach(analyzer -> System.out.println("Phase: " + phase + ", Analyzer: " + analyzer.getName()))); + } + + private static void initCvesToIgnore() { + + if (CVES_TO_IGNORE.isEmpty()) { + // ......................................vendor......product......why was is ignored + CVES_TO_IGNORE.add("CVE-2021-36230"); // hashicorp...terraform....https://github.com/anchore/grype/issues/1377 + } + } + } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 7ec2df8c8..9e3eac14e 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -4,7 +4,6 @@ import java.nio.file.Path; import java.nio.file.Paths; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -15,12 +14,13 @@ import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; public class UrlAnalyzer extends AbstractFileTypeAnalyzer { // The file filter is used to filter supported files. - private FileFilter fileFilter = null; + private final FileFilter fileFilter; private static final String ANALYZER_NAME = "UrlAnalyzer"; @@ -33,7 +33,7 @@ public UrlAnalyzer(UpdateManager updateManager) { } @Override - protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + protected void analyzeDependency(Dependency dependency, Engine engine) { String filePath = dependency.getFilePath(); Path parent = Paths.get(filePath).getParent(); @@ -42,35 +42,28 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); - String source = "UrlAnalyzer"; - // adding vendor evidence - String vendor = urlUpdater.getCpeVendor(); - Evidence evidence; - if (vendor == null) { - vendor = tool; + String cpeVendor = urlUpdater.getCpeVendor(); + String cpeProduct = urlUpdater.getCpeProduct(); + String cpeEdition = urlUpdater.getCpeEdition(); + String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(parent.getFileName().toString()); + + if (cpeVendor == null || cpeProduct == null) { + return; } - evidence = new Evidence(source, "CpeVendor", vendor, Confidence.HIGH); + Evidence evidence; + evidence = new Evidence(ANALYZER_NAME, "CpeVendor", cpeVendor, Confidence.HIGH); dependency.addEvidence(EvidenceType.VENDOR, evidence); - // adding product evidence - String product = urlUpdater.getCpeProduct(); - if (product == null) { // for the product it is reasonable to assume that "tool" is the product in most cases - product = tool; - } - evidence = new Evidence(source, "CpeProduct", product, Confidence.HIGH); + evidence = new Evidence(ANALYZER_NAME, "CpeProduct", cpeProduct, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); - // adding edition evidence - String editionEvidence = urlUpdater.getCpeEdition(); - if (editionEvidence != null) { - evidence = new Evidence(source, "CpeEdition", editionEvidence, Confidence.HIGH); + if (cpeEdition != null) { + evidence = new Evidence(ANALYZER_NAME, "CpeEdition", cpeEdition, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); } - // adding version evidence - String version = urlUpdater.mapUrlVersionToCpeVersion(parent.getFileName().toString()); - evidence = new Evidence(source, "CpeVersion", version, Confidence.HIGH); + evidence = new Evidence(ANALYZER_NAME, "CpeVersion", cpeVersion, Confidence.HIGH); dependency.addEvidence(EvidenceType.VERSION, evidence); } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index ca6b5a4fa..19193c584 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -13,8 +13,6 @@ public UrlFileFilter() { @Override public boolean accept(java.io.File pathname) { -// System.out.println("UrlFileFilter.accept()" + pathname.getName().equals(STATUS_JSON)); -// return pathname.getName().endsWith("urls") && pathname.getName().startsWith("windows"); return pathname.getName().equals(STATUS_JSON); } } From 9574f8dab61a5243951ce767244d6045dac75b67 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Mon, 18 Dec 2023 10:47:14 +0100 Subject: [PATCH 10/47] #158: VersionRange with open boundaries Extended the VersionRange to also model open boundaries that do not include the specified value. Also wrote tests for this class. --- .../tools/ide/version/BoundaryType.java | 19 +++ .../tools/ide/version/VersionObject.java | 7 +- .../tools/ide/version/VersionRange.java | 137 +++++++++++++++- .../tools/ide/version/VersionRangeTest.java | 152 ++++++++++++++++++ 4 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java new file mode 100644 index 000000000..c8abed59a --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java @@ -0,0 +1,19 @@ +package com.devonfw.tools.ide.version; + +/** + * Enum representing the type of interval regarding its boundaries. + */ +public enum BoundaryType { + + /** Closed interval - includes the specified values at the boundaries. */ + CLOSED, + + /** Open interval - excludes the specified values at the boundaries. */ + OPEN, + + /** Left open interval - excludes the lower bound but includes the upper bound. */ + LEFT_OPEN, + + /** Right open interval - includes the lower bound but excludes the upper bound. */ + RIGHT_OPEN +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java index 5b4b4c871..c72e9d9f8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java @@ -3,12 +3,11 @@ /** * Abstract base interface for a version object such as {@link VersionIdentifier} and {@link VersionSegment}. * - * * {@link Comparable} for versions with an extended contract. If two versions are not strictly comparable (e.g. * "1.apple" and "1.banana") we fall back to some heuristics (e.g. lexicographical comparison for - * {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore you can - * use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional information - * as {@link VersionComparisonResult#isUnsafe() unsafe} flag. + * {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore, you + * can use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional + * information as {@link VersionComparisonResult#isUnsafe() unsafe} flag. * * @param type of the object to compare (this class itself). */ diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index d09021a2b..8eed7f151 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -1,7 +1,9 @@ package com.devonfw.tools.ide.version; /** - * Container for a range of versions. + * Container for a range of versions. The lower and upper bounds can be exclusive or inclusive. If a bound is null, it + * means that this direction is unbounded. The boolean defining whether this bound is inclusive or exclusive is ignored + * in this case. */ public final class VersionRange implements Comparable { @@ -9,6 +11,10 @@ public final class VersionRange implements Comparable { private final VersionIdentifier max; + private final boolean leftIsExclusive; + + private final boolean rightIsExclusive; + /** * The constructor. * @@ -20,6 +26,42 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) { super(); this.min = min; this.max = max; + this.leftIsExclusive = false; + this.rightIsExclusive = false; + } + + /** + * The constructor. + * + * @param min the {@link #getMin() minimum}. + * @param max the {@link #getMax() maximum}. + * @param boundaryType the {@link BoundaryType} defining whether the boundaries of the range are inclusive or + * exclusive. + */ + public VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) { + + super(); + this.min = min; + this.max = max; + this.leftIsExclusive = BoundaryType.LEFT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); + this.rightIsExclusive = BoundaryType.RIGHT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); + } + + /** + * The constructor. + * + * @param min the {@link #getMin() minimum}. + * @param max the {@link #getMax() maximum}. + * @param leftIsExclusive - {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. + * @param rightIsExclusive - {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. + */ + public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIsExclusive, boolean rightIsExclusive) { + + super(); + this.min = min; + this.max = max; + this.leftIsExclusive = leftIsExclusive; + this.rightIsExclusive = rightIsExclusive; } /** @@ -38,6 +80,38 @@ public VersionIdentifier getMax() { return this.max; } + /** + * @return {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. + */ + public boolean isLeftExclusive() { + + return this.leftIsExclusive; + } + + /** + * @return {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. + */ + public boolean isRightExclusive() { + + return this.rightIsExclusive; + } + + /** + * @return the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive. + */ + public BoundaryType getBoundaryType() { + + if (this.leftIsExclusive && this.rightIsExclusive) { + return BoundaryType.OPEN; + } else if (this.leftIsExclusive) { + return BoundaryType.LEFT_OPEN; + } else if (this.rightIsExclusive) { + return BoundaryType.RIGHT_OPEN; + } else { + return BoundaryType.CLOSED; + } + } + /** * @param version the {@link VersionIdentifier} to check. * @return {@code true} if the given {@link VersionIdentifier} is contained in this {@link VersionRange}, @@ -46,11 +120,17 @@ public VersionIdentifier getMax() { public boolean contains(VersionIdentifier version) { if (this.min != null) { + if (this.min.equals(version)) { + return !this.leftIsExclusive; + } if (version.isLess(this.min)) { return false; } } if (this.max != null) { + if (this.max.equals(version)) { + return !this.rightIsExclusive; + } if (version.isGreater(this.max)) { return false; } @@ -69,13 +149,44 @@ public int compareTo(VersionRange o) { } return -1; } - return this.min.compareTo(o.min); + int compareMins = this.min.compareTo(o.min); + if (compareMins == 0) { + return this.leftIsExclusive == o.leftIsExclusive ? 0 : this.leftIsExclusive ? 1 : -1; + } else { + return compareMins; + } + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) + return true; + + if (obj == null || getClass() != obj.getClass()) + return false; + + VersionRange o = (VersionRange) obj; + + if (this.min == null && this.max == null) { + return o.min == null && o.max == null; + } + if (this.min == null) { + return o.min == null && this.max.equals(o.max) && this.rightIsExclusive == o.rightIsExclusive; + } + if (this.max == null) { + return this.min.equals(o.min) && o.max == null && this.leftIsExclusive == o.leftIsExclusive; + } + return this.min.equals(o.min) && this.leftIsExclusive == o.leftIsExclusive && this.max.equals(o.max) + && this.rightIsExclusive == o.rightIsExclusive; + } @Override public String toString() { StringBuilder sb = new StringBuilder(); + sb.append(this.leftIsExclusive ? '(' : '['); if (this.min != null) { sb.append(this.min); } @@ -83,6 +194,7 @@ public String toString() { if (this.max != null) { sb.append(this.max); } + sb.append(this.rightIsExclusive ? ')' : ']'); return sb.toString(); } @@ -92,10 +204,29 @@ public String toString() { */ public static VersionRange of(String value) { + boolean leftIsExclusive = false; + boolean rightIsExclusive = false; + + if (value.startsWith("(")) { + leftIsExclusive = true; + value = value.substring(1); + } + if (value.startsWith("[")) { + value = value.substring(1); + } + if (value.endsWith(")")) { + rightIsExclusive = true; + value = value.substring(0, value.length() - 1); + } + if (value.endsWith("]")) { + value = value.substring(0, value.length() - 1); + } + int index = value.indexOf('>'); if (index == -1) { return null; // log warning? } + VersionIdentifier min = null; if (index > 0) { min = VersionIdentifier.of(value.substring(0, index)); @@ -105,7 +236,7 @@ public static VersionRange of(String value) { if (!maxString.isEmpty()) { max = VersionIdentifier.of(maxString); } - return new VersionRange(min, max); + return new VersionRange(min, max, leftIsExclusive, rightIsExclusive); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java new file mode 100644 index 000000000..0a305f8a6 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java @@ -0,0 +1,152 @@ +package com.devonfw.tools.ide.version; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Test of {@link VersionRange}. + */ +public class VersionRangeTest extends Assertions { + + /** + * Test of {@link VersionRange#of(String)}. + */ + @Test + public void testOf() { + + // arrange + String v1String = "1.2>3"; + String v2String = "1>)"; + String v3String = "(1.2>3.4]"; + + // act + VersionRange v1 = VersionRange.of(v1String); + VersionRange v2 = VersionRange.of(v2String); + VersionRange v3 = VersionRange.of(v3String); + + // assert + // v1 + assertThat(v1.getMin()).isEqualTo(VersionIdentifier.of("1.2")); + assertThat(v1.getMax()).isEqualTo(VersionIdentifier.of("3")); + assertThat(v1.isLeftExclusive()).isFalse(); + assertThat(v1.isRightExclusive()).isFalse(); + // v2 + assertThat(v2.getMin()).isEqualTo(VersionIdentifier.of("1")); + assertThat(v2.getMax()).isEqualTo(null); + assertThat(v2.isLeftExclusive()).isFalse(); + assertThat(v2.isRightExclusive()).isTrue(); + // v3 + assertThat(v3.getMin()).isEqualTo(VersionIdentifier.of("1.2")); + assertThat(v3.getMax()).isEqualTo(VersionIdentifier.of("3.4")); + assertThat(v3.isLeftExclusive()).isTrue(); + assertThat(v3.isRightExclusive()).isFalse(); + } + + /** + * Test of {@link VersionRange#toString()}. + */ + @Test + public void testToString() { + + assertThat(VersionRange.of("1.2>3").toString()).isEqualTo("[1.2>3]"); + assertThat(VersionRange.of("1>)").toString()).isEqualTo("[1>)"); + assertThat(VersionRange.of("(1.2>3.4]").toString()).isEqualTo("(1.2>3.4]"); + } + + /** + * Test of {@link VersionRange#equals(Object)}. + */ + @Test + public void testEquals() { + + // assert + // equals + assertThat(VersionRange.of("1.2>")).isEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("(1.2>")).isEqualTo(VersionRange.of("(1.2>)")); + assertThat(VersionRange.of("1.2>3")).isEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of("[1.2>3")).isEqualTo(VersionRange.of("1.2>3]")); + assertThat(VersionRange.of(">3)")).isEqualTo(VersionRange.of(">3)")); + assertThat(VersionRange.of(">")).isEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of("[>)")).isEqualTo(VersionRange.of("(>]")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isEqualTo(VersionRange.of("8u302b08>11.0.14_9")); + // not equals + assertThat(VersionRange.of("1>")).isNotEqualTo(null); + assertThat(VersionRange.of("1.2>")).isNotEqualTo(VersionRange.of("1>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("(1.2>3")).isNotEqualTo(VersionRange.of("1.2.3>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("[1.2>")).isNotEqualTo(VersionRange.of("[1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of(">")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("(8u302b08>11.0.14_9)")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.15_9")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.14_0")); + } + + /** + * Test of {@link VersionRange#contains(VersionIdentifier)} and testing if a {@link VersionIdentifier version} is + * contained in the {@link VersionRange}. + */ + @Test + public void testContains() { + + // assert + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.2"))).isTrue(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4"))).isTrue(); + + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2.1"))).isTrue(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.3.9"))).isTrue(); + } + + /** + * Test of {@link VersionRange#contains(VersionIdentifier)} and testing if a {@link VersionIdentifier version} is not + * contained in the {@link VersionRange}. + */ + @Test + public void testNotContains() { + + // assert + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.1"))).isFalse(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4.1"))).isFalse(); + + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2"))).isFalse(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.4"))).isFalse(); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if versions are compared to be the same. + */ + @Test + public void testCompareToIsSame() { + + // assert + assertThat(VersionRange.of("1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(0); + assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("(1.2>3"))).isEqualTo(0); + assertThat(VersionRange.of("[1.2>3]").compareTo(VersionRange.of("[1.2>4)"))).isEqualTo(0); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is smaller than second. + */ + @Test + public void testCompareToIsSmaller() { + + // assert + assertThat(VersionRange.of("1.1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(-1); + assertThat(VersionRange.of("[1.2>3").compareTo(VersionRange.of("(1.2>4"))).isEqualTo(-1); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is larger than second. + */ + @Test + public void testCompareToIsLarger() { + + // assert + assertThat(VersionRange.of("1.2.1>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(1); + assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("1.2>4"))).isEqualTo(1); + } +} From 55181385c863c02d68d43cef707f61335b71de86 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 19 Dec 2023 10:01:14 +0100 Subject: [PATCH 11/47] #103: removed duplicate VersionRange.equals --- .../tools/ide/version/VersionRange.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index 52d97c703..8f19dd228 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -188,30 +188,6 @@ public boolean equals(Object obj) { } - @Override - public boolean equals(Object obj) { - - if (this == obj) - return true; - - if (obj == null || getClass() != obj.getClass()) - return false; - - VersionRange o = (VersionRange) obj; - - if (this.min == null && this.max == null) { - return o.min == null && o.max == null; - } - if (this.min == null) { - return o.min == null && this.max.equals(o.max); - } - if (this.max == null) { - return this.min.equals(o.min) && o.max == null; - } - return this.min.equals(o.min) && this.max.equals(o.max); - - } - @Override @JsonValue public String toString() { From 4fbef6e838820f54107a3a079102e85c74871523 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 19 Dec 2023 15:31:37 +0100 Subject: [PATCH 12/47] #103: versionRange with open interval --- .../tools/ide/tool/helm/HelmUrlUpdater.java | 5 + .../ide/url/updater/AbstractUrlUpdater.java | 26 ++- .../tools/ide/version/VersionIdentifier.java | 5 + .../tools/ide/version/VersionRange.java | 64 ++++++-- security/pom.xml | 12 ++ .../tools/security/BuildSecurityJsonFile.java | 148 +++++------------- .../security/BuildSecurityJsonFileTest.java | 105 ++----------- 7 files changed, 140 insertions(+), 225 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index 7bdcbf6fa..8f4311f4e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -47,6 +47,11 @@ public String mapUrlVersionToCpeVersion(String version) { return version.substring(getVersionPrefixToRemove().length()); } + public String mapCpeVersionToUrlVersion(String version) { + + return getVersionPrefixToRemove() + version; + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 5803708fa..0e6cec0d1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -105,6 +105,7 @@ public String getCpeVendor() { return null; } + /*** * @return the product name of the tool as specified in the CPE (Common Platform Enumeration) */ @@ -121,15 +122,23 @@ public String getCpeEdition() { return null; } - /*** + /** * @return maps the version as specified by the directory name in the url repository to the version as specified in - * the CPE (Common Platform Enumeration). + * the CPE (Common Platform Enumeration). */ public String mapUrlVersionToCpeVersion(String version) { return version; } + /** + * @return maps the cpe version to the version as specified in the CPE (Common Platform Enumeration). + */ + public String mapCpeVersionToUrlVersion(String version) { + + return version; + } + /** * Retrieves the response body from a given URL. * @@ -294,9 +303,9 @@ private boolean isValidDownload(String url, String tool, String version, HttpRes if (isSuccess(response)) { String contentType = response.headers().firstValue("content-type").orElse("undefined"); boolean isValidContentType = isValidContentType(contentType); - if (!isValidContentType){ + if (!isValidContentType) { logger.error("For tool {} and version {} the download has an invalid content type {} for URL {}", tool, version, - contentType, url); + contentType, url); return false; } return true; @@ -306,7 +315,8 @@ private boolean isValidDownload(String url, String tool, String version, HttpRes } /** - * Checks if the content type was not of type text (this method is required because {@link com.devonfw.tools.ide.tool.pip.PipUrlUpdater} returns text and needs to be overridden) + * Checks if the content type was not of type text (this method is required because + * {@link com.devonfw.tools.ide.tool.pip.PipUrlUpdater} returns text and needs to be overridden) *

* See: #1343 for reference. * @@ -351,7 +361,7 @@ private boolean checkDownloadUrl(String url, UrlVersion urlVersion, OperatingSys if (success) { if (checksum == null || checksum.isEmpty()) { String contentType = response.headers().firstValue("content-type").orElse("undefined"); - checksum = doGenerateChecksum(doGetResponseAsStream( url), url, version, contentType); + checksum = doGenerateChecksum(doGetResponseAsStream(url), url, version, contentType); } success = isChecksumStillValid(url, urlVersion, os, architecture, checksum, tool, version); @@ -472,8 +482,8 @@ private void doUpdateStatusJson(boolean success, int statusCode, UrlVersion urlV modified = true; } - logger.info("For tool {} and version {} the download verification succeeded with status code {} for URL {}.", tool, - version, code, url); + logger.info("For tool {} and version {} the download verification succeeded with status code {} for URL {}.", + tool, version, code, url); } else { if (status != null) { if (errorStatus == null) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionIdentifier.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionIdentifier.java index 797ee49e2..f7f9d3631 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionIdentifier.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionIdentifier.java @@ -1,5 +1,8 @@ package com.devonfw.tools.ide.version; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import java.util.Objects; /** @@ -191,6 +194,7 @@ public boolean equals(Object obj) { } @Override + @JsonSerialize public String toString() { StringBuilder sb = new StringBuilder(); @@ -206,6 +210,7 @@ public String toString() { * @param version the {@link #toString() string representation} of the {@link VersionIdentifier} to parse. * @return the parsed {@link VersionIdentifier}. */ + @JsonCreator public static VersionIdentifier of(String version) { if (version.equals("latest")) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index 8f19dd228..8cd63029d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -18,13 +18,47 @@ public final class VersionRange implements Comparable { private final boolean rightIsExclusive; + private static final String VERSION_SEPARATOR = ">"; + + private static final String START_EXCLUDING_PREFIX = "("; + + private static final String START_INCLUDING_PREFIX = "["; + + private static final String END_EXCLUDING_SUFFIX = ")"; + + private static final String END_INCLUDING_SUFFIX = "]"; + + public static String getVersionSeparator() { + + return VERSION_SEPARATOR; + } + + public static String getStartExcludingPrefix() { + + return START_EXCLUDING_PREFIX; + } + + public static String getStartIncludingPrefix() { + + return START_INCLUDING_PREFIX; + } + + public static String getEndExcludingSuffix() { + + return END_EXCLUDING_SUFFIX; + } + + public static String getEndIncludingSuffix() { + + return END_INCLUDING_SUFFIX; + } + /** * The constructor. * * @param min the {@link #getMin() minimum}. - * @param max the {@link #getMax() maximum} (including). + * @param max the {@link #getMax() maximum}. */ - public VersionRange(VersionIdentifier min, VersionIdentifier max) { super(); @@ -71,7 +105,6 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIs /** * @return the minimum {@link VersionIdentifier} or {@code null} for no lower bound. */ - // @JsonBackReference public VersionIdentifier getMin() { return this.min; @@ -80,7 +113,6 @@ public VersionIdentifier getMin() { /** * @return the maximum {@link VersionIdentifier} or {@code null} for no upper bound. */ - // @JsonBackReference public VersionIdentifier getMax() { return this.max; @@ -193,15 +225,15 @@ public boolean equals(Object obj) { public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(this.leftIsExclusive ? '(' : '['); + sb.append(this.leftIsExclusive ? START_EXCLUDING_PREFIX : START_INCLUDING_PREFIX); if (this.min != null) { sb.append(this.min); } - sb.append('>'); + sb.append(VERSION_SEPARATOR); if (this.max != null) { sb.append(this.max); } - sb.append(this.rightIsExclusive ? ')' : ']'); + sb.append(this.rightIsExclusive ? END_EXCLUDING_SUFFIX : END_INCLUDING_SUFFIX); return sb.toString(); } @@ -215,22 +247,22 @@ public static VersionRange of(String value) { boolean leftIsExclusive = false; boolean rightIsExclusive = false; - if (value.startsWith("(")) { + if (value.startsWith(START_EXCLUDING_PREFIX)) { leftIsExclusive = true; - value = value.substring(1); + value = value.substring(START_EXCLUDING_PREFIX.length()); } - if (value.startsWith("[")) { - value = value.substring(1); + if (value.startsWith(START_INCLUDING_PREFIX)) { + value = value.substring(START_INCLUDING_PREFIX.length()); } - if (value.endsWith(")")) { + if (value.endsWith(END_EXCLUDING_SUFFIX)) { rightIsExclusive = true; - value = value.substring(0, value.length() - 1); + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); } - if (value.endsWith("]")) { - value = value.substring(0, value.length() - 1); + if (value.endsWith(END_INCLUDING_SUFFIX)) { + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); } - int index = value.indexOf('>'); + int index = value.indexOf(VERSION_SEPARATOR); if (index == -1) { return null; // log warning? } diff --git a/security/pom.xml b/security/pom.xml index 4838fe849..369d05e9a 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -31,5 +31,17 @@ compile + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + \ No newline at end of file diff --git a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java index 6ed39c476..640571c4e 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java @@ -3,10 +3,10 @@ import java.math.BigDecimal; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -50,7 +50,6 @@ import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; -import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; // TODO Doesn't yet work with versions defined like this ///latest @@ -126,14 +125,9 @@ private static void run() { securityFile.clearSecurityWarnings(); } - List sortedVersions = context.getUrls().getSortedVersions(tool, edition); - List sortedCpeVersions = sortedVersions.stream().map(VersionIdentifier::toString) - .map(urlUpdater::mapUrlVersionToCpeVersion).map(VersionIdentifier::of) - .collect(Collectors.toCollection(ArrayList::new)); - Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { - addVulnerabilityToSecurityFile(vulnerability, securityFile, sortedVersions, sortedCpeVersions); + addVulnerabilityToSecurityFile(vulnerability, securityFile, urlUpdater); } securityFile.save(); } @@ -165,7 +159,7 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd } private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, - List sortedVersions, List sortedCpeVersions) { + AbstractUrlUpdater urlUpdater) { if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { // if this ever happens, add a case that handles this @@ -196,7 +190,7 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (toLowSeverity) { return; } - VersionRange versionRange = getVersionRangeFromVulnerability(sortedVersions, sortedCpeVersions, vulnerability); + VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, urlUpdater); if (versionRange == null) { logger.info( "Vulnerability {} seems to be irrelevant because its affected versions have no overlap with the versions " @@ -223,125 +217,61 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, /*** * From the vulnerability determine the {@link VersionRange versionRange} to which the vulnerability applies. * - * @param sortedVersions sorted versions of the tool available through IDEasy. - * @param sortedCpeVersions sorted versions of the tool. Must match the format of the CPE versions. See - * {@link AbstractUrlUpdater#mapUrlVersionToCpeVersion(String)}. * @param vulnerability the vulnerability determined by OWASP dependency check. + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to which the vulnerability applies. TODOO * @return the {@link VersionRange versionRange} to which the vulnerability applies. */ - static VersionRange getVersionRangeFromVulnerability(List sortedVersions, - List sortedCpeVersions, Vulnerability vulnerability) { + static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, AbstractUrlUpdater urlUpdater) { VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); - String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); - String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); + String vStartExcluding = matchedVulnerableSoftware.getVersionStartExcluding(); String vStartIncluding = matchedVulnerableSoftware.getVersionStartIncluding(); + String vEndExcluding = matchedVulnerableSoftware.getVersionEndExcluding(); + String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); + String singleVersion = matchedVulnerableSoftware.getVersion(); - if (vEndExcluding == null && vEndIncluding == null && vStartExcluding == null && vStartIncluding == null) { - String singleAffectedVersion = vulnerability.getMatchedVulnerableSoftware().getVersion(); - return VersionRange.of(singleAffectedVersion + ">" + singleAffectedVersion); - } - - return getVersionRangeFromInterval(sortedVersions, sortedCpeVersions, vStartExcluding, vStartIncluding, - vEndIncluding, vEndExcluding); - } - - static VersionRange getVersionRangeFromInterval(List sortedVersions, String vStartExcluding, - String vStartIncluding, String vEndIncluding, String vEndExcluding) { - - return getVersionRangeFromInterval(sortedVersions, sortedVersions, vStartExcluding, vStartIncluding, vEndIncluding, - vEndExcluding); - } + vEndExcluding = Optional.ofNullable(vEndExcluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); + vEndIncluding = Optional.ofNullable(vEndIncluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); + vStartExcluding = Optional.ofNullable(vStartExcluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); + vStartIncluding = Optional.ofNullable(vStartIncluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); + singleVersion = Optional.ofNullable(singleVersion).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); - /*** - * From the interval determine the {@link VersionRange versionRange} to which the vulnerability applies. Since the - * versions as specified in the vulnerability might not be in the {@code sortedVersions} list, the - * {@link VersionRange} is determined by finding the versions in the {@code sortedVersions} list that, when selected, - * cover all affected versions correctly. - */ - static VersionRange getVersionRangeFromInterval(List sortedVersions, - List sortedCpeVersions, String vStartExcluding, String vStartIncluding, String vEndIncluding, - String vEndExcluding) { - - VersionIdentifier min = null; - if (vStartExcluding != null) { - min = findMinFromStartExcluding(sortedVersions, sortedCpeVersions, vStartExcluding); - if (min == null) { - return null; - } - } else if (vStartIncluding != null) { - min = findMinFromStartIncluding(sortedVersions, sortedCpeVersions, vStartIncluding); - if (min == null) { - return null; - } - } - - VersionIdentifier max = null; - if (vEndIncluding != null) { - max = findMaxFromEndIncluding(sortedVersions, sortedCpeVersions, vEndIncluding); - if (max == null) { - return null; - } - } else if (vEndExcluding != null) { - max = findMaxFromEndExcluding(sortedVersions, sortedCpeVersions, vEndExcluding); - if (max == null) { - return null; - } + VersionRange affectedRange = null; + try { + affectedRange = getVersionRangeFromInterval(vStartIncluding, vStartExcluding, vEndExcluding, vEndIncluding, + singleVersion); + } catch (IllegalStateException e) { + throw new IllegalStateException( + "Getting the VersionRange for the vulnerability " + vulnerability.getName() + " failed.", e); } - return new VersionRange(min, max); - } - private static VersionIdentifier findMinFromStartExcluding(List sortedVs, - List sortedCpeVs, String vStartExcluding) { + return affectedRange; - VersionIdentifier startExcl = VersionIdentifier.of(vStartExcluding); - for (int i = sortedCpeVs.size() - 1; i >= 0; i--) { - VersionIdentifier version = sortedCpeVs.get(i); - if (version.isGreater(startExcl) && !version.compareVersion(startExcl).isUnsafe()) { - return sortedVs.get(i); - } - } - return null; } - private static VersionIdentifier findMinFromStartIncluding(List sortedVs, - List sortedCpeVs, String vStartIncluding) { + public static VersionRange getVersionRangeFromInterval(String si, String se, String ee, String ei, String s) + throws IllegalStateException { - VersionIdentifier startIncl = VersionIdentifier.of(vStartIncluding); - for (int i = sortedCpeVs.size() - 1; i >= 0; i--) { - VersionIdentifier version = sortedCpeVs.get(i); - if (version.compareTo(startIncl) >= 0) { - return sortedVs.get(i); + if (ee == null && ei == null && se == null && si == null) { + if (s == null) { + throw new IllegalStateException( + "Vulnerability has no interval of affected versions or single affected version."); } + return VersionRange.of(s + ">" + s); } - return null; - } + se = Optional.ofNullable(se).orElse(""); + si = Optional.ofNullable(si).orElse(""); + ee = Optional.ofNullable(ee).orElse(""); + ei = Optional.ofNullable(ei).orElse(""); - private static VersionIdentifier findMaxFromEndIncluding(List sortedVs, - List sortedCpeVs, String vEndIncluding) { + String leftBoundary = se.isEmpty() ? VersionRange.getStartIncludingPrefix() + si + : VersionRange.getStartExcludingPrefix() + se; - VersionIdentifier endIncl = VersionIdentifier.of(vEndIncluding); - for (int i = 0; i < sortedCpeVs.size(); i++) { - VersionIdentifier version = sortedCpeVs.get(i); - if (version.compareTo(endIncl) <= 0) { - return sortedVs.get(i); - } - } - return null; - } + String rightBoundary = ee.isEmpty() ? ei + VersionRange.getEndIncludingSuffix() + : ee + VersionRange.getEndExcludingSuffix(); - private static VersionIdentifier findMaxFromEndExcluding(List sortedVs, - List sortedCpeVs, String vEndExcluding) { - - VersionIdentifier endExl = VersionIdentifier.of(vEndExcluding); - for (int i = 0; i < sortedCpeVs.size(); i++) { - VersionIdentifier version = sortedCpeVs.get(i); - if (version.isLess(endExl)) { - return sortedVs.get(i); - } - } - return null; + return VersionRange.of(leftBoundary + VersionRange.getVersionSeparator() + rightBoundary); } private static void printAllOwaspAnalyzers(Engine engine) { diff --git a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java index d8f926483..a36350fa8 100644 --- a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java +++ b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java @@ -1,107 +1,28 @@ package com.devonfw.tools.security; -import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.List; - import static com.devonfw.tools.security.BuildSecurityJsonFile.getVersionRangeFromInterval; +/** + * Test of {@link BuildSecurityJsonFile}. + */ public class BuildSecurityJsonFileTest extends Assertions { - /*** - * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing - * vStartExcluding and null for the other parameters . + /** + * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(String, String, String, String, String)}. */ @Test - public void testGetVersionRangeFromIntervalStartExcluding() { - - // arrange - List v = getSortedVersions(); + public void testGetVersionRangeFromInterval() { // act & assert - assertThat(getVersionRangeFromInterval(v, null, null, null, null)).isEqualTo(VersionRange.of(">")); - assertThat(getVersionRangeFromInterval(v, "1", null, null, null)).isEqualTo(VersionRange.of("1.2.3>")); - assertThat(getVersionRangeFromInterval(v, "1.2.3", null, null, null)).isEqualTo(VersionRange.of("1.2.4>")); - assertThat(getVersionRangeFromInterval(v, "1.4", null, null, null)).isEqualTo(VersionRange.of("2.0>")); - assertThat(getVersionRangeFromInterval(v, "1.5", null, null, null)).isEqualTo(VersionRange.of("2.0>")); - assertThat(getVersionRangeFromInterval(v, "2.1", null, null, null)).isNull(); - assertThat(getVersionRangeFromInterval(v, "2.2", null, null, null)).isNull(); - - } - /*** - * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing - * vStartIncluding and null for the other parameters . - */ - @Test - public void testGetVersionRangeFromIntervalStartIncluding() { - - // arrange - List v = getSortedVersions(); - - // act & assert - assertThat(getVersionRangeFromInterval(v, null, "1", null, null)).isEqualTo(VersionRange.of("1.2.3>")); - assertThat(getVersionRangeFromInterval(v, null, "1.2.3", null, null)).isEqualTo(VersionRange.of("1.2.3>")); - assertThat(getVersionRangeFromInterval(v, null, "1.4", null, null)).isEqualTo(VersionRange.of("1.4>")); - assertThat(getVersionRangeFromInterval(v, null, "1.5", null, null)).isEqualTo(VersionRange.of("2.0>")); - assertThat(getVersionRangeFromInterval(v, null, "2.1", null, null)).isEqualTo(VersionRange.of("2.1>")); - assertThat(getVersionRangeFromInterval(v, null, "2.2", null, null)).isNull(); - - } - - /*** - * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing - * vEndIncluding and null for the other parameters . - */ - @Test - public void testGetVersionRangeFromIntervalEndIncluding() { - - // arrange - List v = getSortedVersions(); - - // act & assert - assertThat(getVersionRangeFromInterval(v, null, null, "1", null)).isNull(); - assertThat(getVersionRangeFromInterval(v, null, null, "1.2.3", null)).isEqualTo(VersionRange.of(">1.2.3")); - assertThat(getVersionRangeFromInterval(v, null, null, "1.4", null)).isEqualTo(VersionRange.of(">1.4")); - assertThat(getVersionRangeFromInterval(v, null, null, "1.5", null)).isEqualTo(VersionRange.of(">1.4")); - assertThat(getVersionRangeFromInterval(v, null, null, "2.1", null)).isEqualTo(VersionRange.of(">2.1")); - assertThat(getVersionRangeFromInterval(v, null, null, "2.2", null)).isEqualTo(VersionRange.of(">2.1")); - - } - - /*** - * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(List, String, String, String, String)} and passing - * vEndExcluding and null for the other parameters . - */ - @Test - public void testGetVersionRangeFromIntervalEndExcluding() { - - // arrange - List v = getSortedVersions(); - - // act & assert - assertThat(getVersionRangeFromInterval(v, null, null, null, " 1")).isNull(); - assertThat(getVersionRangeFromInterval(v, null, null, null, "1.2.3")).isNull(); - assertThat(getVersionRangeFromInterval(v, null, null, null, "1.4")).isEqualTo(VersionRange.of(">1.3")); - assertThat(getVersionRangeFromInterval(v, null, null, null, "1.5")).isEqualTo(VersionRange.of(">1.4")); - assertThat(getVersionRangeFromInterval(v, null, null, null, "2.1")).isEqualTo(VersionRange.of(">2.0")); - assertThat(getVersionRangeFromInterval(v, null, null, null, "2.2")).isEqualTo(VersionRange.of(">2.1")); - - } - - private static List getSortedVersions() { - - List sortedVersions = new ArrayList<>(); - sortedVersions.add(VersionIdentifier.of("2.1")); - sortedVersions.add(VersionIdentifier.of("2.0")); - sortedVersions.add(VersionIdentifier.of("1.4")); - sortedVersions.add(VersionIdentifier.of("1.3")); - sortedVersions.add(VersionIdentifier.of("1.2.5")); - sortedVersions.add(VersionIdentifier.of("1.2.4")); - sortedVersions.add(VersionIdentifier.of("1.2.3")); - return sortedVersions; + assertThat(getVersionRangeFromInterval("1", null, null, null, null)).isEqualTo(VersionRange.of("1>")); + assertThat(getVersionRangeFromInterval(null, "1", null, null, null)).isEqualTo(VersionRange.of("(1>")); + assertThat(getVersionRangeFromInterval(null, null, "1", null, null)).isEqualTo(VersionRange.of(">1)")); + assertThat(getVersionRangeFromInterval(null, null, null, "1", null)).isEqualTo(VersionRange.of(">1")); + assertThat(getVersionRangeFromInterval(null, null, null, null, "1")).isEqualTo(VersionRange.of("1>1")); + assertThat(getVersionRangeFromInterval(null, "1", null, "2", "1")).isEqualTo(VersionRange.of("(1>2]")); } -} +} \ No newline at end of file From 1b9224b4ed087e2b3a58a2854e4df16e0b2567f5 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 19 Dec 2023 16:06:48 +0100 Subject: [PATCH 13/47] #103: updated urlSecJsonFile.contains if a single warning affects all versions, it is ignored --- .../devonfw/tools/ide/url/model/file/UrlSecurityJsonFile.java | 4 ++-- .../java/com/devonfw/tools/ide/tool/ToolCommandletTest.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) 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 9ae9475ee..a75d8702d 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 @@ -302,9 +302,9 @@ public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAff VersionRange versionRange = warning.getVersionRange(); if (ignoreWarningsThatAffectAllVersions) { boolean includesOldestVersion = versionRange.getMin() == null - || sortedVersions.get(sortedVersions.size() - 1).equals(versionRange.getMin()); + || warning.getVersionRange().contains(sortedVersions.get(sortedVersions.size() - 1)); boolean includesNewestVersion = versionRange.getMax() == null - || sortedVersions.get(0).equals(versionRange.getMax()); + || warning.getVersionRange().contains(sortedVersions.get(0)); if (includesOldestVersion && includesNewestVersion) { continue; } diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 8d8a846de..8e71db7da 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -32,7 +32,8 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of(">")); + securityFile.addSecurityWarning(VersionRange.of(">")); // should get ignored + securityFile.addSecurityWarning(VersionRange.of("0>11")); // should get ignored securityFile.addSecurityWarning(VersionRange.of("2>5")); // act & assert From 9a86e3400e004bb7008ee5e6b64147a2fc038867 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 21 Dec 2023 15:24:46 +0100 Subject: [PATCH 14/47] #103: rephrase interaction, mapUtil, LICENCE also SecurityRiskInteraction returns configured version and latest version when possible. conversion between cpe and ulr version more rebust by using map and inverse function where map fails. Added asciidoc --- .../tools/ide/tool/ToolCommandlet.java | 63 ++---- .../tool/terraform/TerraformUrlUpdater.java | 3 - .../url/model/file/UrlSecurityJsonFile.java | 12 +- .../ide/url/updater/AbstractUrlUpdater.java | 15 +- .../com/devonfw/tools/ide/util/MapUtil.java | 34 +++ .../tools/ide/tool/ToolCommandletTest.java | 36 ++- documentation/LICENSE.asciidoc | 207 ++++++++++++++++++ documentation/vulnerabilities.asciidoc | 45 ++++ ...nFile.java => BuildSecurityJsonFiles.java} | 118 +++++++--- ...t.java => BuildSecurityJsonFilesTest.java} | 8 +- 10 files changed, 441 insertions(+), 100 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java create mode 100644 documentation/vulnerabilities.asciidoc rename security/src/main/java/com/devonfw/tools/security/{BuildSecurityJsonFile.java => BuildSecurityJsonFiles.java} (73%) rename security/src/test/java/com/devonfw/tools/security/{BuildSecurityJsonFileTest.java => BuildSecurityJsonFilesTest.java} (74%) 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 f9a88c58a..01803e3ca 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 @@ -173,16 +173,6 @@ public boolean install(boolean silent) { return doInstall(silent); } - protected String securityRiskInteractionQuestion(String question, String... options) { - - question += "Do you want to"; - for (int i = 0; i < options.length - 1; i++) { - options[i] += " or"; - } - options[options.length - 1] += "?"; - return this.context.question(question, options); - } - /** * Checks if the given {@link VersionIdentifier} has a matching security warning in the {@link UrlSecurityJsonFile}. * @@ -193,16 +183,10 @@ protected String securityRiskInteractionQuestion(String question, String... opti */ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) { - // TODO maybe instead of returning current return configuredVersion if the users chooses "stay" - - // TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n"; - UrlSecurityJsonFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()) .getSecurityJsonFile(); VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion); - // TODO oder doch eher sowas wie VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, - // edition, selectedVersion); sollte immer das selbe ergeben if (!securityFile.contains(current, true, this.context)) { return configuredVersion; @@ -212,7 +196,6 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured VersionIdentifier latest = allVersions.get(0); 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)) { @@ -232,10 +215,12 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; - String stay = "stay with the current unsafe version (" + current + ")"; - String installLatestSafe = "install the latest safe version (" + latestSafe + ")"; - String installSafeLatest = "install the (safe) latest version (" + latest + ")"; - String installNextSafe = "install the next safe version (" + nextSafe + ")"; + String ask = "Which version do you want to install?"; + + String stay = "Stay with the current unsafe version (" + current + ")."; + String installLatestSafe = "Install the latest safe version (" + latestSafe + ")."; + String installSafeLatest = "Install the (safe) latest version (" + latest + ")."; + String installNextSafe = "Install the next safe version (" + nextSafe + ")."; // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose // "stay" @@ -245,38 +230,38 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured } if (current.equals(latest)) { - String answer = securityRiskInteractionQuestion(currentIsUnsafe + "There are no updates available.", stay, + String answer = this.context.question(currentIsUnsafe + "There are no updates available. " + ask, stay, installLatestSafe); - return answer.startsWith(stay) ? current : latestSafe; + return answer.equals(stay) ? configuredVersion : latestSafe; - } else if (nextSafe == null) { - String answer = securityRiskInteractionQuestion(currentIsUnsafe + " All newer versions are also not safe.", stay, + } else if (nextSafe == null) { // install an older version that is safe or stay with the current unsafe version + String answer = this.context.question(currentIsUnsafe + " All newer versions are also not safe. " + ask, stay, installLatestSafe); - return answer.startsWith(stay) ? current : latestSafe; + return answer.equals(stay) ? configuredVersion : latestSafe; } else if (nextSafe.equals(latest)) { - String answer = securityRiskInteractionQuestion( - currentIsUnsafe + " Of the newer versions, only the latest is safe.", stay, installSafeLatest); - return answer.startsWith(stay) ? current : latestSafe; + String answer = this.context.question(currentIsUnsafe + " Of the newer versions, only the latest is safe. " + ask, + stay, installSafeLatest); + return answer.equals(stay) ? configuredVersion : VersionIdentifier.LATEST; } else if (nextSafe.equals(latestSafe)) { - String answer = securityRiskInteractionQuestion(currentIsUnsafe + " Of the newer versions, only the version " - + nextSafe + " is safe, Which is not the latest.", stay, "Install the safe version (" + nextSafe + ")"); - return answer.startsWith(stay) ? current : nextSafe; + String answer = this.context.question( + currentIsUnsafe + " Of the newer versions, only the version " + nextSafe + + " is safe, which is however not the latest." + ask, + stay, "Install the safe version (" + nextSafe + ")"); + return answer.equals(stay) ? configuredVersion : nextSafe; } else { if (latestSafe.equals(latest)) { - String answer = securityRiskInteractionQuestion(currentIsUnsafe, stay, installNextSafe, installSafeLatest); - return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; + String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installSafeLatest); + return answer.equals(stay) ? configuredVersion + : answer.equals(installNextSafe) ? nextSafe : VersionIdentifier.LATEST; } else { - String answer = securityRiskInteractionQuestion(currentIsUnsafe, stay, installNextSafe, installLatestSafe); - return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe; + String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installLatestSafe); + return answer.equals(stay) ? configuredVersion : answer.equals(installNextSafe) ? nextSafe : latestSafe; } } - - // VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion); - // setVersion(chosenVersion, silent); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java index 37f182e21..623c4b824 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java @@ -53,7 +53,4 @@ public String getCpeProduct() { return "terraform"; } - // add matche cpe the the warning and print it in ide, to to wether the vul maybe oinly applies to the enterprise - // edition - // or can I filter this enterpsrise version by adding overriding the eidtion methiod with the normal edition string? } 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 a75d8702d..771a4ebc5 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 @@ -28,9 +28,8 @@ */ public class UrlSecurityJsonFile extends AbstractUrlFile { - /*** + /** * A simple container with the information about a security warning. - * */ public static class UrlSecurityWarning { @@ -62,6 +61,8 @@ public UrlSecurityWarning() { * * @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. @@ -258,12 +259,13 @@ public boolean addSecurityWarning(VersionRange versionRange) { return added; } - /*** + /** * Adds a new security warning to the security json file. * * @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 version range. + * @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. @@ -283,7 +285,7 @@ public boolean addSecurityWarning(VersionRange versionRange, String matchedCpe, return added; } - /*** + /** * For a given version, returns whether there is a security risk by locking at the warnings in the security json file. * * @param version the version to check for security risks. diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 0e6cec0d1..2468ab415 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -97,8 +97,7 @@ protected final String getToolWithEdition() { return tool + "/" + edition; } - /*** - * + /** * @return the vendor of the tool as specified in the CPE (Common Platform Enumeration) */ public String getCpeVendor() { @@ -106,7 +105,7 @@ public String getCpeVendor() { return null; } - /*** + /** * @return the product name of the tool as specified in the CPE (Common Platform Enumeration) */ public String getCpeProduct() { @@ -114,7 +113,7 @@ public String getCpeProduct() { return null; } - /*** + /** * @return the edition of the tool as specified in the CPE (Common Platform Enumeration) */ public String getCpeEdition() { @@ -132,7 +131,13 @@ public String mapUrlVersionToCpeVersion(String version) { } /** - * @return maps the cpe version to the version as specified in the CPE (Common Platform Enumeration). + * This method is only used as fallback if the passed version is not in the image of + * {@link #mapUrlVersionToCpeVersion(String)}. This doesn't have to be inverse of + * {@link #mapUrlVersionToCpeVersion(String)}. It must only be sufficient to get the correct VersionRange from the + * matched vulnerable software. + * + * @return maps the version as specified in the CPE (Common Platform Enumeration) to the version as specified by the + * directory name in the url repository */ public String mapCpeVersionToUrlVersion(String version) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java new file mode 100644 index 000000000..89d0d37f4 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java @@ -0,0 +1,34 @@ +package com.devonfw.tools.ide.util; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Utility class for operations on maps. + */ +public class MapUtil { + + /** + * Creates a {@link HashMap} with the given {@code keys} and {@code values} which are passed as {@link List lists}. + * The map is populated by iterating through both lists simultaneously until one of the list is exhausted. + */ + public static Map createMapWithLists(List keys, List values) { + + Map resultMap = new HashMap<>(); + + // Create iterators for both lists + Iterator keysIterator = keys.iterator(); + Iterator valuesIterator = values.iterator(); + + // Iterate through both iterators simultaneously + while (keysIterator.hasNext() && valuesIterator.hasNext()) { + K key = keysIterator.next(); + V value = valuesIterator.next(); + resultMap.put(key, value); + } + + return resultMap; + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 8e71db7da..a279403fd 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -12,12 +12,12 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -/*** +/** * Test of {@link ToolCommandlet}. */ public class ToolCommandletTest extends AbstractIdeContextTest { - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. But * there is a warning that affects all versions. This warning is then ignored, but the other warnings are considered. */ @@ -26,7 +26,7 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { // arrange Class dummyTool = Azure.class; - String[] answers = {"1", "2", "3"}; + String[] answers = { "1", "2", "3" }; IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) @@ -44,11 +44,10 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { // answer to the interaction is 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("6")); // answer to the interaction is 3 - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("*")); } - - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. Only * the warnings all considered together cover all versions and there is no single warning that affects all versions. */ @@ -70,7 +69,8 @@ public void testSecurityRiskInteractionAllVersionAffectedByMultipleWarning() { assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); } - /*** + + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the set version is the latest but * vulnerable. */ @@ -89,12 +89,12 @@ public void testSecurityRiskInteractionCurrentIsLatest() { // act & assert // answer to the interaction is 1 - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); // answer to the interaction is 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("6")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where there are no newer versions that * are safe, but there is a previous version that is safe. */ @@ -119,7 +119,7 @@ public void testSecurityRiskInteractionNextSafeIsNull() { assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("5")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version is also the * latest. */ @@ -141,10 +141,10 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { // answer to the interaction is 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("7")); // answer to the interaction is 2 - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("*")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version is also the * latest safe version, and the overall latest version is not safe. */ @@ -169,7 +169,7 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version differs from * the latest safe, which is also the overall latest version. */ @@ -193,10 +193,10 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( // answer to the interaction is 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); // answer to the interaction is 3 - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("*")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where the next safe version differs from * the latest safe, and the overall latest version is not safe. */ @@ -223,7 +223,7 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("7")); } - /*** + /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where set version is safe. */ @Test @@ -243,9 +243,7 @@ public void testSecurityRiskInteractionCurrentVersionIsSafe() { assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); } - - - /*** + /** * Creates the context and data for the tests of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)}. * * @param dummyTool the dummy tool to be used for the tests. The diff --git a/documentation/LICENSE.asciidoc b/documentation/LICENSE.asciidoc index 4e22d69b6..31bc2c3e5 100644 --- a/documentation/LICENSE.asciidoc +++ b/documentation/LICENSE.asciidoc @@ -24,6 +24,7 @@ The following table shows the components that may be used. The column `inclusion |======================= |*Component*|*Inclusion*|*License* |https://github.com/devonfw/IDEasy[IDEasy] | Directly included |https://github.com/devonfw/IDEasy/blob/master/LICENSE[ASL 2.0] +|https://owasp.org/www-project-dependency-check/[OWASP] | Directly included |https://github.com/devonfw/IDEasy/blob/master/LICENSE[ASL 2.0] |https://github.com/eclipse-ee4j/jsonp[JSON-P] API | Directly included |https://github.com/eclipse-ee4j/jsonp/blob/master/LICENSE.md[EPL 2.0] |https://github.com/eclipse-ee4j/jsonp[JSON-P] Implementation | Directly included |https://github.com/eclipse-ee4j/jsonp/blob/master/LICENSE.md[EPL 2.0] |https://openjdk.java.net/[OpenJDK] / https://adoptopenjdk.net/[AdoptOpenJDK] (Java) |Default Setup| https://openjdk.java.net/legal/gplv2+ce.html[GPLv2] @@ -2652,6 +2653,212 @@ The externally maintained libraries used by Node.js are: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + + - OWASP, located at security/security, is licensed as follows: + """ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + """ ``` == MICROSOFT SOFTWARE LICENSE TERMS diff --git a/documentation/vulnerabilities.asciidoc b/documentation/vulnerabilities.asciidoc new file mode 100644 index 000000000..cb8c964ea --- /dev/null +++ b/documentation/vulnerabilities.asciidoc @@ -0,0 +1,45 @@ +:toc: +toc::[] + += Vulnerabilities + +We support you with automatic downloads and "installations" of arbitrary software. +However, security is also very important, and therefore we implemented various security features like e.g. verification of checksums (SHA-256) of downloaded files from a different source to prevent man-in-the-middle attacks. +Also, a tool version that you want to use for your project may have severe https://owasp.org/www-community/vulnerabilities/[vulnerabilities]. +Therefore, we maintain a list of known https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures[CVEs] for the tools we support. Take a look at the `_ide/urls/«tool»/«edition»/security.json` file. +In case you are reading this page, you most probably got a security warning because you are using a vulnerable software. +In that case your attention and action is required, and you should consider an update as soon as possible and follow the suggestions prompted by IDEasy. + +Please note that if you are using the vulnerable tool on your own behalf you alone are responsible (e.g. for central tools like `git` or tools that you did setup by yourself via our IDE product). +However, in case you are team-mate of a project and did not choose the tool and its version yourself, you should first run `update` to ensure you have the latest settings and tool versions. +If that already fixes the security warnings, you are done. +Otherwise, contact your IDE-admin, who is responsible for maintaining your settings git repository. +NOTE: You can find it in the `settings` folder of your IDE installation next to the `workspaces` folder. +You may open a shell in this settings folder and call `git remote -v` to figure out the git URL and navigate there to find our your IDE-admin. + +Please note that for each tool that is setup via our IDE product, you can call `set-version «tool» latest` to configure it to the latest version. +Also note that you can use the asterisk in a tool version. +So e.g. if you do `set-version java 17*` you will configure to always get the latest `17.x` version of `java`. +Further, do not forget to call `install «tool»` after you have configured a new version to actually install the update. +If you are an IDE admin you then should test your changes and after some QA process push them to the settings git repo. +After that, instruct your team to run `update` so all developers get the updates automatically and the vulnerabilities are gone for your team. +Thank you for caring about IT security and keeping us safe!!! + +== Git + +Git is a link:setup.asciidoc#prerequisites[prerequisite] of our IDE product and therefore not managed by it. +In other words: You have installed Git on your machine, and you alone are responsible for installing updates. +To update your git client simply go to https://git-scm.com/downloads[git downloads] and download the latest version for your operating system and run the installer. +This will automatically update your existing installation. +For security consider the philosophy of "latest is greatest". +Git also does a great job in keeping downward compatible so newer versions typically are better and more secure. + +You can find CVEs for git https://www.cvedetails.com/vulnerability-list/vendor_id-4008/GIT.html[here]. +Please be aware that CVEs like https://www.cvedetails.com/cve/CVE-2022-25648/[CVE-2022-25648] can allow remote code execution and therefore have to be fixed urgently. + +== Maven + +CVEs for maven can be found https://maven.apache.org/security.html[here]. +Maven is not always downward compatible. +Therefore, when changing the major or minor version, please do according testing and QA before pushing changes to your team. + diff --git a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java similarity index 73% rename from security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java rename to security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java index 640571c4e..e9fecfa4d 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFile.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -1,15 +1,21 @@ package com.devonfw.tools.security; +import java.io.File; import java.math.BigDecimal; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import com.devonfw.tools.ide.url.model.folder.UrlVersion; +import com.devonfw.tools.ide.util.MapUtil; +import com.devonfw.tools.ide.version.VersionIdentifier; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -54,17 +60,17 @@ // TODO Doesn't yet work with versions defined like this ///latest -/*** - * This class is used to build the {@link UrlSecurityJsonFile} file for IDEasy. It scans the +/** + * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for * vulnerabilities by using the OWASP package. For this the * {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion}. If a vulnerability is found, it is added to the * {@link UrlSecurityJsonFile} of the corresponding tool and edition. The previous content of the file is overwritten. */ -public class BuildSecurityJsonFile { +public class BuildSecurityJsonFiles { - private static final Logger logger = LoggerFactory.getLogger(BuildSecurityJsonFile.class); + private static final Logger logger = LoggerFactory.getLogger(BuildSecurityJsonFiles.class); private static final String CVE_BASE_URL = "https://nvd.nist.gov/vuln/detail/"; @@ -109,7 +115,6 @@ private static void run() { initCvesToIgnore(); context = new IdeContextConsole(IdeLogLevel.INFO, null, false); UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); - Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); Set> foundToolsAndEditions = new HashSet<>(); for (Dependency dependency : dependencies) { @@ -119,19 +124,28 @@ private static void run() { String edition = parent.getParent().getFileName().toString(); AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); + System.out.println(tool + ", " + edition); + UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool, edition).getSecurityJsonFile(); boolean newlyAdded = foundToolsAndEditions.add(new Pair<>(tool, edition)); if (newlyAdded) { // to assure that the file is cleared only once per tool and edition securityFile.clearSecurityWarnings(); } + List sortedVersions = context.getUrls().getSortedVersions(tool, edition).stream() + .map(VersionIdentifier::toString).toList(); + List sortedCpeVersions = sortedVersions.stream().map(urlUpdater::mapUrlVersionToCpeVersion) + .collect(Collectors.toList()); + Map cpeToUrlVersion = MapUtil.createMapWithLists(sortedCpeVersions, sortedVersions); + Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { - addVulnerabilityToSecurityFile(vulnerability, securityFile, urlUpdater); + addVulnerabilityToSecurityFile(vulnerability, securityFile, urlUpdater, cpeToUrlVersion); } securityFile.save(); } actuallyIgnoredCves.forEach(cve -> context.info("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); + printAffectedVersions(context); } private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { @@ -159,10 +173,10 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd } private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, - AbstractUrlUpdater urlUpdater) { + AbstractUrlUpdater urlUpdater, Map cpeToUrlVersion) { if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { - // if this ever happens, add a case that handles this + // TODO if this ever happens, add a case that handles this throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); } boolean hasV3Severity = vulnerability.getCvssV3() != null; @@ -190,7 +204,7 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (toLowSeverity) { return; } - VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, urlUpdater); + VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, urlUpdater, cpeToUrlVersion); if (versionRange == null) { logger.info( "Vulnerability {} seems to be irrelevant because its affected versions have no overlap with the versions " @@ -199,29 +213,31 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, vulnerability.getName()); return; } - // for manual detection of false positives: check of the interval of affected versions is correctly transformed to - // the versionRange VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); String matchedCpe = matchedVulnerableSoftware.toCpe23FS(); // the fields of cpe2.3 are: // cpe2.3 part, vendor, product, version, update, edition, language, swEdition, targetSw, targetHw, other; - String interval = String.format(" ( %s, [ %s, ] %s, ) %s", matchedVulnerableSoftware.getVersionStartExcluding(), - matchedVulnerableSoftware.getVersionStartIncluding(), matchedVulnerableSoftware.getVersionEndIncluding(), - matchedVulnerableSoftware.getVersionEndExcluding()); + 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.", + matchedVulnerableSoftware.getVersionStartExcluding(), matchedVulnerableSoftware.getVersionStartIncluding(), + matchedVulnerableSoftware.getVersionEndIncluding(), matchedVulnerableSoftware.getVersionEndExcluding()); securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, severityVersion, cveName, description, nistUrl, referenceUrls); } - /*** + /** * From the vulnerability determine the {@link VersionRange versionRange} to which the vulnerability applies. * * @param vulnerability the vulnerability determined by OWASP dependency check. - * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to which the vulnerability applies. TODOO + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get maps between CPE Version and + * {@link UrlVersion#getName() Url Version}. * @return the {@link VersionRange versionRange} to which the vulnerability applies. */ - static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, AbstractUrlUpdater urlUpdater) { + static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, AbstractUrlUpdater urlUpdater, + Map cpeToUrlVersion) { VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); @@ -231,13 +247,13 @@ static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); String singleVersion = matchedVulnerableSoftware.getVersion(); - vEndExcluding = Optional.ofNullable(vEndExcluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); - vEndIncluding = Optional.ofNullable(vEndIncluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); - vStartExcluding = Optional.ofNullable(vStartExcluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); - vStartIncluding = Optional.ofNullable(vStartIncluding).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); - singleVersion = Optional.ofNullable(singleVersion).map(urlUpdater::mapCpeVersionToUrlVersion).orElse(null); + vStartExcluding = getUrlVersion(vStartExcluding, urlUpdater, cpeToUrlVersion); + vStartIncluding = getUrlVersion(vStartIncluding, urlUpdater, cpeToUrlVersion); + vEndExcluding = getUrlVersion(vEndExcluding, urlUpdater, cpeToUrlVersion); + vEndIncluding = getUrlVersion(vEndIncluding, urlUpdater, cpeToUrlVersion); + singleVersion = getUrlVersion(singleVersion, urlUpdater, cpeToUrlVersion); - VersionRange affectedRange = null; + VersionRange affectedRange; try { affectedRange = getVersionRangeFromInterval(vStartIncluding, vStartExcluding, vEndExcluding, vEndIncluding, singleVersion); @@ -245,9 +261,22 @@ static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability throw new IllegalStateException( "Getting the VersionRange for the vulnerability " + vulnerability.getName() + " failed.", e); } - return affectedRange; + } + 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); + } else { + urlVersion = urlUpdater.mapCpeVersionToUrlVersion(cpeVersion); + System.out.println("tool mapped using mapCpeVersionToUrlVersion cpe " + cpeVersion + " -> url " +urlVersion); + + } + } + return urlVersion; } public static VersionRange getVersionRangeFromInterval(String si, String se, String ee, String ei, String s) @@ -258,7 +287,7 @@ public static VersionRange getVersionRangeFromInterval(String si, String se, Str throw new IllegalStateException( "Vulnerability has no interval of affected versions or single affected version."); } - return VersionRange.of(s + ">" + s); + return VersionRange.of(s + VersionRange.getVersionSeparator() + s); } se = Optional.ofNullable(se).orElse(""); si = Optional.ofNullable(si).orElse(""); @@ -274,6 +303,45 @@ public static VersionRange getVersionRangeFromInterval(String si, String se, Str return VersionRange.of(leftBoundary + VersionRange.getVersionSeparator() + rightBoundary); } + private static void printAffectedVersions(IdeContext context) { + + Path urlsPath = context.getUrlsPath(); + for (File tool : urlsPath.toFile().listFiles()) { + if (!Files.isDirectory(tool.toPath())) { + continue; + } + for (File edition : tool.listFiles()) { + if (!edition.isDirectory()) { + continue; + } + List sortedVersions = context.getUrls().getSortedVersions(tool.getName(), edition.getName()); + UrlSecurityJsonFile securityJsonFile = context.getUrls().getEdition(tool.getName(), edition.getName()) + .getSecurityJsonFile(); + + VersionIdentifier min = null; + for (int i = sortedVersions.size() - 1; i >= 0; i--) { + VersionIdentifier version = sortedVersions.get(i); + if (securityJsonFile.contains(version)) { + if (min == null) { + min = version; + } + + } else { + if (min != null) { + System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + + new VersionRange(min, version, false, true) + " are affected by vulnerabilities."); + min = null; + } + } + } + if (min != null) { + System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + + new VersionRange(min, null, false, true) + " are affected by vulnerabilities."); + } + } + } + } + private static void printAllOwaspAnalyzers(Engine engine) { engine.getMode().getPhases().forEach(phase -> engine.getAnalyzers(phase) diff --git a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java similarity index 74% rename from security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java rename to security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java index a36350fa8..2b4ba0c8e 100644 --- a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFileTest.java +++ b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java @@ -4,15 +4,15 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import static com.devonfw.tools.security.BuildSecurityJsonFile.getVersionRangeFromInterval; +import static com.devonfw.tools.security.BuildSecurityJsonFiles.getVersionRangeFromInterval; /** - * Test of {@link BuildSecurityJsonFile}. + * Test of {@link BuildSecurityJsonFiles}. */ -public class BuildSecurityJsonFileTest extends Assertions { +public class BuildSecurityJsonFilesTest extends Assertions { /** - * Test of {@link BuildSecurityJsonFile#getVersionRangeFromInterval(String, String, String, String, String)}. + * Test of {@link BuildSecurityJsonFiles#getVersionRangeFromInterval(String, String, String, String, String)}. */ @Test public void testGetVersionRangeFromInterval() { From fe9109f3448fe198c62437c7a3299a0beef8e27c Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Fri, 22 Dec 2023 19:01:47 +0100 Subject: [PATCH 15/47] #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); } } From b19b877eabb835f194724a27a317554b1aab3898 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Fri, 22 Dec 2023 22:38:40 +0100 Subject: [PATCH 16/47] #103: fixed write json bug, and more - changed pom.xml - getCpeEdition now has argument, since there is only a single UrlUpdater for multiple editions of a tool - some cleanup --- .../ide/url/model/file/UrlSecurityJsonFile.java | 8 ++++---- .../ide/url/updater/AbstractUrlUpdater.java | 5 +++-- .../java/com/devonfw/tools/ide/util/MapUtil.java | 2 +- security/pom.xml | 16 +--------------- .../tools/security/BuildSecurityJsonFiles.java | 11 +++-------- .../com/devonfw/tools/security/UrlAnalyzer.java | 2 +- 6 files changed, 13 insertions(+), 31 deletions(-) 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 e84d6d56b..b940e5da3 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 @@ -10,13 +10,13 @@ import java.util.Objects; 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; +import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.json.mapping.JsonMapping; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarningsJson; import com.devonfw.tools.ide.url.model.folder.UrlEdition; import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; @@ -177,7 +177,7 @@ protected void doSave() { String jsonString; try { - jsonString = mapper.writeValueAsString(this.urlSecurityWarningsJson.getWarnings()); + jsonString = mapper.writeValueAsString(this.urlSecurityWarningsJson); } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 2468ab415..11f0f588f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -114,9 +114,10 @@ public String getCpeProduct() { } /** - * @return the edition of the tool as specified in the CPE (Common Platform Enumeration) + * @param urlEdition the {@link UrlEdition} to get the CPE (Common Platform Enumeration) edition for. + * @return the edition as specified in the CPE. */ - public String getCpeEdition() { + public String getCpeEdition(String urlEdition) { return null; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java index 89d0d37f4..cee8beea9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java +++ b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java @@ -6,7 +6,7 @@ import java.util.Map; /** - * Utility class for operations on maps. + * Utility class for operations with maps. */ public class MapUtil { diff --git a/security/pom.xml b/security/pom.xml index 369d05e9a..69d89b850 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -13,9 +13,7 @@ security - 17 - 17 - UTF-8 + 17 @@ -31,17 +29,5 @@ compile - - - - org.apache.maven.plugins - maven-compiler-plugin - - 9 - 9 - - - - \ No newline at end of file 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 e6b2e9d26..0d663ab5c 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -13,9 +13,6 @@ import java.util.Set; import java.util.stream.Collectors; -import com.devonfw.tools.ide.url.model.folder.UrlVersion; -import com.devonfw.tools.ide.util.MapUtil; -import com.devonfw.tools.ide.version.VersionIdentifier; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -54,8 +51,11 @@ import com.devonfw.tools.ide.context.IdeContextConsole; import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; +import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.util.MapUtil; +import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; // TODO Doesn't yet work with versions defined like this ///latest @@ -123,9 +123,6 @@ private static void run() { String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); - - System.out.println(tool + ", " + edition); - UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool, edition).getSecurityJsonFile(); boolean newlyAdded = foundToolsAndEditions.add(new Pair<>(tool, edition)); if (newlyAdded) { // to assure that the file is cleared only once per tool and edition @@ -272,10 +269,8 @@ private static String getUrlVersion(String cpeVersion, AbstractUrlUpdater urlUpd if (cpeVersion != null) { if (cpeToUrlVersion.containsKey(cpeVersion)) { urlVersion = cpeToUrlVersion.get(cpeVersion); - 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); } } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 9e3eac14e..746e72549 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -45,7 +45,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) { // adding vendor evidence String cpeVendor = urlUpdater.getCpeVendor(); String cpeProduct = urlUpdater.getCpeProduct(); - String cpeEdition = urlUpdater.getCpeEdition(); + String cpeEdition = urlUpdater.getCpeEdition(edition); String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(parent.getFileName().toString()); if (cpeVendor == null || cpeProduct == null) { From fd64100ebded0b56eb578ec7ac9cb377fc5aebdf Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 2 Jan 2024 10:22:10 +0100 Subject: [PATCH 17/47] #103: some final cleanup --- .../ide/url/model/file/UrlSecurityJsonFile.java | 4 ++-- .../com/devonfw/tools/ide/util/MapUtil.java | 2 +- .../tools/security/BuildSecurityJsonFiles.java | 17 ++++++++--------- 3 files changed, 11 insertions(+), 12 deletions(-) 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 b940e5da3..1abb5113d 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 @@ -64,8 +64,8 @@ public boolean addSecurityWarning(VersionRange versionRange) { * * @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 interval the interval of vulnerability that was used to determine the {@link VersionRange}. This can be used + * to manually 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. diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java index cee8beea9..8ec199494 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java +++ b/cli/src/main/java/com/devonfw/tools/ide/util/MapUtil.java @@ -14,7 +14,7 @@ public class MapUtil { * Creates a {@link HashMap} with the given {@code keys} and {@code values} which are passed as {@link List lists}. * The map is populated by iterating through both lists simultaneously until one of the list is exhausted. */ - public static Map createMapWithLists(List keys, List values) { + public static Map createMapfromLists(List keys, List values) { Map resultMap = new HashMap<>(); 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 0d663ab5c..0363e2e54 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Optional; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -59,6 +59,8 @@ import com.devonfw.tools.ide.version.VersionRange; // TODO Doesn't yet work with versions defined like this ///latest +// TODO Sometimes when running this class is takes a long time to finish. Maybe this is because of the OWASP package, which +// is updating the vulnerabilities. A dirty fix is to stop the program and restart it. /** * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the @@ -133,7 +135,7 @@ private static void run() { .map(VersionIdentifier::toString).toList(); List sortedCpeVersions = sortedVersions.stream().map(urlUpdater::mapUrlVersionToCpeVersion) .collect(Collectors.toList()); - Map cpeToUrlVersion = MapUtil.createMapWithLists(sortedCpeVersions, sortedVersions); + Map cpeToUrlVersion = MapUtil.createMapfromLists(sortedCpeVersions, sortedVersions); Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { @@ -174,7 +176,8 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { // TODO if this ever happens, add a case that handles this - throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName()); + throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName() + "\\n" + + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); } boolean hasV3Severity = vulnerability.getCvssV3() != null; double severityDouble = hasV3Severity ? vulnerability.getCvssV3().getBaseScore() @@ -287,15 +290,11 @@ public static VersionRange getVersionRangeFromInterval(String si, String se, Str } return VersionRange.of(s + VersionRange.getVersionSeparator() + s); } - se = Optional.ofNullable(se).orElse(""); - si = Optional.ofNullable(si).orElse(""); - ee = Optional.ofNullable(ee).orElse(""); - ei = Optional.ofNullable(ei).orElse(""); - String leftBoundary = se.isEmpty() ? VersionRange.getStartIncludingPrefix() + si + String leftBoundary = se == null ? VersionRange.getStartIncludingPrefix() + Objects.toString(si, "") : VersionRange.getStartExcludingPrefix() + se; - String rightBoundary = ee.isEmpty() ? ei + VersionRange.getEndIncludingSuffix() + String rightBoundary = ee == null ? Objects.toString(ei, "") + VersionRange.getEndIncludingSuffix() : ee + VersionRange.getEndExcludingSuffix(); return VersionRange.of(leftBoundary + VersionRange.getVersionSeparator() + rightBoundary); From 312afdd0cdf57a7bd8099b99b477fdbf0e0912c5 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 2 Jan 2024 11:09:51 +0100 Subject: [PATCH 18/47] #103: updated to be in line with #158 --- .../tools/ide/version/VersionRange.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index 8cd63029d..70f32932e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -244,23 +244,9 @@ public String toString() { @JsonCreator public static VersionRange of(String value) { - boolean leftIsExclusive = false; - boolean rightIsExclusive = false; - - if (value.startsWith(START_EXCLUDING_PREFIX)) { - leftIsExclusive = true; - value = value.substring(START_EXCLUDING_PREFIX.length()); - } - if (value.startsWith(START_INCLUDING_PREFIX)) { - value = value.substring(START_INCLUDING_PREFIX.length()); - } - if (value.endsWith(END_EXCLUDING_SUFFIX)) { - rightIsExclusive = true; - value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); - } - if (value.endsWith(END_INCLUDING_SUFFIX)) { - value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); - } + boolean leftIsExclusive = value.startsWith(START_EXCLUDING_PREFIX); + boolean rightIsExclusive = value.endsWith(END_EXCLUDING_SUFFIX); + value = removeAffixes(value); int index = value.indexOf(VERSION_SEPARATOR); if (index == -1) { @@ -279,4 +265,19 @@ public static VersionRange of(String value) { return new VersionRange(min, max, leftIsExclusive, rightIsExclusive); } + private static String removeAffixes(String value) { + + if (value.startsWith(START_EXCLUDING_PREFIX)) { + value = value.substring(START_EXCLUDING_PREFIX.length()); + } else if (value.startsWith(START_INCLUDING_PREFIX)) { + value = value.substring(START_INCLUDING_PREFIX.length()); + } + if (value.endsWith(END_EXCLUDING_SUFFIX)) { + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); + } else if (value.endsWith(END_INCLUDING_SUFFIX)) { + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); + } + return value; + } + } From 9b2867975906d066cd8bc74063b396fb4ac57624 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sat, 20 Jan 2024 17:29:13 +0100 Subject: [PATCH 19/47] #103: fixed small bug due to merged main --- .../tools/ide/tool/ToolCommandletTest.java | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index a279403fd..d39169215 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -12,9 +12,7 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -/** - * Test of {@link ToolCommandlet}. - */ +/** Test of {@link ToolCommandlet} */ public class ToolCommandletTest extends AbstractIdeContextTest { /** @@ -32,12 +30,18 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of(">")); // should get ignored - securityFile.addSecurityWarning(VersionRange.of("0>11")); // should get ignored - securityFile.addSecurityWarning(VersionRange.of("2>5")); + securityFile.addSecurityWarning(VersionRange.of("(,)")); // should get ignored + securityFile.addSecurityWarning(VersionRange.of("[0,11]")); // should get ignored + securityFile.addSecurityWarning(VersionRange.of("[2,5]")); // act & assert // no answer required + + // TODO move + // version of 1 to var namend current + // then save this also to stay + // extract method that calcs next safe, latest save + // and introduce var named latest and make it to * assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); // answer to the interaction is 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("2")); @@ -60,8 +64,8 @@ public void testSecurityRiskInteractionAllVersionAffectedByMultipleWarning() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("1>5")); - securityFile.addSecurityWarning(VersionRange.of("6>")); + securityFile.addSecurityWarning(VersionRange.of("[1,5]")); + securityFile.addSecurityWarning(VersionRange.of("[6,)")); // act & assert assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); @@ -84,8 +88,8 @@ public void testSecurityRiskInteractionCurrentIsLatest() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("2>5")); - securityFile.addSecurityWarning(VersionRange.of("7>9")); + securityFile.addSecurityWarning(VersionRange.of("[2,5]")); + securityFile.addSecurityWarning(VersionRange.of("[7,9]")); // act & assert // answer to the interaction is 1 @@ -108,9 +112,9 @@ public void testSecurityRiskInteractionNextSafeIsNull() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3")); - securityFile.addSecurityWarning(VersionRange.of("6>7")); - securityFile.addSecurityWarning(VersionRange.of("8>")); + securityFile.addSecurityWarning(VersionRange.of("[3,3]")); + securityFile.addSecurityWarning(VersionRange.of("[6,7]")); + securityFile.addSecurityWarning(VersionRange.of("[8,)")); // act & assert // answer to the interaction is 1 @@ -133,9 +137,9 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3")); - securityFile.addSecurityWarning(VersionRange.of("6>7")); - securityFile.addSecurityWarning(VersionRange.of("8>8")); + securityFile.addSecurityWarning(VersionRange.of("[3,3]")); + securityFile.addSecurityWarning(VersionRange.of("[6,7]")); + securityFile.addSecurityWarning(VersionRange.of("[8,8]")); // act & assert // answer to the interaction is 1 @@ -158,9 +162,9 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3")); - securityFile.addSecurityWarning(VersionRange.of("5>6")); - securityFile.addSecurityWarning(VersionRange.of("8>9")); + securityFile.addSecurityWarning(VersionRange.of("[3,3]")); + securityFile.addSecurityWarning(VersionRange.of("[5,6]")); + securityFile.addSecurityWarning(VersionRange.of("[8,9]")); // act & assert // answer to the interaction is 1 @@ -183,9 +187,9 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3")); - securityFile.addSecurityWarning(VersionRange.of("5>6")); - securityFile.addSecurityWarning(VersionRange.of("8>8")); + securityFile.addSecurityWarning(VersionRange.of("[3,3]")); + securityFile.addSecurityWarning(VersionRange.of("[5,6]")); + securityFile.addSecurityWarning(VersionRange.of("[8,8]")); // act & assert // answer to the interaction is 1 @@ -210,9 +214,9 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("3>3")); - securityFile.addSecurityWarning(VersionRange.of("6>6")); - securityFile.addSecurityWarning(VersionRange.of("8>9")); + securityFile.addSecurityWarning(VersionRange.of("[3,3]")); + securityFile.addSecurityWarning(VersionRange.of("[6,6]")); + securityFile.addSecurityWarning(VersionRange.of("[8,9]")); // act & assert // answer to the interaction is 1 @@ -223,9 +227,7 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("7")); } - /** - * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where set version is safe. - */ + /** Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where set version is safe. */ @Test public void testSecurityRiskInteractionCurrentVersionIsSafe() { @@ -235,8 +237,8 @@ public void testSecurityRiskInteractionCurrentVersionIsSafe() { ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); - securityFile.addSecurityWarning(VersionRange.of("1>5")); - securityFile.addSecurityWarning(VersionRange.of("7>8")); + securityFile.addSecurityWarning(VersionRange.of("[1,5]")); + securityFile.addSecurityWarning(VersionRange.of("[7,8]")); // act & assert assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); @@ -261,7 +263,8 @@ private IdeContext getContextForSecurityJsonTests(Class Date: Sat, 20 Jan 2024 17:56:09 +0100 Subject: [PATCH 20/47] #103: fixed bugs - fixed pom bug - fixed bug in BuildSecurityJsonFiles due to moved method that was introduced in the merge of main into this branch --- .../tools/ide/tool/ToolCommandletTest.java | 6 +++- security/pom.xml | 2 +- .../security/BuildSecurityJsonFiles.java | 29 ++++++++++++++----- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index d39169215..f00bc8ed5 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -42,7 +42,11 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { // then save this also to stay // extract method that calcs next safe, latest save // and introduce var named latest and make it to * - assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); + + // the current version is safe, so no interaction needed and no answer is consumed + VersionIdentifier currentVersion = VersionIdentifier.of("1"); + assertThat(tool.securityRiskInteraction(currentVersion)).isEqualTo(currentVersion); + // answer to the interaction is 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("2")); // answer to the interaction is 2 diff --git a/security/pom.xml b/security/pom.xml index 69d89b850..0c587e2ab 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -25,7 +25,7 @@ com.devonfw.tools.IDEasy ide-cli - 2024.01.001-SNAPSHOT + 2024.02.001-alpha-SNAPSHOT compile 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 4a8ebcc0a..7d1a60743 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.stream.Collectors; +import com.devonfw.tools.ide.version.BoundaryType; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -295,16 +296,28 @@ public static VersionRange getVersionRangeFromInterval(String si, String se, Str throw new IllegalStateException( "Vulnerability has no interval of affected versions or single affected version."); } - return VersionRange.of(s + VersionRange.getVersionSeparator() + s); + VersionIdentifier singleAffectedVersion = VersionIdentifier.of(s); + return new VersionRange(singleAffectedVersion, singleAffectedVersion, BoundaryType.OPEN); } - String leftBoundary = se == null ? VersionRange.getStartIncludingPrefix() + Objects.toString(si, "") - : VersionRange.getStartExcludingPrefix() + se; + boolean leftExclusive = si == null; + boolean rightExclusive = ei == null; - String rightBoundary = ee == null ? Objects.toString(ei, "") + VersionRange.getEndIncludingSuffix() - : ee + VersionRange.getEndExcludingSuffix(); + VersionIdentifier min = null; + if (si != null) { + min = VersionIdentifier.of(si); + } else if (se != null) { + min = VersionIdentifier.of(se); + } + + VersionIdentifier max = null; + if (ei != null) { + max = VersionIdentifier.of(ei); + } else if (ee != null) { + max = VersionIdentifier.of(ee); + } - return VersionRange.of(leftBoundary + VersionRange.getVersionSeparator() + rightBoundary); + return new VersionRange(min, max, BoundaryType.of(leftExclusive, rightExclusive)); } private static void printAffectedVersions(IdeContext context) { @@ -333,14 +346,14 @@ private static void printAffectedVersions(IdeContext context) { } else { if (min != null) { System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " - + new VersionRange(min, version, false, true) + " are affected by vulnerabilities."); + + new VersionRange(min, version, BoundaryType.of(false, true)) + " are affected by vulnerabilities."); min = null; } } } if (min != null) { System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " - + new VersionRange(min, null, false, true) + " are affected by vulnerabilities."); + + new VersionRange(min, null, BoundaryType.of(false, true)) + " are affected by vulnerabilities."); } } } From 98b3da36d61d79cb0eb265e789c9fa48ba41058b Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sat, 20 Jan 2024 21:25:09 +0100 Subject: [PATCH 21/47] #103: fixed bug - bug when creating version range from single version was fixed --- .../tools/security/BuildSecurityJsonFiles.java | 2 +- .../tools/security/BuildSecurityJsonFilesTest.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) 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 7d1a60743..243c33a1d 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -297,7 +297,7 @@ public static VersionRange getVersionRangeFromInterval(String si, String se, Str "Vulnerability has no interval of affected versions or single affected version."); } VersionIdentifier singleAffectedVersion = VersionIdentifier.of(s); - return new VersionRange(singleAffectedVersion, singleAffectedVersion, BoundaryType.OPEN); + return new VersionRange(singleAffectedVersion, singleAffectedVersion, BoundaryType.CLOSED); } boolean leftExclusive = si == null; diff --git a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java index 2b4ba0c8e..775de4801 100644 --- a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java +++ b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java @@ -18,11 +18,11 @@ public class BuildSecurityJsonFilesTest extends Assertions { public void testGetVersionRangeFromInterval() { // act & assert - assertThat(getVersionRangeFromInterval("1", null, null, null, null)).isEqualTo(VersionRange.of("1>")); - assertThat(getVersionRangeFromInterval(null, "1", null, null, null)).isEqualTo(VersionRange.of("(1>")); - assertThat(getVersionRangeFromInterval(null, null, "1", null, null)).isEqualTo(VersionRange.of(">1)")); - assertThat(getVersionRangeFromInterval(null, null, null, "1", null)).isEqualTo(VersionRange.of(">1")); - assertThat(getVersionRangeFromInterval(null, null, null, null, "1")).isEqualTo(VersionRange.of("1>1")); - assertThat(getVersionRangeFromInterval(null, "1", null, "2", "1")).isEqualTo(VersionRange.of("(1>2]")); + assertThat(getVersionRangeFromInterval("1", null, null, null, null)).isEqualTo(VersionRange.of("[1,)")); + assertThat(getVersionRangeFromInterval(null, "1", null, null, null)).isEqualTo(VersionRange.of("(1,)")); + assertThat(getVersionRangeFromInterval(null, null, "1", null, null)).isEqualTo(VersionRange.of("(,1)")); + assertThat(getVersionRangeFromInterval(null, null, null, "1", null)).isEqualTo(VersionRange.of("(,1]")); + assertThat(getVersionRangeFromInterval(null, null, null, null, "1")).isEqualTo(VersionRange.of("[1,1]")); + assertThat(getVersionRangeFromInterval(null, "1", null, "2", "1")).isEqualTo(VersionRange.of("(1,2]")); } } \ No newline at end of file From 06cc433883c63732050dbbc7ba3db9f97f9efbcb Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sun, 21 Jan 2024 13:19:35 +0100 Subject: [PATCH 22/47] #103: added logging test --- .../tools/ide/tool/ToolCommandlet.java | 12 +-- .../tools/ide/tool/ToolCommandletTest.java | 99 ++++++++++++++----- 2 files changed, 77 insertions(+), 34 deletions(-) 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 6d57525b8..118a4ab41 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 @@ -222,8 +222,6 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured String installLatestSafe = "Install the latest safe version (" + latestSafe + ")."; String installSafeLatest = "Install the (safe) latest version (" + latest + ")."; String installNextSafe = "Install the next safe version (" + nextSafe + ")."; - // I don't need to offer "install latest which is unsafe" as option since the user can set to the latest and choose - // "stay" if (latestSafe == null) { this.context.warning(currentIsUnsafe + "There is no safe version available."); @@ -236,20 +234,20 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured return answer.equals(stay) ? configuredVersion : latestSafe; } else if (nextSafe == null) { // install an older version that is safe or stay with the current unsafe version - String answer = this.context.question(currentIsUnsafe + " All newer versions are also not safe. " + ask, stay, + String answer = this.context.question(currentIsUnsafe + "All newer versions are also not safe. " + ask, stay, installLatestSafe); return answer.equals(stay) ? configuredVersion : latestSafe; } else if (nextSafe.equals(latest)) { - String answer = this.context.question(currentIsUnsafe + " Of the newer versions, only the latest is safe. " + ask, + String answer = this.context.question(currentIsUnsafe + "Of the newer versions, only the latest is safe. " + ask, stay, installSafeLatest); return answer.equals(stay) ? configuredVersion : VersionIdentifier.LATEST; } else if (nextSafe.equals(latestSafe)) { String answer = this.context.question( - currentIsUnsafe + " Of the newer versions, only the version " + nextSafe - + " is safe, which is however not the latest." + ask, - stay, "Install the safe version (" + nextSafe + ")"); + currentIsUnsafe + "Of the newer versions, only version " + nextSafe + + " is safe, which is however not the latest. " + ask, + stay, "Install the safe version (" + nextSafe + ")."); return answer.equals(stay) ? configuredVersion : nextSafe; } else { diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index f00bc8ed5..214080737 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -1,7 +1,9 @@ package com.devonfw.tools.ide.tool; import java.nio.file.Path; +import java.util.List; +import com.devonfw.tools.ide.log.IdeLogLevel; import org.junit.jupiter.api.Test; import com.devonfw.tools.ide.context.AbstractIdeContextTest; @@ -17,7 +19,8 @@ public class ToolCommandletTest extends AbstractIdeContextTest { /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. But - * there is a warning that affects all versions. This warning is then ignored, but the other warnings are considered. + * there is a vulnerability that affects all versions. This vulnerability is then ignored, but the other + * vulnerabilities are considered. */ @Test public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { @@ -26,6 +29,7 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { Class dummyTool = Azure.class; String[] answers = { "1", "2", "3" }; IdeContext context = getContextForSecurityJsonTests(dummyTool, answers); + ToolCommandlet tool = context.getCommandletManager().getCommandlet(dummyTool); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool.getName(), tool.getEdition()) .getSecurityJsonFile(); @@ -35,29 +39,29 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { securityFile.addSecurityWarning(VersionRange.of("[2,5]")); // act & assert - // no answer required - - // TODO move - // version of 1 to var namend current - // then save this also to stay - // extract method that calcs next safe, latest save - // and introduce var named latest and make it to * - // the current version is safe, so no interaction needed and no answer is consumed VersionIdentifier currentVersion = VersionIdentifier.of("1"); assertThat(tool.securityRiskInteraction(currentVersion)).isEqualTo(currentVersion); + assertThat(((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages()).isEmpty(); - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("2")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (2)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (6)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("6")); - // answer to the interaction is 3 + // answer to the interaction is option 3 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("*")); } /** * Test of {@link ToolCommandlet#securityRiskInteraction(VersionIdentifier)} where no safe version is available. Only - * the warnings all considered together cover all versions and there is no single warning that affects all versions. + * all the vulnerabilities considered together cover all versions and there is no single vulnerability that affects + * all versions. */ @Test public void testSecurityRiskInteractionAllVersionAffectedByMultipleWarning() { @@ -76,6 +80,10 @@ public void testSecurityRiskInteractionAllVersionAffectedByMultipleWarning() { assertThat(tool.securityRiskInteraction(VersionIdentifier.of("1"))).isEqualTo(VersionIdentifier.of("1")); assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); + assertThat(((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages()).isEmpty(); + assertThat(((IdeTestContext) context).level(IdeLogLevel.WARNING).getMessages()) + .allMatch(message -> message.contains("There is no safe version available.")); + } /** @@ -96,9 +104,15 @@ public void testSecurityRiskInteractionCurrentIsLatest() { securityFile.addSecurityWarning(VersionRange.of("[7,9]")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("There are no updates available."); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (9)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (6)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("6")); } @@ -121,9 +135,15 @@ public void testSecurityRiskInteractionNextSafeIsNull() { securityFile.addSecurityWarning(VersionRange.of("[8,)")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("All newer versions are also not safe."); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (6)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (5)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("5")); } @@ -146,9 +166,15 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { securityFile.addSecurityWarning(VersionRange.of("[8,8]")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("7")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("Of the newer versions, only the latest is safe."); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (7)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the (safe) latest version (9)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("*")); } @@ -171,9 +197,15 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { securityFile.addSecurityWarning(VersionRange.of("[8,9]")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)) + .contains("Of the newer versions, only version 7 is safe, which is however not the latest."); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the safe version (7)."); + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); } @@ -196,11 +228,17 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( securityFile.addSecurityWarning(VersionRange.of("[8,8]")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (7)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); - // answer to the interaction is 3 + // answer to the interaction is option 3 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("*")); } @@ -223,11 +261,17 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() securityFile.addSecurityWarning(VersionRange.of("[8,9]")); // act & assert - // answer to the interaction is 1 + // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("3")); - // answer to the interaction is 2 + List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); + assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (3)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (4)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest safe version (7)."); + + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("4")); - // answer to the interaction is 3 + // answer to the interaction is option 3 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("7")); } @@ -247,6 +291,7 @@ public void testSecurityRiskInteractionCurrentVersionIsSafe() { // act & assert assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); assertThat(tool.securityRiskInteraction(VersionIdentifier.of("9"))).isEqualTo(VersionIdentifier.of("9")); + assertThat(((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages()).isEmpty(); } /** From 80ab2313dd88166aff676eaddbb83c1b33bf0fc4 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sun, 21 Jan 2024 13:41:49 +0100 Subject: [PATCH 23/47] #103: refactored code - renamed methods in SystemPath - split long method securityRiskInteraction into componets --- .../devonfw/tools/ide/common/SystemPath.java | 6 +- .../tools/ide/tool/LocalToolCommandlet.java | 2 +- .../tools/ide/tool/ToolCommandlet.java | 87 ++++++++++++++----- .../tools/ide/context/IdeContextTest.java | 4 +- 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index 9219eea93..f121d145f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -155,16 +155,16 @@ public Path findBinary(Path toolPath) { * @return the {@link Path} to the directory of the tool where the binaries can be found or {@code null} if the tool * is not installed. */ - public Path getPath(String tool) { + public Path retrievePath(String tool) { return this.tool2pathMap.get(tool); } /** * @param tool the name of the tool. - * @param path the new {@link #getPath(String) tool bin path}. + * @param path the new {@link #retrievePath(String) tool bin path}. */ - public void setPath(String tool, Path path) { + public void addPath(String tool, Path path) { this.paths.add(path); this.tool2pathMap.put(tool, path); diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index f86c6a732..28db81a1c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -77,7 +77,7 @@ protected boolean doInstall(boolean silent) { fileAccess.backup(toolPath); } fileAccess.symlink(installation.linkDir(), toolPath); - this.context.getPath().setPath(this.tool, installation.binDir()); + this.context.getPath().addPath(this.tool, installation.binDir()); if (installedVersion == null) { this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion); } else { 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 118a4ab41..75862e27e 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 @@ -196,26 +196,37 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured List allVersions = this.context.getUrls().getSortedVersions(this.tool, this.getEdition()); VersionIdentifier latest = allVersions.get(0); - 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, 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, securityFile.getParent())) { - latestSafe = allVersions.get(i); - break; - } - } + VersionIdentifier nextSafe = getNextSafeVersion(current, securityFile, allVersions); + VersionIdentifier latestSafe = getLatestSafe(allVersions, securityFile); + String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::getCveName) .collect(Collectors.joining(", ")); String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; + if (latestSafe == null) { + this.context.warning(currentIsUnsafe + "There is no safe version available."); + return configuredVersion; + } + + return whichVersionDoYouWantToInstall(configuredVersion, current, nextSafe, latestSafe, latest, currentIsUnsafe); + } + + /** + * Using all the safety information about the versions, this method asks the user which version to install. + * + * @param configuredVersion the version that was configured in the environment variables. + * @param current the current version that was resolved from the configured version. + * @param nextSafe the next safe version. + * @param latestSafe the latest safe version. + * @param latest the latest version. + * @param currentIsUnsafe the message that is printed if the current version is unsafe. + * @return the version that the user wants to install. + */ + private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier configuredVersion, + VersionIdentifier current, VersionIdentifier nextSafe, VersionIdentifier latestSafe, VersionIdentifier latest, + String currentIsUnsafe) { + String ask = "Which version do you want to install?"; String stay = "Stay with the current unsafe version (" + current + ")."; @@ -223,11 +234,6 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured String installSafeLatest = "Install the (safe) latest version (" + latest + ")."; String installNextSafe = "Install the next safe version (" + nextSafe + ")."; - if (latestSafe == null) { - this.context.warning(currentIsUnsafe + "There is no safe version available."); - return configuredVersion; - } - if (current.equals(latest)) { String answer = this.context.question(currentIsUnsafe + "There are no updates available. " + ask, stay, installLatestSafe); @@ -263,6 +269,47 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured } } + /** + * Gets the latest safe version from the list of all versions. + * + * @param allVersions all versions of the tool. + * @param securityFile the {@link UrlSecurityJsonFile} to check if a version is safe or not. + * @return the latest safe version or {@code null} if there is no safe version. + */ + private VersionIdentifier getLatestSafe(List allVersions, UrlSecurityJsonFile securityFile) { + + VersionIdentifier latestSafe = null; + for (int i = 0; i < allVersions.size(); i++) { + if (!securityFile.contains(allVersions.get(i), true, this.context, securityFile.getParent())) { + latestSafe = allVersions.get(i); + break; + } + } + return latestSafe; + } + + /** + * Gets the next safe version from the list of all versions starting from the current version. + * + * @param current the current version. + * @param securityFile the {@link UrlSecurityJsonFile} to check if a version is safe or not. + * @param allVersions all versions of the tool. + * @return the next safe version or {@code null} if there is no safe version. + */ + private VersionIdentifier getNextSafeVersion(VersionIdentifier current, UrlSecurityJsonFile securityFile, + List allVersions) { + + 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, securityFile.getParent())) { + nextSafe = allVersions.get(i); + break; + } + } + return nextSafe; + } + /** * Installs or updates the managed {@link #getName() tool}. * diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java index dc0d443a7..4a6405313 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java @@ -46,9 +46,9 @@ public void testBasicProjectEnvironment() { assertThat(systemPath.toString()).isNotEqualTo(envPath).endsWith(envPath); Path softwarePath = context.getSoftwarePath(); Path javaBin = softwarePath.resolve("java/bin"); - assertThat(systemPath.getPath("java")).isEqualTo(javaBin); + assertThat(systemPath.retrievePath("java")).isEqualTo(javaBin); Path mvnBin = softwarePath.resolve("mvn/bin"); - assertThat(systemPath.getPath("mvn")).isEqualTo(mvnBin); + assertThat(systemPath.retrievePath("mvn")).isEqualTo(mvnBin); assertThat(systemPath.toString()).contains(javaBin.toString(), mvnBin.toString()); assertThat(variables.getType()).isSameAs(EnvironmentVariablesType.RESOLVED); assertThat(variables.getByType(EnvironmentVariablesType.RESOLVED)).isSameAs(variables); From a26df569a7baf8a881d29c4ad9df1fc40a2c6050 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sun, 21 Jan 2024 14:49:56 +0100 Subject: [PATCH 24/47] 103: first half of team review --- .../tools/ide/tool/ToolCommandlet.java | 46 ++++++++++++++----- .../tools/ide/tool/helm/HelmUrlUpdater.java | 6 +-- .../url/model/file/UrlSecurityJsonFile.java | 9 +++- .../model/file/json/UrlSecurityWarning.java | 32 ++----------- .../ide/url/model/folder/UrlEdition.java | 5 ++ .../tools/ide/version/VersionRange.java | 12 ++++- .../ide/context/AbstractIdeContextTest.java | 1 + .../security/BuildSecurityJsonFiles.java | 1 - .../devonfw/tools/security/UrlAnalyzer.java | 10 ++++ .../devonfw/tools/security/UrlFileFilter.java | 12 ++--- 10 files changed, 80 insertions(+), 54 deletions(-) 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 75862e27e..922e3e069 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 @@ -237,34 +237,56 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi if (current.equals(latest)) { String answer = this.context.question(currentIsUnsafe + "There are no updates available. " + ask, stay, installLatestSafe); - return answer.equals(stay) ? configuredVersion : latestSafe; - + if (answer.equals(stay)) { + return configuredVersion; + } else { + return latestSafe; + } } else if (nextSafe == null) { // install an older version that is safe or stay with the current unsafe version String answer = this.context.question(currentIsUnsafe + "All newer versions are also not safe. " + ask, stay, installLatestSafe); - return answer.equals(stay) ? configuredVersion : latestSafe; - + if (answer.equals(stay)) { + return configuredVersion; + } else { + return latestSafe; + } } else if (nextSafe.equals(latest)) { String answer = this.context.question(currentIsUnsafe + "Of the newer versions, only the latest is safe. " + ask, stay, installSafeLatest); - return answer.equals(stay) ? configuredVersion : VersionIdentifier.LATEST; - + if (answer.equals(stay)) { + return configuredVersion; + } else { + return VersionIdentifier.LATEST; + } } else if (nextSafe.equals(latestSafe)) { String answer = this.context.question( currentIsUnsafe + "Of the newer versions, only version " + nextSafe + " is safe, which is however not the latest. " + ask, stay, "Install the safe version (" + nextSafe + ")."); - return answer.equals(stay) ? configuredVersion : nextSafe; - + if (answer.equals(stay)) { + return configuredVersion; + } else { + return nextSafe; + } } else { if (latestSafe.equals(latest)) { String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installSafeLatest); - return answer.equals(stay) ? configuredVersion - : answer.equals(installNextSafe) ? nextSafe : VersionIdentifier.LATEST; - + if (answer.equals(stay)) { + return configuredVersion; + } else if (answer.equals(installNextSafe)) { + return nextSafe; + } else { + return VersionIdentifier.LATEST; + } } else { String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installLatestSafe); - return answer.equals(stay) ? configuredVersion : answer.equals(installNextSafe) ? nextSafe : latestSafe; + if (answer.equals(stay)) { + return configuredVersion; + } else if (answer.equals(installNextSafe)) { + return nextSafe; + } else { + return latestSafe; + } } } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index 8f4311f4e..6c001f779 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -26,19 +26,19 @@ protected String getVersionPrefixToRemove() { @Override protected String getGithubOrganization() { - return "helm"; + return getTool(); } @Override public String getCpeVendor() { - return "helm"; + return getTool(); } @Override public String getCpeProduct() { - return "helm"; + return getTool(); } @Override 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 1abb5113d..a38117116 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 @@ -31,8 +31,6 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { /** {@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 UrlSecurityWarningsJson urlSecurityWarningsJson = new UrlSecurityWarningsJson(); /** @@ -135,6 +133,12 @@ public boolean contains(VersionIdentifier version) { return contains(version, false, null, null); } + /** + * @param version the {@link VersionIdentifier version} to check for security risks listed in the + * {@link UrlSecurityJsonFile}. + * @return the {@link UrlSecurityWarning UrlSecurityWarnings} for the given {@code version} or {@code null} if no such + * warnings exist. + */ public Set getMatchingSecurityWarnings(VersionIdentifier version) { Set matchedWarnings = new HashSet<>(); @@ -146,6 +150,7 @@ public Set getMatchingSecurityWarnings(VersionIdentifier ver return matchedWarnings; } + /** Clears all security warnings. */ public void clearSecurityWarnings() { this.urlSecurityWarningsJson.getWarnings().clear(); 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 index b6cbf766f..edfee27ed 100644 --- 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 @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.List; +import java.util.Optional; /** * A simple container with the information about a security warning. @@ -26,8 +27,6 @@ public class UrlSecurityWarning { private String nistUrl; - private List referenceUrls; - public UrlSecurityWarning() { super(); @@ -61,7 +60,6 @@ public UrlSecurityWarning(VersionRange versionRange, String matchedCpe, String i this.cveName = cveName; this.description = description; this.nistUrl = nistUrl; - this.referenceUrls = referenceUrl; } // these setters and getters are needed for the jackson (de)serialization @@ -105,11 +103,6 @@ public void setNistUrl(String nistUrl) { this.nistUrl = nistUrl; } - public void setReferenceUrl(List referenceUrl) { - - this.referenceUrls = referenceUrl; - } - public VersionRange getVersionRange() { return versionRange; @@ -150,19 +143,12 @@ 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; + String versionRangeString = Optional.ofNullable(this.versionRange).map(Object::toString).orElse(""); + String severity = Optional.ofNullable(this.severity).map(Object::toString).orElse(""); + String s = versionRangeString + severity + this.severityVersion + this.cveName + this.description + this.nistUrl; return s.hashCode(); } @@ -195,16 +181,6 @@ public boolean equals(Object obj) { 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; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java index 9ee4f71e3..21b665e06 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java @@ -51,6 +51,11 @@ public UrlSecurityFile getSecurityFile() { return this.securityFile; } + /** + * @return the {@link UrlSecurityJsonFile} of this {@link UrlEdition}. Will be lazily initialized on the first call of + * this method. If the file exists, it will be loaded, otherwise it will be empty and only created on save if + * data was added. + */ public UrlSecurityJsonFile getSecurityJsonFile() { if (this.securityJsonFile == null) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index 4714ae073..63c9e9a7b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -119,8 +119,16 @@ public int compareTo(VersionRange o) { } int compareMins = this.min.compareTo(o.min); if (compareMins == 0) { - return this.boundaryType.isLeftExclusive() == o.boundaryType.isLeftExclusive() ? 0 - : this.boundaryType.isLeftExclusive() ? 1 : -1; + + if (this.boundaryType.isLeftExclusive()) { + if (o.boundaryType.isLeftExclusive()) { + return 0; + } else { + return -1; + } + } else { + return 1; + } } else { return compareMins; } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index 4f682aa78..461a5e218 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -58,6 +58,7 @@ protected static IdeTestContext newContext(String projectName, String projectPat * @param copyForMutation - {@code true} to create a copy of the project that can be modified by the test, * {@code false} otherwise (only to save resources if you are 100% sure that your test never modifies anything * in that project. + * @param answers the answers to use for the {@link IdeTestContext}. * @return the {@link IdeTestContext} pointing to that project. */ protected static IdeTestContext newContext(String projectName, String projectPath, boolean copyForMutation, 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 243c33a1d..967afdf2b 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 70e2c3efa..54cb3b2be 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -16,6 +16,10 @@ import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; +/** + * Analyzes file paths to detect tool, edition and version of software listed in a directory structure like this: + * ...//// + */ public class UrlAnalyzer extends AbstractFileTypeAnalyzer { // The file filter is used to filter supported files. @@ -25,6 +29,12 @@ public class UrlAnalyzer extends AbstractFileTypeAnalyzer { private final UpdateManager updateManager; + /** + * The constructor which initializes its {@link FileFilter} with a {@link UrlFileFilter}. + * + * @param updateManager the {@link UpdateManager} used to convert IDEasys tool/edition/version naming to the naming + * conventions of official CPEs. + */ public UrlAnalyzer(UpdateManager updateManager) { fileFilter = new UrlFileFilter(); diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index 19193c584..0b09c0888 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -2,17 +2,17 @@ import java.io.FileFilter; -import static com.devonfw.tools.ide.url.model.file.UrlStatusFile.STATUS_JSON; +import com.devonfw.tools.ide.url.model.file.UrlStatusFile; +/** + * This {@link FileFilter} only accepts files with name {@link UrlStatusFile#STATUS_JSON}. The paths of the accepted + * files are then analyzed by the {@link UrlAnalyzer} to detect the available tools, editions and versions. + */ public class UrlFileFilter implements FileFilter { - public UrlFileFilter() { - - } - @Override public boolean accept(java.io.File pathname) { - return pathname.getName().equals(STATUS_JSON); + return pathname.getName().equals(UrlStatusFile.STATUS_JSON); } } From dbee293f90d849afbf99f5a4b3054e0475408cbf Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Sun, 21 Jan 2024 18:50:48 +0100 Subject: [PATCH 25/47] #103: more change requests from team review and bug fix - fixed bug: VersionRange.compareTo() - removed severityVersion in UrlSecurityJsonWarning - updateManager.getUrlUpdater also takes Edition as input - updatet to owasp dependency-check-core 9.0.9 from 8.4.2 --- .../url/model/file/UrlSecurityJsonFile.java | 20 ++-- .../model/file/json/UrlSecurityWarning.java | 23 +---- .../tools/ide/url/updater/UpdateManager.java | 12 ++- .../tools/ide/version/VersionRange.java | 10 +- security/pom.xml | 3 +- .../security/BuildSecurityJsonFiles.java | 91 ++++++++++--------- .../devonfw/tools/security/UrlAnalyzer.java | 2 +- 7 files changed, 76 insertions(+), 85 deletions(-) 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 a38117116..2fc3c9755 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 @@ -5,6 +5,7 @@ import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -44,14 +45,12 @@ public UrlSecurityJsonFile(UrlEdition parent) { } /** - * A wrapper for - * {@link #addSecurityWarning(VersionRange, String, String, BigDecimal, String, String, String, String, List)} used in - * the unit tests. + * A wrapper for {@link #addSecurityWarning(VersionRange, String, String, BigDecimal, String, String, String)} + * used in the unit tests. */ public boolean addSecurityWarning(VersionRange versionRange) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null, null, null, null, - null); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null, null, null); boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; @@ -65,19 +64,16 @@ public boolean addSecurityWarning(VersionRange versionRange) { * @param interval the interval of vulnerability that was used to determine the {@link VersionRange}. This can be used * to manually 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. * @return {@code true} if the security match was added, {@code false} if it was already present. */ public boolean addSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, - String severityVersion, String cveName, String description, String nistUrl, List referenceUrl) { + String cveName, String description, String nistUrl) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, matchedCpe, interval, severity, - severityVersion, cveName, description, nistUrl, referenceUrl); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, matchedCpe, interval, severity, cveName, + description, nistUrl); boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; @@ -150,7 +146,7 @@ public Set getMatchingSecurityWarnings(VersionIdentifier ver return matchedWarnings; } - /** Clears all security warnings. */ + /** Clears all security warnings. */ public void clearSecurityWarnings() { this.urlSecurityWarningsJson.getWarnings().clear(); 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 index edfee27ed..560a2ec08 100644 --- 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 @@ -19,8 +19,6 @@ public class UrlSecurityWarning { private BigDecimal severity; - private String severityVersion; - private String cveName; private String description; @@ -41,22 +39,18 @@ public UrlSecurityWarning() { * @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) { + String cveName, String description, String nistUrl) { 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; @@ -83,11 +77,6 @@ public void setSeverity(BigDecimal severity) { this.severity = severity; } - public void setSeverityVersion(String severityVersion) { - - this.severityVersion = severityVersion; - } - public void setCveName(String cveName) { this.cveName = cveName; @@ -123,11 +112,6 @@ public BigDecimal getSeverity() { return severity; } - public String getSeverityVersion() { - - return severityVersion; - } - public String getCveName() { return cveName; @@ -148,7 +132,7 @@ public int hashCode() { String versionRangeString = Optional.ofNullable(this.versionRange).map(Object::toString).orElse(""); String severity = Optional.ofNullable(this.severity).map(Object::toString).orElse(""); - String s = versionRangeString + severity + this.severityVersion + this.cveName + this.description + this.nistUrl; + String s = versionRangeString + severity + this.cveName + this.description + this.nistUrl; return s.hashCode(); } @@ -169,9 +153,6 @@ public boolean equals(Object obj) { 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; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 29e8b4555..52654e4ad 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -97,9 +97,17 @@ public void updateAll() { } } - public AbstractUrlUpdater getUrlUpdater(String tool) { + /** + * Retrieves the {@link AbstractUrlUpdater updater} that matches the given tool and edition. + * + * @param tool the tool to retrieve the updater for. + * @param edition the edition to retrieve the updater for. + * @return the {@link AbstractUrlUpdater updater} that matches the given tool and edition. + */ + public AbstractUrlUpdater retrieveUrlUpdater(String tool, String edition) { - return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst().orElse(null); + return updaters.stream().filter(updater -> updater.getTool().equals(tool) && updater.getEdition().equals(edition)) + .findFirst().orElse(null); } public UrlRepository getUrlRepository() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index 63c9e9a7b..df9d11ec1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -120,14 +120,14 @@ public int compareTo(VersionRange o) { int compareMins = this.min.compareTo(o.min); if (compareMins == 0) { - if (this.boundaryType.isLeftExclusive()) { - if (o.boundaryType.isLeftExclusive()) { - return 0; + if (this.boundaryType.isLeftExclusive() == o.boundaryType.isLeftExclusive()) { + return 0; + } else { + if (this.boundaryType.isLeftExclusive()) { + return 1; } else { return -1; } - } else { - return 1; } } else { return compareMins; diff --git a/security/pom.xml b/security/pom.xml index 0c587e2ab..072218910 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -14,13 +14,14 @@ 17 + 9.0.9 org.owasp dependency-check-core - 8.4.2 + ${owasp.version} com.devonfw.tools.IDEasy 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 967afdf2b..0b85b349f 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -43,8 +43,6 @@ import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.utils.Pair; import org.owasp.dependencycheck.utils.Settings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.devonfw.tools.ide.context.AbstractIdeContext; import com.devonfw.tools.ide.context.IdeContext; @@ -58,10 +56,6 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -// TODO Doesn't yet work with versions defined like this ///latest -// TODO Sometimes when running this class is takes a long time to finish. Maybe this is because of the OWASP package, which -// is updating the vulnerabilities. A dirty fix is to stop the program and restart it. - /** * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for @@ -69,11 +63,11 @@ * {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion}. If a vulnerability is found, it is added to the * {@link UrlSecurityJsonFile} of the corresponding tool and edition. The previous content of the file is overwritten. + * Sometimes when running this class is takes a long time to finish. Maybe this is because of the OWASP package, which + * is updating the vulnerabilities. A dirty fix is to stop the program and restart it. */ public class BuildSecurityJsonFiles { - private static final Logger logger = LoggerFactory.getLogger(BuildSecurityJsonFiles.class); - private static final String CVE_BASE_URL = "https://nvd.nist.gov/vuln/detail/"; private static final Set CVES_TO_IGNORE = new HashSet<>(); @@ -109,7 +103,6 @@ public static void main(String[] args) { throw new RuntimeException("These two args could not be parsed as BigDecimal"); } run(); - } private static void run() { @@ -124,7 +117,7 @@ private static void run() { Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); - AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); + AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool, edition).getSecurityJsonFile(); boolean newlyAdded = foundToolsAndEditions.add(new Pair<>(tool, edition)); if (newlyAdded) { // to assure that the file is cleared only once per tool and edition @@ -143,7 +136,7 @@ private static void run() { } securityFile.save(); } - actuallyIgnoredCves.forEach(cve -> context.info("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); + actuallyIgnoredCves.forEach(cve -> context.debug("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); printAffectedVersions(context); } @@ -179,12 +172,17 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName() + "\\n" + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); } + double severityDouble; boolean hasV3Severity = vulnerability.getCvssV3() != null; - double severityDouble = hasV3Severity ? vulnerability.getCvssV3().getBaseScore() - : vulnerability.getCvssV2().getScore(); + if (hasV3Severity) { + severityDouble = vulnerability.getCvssV3().getCvssData().getBaseScore(); + } else { + severityDouble = vulnerability.getCvssV2().getCvssData().getBaseScore(); + } + vulnerability.getCvssV3().getCvssData().getBaseScore(); + vulnerability.getCvssV2().getCvssData().getBaseScore(); String formatted = String.format(Locale.US, "%.1f", severityDouble); BigDecimal severity = new BigDecimal(formatted); - String severityVersion = hasV3Severity ? "v3" : "v2"; String cveName = vulnerability.getName(); if (CVES_TO_IGNORE.contains(cveName)) { actuallyIgnoredCves.add(cveName); @@ -198,15 +196,17 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, if (referenceUrls.isEmpty()) { referenceUrls.add("No references found, try searching for the CVE name (" + cveName + ") on the web."); } - boolean tooLowSeverity = hasV3Severity ? severity.compareTo(minV3Severity) < 0 - : severity.compareTo(minV2Severity) < 0; - - if (tooLowSeverity) { + if (hasV3Severity) { + if (severity.compareTo(minV3Severity) < 0) { + return; + } + } else if (severity.compareTo(minV2Severity) < 0) { return; } + VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, urlUpdater, cpeToUrlVersion); if (versionRange == null) { - logger.info( + context.info( "Vulnerability {} seems to be irrelevant because its affected versions have no overlap with the versions " + "available through IDEasy. If you think the versions should match, see the methode " + "mapUrlVersionToCpeVersion() in the UrlUpdater of the tool.", @@ -224,8 +224,7 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, matchedVulnerableSoftware.getVersionEndIncluding(), matchedVulnerableSoftware.getVersionEndExcluding(), matchedVulnerableSoftware.getVersion()); - securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, severityVersion, cveName, description, - nistUrl, referenceUrls); + securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, cveName, description, nistUrl); } @@ -287,33 +286,44 @@ private static String getUrlVersion(String cpeVersion, AbstractUrlUpdater urlUpd return urlVersion; } - public static VersionRange getVersionRangeFromInterval(String si, String se, String ee, String ei, String s) - throws IllegalStateException { + /** + * Determines the {@link VersionRange} from the interval provided by OWASP. + * + * @param startIncluding The {@link String version} of the start of the interval, including this version. + * @param startExcluding The {@link String version} of the start of the interval, excluding this version. + * @param endExcluding The {@link String version} of the end of the interval, excluding this version. + * @param endIncluding The {@link String version} of the end of the interval, including this version. + * @param singleVersion If the OWASP vulnerability only affects a single version, this is the {@link String version}. + * @return the {@link VersionRange}. + * @throws IllegalStateException if all parameters are {@code null}. + */ + public static VersionRange getVersionRangeFromInterval(String startIncluding, String startExcluding, + String endExcluding, String endIncluding, String singleVersion) throws IllegalStateException { - if (ee == null && ei == null && se == null && si == null) { - if (s == null) { + if (endExcluding == null && endIncluding == null && startExcluding == null && startIncluding == null) { + if (singleVersion == null) { throw new IllegalStateException( "Vulnerability has no interval of affected versions or single affected version."); } - VersionIdentifier singleAffectedVersion = VersionIdentifier.of(s); + VersionIdentifier singleAffectedVersion = VersionIdentifier.of(singleVersion); return new VersionRange(singleAffectedVersion, singleAffectedVersion, BoundaryType.CLOSED); } - boolean leftExclusive = si == null; - boolean rightExclusive = ei == null; + boolean leftExclusive = startIncluding == null; + boolean rightExclusive = endIncluding == null; VersionIdentifier min = null; - if (si != null) { - min = VersionIdentifier.of(si); - } else if (se != null) { - min = VersionIdentifier.of(se); + if (startIncluding != null) { + min = VersionIdentifier.of(startIncluding); + } else if (startExcluding != null) { + min = VersionIdentifier.of(startExcluding); } VersionIdentifier max = null; - if (ei != null) { - max = VersionIdentifier.of(ei); - } else if (ee != null) { - max = VersionIdentifier.of(ee); + if (endIncluding != null) { + max = VersionIdentifier.of(endIncluding); + } else if (endExcluding != null) { + max = VersionIdentifier.of(endExcluding); } return new VersionRange(min, max, BoundaryType.of(leftExclusive, rightExclusive)); @@ -344,25 +354,20 @@ private static void printAffectedVersions(IdeContext context) { } else { if (min != null) { - System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + context.info("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + new VersionRange(min, version, BoundaryType.of(false, true)) + " are affected by vulnerabilities."); min = null; } } } if (min != null) { - System.out.println("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + context.info("Tool " + tool.getName() + " with edition " + edition.getName() + " and versions " + new VersionRange(min, null, BoundaryType.of(false, true)) + " are affected by vulnerabilities."); } } } } - private static void printAllOwaspAnalyzers(Engine engine) { - - engine.getMode().getPhases().forEach(phase -> engine.getAnalyzers(phase) - .forEach(analyzer -> System.out.println("Phase: " + phase + ", Analyzer: " + analyzer.getName()))); - } private static void initCvesToIgnore() { diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 54cb3b2be..97e1c0a61 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -49,7 +49,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) { String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); - AbstractUrlUpdater urlUpdater = updateManager.getUrlUpdater(tool); + AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); // adding vendor evidence String cpeVendor = urlUpdater.getCpeVendor(); From ea1bb2695cfc569debc72da2a232c8cbe21a4c93 Mon Sep 17 00:00:00 2001 From: Mattes Mrzik Date: Mon, 22 Jan 2024 13:58:28 +0100 Subject: [PATCH 26/47] Update cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> --- .../main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 922e3e069..6892c7102 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 @@ -202,7 +202,7 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::getCveName) .collect(Collectors.joining(", ")); String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " - + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; + + "which has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; if (latestSafe == null) { this.context.warning(currentIsUnsafe + "There is no safe version available."); From b02bfcf4cd6ba658cccddcdae9188c9df2f0054c Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 23 Jan 2024 21:37:55 +0100 Subject: [PATCH 27/47] #103: checkpoint to not accidentally lose progress --- .../tool/SecurityRiskInteractionAnswer.java | 13 ++ .../tools/ide/tool/ToolCommandlet.java | 87 ++++----- .../AndroidStudioUrlUpdater.java | 6 + .../tools/ide/tool/aws/AwsUrlUpdater.java | 6 + .../tools/ide/tool/az/AzureUrlUpdater.java | 6 + .../ide/tool/cobigen/CobigenUrlUpdater.java | 6 + .../tool/docker/DockerDesktopUrlUpdater.java | 6 + .../ide/tool/dotnet/DotNetUrlUpdater.java | 10 +- .../ide/tool/gcloud/GCloudUrlUpdater.java | 10 +- .../ide/tool/gcviewer/GcViewerUrlUpdater.java | 6 + .../tools/ide/tool/gh/GhUrlUpdater.java | 8 +- .../ide/tool/gradle/GradleUrlUpdater.java | 6 + .../tools/ide/tool/helm/HelmUrlUpdater.java | 6 + .../intellij/IntellijCommunityUrlUpdater.java | 16 ++ .../intellij/IntellijUltimateUrlUpdater.java | 16 ++ .../ide/tool/intellij/IntellijUrlUpdater.java | 30 ++- .../ide/tool/jasypt/JasyptUrlUpdater.java | 6 + .../tools/ide/tool/java/JavaUrlUpdater.java | 6 + .../ide/tool/jenkins/JenkinsUrlUpdater.java | 6 + .../tools/ide/tool/jmc/JmcUrlUpdater.java | 7 +- .../tool/kotlinc/KotlincNativeUrlUpdater.java | 6 + .../ide/tool/kotlinc/KotlincUrlUpdater.java | 6 + .../tool/lazydocker/LazyDockerUrlUpdater.java | 10 +- .../tools/ide/tool/mvn/MvnUrlUpdater.java | 6 + .../tools/ide/tool/node/NodeUrlUpdater.java | 6 + .../tools/ide/tool/npm/NpmUrlUpdater.java | 8 +- .../tools/ide/tool/oc/OcUrlUpdater.java | 6 + .../tools/ide/tool/pip/PipUrlUpdater.java | 7 + .../ide/tool/python/PythonUrlUpdater.java | 6 + .../ide/tool/quarkus/QuarkusUrlUpdater.java | 6 + .../tools/ide/tool/sonar/SonarUrlUpdater.java | 6 + .../tool/terraform/TerraformUrlUpdater.java | 6 + .../ide/tool/tomcat/TomcatUrlUpdater.java | 6 + .../ide/tool/vscode/VsCodeUrlUpdater.java | 6 + .../url/model/file/UrlSecurityJsonFile.java | 15 +- .../model/file/json/UrlSecurityWarning.java | 33 +--- .../ide/url/updater/AbstractUrlUpdater.java | 5 +- .../tools/ide/url/updater/UpdateManager.java | 13 +- .../java/com/devonfw/tools/ide/util/Pair.java | 23 +++ .../tools/ide/tool/ToolCommandletTest.java | 16 +- .../tools/ide/tool/UrlUpdaterMock.java | 6 + .../tools/ide/tool/UrlUpdaterMockSingle.java | 4 - .../tool/intellij/IntellijUrlUpdaterMock.java | 12 ++ .../security/BuildSecurityJsonFiles.java | 180 ++++++++++++------ .../devonfw/tools/security/UrlAnalyzer.java | 28 ++- .../devonfw/tools/security/UrlFileFilter.java | 16 ++ 46 files changed, 495 insertions(+), 210 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/util/Pair.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java new file mode 100644 index 000000000..79c086969 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java @@ -0,0 +1,13 @@ +package com.devonfw.tools.ide.tool; + +public enum SecurityRiskInteractionAnswer { + + STAY, + + LATEST_SAFE, + + SAFE_LATEST, + + NEXT_SAFE, + +} 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 922e3e069..77be1032b 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 @@ -4,7 +4,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,8 +28,11 @@ import com.devonfw.tools.ide.property.StringListProperty; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.util.FilenameUtil; +import com.devonfw.tools.ide.util.Pair; import com.devonfw.tools.ide.version.VersionIdentifier; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.*; + /** * {@link Commandlet} for a tool integrated into the IDE. */ @@ -198,11 +204,10 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured VersionIdentifier nextSafe = getNextSafeVersion(current, securityFile, allVersions); VersionIdentifier latestSafe = getLatestSafe(allVersions, securityFile); - String cves = securityFile.getMatchingSecurityWarnings(current).stream().map(UrlSecurityWarning::getCveName) .collect(Collectors.joining(", ")); String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is selected, " - + "which is has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; + + "which has one or more vulnerabilities:\n\n" + cves + "\n\n(See also " + securityFile.getPath() + ")\n\n"; if (latestSafe == null) { this.context.warning(currentIsUnsafe + "There is no safe version available."); @@ -229,66 +234,44 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi String ask = "Which version do you want to install?"; - String stay = "Stay with the current unsafe version (" + current + ")."; - String installLatestSafe = "Install the latest safe version (" + latestSafe + ")."; - String installSafeLatest = "Install the (safe) latest version (" + latest + ")."; - String installNextSafe = "Install the next safe version (" + nextSafe + ")."; + // enum id, option message, version that is returned if this option is selected + Map> options = new HashMap<>(); + options.put(STAY, Pair.of("Stay with the current unsafe version (" + current + ").", configuredVersion)); + options.put(LATEST_SAFE, Pair.of("Install the latest safe version (" + latestSafe + ").", latestSafe)); + options.put(SAFE_LATEST, Pair.of("Install the (safe) latest version (" + latest + ").", VersionIdentifier.LATEST)); + options.put(NEXT_SAFE, Pair.of("Install the next safe version (" + nextSafe + ").", nextSafe)); if (current.equals(latest)) { - String answer = this.context.question(currentIsUnsafe + "There are no updates available. " + ask, stay, - installLatestSafe); - if (answer.equals(stay)) { - return configuredVersion; - } else { - return latestSafe; - } + return getAnswer(options, currentIsUnsafe + "There are no updates available. " + ask, STAY, LATEST_SAFE); } else if (nextSafe == null) { // install an older version that is safe or stay with the current unsafe version - String answer = this.context.question(currentIsUnsafe + "All newer versions are also not safe. " + ask, stay, - installLatestSafe); - if (answer.equals(stay)) { - return configuredVersion; - } else { - return latestSafe; - } + return getAnswer(options, currentIsUnsafe + "All newer versions are also not safe. " + ask, STAY, LATEST_SAFE); } else if (nextSafe.equals(latest)) { - String answer = this.context.question(currentIsUnsafe + "Of the newer versions, only the latest is safe. " + ask, - stay, installSafeLatest); - if (answer.equals(stay)) { - return configuredVersion; - } else { - return VersionIdentifier.LATEST; - } + return getAnswer(options, currentIsUnsafe + "Of the newer versions, only the latest is safe. " + ask, STAY, + SAFE_LATEST); } else if (nextSafe.equals(latestSafe)) { - String answer = this.context.question( - currentIsUnsafe + "Of the newer versions, only version " + nextSafe - + " is safe, which is however not the latest. " + ask, - stay, "Install the safe version (" + nextSafe + ")."); - if (answer.equals(stay)) { - return configuredVersion; - } else { - return nextSafe; - } + return getAnswer(options, currentIsUnsafe + "Of the newer versions, only version " + nextSafe + + " is safe, which is however not the latest. " + ask, STAY, NEXT_SAFE); } else { if (latestSafe.equals(latest)) { - String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installSafeLatest); - if (answer.equals(stay)) { - return configuredVersion; - } else if (answer.equals(installNextSafe)) { - return nextSafe; - } else { - return VersionIdentifier.LATEST; - } + return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, SAFE_LATEST); } else { - String answer = this.context.question(currentIsUnsafe + ask, stay, installNextSafe, installLatestSafe); - if (answer.equals(stay)) { - return configuredVersion; - } else if (answer.equals(installNextSafe)) { - return nextSafe; - } else { - return latestSafe; - } + return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, LATEST_SAFE); + } + } + } + + private VersionIdentifier getAnswer(Map> options, + String question, SecurityRiskInteractionAnswer... possibleAnswers) { + + String[] availableOptions = Arrays.stream(possibleAnswers).map(options::get).map(Pair::getFirst) + .toArray(String[]::new); + String answer = this.context.question(question, availableOptions); + for (SecurityRiskInteractionAnswer possibleAnswer : possibleAnswers) { + if (options.get(possibleAnswer).getFirst().equals(answer)) { + return options.get(possibleAnswer).getSecond(); } } + throw new IllegalStateException("Invalid answer " + answer); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java index a4d5854b2..fd705960d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java @@ -40,6 +40,12 @@ protected String getTool() { return "android-studio"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override public void update(UrlRepository urlRepository) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java index 7f9df6110..52cc4eb4a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "aws"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java index 9aca42153..2bb343633 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java @@ -18,6 +18,12 @@ protected String getTool() { return "az"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java index 484118ee9..85779f291 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java @@ -12,6 +12,12 @@ protected String getTool() { return "cobigen"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getMavenGroupIdPath() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java index 6b57fc794..36e09430b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java @@ -23,6 +23,12 @@ protected String getTool() { return "docker"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java index ff1e57e62..e68435479 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java @@ -13,6 +13,12 @@ protected String getTool() { return "dotnet"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getVersionPrefixToRemove() { @@ -48,10 +54,10 @@ protected String getGithubRepository() { @Override protected String mapVersion(String version) { + if (version.matches("v\\d+\\.\\d+\\.\\d+")) { return super.mapVersion(version); - } - else { + } else { return null; } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java index e046b0376..fbd07a12a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java @@ -10,15 +10,23 @@ public class GCloudUrlUpdater extends GithubUrlUpdater { private static final VersionIdentifier MIN_GCLOUD_VID = VersionIdentifier.of("299.0.0"); + private static final VersionIdentifier MIN_ARM_GCLOUD_VID = VersionIdentifier.of("366.0.0"); + private static final String BASE_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${version}-"; - + @Override protected String getTool() { return "gcloud"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubRepository() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java index d940e85db..d0643c8d6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java @@ -13,6 +13,12 @@ protected String getTool() { return "gcviewer"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java index a0dd40801..b063314df 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java @@ -8,7 +8,7 @@ * {@link GithubUrlUpdater} for "gh" (github CLI). */ public class GhUrlUpdater extends GithubUrlUpdater { - + private static final VersionIdentifier MIN_MAC_ARM_VID = VersionIdentifier.of("2.23.0"); @Override @@ -17,6 +17,12 @@ protected String getTool() { return "gh"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java index 9355be394..aa0f99ece 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java @@ -44,6 +44,12 @@ protected String getTool() { return "gradle"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index 6c001f779..bae59bb62 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -17,6 +17,12 @@ protected String getTool() { return "helm"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java new file mode 100644 index 000000000..c882582cd --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java @@ -0,0 +1,16 @@ +package com.devonfw.tools.ide.tool.intellij; + +public class IntellijCommunityUrlUpdater extends IntellijUrlUpdater { + + @Override + protected String getEdition() { + + return getTool(); + } + + @Override + IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + + return releases[1]; + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java new file mode 100644 index 000000000..d7513091d --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java @@ -0,0 +1,16 @@ +package com.devonfw.tools.ide.tool.intellij; + +public class IntellijUltimateUrlUpdater extends IntellijUrlUpdater { + + @Override + protected String getEdition() { + + return "ultimate"; + } + + @Override + IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + + return releases[0]; + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java index 9ade7931d..9395968fc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java @@ -20,16 +20,12 @@ /** * {@link IntellijUrlUpdater} base class for IntelliJ. */ -public class IntellijUrlUpdater extends JsonUrlUpdater { +public abstract class IntellijUrlUpdater extends JsonUrlUpdater { private static final String VERSION_BASE_URL = "https://data.services.jetbrains.com"; private static final String JSON_URL = "products?code=IIU%2CIIC&release.type=release"; - private static final String ULTIMATE_EDITION = "ultimate"; - - private static final String COMMUNITY_EDITION = "intellij"; - private static final ObjectMapper MAPPER = JsonMapping.create(); private static final Logger logger = LoggerFactory.getLogger(IntellijUrlUpdater.class); @@ -41,23 +37,13 @@ public void update(UrlRepository urlRepository) { try { String response = doGetResponseBodyAsString(doGetVersionUrl()); IntellijJsonObject[] jsonObj = MAPPER.readValue(response, IntellijJsonObject[].class); - // Has 2 elements, 1. Ultimate Edition, 2. Community Edition - IntellijJsonObject ultimateRelease; - IntellijJsonObject communityRelease; if (jsonObj.length == 2) { - ultimateRelease = jsonObj[0]; - communityRelease = jsonObj[1]; - UrlEdition edition; - - if (ultimateRelease != null) { - edition = tool.getOrCreateChild(ULTIMATE_EDITION); - addVersionForEdition(ultimateRelease, edition); - } + IntellijJsonObject release = getIntellijJsonRelease(jsonObj); + UrlEdition edition = tool.getOrCreateChild(getEdition()); - if (communityRelease != null) { - edition = tool.getOrCreateChild(COMMUNITY_EDITION); - addVersionForEdition(communityRelease, edition); + if (release != null) { + addVersionForEdition(release, edition); } } @@ -66,6 +52,12 @@ public void update(UrlRepository urlRepository) { } } + /** + * @param releases Has 2 elements, 1. Ultimate Edition, 2. Community Edition + * @return The release for the {@link #getEdition() edition}. + */ + abstract IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases); + /** * Adds a version for the provided {@link UrlEdition} * diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java index 5dd1629a3..10be2c97a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java @@ -12,6 +12,12 @@ protected String getTool() { return "jasypt"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getMavenGroupIdPath() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index 4ab982093..905269bc9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -16,6 +16,12 @@ protected String getTool() { return "java"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String mapVersion(String version) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java index 873648087..d48d118ca 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java @@ -16,6 +16,12 @@ protected String getTool() { return "jenkins"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getVersionUrl() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java index 699b5a9b7..43bbbd0b4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java @@ -14,6 +14,12 @@ protected String getTool() { return "jmc"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { @@ -26,7 +32,6 @@ protected String getGithubRepository() { return "jmc-build"; } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java index c9171cb5f..502845c8a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "kotlinc-native"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java index 8f42bdf68..a2dde36cc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "kotlinc"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java index 94f3df860..32e019c09 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java @@ -8,9 +8,9 @@ * {@link GithubUrlUpdater} for lazydocker. */ public class LazyDockerUrlUpdater extends GithubUrlUpdater { - + private static final VersionIdentifier MIN_WIN_VID = VersionIdentifier.of("0.7.4"); - + private static final VersionIdentifier MIN_ARM_VID = VersionIdentifier.of("0.15.0"); @Override @@ -19,6 +19,12 @@ protected String getTool() { return "lazydocker"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java index b22e9f2ae..4e81b8280 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "mvn"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java index 8f0e40b78..952e068d3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java @@ -21,6 +21,12 @@ protected String getTool() { return "node"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java index e1273404a..fc36e9da2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java @@ -15,9 +15,15 @@ protected String getTool() { return "npm"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { - + doAddVersion(urlVersion, "https://registry.npmjs.org/npm/-/npm-${version}.tgz"); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java index 9a7ade068..95c403b09 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "oc"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java index 448f40e5c..d4eb79f6f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java @@ -15,6 +15,12 @@ protected String getTool() { return "pip"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { @@ -23,6 +29,7 @@ protected void addVersion(UrlVersion urlVersion) { @Override protected boolean isValidContentType(String contentType) { + // pip is not a binary download but a script with content-type `text/x-Python` so we override this check here return true; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java index 793ad52b2..aba4243c0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java @@ -35,6 +35,12 @@ protected String getTool() { return "python"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java index 86faa1442..3c8346783 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java @@ -17,6 +17,12 @@ protected String getTool() { return "quarkus"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java index f7bbd91af..b6a88f47b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java @@ -13,6 +13,12 @@ protected String getTool() { return "sonar"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java index 623c4b824..5c8a42cbc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java @@ -17,6 +17,12 @@ protected String getTool() { return "terraform"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java index 7a3381510..014671419 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java @@ -14,6 +14,12 @@ protected String getTool() { return "tomcat"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java index fc12b3e5b..79fecc276 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java @@ -14,6 +14,12 @@ protected String getTool() { return "vscode"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override protected String getGithubOrganization() { 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 2fc3c9755..6118a0ef7 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 @@ -45,12 +45,11 @@ public UrlSecurityJsonFile(UrlEdition parent) { } /** - * A wrapper for {@link #addSecurityWarning(VersionRange, String, String, BigDecimal, String, String, String)} - * used in the unit tests. + * A wrapper for {@link #addSecurityWarning(VersionRange, BigDecimal, String, String, String)} used in the unit tests. */ public boolean addSecurityWarning(VersionRange versionRange) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null, null, null); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null); boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; @@ -60,20 +59,16 @@ public boolean addSecurityWarning(VersionRange versionRange) { * Adds a new security warning to the security json file. * * @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 can be used - * to manually check if the mapping from CPE version to UrlVersion was correct. * @param severity the severity of the security risk. * @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. * @return {@code true} if the security match was added, {@code false} if it was already present. */ - public boolean addSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, - String cveName, String description, String nistUrl) { + public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity, String cveName, String description, + String nistUrl) { - UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, matchedCpe, interval, severity, cveName, - description, nistUrl); + UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, cveName, description, nistUrl); boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); this.modified = this.modified || added; return added; 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 index 560a2ec08..6e9a122ee 100644 --- 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 @@ -13,10 +13,6 @@ public class UrlSecurityWarning { private VersionRange versionRange; - private String matchedCpe; - - private String interval; - private BigDecimal severity; private String cveName; @@ -35,21 +31,16 @@ public UrlSecurityWarning() { * * @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 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. */ - public UrlSecurityWarning(VersionRange versionRange, String matchedCpe, String interval, BigDecimal severity, - String cveName, String description, String nistUrl) { + public UrlSecurityWarning(VersionRange versionRange, BigDecimal severity, String cveName, String description, + String nistUrl) { super(); this.versionRange = versionRange; - this.matchedCpe = matchedCpe; - this.interval = interval; this.severity = severity; this.cveName = cveName; this.description = description; @@ -62,16 +53,6 @@ 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; @@ -97,16 +78,6 @@ public VersionRange getVersionRange() { return versionRange; } - public String getMatchedCpe() { - - return matchedCpe; - } - - public String getInterval() { - - return interval; - } - public BigDecimal getSeverity() { return severity; diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 11f0f588f..b5a64c17a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -78,10 +78,7 @@ public abstract class AbstractUrlUpdater extends AbstractProcessorWithTimeout im /** * @return the name of the {@link UrlEdition edition} handled by this updater. */ - protected String getEdition() { - - return getTool(); - } + abstract protected String getEdition(); /** * @return the combination of {@link #getTool() tool} and {@link #getEdition() edition} but simplified if both are diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 52654e4ad..a97ecf68b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -5,6 +5,8 @@ import java.util.Arrays; import java.util.List; +import com.devonfw.tools.ide.tool.intellij.IntellijCommunityUrlUpdater; +import com.devonfw.tools.ide.tool.intellij.IntellijUltimateUrlUpdater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,11 +63,12 @@ public class UpdateManager extends AbstractProcessorWithTimeout { new AzureUrlUpdater(), new CobigenUrlUpdater(), new DockerDesktopUrlUpdater(), new DotNetUrlUpdater(), new EclipseCppUrlUpdater(), new EclipseJeeUrlUpdater(), new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(), new GcViewerUrlUpdater(), new GhUrlUpdater(), new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), - new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijUrlUpdater(), new JavaUrlUpdater(), - new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), new KotlincNativeUrlUpdater(), - new LazyDockerUrlUpdater(), new MvnUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), - new PipUrlUpdater(), new PythonUrlUpdater(), new QuarkusUrlUpdater(), new DockerRancherDesktopUrlUpdater(), - new SonarUrlUpdater(), new TerraformUrlUpdater(), new TomcatUrlUpdater(), new VsCodeUrlUpdater()); + new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijCommunityUrlUpdater(), new IntellijUltimateUrlUpdater(), + new JavaUrlUpdater(), new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), + new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), new MvnUrlUpdater(), new NodeUrlUpdater(), + new NpmUrlUpdater(), new OcUrlUpdater(), new PipUrlUpdater(), new PythonUrlUpdater(), new QuarkusUrlUpdater(), + new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(), new TerraformUrlUpdater(), new TomcatUrlUpdater(), + new VsCodeUrlUpdater()); /** * The constructor. diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java b/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java new file mode 100644 index 000000000..d0006de8e --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java @@ -0,0 +1,23 @@ +package com.devonfw.tools.ide.util; + +public class Pair { + private final K first; + private final V second; + + public Pair(K first, V second) { + this.first = first; + this.second = second; + } + + public K getFirst() { + return first; + } + + public V getSecond() { + return second; + } + + public static Pair of(K first, V second) { + return new Pair<>(first, second); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 214080737..9e780620b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -47,7 +47,7 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("2")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (2)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (6)."); assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); @@ -107,7 +107,7 @@ public void testSecurityRiskInteractionCurrentIsLatest() { // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("*")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("There are no updates available."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (9)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (6)."); @@ -138,7 +138,7 @@ public void testSecurityRiskInteractionNextSafeIsNull() { // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("6")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("All newer versions are also not safe."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (6)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (5)."); @@ -169,7 +169,7 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("7")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("Of the newer versions, only the latest is safe."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (7)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the (safe) latest version (9)."); @@ -200,11 +200,11 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)) .contains("Of the newer versions, only version 7 is safe, which is however not the latest."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); - assertThat(interactions.get(2)).isEqualTo("Option 2: Install the safe version (7)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (7)."); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); } @@ -231,7 +231,7 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("5")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (7)."); assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); @@ -264,7 +264,7 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() // answer to the interaction is option 1 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("3")); List interactions = ((IdeTestContext) context).level(IdeLogLevel.INTERACTION).getMessages(); - assertThat(interactions.get(0)).contains("which is has one or more vulnerabilities"); + assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (3)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (4)."); assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest safe version (7)."); diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMock.java b/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMock.java index f5917062f..62a0f616e 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMock.java @@ -23,6 +23,12 @@ protected String getTool() { return "mocked"; } + @Override + protected String getEdition() { + + return getTool(); + } + @Override public void update(UrlRepository urlRepository) { super.update(urlRepository); diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMockSingle.java b/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMockSingle.java index dc4195f2b..c9e5cee73 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMockSingle.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMockSingle.java @@ -1,14 +1,10 @@ package com.devonfw.tools.ide.tool; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import com.devonfw.tools.ide.url.model.folder.UrlRepository; import com.devonfw.tools.ide.url.model.folder.UrlVersion; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.JsonUrlUpdater; import com.devonfw.tools.ide.url.updater.UrlUpdater; /** diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java index 43f1e1c78..a84adc82b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java @@ -11,4 +11,16 @@ protected String getVersionBaseUrl() { return TEST_BASE_URL; } + + @Override + protected String getEdition() { + + return null; + } + + @Override + IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + + return null; + } } 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 0b85b349f..ded9e1313 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -13,6 +13,7 @@ import java.util.stream.Collectors; import com.devonfw.tools.ide.version.BoundaryType; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -37,7 +38,6 @@ import org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer; import org.owasp.dependencycheck.analyzer.YarnAuditAnalyzer; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.Reference; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.exception.ExceptionCollection; @@ -63,8 +63,8 @@ * {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion}. If a vulnerability is found, it is added to the * {@link UrlSecurityJsonFile} of the corresponding tool and edition. The previous content of the file is overwritten. - * Sometimes when running this class is takes a long time to finish. Maybe this is because of the OWASP package, which - * is updating the vulnerabilities. A dirty fix is to stop the program and restart it. + * Sometimes when running this class is takes a long time to finish. This is because of the OWASP package, which is + * updating the vulnerability database. */ public class BuildSecurityJsonFiles { @@ -93,12 +93,14 @@ public class BuildSecurityJsonFiles { */ public static void main(String[] args) { - if (args.length != 2) { - throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); - } + // if (args.length != 2) { + // throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); + // } try { - minV2Severity = new BigDecimal(String.format(args[0])); - minV3Severity = new BigDecimal(String.format(args[1])); + // minV2Severity = new BigDecimal(String.format(args[0])); + // minV3Severity = new BigDecimal(String.format(args[1])); + minV2Severity = new BigDecimal(String.format("0")); + minV3Severity = new BigDecimal(String.format("0")); } catch (NumberFormatException e) { throw new RuntimeException("These two args could not be parsed as BigDecimal"); } @@ -112,8 +114,13 @@ private static void run() { UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); Set> foundToolsAndEditions = new HashSet<>(); + + // TODO clean this up + + for (Dependency dependency : dependencies) { String filePath = dependency.getFilePath(); + System.out.println("filePath in BuildSecurityJsonFiles = " + filePath); Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); @@ -132,11 +139,12 @@ private static void run() { Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { - addVulnerabilityToSecurityFile(vulnerability, securityFile, urlUpdater, cpeToUrlVersion); + addVulnerabilityToSecurityFile(vulnerability, securityFile, cpeToUrlVersion, urlUpdater); } securityFile.save(); } - actuallyIgnoredCves.forEach(cve -> context.debug("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); + actuallyIgnoredCves + .forEach(cve -> context.debug("Ignored CVE " + cve + " because it is listed in CVES_TO_IGNORE.")); printAffectedVersions(context); } @@ -154,48 +162,43 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd engine.getMode().getPhases().forEach( phase -> engine.getAnalyzers(phase).removeIf(analyzer -> ANALYZERS_TO_IGNORE.contains(analyzer.getClass()))); + System.out.println("engine.scan"); + System.out.println("updateManager.getUrlRepository()"+ updateManager.getUrlRepository()); engine.scan(updateManager.getUrlRepository().getPath().toString()); + System.out.println("engine.scan done"); try { + System.out.println("engine.analyzeDependencies()"); engine.analyzeDependencies(); + System.out.println("engine.analyzeDependencies() done"); } catch (ExceptionCollection e) { throw new RuntimeException(e); } + System.out.println("before return engine.getDependencies"); return engine.getDependencies(); } + /** + * Adds a {@link UrlSecurityWarning} to the {@link UrlSecurityJsonFile} of the tool and edition to which the + * vulnerability applies. + * + * @param vulnerability the {@link Vulnerability} determined by OWASP dependency check. + * @param securityFile the {@link UrlSecurityJsonFile} of the tool and edition to which the vulnerability applies. + * @param cpeToUrlVersion a {@link Map} from CPE Version to {@link UrlVersion#getName() Url Version}. + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get maps between CPE Version and + * {@link UrlVersion#getName() Url Version} naming. + */ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, - AbstractUrlUpdater urlUpdater, Map cpeToUrlVersion) { + Map cpeToUrlVersion, AbstractUrlUpdater urlUpdater) { - if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { - // TODO if this ever happens, add a case that handles this - throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName() + "\\n" - + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); - } - double severityDouble; - boolean hasV3Severity = vulnerability.getCvssV3() != null; - if (hasV3Severity) { - severityDouble = vulnerability.getCvssV3().getCvssData().getBaseScore(); - } else { - severityDouble = vulnerability.getCvssV2().getCvssData().getBaseScore(); - } - vulnerability.getCvssV3().getCvssData().getBaseScore(); - vulnerability.getCvssV2().getCvssData().getBaseScore(); - String formatted = String.format(Locale.US, "%.1f", severityDouble); - BigDecimal severity = new BigDecimal(formatted); String cveName = vulnerability.getName(); if (CVES_TO_IGNORE.contains(cveName)) { actuallyIgnoredCves.add(cveName); return; } - String description = vulnerability.getDescription(); - String nistUrl = CVE_BASE_URL + cveName; - List referenceUrls = vulnerability.getReferences().stream().map(Reference::getUrl) - .collect(Collectors.toList()); - if (referenceUrls.isEmpty()) { - referenceUrls.add("No references found, try searching for the CVE name (" + cveName + ") on the web."); - } + boolean hasV3Severity = vulnerability.getCvssV3() != null; + BigDecimal severity = getBigDecimalSeverity(vulnerability, hasV3Severity); if (hasV3Severity) { if (severity.compareTo(minV3Severity) < 0) { return; @@ -204,7 +207,7 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, return; } - VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, urlUpdater, cpeToUrlVersion); + VersionRange versionRange = getVersionRangeFromVulnerability(vulnerability, cpeToUrlVersion, urlUpdater); if (versionRange == null) { context.info( "Vulnerability {} seems to be irrelevant because its affected versions have no overlap with the versions " @@ -213,31 +216,80 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, vulnerability.getName()); return; } + + String description = vulnerability.getDescription(); + String nistUrl = CVE_BASE_URL + cveName; + debugInfo(vulnerability, versionRange, severity, cveName, description, nistUrl, securityFile); + securityFile.addSecurityWarning(versionRange, severity, cveName, description, nistUrl); + } + + /** + * Prints debug information about the vulnerability. + * + * @param vulnerability the vulnerability determined by OWASP dependency check. + * @param versionRange the {@link VersionRange} to which the vulnerability applies. + * @param severity the severity of the vulnerability. + * @param cveName the CVE name of the vulnerability. + * @param description the description of the vulnerability. + * @param nistUrl the NIST url of the vulnerability. + * @param securityFile the {@link UrlSecurityJsonFile} of the tool and edition to which the vulnerability is added. + */ + private static void debugInfo(Vulnerability vulnerability, VersionRange versionRange, BigDecimal severity, + String cveName, String description, String nistUrl, UrlSecurityJsonFile securityFile) { + + context.debug("Writing vulnerability {} to security file at {}.", cveName, securityFile.getPath()); VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); - String matchedCpe = matchedVulnerableSoftware.toCpe23FS(); + context.debug("Matched CPE: {}.", matchedVulnerableSoftware.toCpe23FS()); // 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, 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.getVersion()); + context.debug("The severity of the vulnerability is {}.", severity); + context.debug("The description of the vulnerability is {}.", description); + context.debug("The NIST url of the vulnerability is {}.", nistUrl); + context.debug( + "The determined VersionRange is {}, given OWASPs versionStartExcluding {}, versionStartIncluding {}, " + + "versionEndIncluding {}, versionEndExcluding {}, single Version {} ", + versionRange, matchedVulnerableSoftware.getVersionStartExcluding(), + matchedVulnerableSoftware.getVersionStartIncluding(), matchedVulnerableSoftware.getVersionEndIncluding(), + matchedVulnerableSoftware.getVersionEndExcluding(), matchedVulnerableSoftware.getVersion()); + } - securityFile.addSecurityWarning(versionRange, matchedCpe, interval, severity, cveName, description, nistUrl); + /** + * Determines the severity of the vulnerability. + * + * @param vulnerability the vulnerability determined by OWASP dependency check. + * @param hasV3Severity {@code true} if the vulnerability has a V3 severity, {@code false} if it has a V2 severity. + * @return the {@link BigDecimal severity} of the vulnerability. + */ + private static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability, boolean hasV3Severity) { + if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { + // TODO if this ever happens, add a case that handles this + throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName() + "\\n" + + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); + } + double severityDouble; + if (hasV3Severity) { + severityDouble = vulnerability.getCvssV3().getCvssData().getBaseScore(); + } else { + severityDouble = vulnerability.getCvssV2().getCvssData().getBaseScore(); + } + String formatted = String.format(Locale.US, "%.1f", severityDouble); + BigDecimal severity = new BigDecimal(formatted); + return severity; } /** * From the vulnerability determine the {@link VersionRange versionRange} to which the vulnerability applies. * * @param vulnerability the vulnerability determined by OWASP dependency check. - * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get maps between CPE Version and - * {@link UrlVersion#getName() Url Version}. + * @param cpeToUrlVersion a {@link Map} from CPE Version to {@link UrlVersion#getName() Url Version}. + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get mapping functions between CPE Version and * + * {@link UrlVersion#getName() Url Version}. This is used as backup if the {@code cpeToUrlVersion} does not * + * contain the CPE Version. * @return the {@link VersionRange versionRange} to which the vulnerability applies. */ - static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, AbstractUrlUpdater urlUpdater, - Map cpeToUrlVersion) { + static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, Map cpeToUrlVersion, + AbstractUrlUpdater urlUpdater) { VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); @@ -247,11 +299,11 @@ static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability String vEndIncluding = matchedVulnerableSoftware.getVersionEndIncluding(); String singleVersion = matchedVulnerableSoftware.getVersion(); - vStartExcluding = getUrlVersion(vStartExcluding, urlUpdater, cpeToUrlVersion); - vStartIncluding = getUrlVersion(vStartIncluding, urlUpdater, cpeToUrlVersion); - vEndExcluding = getUrlVersion(vEndExcluding, urlUpdater, cpeToUrlVersion); - vEndIncluding = getUrlVersion(vEndIncluding, urlUpdater, cpeToUrlVersion); - singleVersion = getUrlVersion(singleVersion, urlUpdater, cpeToUrlVersion); + vStartExcluding = getUrlVersion(vStartExcluding, cpeToUrlVersion, urlUpdater); + vStartIncluding = getUrlVersion(vStartIncluding, cpeToUrlVersion, urlUpdater); + vEndExcluding = getUrlVersion(vEndExcluding, cpeToUrlVersion, urlUpdater); + vEndIncluding = getUrlVersion(vEndIncluding, cpeToUrlVersion, urlUpdater); + singleVersion = getUrlVersion(singleVersion, cpeToUrlVersion, urlUpdater); VersionRange affectedRange; try { @@ -265,15 +317,17 @@ static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability } /** - * TODO + * Maps the CPE Version to the {@link UrlVersion#getName() Url Version}. * - * @param cpeVersion - * @param urlUpdater - * @param cpeToUrlVersion - * @return + * @param cpeVersion the CPE Version to map. + * @param cpeToUrlVersion a {@link Map} from CPE Version to {@link UrlVersion#getName() Url Version}. + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get mapping functions between CPE Version and + * {@link UrlVersion#getName() Url Version}. This is used as backup if the {@code cpeToUrlVersion} does not + * contain the CPE Version. + * @return the {@link UrlVersion#getName() Url Version} of the CPE Version. */ - private static String getUrlVersion(String cpeVersion, AbstractUrlUpdater urlUpdater, - Map cpeToUrlVersion) { + private static String getUrlVersion(String cpeVersion, Map cpeToUrlVersion, + AbstractUrlUpdater urlUpdater) { String urlVersion = null; if (cpeVersion != null) { @@ -329,6 +383,11 @@ public static VersionRange getVersionRangeFromInterval(String startIncluding, St return new VersionRange(min, max, BoundaryType.of(leftExclusive, rightExclusive)); } + /** + * Prints the affected versions of each tool and edition. + * + * @param context the {@link IdeContext} to use to get the {@link UrlSecurityJsonFile}. + */ private static void printAffectedVersions(IdeContext context) { Path urlsPath = context.getUrlsPath(); @@ -368,11 +427,10 @@ private static void printAffectedVersions(IdeContext context) { } } - private static void initCvesToIgnore() { if (CVES_TO_IGNORE.isEmpty()) { - // ......................................vendor......product......why was is ignored + // .................CVE..................vendor......product......why was is ignored CVES_TO_IGNORE.add("CVE-2021-36230"); // hashicorp...terraform....https://github.com/anchore/grype/issues/1377 } } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 97e1c0a61..8acbcd848 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -37,25 +37,37 @@ public class UrlAnalyzer extends AbstractFileTypeAnalyzer { */ public UrlAnalyzer(UpdateManager updateManager) { - fileFilter = new UrlFileFilter(); + this.fileFilter = new UrlFileFilter(); this.updateManager = updateManager; } @Override protected void analyzeDependency(Dependency dependency, Engine engine) { - String filePath = dependency.getFilePath(); - Path parent = Paths.get(filePath).getParent(); - String tool = parent.getParent().getParent().getFileName().toString(); - String edition = parent.getParent().getFileName().toString(); + System.out.println("UrlAnalyzer: file path = " + dependency.getFilePath()); - AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); + Path versionFolder = Paths.get(dependency.getFilePath()).getParent(); + String tool = versionFolder.getParent().getParent().getFileName().toString(); + String edition = versionFolder.getParent().getFileName().toString(); + + System.out.println("UrlAnalyzer: tool = " + tool + ", edition = " + edition); + AbstractUrlUpdater urlUpdater = this.updateManager.retrieveUrlUpdater(tool, edition); + if (urlUpdater == null) { + System.out.println("no urlupdater for " + tool + " " + edition); + } + try { + String cpeVendor = urlUpdater.getCpeVendor(); + } catch (Exception e) { + System.out.println("UrlAnalyzer: getCpeVendor failed for " + tool + " " + edition); + System.exit(1); + } // adding vendor evidence String cpeVendor = urlUpdater.getCpeVendor(); + System.out.println("UrlAnalyzer: cpeVendor = " + cpeVendor); String cpeProduct = urlUpdater.getCpeProduct(); String cpeEdition = urlUpdater.getCpeEdition(edition); - String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(parent.getFileName().toString()); + String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(versionFolder.getFileName().toString()); if (cpeVendor == null || cpeProduct == null) { return; @@ -92,7 +104,7 @@ protected String getAnalyzerEnabledSettingKey() { @Override protected FileFilter getFileFilter() { - return fileFilter; + return this.fileFilter; } @Override diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index 0b09c0888..c82d0cd53 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -1,8 +1,12 @@ package com.devonfw.tools.security; import java.io.FileFilter; +import java.nio.file.Path; +import java.nio.file.Paths; import com.devonfw.tools.ide.url.model.file.UrlStatusFile; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; /** * This {@link FileFilter} only accepts files with name {@link UrlStatusFile#STATUS_JSON}. The paths of the accepted @@ -10,9 +14,21 @@ */ public class UrlFileFilter implements FileFilter { + UpdateManager updateManager = new UpdateManager(Paths.get("C:\\projects\\_ide\\urls"), null); + + @Override public boolean accept(java.io.File pathname) { + boolean found = pathname.getName().equals(UrlStatusFile.STATUS_JSON); + if (found) { + Path versionFolder = Paths.get(pathname.getPath()).getParent(); + String tool = versionFolder.getParent().getParent().getFileName().toString(); + String edition = versionFolder.getParent().getFileName().toString(); + AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); + System.out.println("UrlAnalyzer: tool = " + tool + ", edition = " + edition + ", urlUpdater = " + urlUpdater); + } + return pathname.getName().equals(UrlStatusFile.STATUS_JSON); } } From 0c540941ccdad05d807ece5352c581ab249aab5b Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Tue, 23 Jan 2024 23:12:04 +0100 Subject: [PATCH 28/47] #103: fixed intellij updater test --- .../tools/ide/tool/intellij/IntellijUrlUpdaterMock.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java index a84adc82b..f7e28cc69 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java @@ -15,12 +15,12 @@ protected String getVersionBaseUrl() { @Override protected String getEdition() { - return null; + return getTool(); } @Override IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { - return null; + return releases[1]; } } From 55f139c278ababadcb0edf208efd5b4586609eb1 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Wed, 24 Jan 2024 16:37:21 +0100 Subject: [PATCH 29/47] #103: done with change requests --- .../tools/ide/tool/ToolCommandlet.java | 4 +- .../url/model/file/UrlSecurityJsonFile.java | 6 +- .../ide/url/updater/AbstractUrlUpdater.java | 15 ++-- .../tools/ide/tool/ToolCommandletTest.java | 25 +++--- .../security/BuildSecurityJsonFiles.java | 80 ++++++++++--------- .../devonfw/tools/security/UrlAnalyzer.java | 29 ++----- .../devonfw/tools/security/UrlFileFilter.java | 18 +---- 7 files changed, 80 insertions(+), 97 deletions(-) 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 77be1032b..dcb355178 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 @@ -237,8 +237,8 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi // enum id, option message, version that is returned if this option is selected Map> options = new HashMap<>(); options.put(STAY, Pair.of("Stay with the current unsafe version (" + current + ").", configuredVersion)); - options.put(LATEST_SAFE, Pair.of("Install the latest safe version (" + latestSafe + ").", latestSafe)); - options.put(SAFE_LATEST, Pair.of("Install the (safe) latest version (" + latest + ").", VersionIdentifier.LATEST)); + options.put(LATEST_SAFE, Pair.of("Install the latest of all safe versions (" + latestSafe + ").", latestSafe)); + options.put(SAFE_LATEST, Pair.of("Install the latest version (" + latest + "). This version is save.", VersionIdentifier.LATEST)); options.put(NEXT_SAFE, Pair.of("Install the next safe version (" + nextSafe + ").", nextSafe)); if (current.equals(latest)) { 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 6118a0ef7..e32fc9bc2 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 @@ -5,15 +5,11 @@ import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.json.mapping.JsonMapping; import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; @@ -91,7 +87,7 @@ public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAffectAllVersions, IdeContext context, UrlEdition edition) { - List sortedVersions = null; + List sortedVersions = List.of(); if (ignoreWarningsThatAffectAllVersions) { sortedVersions = Objects.requireNonNull(context).getUrls().getSortedVersions(edition.getName(), edition.getName()); diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index b5a64c17a..21abd9428 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -16,9 +16,6 @@ import java.util.Objects; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.devonfw.tools.ide.os.OperatingSystem; import com.devonfw.tools.ide.os.SystemArchitecture; import com.devonfw.tools.ide.url.model.file.UrlChecksum; @@ -34,6 +31,8 @@ import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.util.DateTimeUtil; import com.devonfw.tools.ide.util.HexUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Abstract base implementation of {@link UrlUpdater}. Contains methods for retrieving response bodies from URLs, @@ -99,7 +98,7 @@ protected final String getToolWithEdition() { */ public String getCpeVendor() { - return null; + return ""; } /** @@ -107,7 +106,7 @@ public String getCpeVendor() { */ public String getCpeProduct() { - return null; + return ""; } /** @@ -116,12 +115,12 @@ public String getCpeProduct() { */ public String getCpeEdition(String urlEdition) { - return null; + return ""; } /** - * @return maps the version as specified by the directory name in the url repository to the version as specified in - * the CPE (Common Platform Enumeration). + * @param version the {@link UrlVersion#getName() version} to get the CPE (Common Platform Enumeration) version for. + * @return the version as specified in the CPE (Common Platform Enumeration). */ public String mapUrlVersionToCpeVersion(String version) { diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 9e780620b..9f8a5dd1d 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -3,16 +3,15 @@ import java.nio.file.Path; import java.util.List; -import com.devonfw.tools.ide.log.IdeLogLevel; -import org.junit.jupiter.api.Test; - import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.tool.az.Azure; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; +import org.junit.jupiter.api.Test; /** Test of {@link ToolCommandlet} */ public class ToolCommandletTest extends AbstractIdeContextTest { @@ -50,7 +49,8 @@ public void testSecurityRiskInteractionAllVersionAffectedBySingleWarning() { assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (2)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (6)."); - assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest version (9). This version is save."); + assertThat(interactions.size()).isEqualTo(4); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("2"))).isEqualTo(VersionIdentifier.of("6")); @@ -110,7 +110,8 @@ public void testSecurityRiskInteractionCurrentIsLatest() { assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("There are no updates available."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (9)."); - assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (6)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest of all safe versions (6)."); + assertThat(interactions.size()).isEqualTo(3); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("*"))).isEqualTo(VersionIdentifier.of("6")); @@ -141,7 +142,8 @@ public void testSecurityRiskInteractionNextSafeIsNull() { assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("All newer versions are also not safe."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (6)."); - assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest safe version (5)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest of all safe versions (5)."); + assertThat(interactions.size()).isEqualTo(3); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("6"))).isEqualTo(VersionIdentifier.of("5")); @@ -172,7 +174,8 @@ public void testSecurityRiskInteractionNextSafeIsLatest() { assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(0)).contains("Of the newer versions, only the latest is safe."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (7)."); - assertThat(interactions.get(2)).isEqualTo("Option 2: Install the (safe) latest version (9)."); + assertThat(interactions.get(2)).isEqualTo("Option 2: Install the latest version (9). This version is save."); + assertThat(interactions.size()).isEqualTo(3); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("7"))).isEqualTo(VersionIdentifier.of("*")); @@ -205,6 +208,8 @@ public void testSecurityRiskInteractionNextSafeIsLatestSafe() { .contains("Of the newer versions, only version 7 is safe, which is however not the latest."); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (7)."); + assertThat(interactions.size()).isEqualTo(3); + // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); } @@ -234,7 +239,8 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeButIsLatest( assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (5)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (7)."); - assertThat(interactions.get(3)).isEqualTo("Option 3: Install the (safe) latest version (9)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest version (9). This version is save."); + assertThat(interactions.size()).isEqualTo(4); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("5"))).isEqualTo(VersionIdentifier.of("7")); @@ -267,7 +273,8 @@ public void testSecurityRiskInteractionLatestSafeDiffersFromNextSafeAndLatest() assertThat(interactions.get(0)).contains("which has one or more vulnerabilities"); assertThat(interactions.get(1)).isEqualTo("Option 1: Stay with the current unsafe version (3)."); assertThat(interactions.get(2)).isEqualTo("Option 2: Install the next safe version (4)."); - assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest safe version (7)."); + assertThat(interactions.get(3)).isEqualTo("Option 3: Install the latest of all safe versions (7)."); + assertThat(interactions.size()).isEqualTo(4); // answer to the interaction is option 2 assertThat(tool.securityRiskInteraction(VersionIdentifier.of("3"))).isEqualTo(VersionIdentifier.of("4")); 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 ded9e1313..2ce008015 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -12,8 +12,19 @@ import java.util.Set; import java.util.stream.Collectors; -import com.devonfw.tools.ide.version.BoundaryType; +import com.devonfw.tools.ide.context.AbstractIdeContext; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; +import com.devonfw.tools.ide.url.model.folder.UrlVersion; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.util.MapUtil; +import com.devonfw.tools.ide.version.BoundaryType; +import com.devonfw.tools.ide.version.VersionIdentifier; +import com.devonfw.tools.ide.version.VersionRange; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -44,18 +55,6 @@ import org.owasp.dependencycheck.utils.Pair; import org.owasp.dependencycheck.utils.Settings; -import com.devonfw.tools.ide.context.AbstractIdeContext; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.context.IdeContextConsole; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; -import com.devonfw.tools.ide.url.model.folder.UrlVersion; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.UpdateManager; -import com.devonfw.tools.ide.util.MapUtil; -import com.devonfw.tools.ide.version.VersionIdentifier; -import com.devonfw.tools.ide.version.VersionRange; - /** * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for @@ -79,6 +78,8 @@ public class BuildSecurityJsonFiles { NodeAuditAnalyzer.class, YarnAuditAnalyzer.class, PnpmAuditAnalyzer.class, RetireJsAnalyzer.class, FalsePositiveAnalyzer.class); + // private static final Set> ANALYZERS_TO_IGNORE = Set.of( FileNameAnalyzer.class); + private static BigDecimal minV2Severity; private static BigDecimal minV3Severity; @@ -97,10 +98,11 @@ public static void main(String[] args) { // throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); // } try { - // minV2Severity = new BigDecimal(String.format(args[0])); + minV2Severity = new BigDecimal(String.format(args[0])); + minV3Severity = minV2Severity; // minV3Severity = new BigDecimal(String.format(args[1])); - minV2Severity = new BigDecimal(String.format("0")); - minV3Severity = new BigDecimal(String.format("0")); + // minV2Severity = new BigDecimal(String.format("0")); + // minV3Severity = new BigDecimal(String.format("0")); } catch (NumberFormatException e) { throw new RuntimeException("These two args could not be parsed as BigDecimal"); } @@ -114,13 +116,8 @@ private static void run() { UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); Set> foundToolsAndEditions = new HashSet<>(); - - // TODO clean this up - - for (Dependency dependency : dependencies) { String filePath = dependency.getFilePath(); - System.out.println("filePath in BuildSecurityJsonFiles = " + filePath); Path parent = Paths.get(filePath).getParent(); String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); @@ -131,12 +128,7 @@ private static void run() { securityFile.clearSecurityWarnings(); } - List sortedVersions = context.getUrls().getSortedVersions(tool, edition).stream() - .map(VersionIdentifier::toString).toList(); - List sortedCpeVersions = sortedVersions.stream().map(urlUpdater::mapUrlVersionToCpeVersion) - .collect(Collectors.toList()); - Map cpeToUrlVersion = MapUtil.createMapfromLists(sortedCpeVersions, sortedVersions); - + Map cpeToUrlVersion = buildCpeToUrlVersionMap(tool, edition, urlUpdater); Set vulnerabilities = dependency.getVulnerabilities(true); for (Vulnerability vulnerability : vulnerabilities) { addVulnerabilityToSecurityFile(vulnerability, securityFile, cpeToUrlVersion, urlUpdater); @@ -148,10 +140,31 @@ private static void run() { printAffectedVersions(context); } + /** + * Creates a {@link Map} from CPE Version to {@link UrlVersion#getName() Url Version} containing all versions provided + * by IDEasy for the given tool and edition. + * + * @param tool the tool to get the {@link Map map} for. + * @param edition the edition to get the {@link Map map} for. + * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get + * {@link AbstractUrlUpdater#mapUrlVersionToCpeVersion(String) mapping functions} between CPE Version and + * {@link UrlVersion#getName() Url Version}. + * @return the {@link Map} from CPE Version to {@link UrlVersion#getName() Url Version}. + */ + private static Map buildCpeToUrlVersionMap(String tool, String edition, + AbstractUrlUpdater urlUpdater) { + + List sortedVersions = context.getUrls().getSortedVersions(tool, edition).stream() + .map(VersionIdentifier::toString).toList(); + List sortedCpeVersions = sortedVersions.stream().map(urlUpdater::mapUrlVersionToCpeVersion) + .collect(Collectors.toList()); + Map cpeToUrlVersion = MapUtil.createMapfromLists(sortedCpeVersions, sortedVersions); + return cpeToUrlVersion; + } + private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { Settings settings = new Settings(); - // Using "try with resource" or engine.close() at the end resulted in SEVERE warning by OWASP. Engine engine = new Engine(settings); FileTypeAnalyzer urlAnalyzer = new UrlAnalyzer(updateManager); @@ -162,20 +175,15 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd engine.getMode().getPhases().forEach( phase -> engine.getAnalyzers(phase).removeIf(analyzer -> ANALYZERS_TO_IGNORE.contains(analyzer.getClass()))); - System.out.println("engine.scan"); - System.out.println("updateManager.getUrlRepository()"+ updateManager.getUrlRepository()); engine.scan(updateManager.getUrlRepository().getPath().toString()); - System.out.println("engine.scan done"); - try { - System.out.println("engine.analyzeDependencies()"); engine.analyzeDependencies(); - System.out.println("engine.analyzeDependencies() done"); } catch (ExceptionCollection e) { throw new RuntimeException(e); } - System.out.println("before return engine.getDependencies"); - return engine.getDependencies(); + Dependency[] dependencies = engine.getDependencies(); + engine.close(); + return dependencies; } /** diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 8acbcd848..a56d6fc67 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -1,9 +1,7 @@ package com.devonfw.tools.security; -import java.io.FileFilter; -import java.nio.file.Path; -import java.nio.file.Paths; - +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -13,8 +11,9 @@ import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.UpdateManager; +import java.io.FileFilter; +import java.nio.file.Path; +import java.nio.file.Paths; /** * Analyzes file paths to detect tool, edition and version of software listed in a directory structure like this: @@ -44,32 +43,18 @@ public UrlAnalyzer(UpdateManager updateManager) { @Override protected void analyzeDependency(Dependency dependency, Engine engine) { - System.out.println("UrlAnalyzer: file path = " + dependency.getFilePath()); - Path versionFolder = Paths.get(dependency.getFilePath()).getParent(); String tool = versionFolder.getParent().getParent().getFileName().toString(); String edition = versionFolder.getParent().getFileName().toString(); - System.out.println("UrlAnalyzer: tool = " + tool + ", edition = " + edition); AbstractUrlUpdater urlUpdater = this.updateManager.retrieveUrlUpdater(tool, edition); - if (urlUpdater == null) { - System.out.println("no urlupdater for " + tool + " " + edition); - } - try { - String cpeVendor = urlUpdater.getCpeVendor(); - } catch (Exception e) { - System.out.println("UrlAnalyzer: getCpeVendor failed for " + tool + " " + edition); - System.exit(1); - } - // adding vendor evidence String cpeVendor = urlUpdater.getCpeVendor(); - System.out.println("UrlAnalyzer: cpeVendor = " + cpeVendor); String cpeProduct = urlUpdater.getCpeProduct(); String cpeEdition = urlUpdater.getCpeEdition(edition); String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(versionFolder.getFileName().toString()); - if (cpeVendor == null || cpeProduct == null) { + if (cpeVendor.isBlank() || cpeProduct.isBlank()) { return; } Evidence evidence; @@ -79,7 +64,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) { evidence = new Evidence(ANALYZER_NAME, "CpeProduct", cpeProduct, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); - if (cpeEdition != null) { + if (cpeEdition.isBlank()) { evidence = new Evidence(ANALYZER_NAME, "CpeEdition", cpeEdition, Confidence.HIGH); dependency.addEvidence(EvidenceType.PRODUCT, evidence); } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index c82d0cd53..025f15919 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -1,13 +1,11 @@ package com.devonfw.tools.security; -import java.io.FileFilter; -import java.nio.file.Path; -import java.nio.file.Paths; - import com.devonfw.tools.ide.url.model.file.UrlStatusFile; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; import com.devonfw.tools.ide.url.updater.UpdateManager; +import java.io.FileFilter; +import java.nio.file.Paths; + /** * This {@link FileFilter} only accepts files with name {@link UrlStatusFile#STATUS_JSON}. The paths of the accepted * files are then analyzed by the {@link UrlAnalyzer} to detect the available tools, editions and versions. @@ -16,19 +14,9 @@ public class UrlFileFilter implements FileFilter { UpdateManager updateManager = new UpdateManager(Paths.get("C:\\projects\\_ide\\urls"), null); - @Override public boolean accept(java.io.File pathname) { - boolean found = pathname.getName().equals(UrlStatusFile.STATUS_JSON); - if (found) { - Path versionFolder = Paths.get(pathname.getPath()).getParent(); - String tool = versionFolder.getParent().getParent().getFileName().toString(); - String edition = versionFolder.getParent().getFileName().toString(); - AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); - System.out.println("UrlAnalyzer: tool = " + tool + ", edition = " + edition + ", urlUpdater = " + urlUpdater); - } - return pathname.getName().equals(UrlStatusFile.STATUS_JSON); } } From f34fc222eacd5423cf51ed223ef5b275a9bd2c02 Mon Sep 17 00:00:00 2001 From: Mattes Mrzik Date: Thu, 25 Jan 2024 11:56:45 +0100 Subject: [PATCH 30/47] Update security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> --- .../java/com/devonfw/tools/security/BuildSecurityJsonFiles.java | 1 - 1 file changed, 1 deletion(-) 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 2ce008015..671af3275 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -78,7 +78,6 @@ public class BuildSecurityJsonFiles { NodeAuditAnalyzer.class, YarnAuditAnalyzer.class, PnpmAuditAnalyzer.class, RetireJsAnalyzer.class, FalsePositiveAnalyzer.class); - // private static final Set> ANALYZERS_TO_IGNORE = Set.of( FileNameAnalyzer.class); private static BigDecimal minV2Severity; From 1c66c7782e3c7bf0720b274ccc231e880a326916 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 25 Jan 2024 12:37:57 +0100 Subject: [PATCH 31/47] #103: small fix --- .../security/BuildSecurityJsonFiles.java | 49 +++++++++++-------- .../devonfw/tools/security/UrlFileFilter.java | 2 - 2 files changed, 28 insertions(+), 23 deletions(-) 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 2ce008015..e51853c32 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -58,8 +58,10 @@ /** * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for - * vulnerabilities by using the OWASP package. For this the - * {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the + * vulnerabilities by using the OWASP package. You must pass two arguments to the main method: minV2Severity and + * minV3Severity. V2 and V3 severity are differentiated, because they are often considered not comparable. + * Vulnerabilities with severity lower than these values will not be added to the {@link UrlSecurityJsonFile}. For this + * the {@link com.devonfw.tools.ide.url.model.file.UrlStatusFile#STATUS_JSON} must be present in the * {@link com.devonfw.tools.ide.url.model.folder.UrlVersion}. If a vulnerability is found, it is added to the * {@link UrlSecurityJsonFile} of the corresponding tool and edition. The previous content of the file is overwritten. * Sometimes when running this class is takes a long time to finish. This is because of the OWASP package, which is @@ -78,8 +80,6 @@ public class BuildSecurityJsonFiles { NodeAuditAnalyzer.class, YarnAuditAnalyzer.class, PnpmAuditAnalyzer.class, RetireJsAnalyzer.class, FalsePositiveAnalyzer.class); - // private static final Set> ANALYZERS_TO_IGNORE = Set.of( FileNameAnalyzer.class); - private static BigDecimal minV2Severity; private static BigDecimal minV3Severity; @@ -90,19 +90,16 @@ public class BuildSecurityJsonFiles { /** * @param args Set {@code minV2Severity} with {@code args[0]} and {@code minV3Severity} with {@code args[1]}. - * Vulnerabilities with severity lower than these values will not be added to {@link UrlSecurityJsonFile}. + * Vulnerabilities with severity lower than these values will not be added to the {@link UrlSecurityJsonFile}s. */ public static void main(String[] args) { - // if (args.length != 2) { - // throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); - // } + if (args.length != 2) { + throw new RuntimeException("Please provide 2 numbers: minV2Severity and minV3Severity"); + } try { minV2Severity = new BigDecimal(String.format(args[0])); - minV3Severity = minV2Severity; - // minV3Severity = new BigDecimal(String.format(args[1])); - // minV2Severity = new BigDecimal(String.format("0")); - // minV3Severity = new BigDecimal(String.format("0")); + minV3Severity = new BigDecimal(String.format(args[1])); } catch (NumberFormatException e) { throw new RuntimeException("These two args could not be parsed as BigDecimal"); } @@ -162,6 +159,16 @@ private static Map buildCpeToUrlVersionMap(String tool, String e return cpeToUrlVersion; } + /** + * Uses the {@link Engine OWASP engine} to scan the {@link AbstractIdeContext#getUrlsPath() ide-url} folder for + * dependencies and then runs {@link Engine#analyzeDependencies() analyzes} them to get the {@link Vulnerability + * vulnerabilities}. + * + * @param updateManager the {@link UpdateManager} to use to get the {@link AbstractUrlUpdater} of the tool to get CPE + * Vendor, CPE Product and CPE edition of the tool, as well as the + * {@link AbstractUrlUpdater#mapCpeVersionToUrlVersion(String) CPE naming of its version} + * @return the {@link Dependency dependencies} with associated {@link Vulnerability vulnerabilities}. + */ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { Settings settings = new Settings(); @@ -196,7 +203,7 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd * @param urlUpdater the {@link AbstractUrlUpdater} of the tool to get maps between CPE Version and * {@link UrlVersion#getName() Url Version} naming. */ - private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, + protected static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, UrlSecurityJsonFile securityFile, Map cpeToUrlVersion, AbstractUrlUpdater urlUpdater) { String cveName = vulnerability.getName(); @@ -205,9 +212,8 @@ private static void addVulnerabilityToSecurityFile(Vulnerability vulnerability, return; } - boolean hasV3Severity = vulnerability.getCvssV3() != null; - BigDecimal severity = getBigDecimalSeverity(vulnerability, hasV3Severity); - if (hasV3Severity) { + BigDecimal severity = getBigDecimalSeverity(vulnerability); + if (vulnerability.getCvssV3() != null) { if (severity.compareTo(minV3Severity) < 0) { return; } @@ -265,10 +271,10 @@ private static void debugInfo(Vulnerability vulnerability, VersionRange versionR * Determines the severity of the vulnerability. * * @param vulnerability the vulnerability determined by OWASP dependency check. - * @param hasV3Severity {@code true} if the vulnerability has a V3 severity, {@code false} if it has a V2 severity. * @return the {@link BigDecimal severity} of the vulnerability. */ - private static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability, boolean hasV3Severity) { + protected static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability) { + if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { // TODO if this ever happens, add a case that handles this @@ -276,7 +282,7 @@ private static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability, boo + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); } double severityDouble; - if (hasV3Severity) { + if (vulnerability.getCvssV3() != null) { severityDouble = vulnerability.getCvssV3().getCvssData().getBaseScore(); } else { severityDouble = vulnerability.getCvssV2().getCvssData().getBaseScore(); @@ -296,8 +302,8 @@ private static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability, boo * contain the CPE Version. * @return the {@link VersionRange versionRange} to which the vulnerability applies. */ - static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, Map cpeToUrlVersion, - AbstractUrlUpdater urlUpdater) { + protected static VersionRange getVersionRangeFromVulnerability(Vulnerability vulnerability, + Map cpeToUrlVersion, AbstractUrlUpdater urlUpdater) { VulnerableSoftware matchedVulnerableSoftware = vulnerability.getMatchedVulnerableSoftware(); @@ -435,6 +441,7 @@ private static void printAffectedVersions(IdeContext context) { } } + /** Initializes the {@link #CVES_TO_IGNORE}. */ private static void initCvesToIgnore() { if (CVES_TO_IGNORE.isEmpty()) { diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index 025f15919..121e8ab8e 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -12,8 +12,6 @@ */ public class UrlFileFilter implements FileFilter { - UpdateManager updateManager = new UpdateManager(Paths.get("C:\\projects\\_ide\\urls"), null); - @Override public boolean accept(java.io.File pathname) { From 6a20d3c2c2a5504f1d35707993019d20a9eb22c9 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 25 Jan 2024 14:59:24 +0100 Subject: [PATCH 32/47] #103: added tests for BuildSecurityJsonFiles.addVulnerabilityToSecurityFile --- .../java/com/devonfw/tools/ide/util/Pair.java | 26 +++ .../security/BuildSecurityJsonFiles.java | 10 +- .../security/BuildSecurityJsonFilesTest.java | 177 +++++++++++++++++- 3 files changed, 201 insertions(+), 12 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java b/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java index d0006de8e..a32584db4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java +++ b/cli/src/main/java/com/devonfw/tools/ide/util/Pair.java @@ -1,23 +1,49 @@ package com.devonfw.tools.ide.util; +/** + * Simple generic pair of two values. + * + * @param type of {@link #getFirst() first value}. + * @param type of {@link #getSecond() second value}. + */ public class Pair { private final K first; + private final V second; + /** + * The constructor. + * + * @param first the {@link #getFirst() first value}. + * @param second the {@link #getSecond() second value}. + */ public Pair(K first, V second) { + this.first = first; this.second = second; } + /** @return the first value of the pair. */ public K getFirst() { + return first; } + /** @return the second value of the pair. */ public V getSecond() { + return second; } + /** + * Returns a new instance of {@link Pair}. + * + * @param first the {@link #getFirst() first value}. + * @param second the {@link #getSecond() second value}. + * @return the new {@link Pair}. + */ public static Pair of(K first, V second) { + return new Pair<>(first, second); } } 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 40d6bd77f..0653242d9 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -80,13 +80,12 @@ public class BuildSecurityJsonFiles { NodeAuditAnalyzer.class, YarnAuditAnalyzer.class, PnpmAuditAnalyzer.class, RetireJsAnalyzer.class, FalsePositiveAnalyzer.class); - private static BigDecimal minV2Severity; + private static BigDecimal minV2Severity = new BigDecimal("0.0"); - private static BigDecimal minV3Severity; + private static BigDecimal minV3Severity = new BigDecimal("0.0"); private static final Set actuallyIgnoredCves = new HashSet<>(); - - private static IdeContext context; + private static final IdeContext context = new IdeContextConsole(IdeLogLevel.INFO, null, false);; /** * @param args Set {@code minV2Severity} with {@code args[0]} and {@code minV3Severity} with {@code args[1]}. @@ -109,7 +108,6 @@ public static void main(String[] args) { private static void run() { initCvesToIgnore(); - context = new IdeContextConsole(IdeLogLevel.INFO, null, false); UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); Set> foundToolsAndEditions = new HashSet<>(); @@ -344,7 +342,7 @@ private static String getUrlVersion(String cpeVersion, Map cpeTo String urlVersion = null; if (cpeVersion != null) { - if (cpeToUrlVersion.containsKey(cpeVersion)) { + if (cpeToUrlVersion!= null && cpeToUrlVersion.containsKey(cpeVersion)) { urlVersion = cpeToUrlVersion.get(cpeVersion); } else { urlVersion = urlUpdater.mapCpeVersionToUrlVersion(cpeVersion); diff --git a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java index 775de4801..ad76d0ed3 100644 --- a/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java +++ b/security/src/test/java/com/devonfw/tools/security/BuildSecurityJsonFilesTest.java @@ -1,19 +1,38 @@ package com.devonfw.tools.security; +import java.math.BigDecimal; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; + +import com.devonfw.tools.ide.tool.mvn.MvnUrlUpdater; +import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; +import com.devonfw.tools.ide.url.model.folder.UrlEdition; +import com.devonfw.tools.ide.url.model.folder.UrlRepository; +import com.devonfw.tools.ide.url.model.folder.UrlTool; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.version.BoundaryType; +import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.dependency.VulnerableSoftware; +import us.springett.parsers.cpe.exceptions.CpeValidationException; +import us.springett.parsers.cpe.values.Part; +import static com.devonfw.tools.security.BuildSecurityJsonFiles.addVulnerabilityToSecurityFile; import static com.devonfw.tools.security.BuildSecurityJsonFiles.getVersionRangeFromInterval; -/** - * Test of {@link BuildSecurityJsonFiles}. - */ +/** Test of {@link BuildSecurityJsonFiles}. */ public class BuildSecurityJsonFilesTest extends Assertions { - /** - * Test of {@link BuildSecurityJsonFiles#getVersionRangeFromInterval(String, String, String, String, String)}. - */ + /** Test of {@link BuildSecurityJsonFiles#getVersionRangeFromInterval(String, String, String, String, String)}. */ @Test public void testGetVersionRangeFromInterval() { @@ -25,4 +44,150 @@ public void testGetVersionRangeFromInterval() { assertThat(getVersionRangeFromInterval(null, null, null, null, "1")).isEqualTo(VersionRange.of("[1,1]")); assertThat(getVersionRangeFromInterval(null, "1", null, "2", "1")).isEqualTo(VersionRange.of("(1,2]")); } + + /** + * Test of {@link BuildSecurityJsonFiles#getBigDecimalSeverity(Vulnerability)}. + * + * @throws CpeValidationException if the CPE is invalid. Should never happen since it is hard coded. + */ + @Test + public void testGetBigDecimalSeverity() throws CpeValidationException { + + // arrange + Vulnerability vulnerabilityV2 = getTestVulnerability(2.2, false, null, null, null, null, "1.0.0"); + Vulnerability vulnerabilityV3 = getTestVulnerability(3.0, true, null, null, null, null, "1.0.0"); + + // act & assert + assertThat(BuildSecurityJsonFiles.getBigDecimalSeverity(vulnerabilityV2)).isEqualTo(BigDecimal.valueOf(2.2)); + assertThat(BuildSecurityJsonFiles.getBigDecimalSeverity(vulnerabilityV3)).isEqualTo(BigDecimal.valueOf(3.0)); + } + + /** + * Test of + * {@link BuildSecurityJsonFiles#addVulnerabilityToSecurityFile(Vulnerability, UrlSecurityJsonFile, Map, AbstractUrlUpdater)} + * if the vulnerability affects a single version. Also tests the case where the bool {@code isV3Severity} is set to + * {@code true}. + * + * @throws CpeValidationException if the CPE is invalid. Should never happen since it is hard coded. + */ + @Test + public void testAddVulnerabilityToSecurityFileSingleVersion() throws CpeValidationException { + + // arrange + String affectedVersion = "1.0.0"; + Vulnerability vulnerability = getTestVulnerability(3.0, false, null, null, null, null, affectedVersion); + AbstractUrlUpdater updater = new MvnUrlUpdater(); + UrlSecurityJsonFile securityJsonFile = getTestUrlSecurityJsonFile(); + + // act + addVulnerabilityToSecurityFile(vulnerability, securityJsonFile, null, updater); + Set warnings = securityJsonFile + .getMatchingSecurityWarnings(VersionIdentifier.of(affectedVersion)); + + // assert + assertThat(warnings).hasSize(1); + UrlSecurityWarning warning = warnings.iterator().next(); + assertThat(warning.getVersionRange()).isEqualTo(new VersionRange(VersionIdentifier.of(affectedVersion), + VersionIdentifier.of(affectedVersion), BoundaryType.CLOSED)); + assertThat(warning.getCveName()).isEqualTo("testCveName"); + assertThat(warning.getSeverity()).isEqualTo(BigDecimal.valueOf(3.0)); + assertThat(warning.getDescription()).isEqualTo("testDescription"); + assertThat(warning.getNistUrl()).isEqualTo("https://nvd.nist.gov/vuln/detail/testCveName"); + } + + /** + * Test of + * {@link BuildSecurityJsonFiles#addVulnerabilityToSecurityFile(Vulnerability, UrlSecurityJsonFile, Map, AbstractUrlUpdater)} + * when the vulnerability affects a range of versions. Also tests the case where the bool {@code isV3Severity} is set + * to {@code true}. + * + * @throws CpeValidationException if the CPE is invalid. Should never happen since it is hard coded. + */ + @Test + public void testAddVulnerabilityToSecurityFileInterval() throws CpeValidationException { + + // arrange + Vulnerability vulnerability = getTestVulnerability(3.1, true, null, "3", "1", null, null); + AbstractUrlUpdater updater = new MvnUrlUpdater(); + UrlSecurityJsonFile securityJsonFile = getTestUrlSecurityJsonFile(); + + // act + addVulnerabilityToSecurityFile(vulnerability, securityJsonFile, null, updater); + Set warnings = securityJsonFile.getMatchingSecurityWarnings(VersionIdentifier.of("2")); + + // assert + assertThat(warnings).hasSize(1); + UrlSecurityWarning warning = warnings.iterator().next(); + assertThat(warning.getVersionRange()) + .isEqualTo(new VersionRange(VersionIdentifier.of("1"), VersionIdentifier.of("3"), BoundaryType.LEFT_OPEN)); + assertThat(warning.getCveName()).isEqualTo("testCveName"); + assertThat(warning.getSeverity()).isEqualTo(BigDecimal.valueOf(3.1)); + assertThat(warning.getDescription()).isEqualTo("testDescription"); + assertThat(warning.getNistUrl()).isEqualTo("https://nvd.nist.gov/vuln/detail/testCveName"); + } + + /** + * Creates a {@link Vulnerability} with the given parameters. To be used in the {@link BuildSecurityJsonFilesTest + * tests}. + * + * @param severity the severity of the vulnerability. + * @param isV3Severity whether the severity is a V3 severity or not. + * @param versionEndExcluding if the vulnerability should affect a range of versions, the version that marks the end + * of the range, excluding this version. + * @param versionEndIncluding if the vulnerability should affect a range of versions, the version that marks the end + * of the range, including this version. + * @param versionStartExcluding if the vulnerability should affect a range of versions, the version that marks the + * start of the range, excluding this version. + * @param versionStartIncluding if the vulnerability should affect a range of versions, the version that marks the + * start of the range, including this version. + * @param version if the vulnerability should affect a single version, the version that is affected. + * @return the created {@link Vulnerability}. + * @throws CpeValidationException if the CPE is invalid. Should never happen since it is hard coded. + */ + private Vulnerability getTestVulnerability(double severity, boolean isV3Severity, String versionEndExcluding, + String versionEndIncluding, String versionStartExcluding, String versionStartIncluding, String version) + throws CpeValidationException { + + Vulnerability vulnerability = new Vulnerability(); + if (!isV3Severity) { + CvssV2Data data2 = new CvssV2Data("2.0", null, null, null, null, null, null, null, severity, null, null, null, + null, null, null, null, null, null, null, null); + CvssV2 cvssV2 = new CvssV2(null, null, data2, null, null, null, null, null, null, null, null); + vulnerability.setCvssV2(cvssV2); + } else { + CvssV3Data data3 = new CvssV3Data(CvssV3Data.Version._3_1, null, null, null, null, null, null, null, null, null, + severity, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null); + CvssV3 cvssV3 = new CvssV3(null, null, data3, null, null); + vulnerability.setCvssV3(cvssV3); + } + vulnerability.setName("testCveName"); + vulnerability.setDescription("testDescription"); + boolean vulnerable = true; + if (version == null) { + version = "*"; + } + VulnerableSoftware matchedVulnerableSoftware = new VulnerableSoftware(Part.ANY, "vendor", "product", version, + "update", "edition", "language", "softwareEdition", "targetSoftware", "targetHardware", "other", + versionEndExcluding, versionEndIncluding, versionStartExcluding, versionStartIncluding, vulnerable); + vulnerability.setMatchedVulnerableSoftware(matchedVulnerableSoftware); + + return vulnerability; + } + + /** + * Creates a {@link UrlSecurityJsonFile} for testing purposes using the files in resources. + * + * @return the created {@link UrlSecurityJsonFile}. + */ + private static UrlSecurityJsonFile getTestUrlSecurityJsonFile() { + + UrlRepository ulrRepo = new UrlRepository(Paths.get("src/test/resources/_ide/urls")); + ulrRepo.load(true); + UrlTool urlTool = ulrRepo.getOrCreateChild("testTool"); + UrlEdition urlEdition = urlTool.getOrCreateChild("testEdition"); + UrlSecurityJsonFile securityJsonFile = new UrlSecurityJsonFile(urlEdition); + return securityJsonFile; + } + } \ No newline at end of file From cbe086d9227436a39b80acb4967e05dc9a81948c Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Thu, 25 Jan 2024 16:19:30 +0100 Subject: [PATCH 33/47] #103: test for UrlSecurityJson --- .../url/model/file/UrlSecurityJsonFile.java | 5 + .../tools/ide/tool/ToolCommandletTest.java | 6 +- .../url/model/UrlSecurityJsonFileTest.java | 147 ++++++++++++++++++ .../_ide/urls/mvn/mvn/security.json | 15 ++ 4 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java create mode 100644 cli/src/test/resources/ide-projects/_ide/urls/mvn/mvn/security.json 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 e32fc9bc2..c15dcae90 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 @@ -181,4 +181,9 @@ protected void doSave() { throw new IllegalStateException("Failed to save the UrlSecurityJsonFile " + getPath(), e); } } + + public UrlSecurityWarningsJson getUrlSecurityWarningsJson() { + + return this.urlSecurityWarningsJson; + } } \ No newline at end of file diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 9f8a5dd1d..c67e9fa74 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -317,11 +317,11 @@ private IdeContext getContextForSecurityJsonTests(Class warnings1 = securityFile.getMatchingSecurityWarnings(VersionIdentifier.of("3.2")); + Set warnings2 = securityFile.getMatchingSecurityWarnings(VersionIdentifier.of("1.2.3")); + Set warnings3 = securityFile.getMatchingSecurityWarnings(VersionIdentifier.of("4")); + + // assert + assertThat(warnings1).hasSize(2); + assertThat(warnings1).containsExactly(warning1, warning2); + assertThat(warnings2).hasSize(1); + assertThat(warnings2).containsExactly(warning2); + assertThat(warnings3).isEmpty(); + } +} diff --git a/cli/src/test/resources/ide-projects/_ide/urls/mvn/mvn/security.json b/cli/src/test/resources/ide-projects/_ide/urls/mvn/mvn/security.json new file mode 100644 index 000000000..57d9df8d5 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/mvn/mvn/security.json @@ -0,0 +1,15 @@ +{ + "warnings" : [ { + "versionRange" : "[3.0.6,3.2.1)", + "severity" : 5.8, + "cveName" : "testName1", + "description" : "testDescription1", + "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName1" + }, { + "versionRange" : "(,3.8.1)", + "severity" : 9.1, + "cveName" : "testName2", + "description" : "testDescription2", + "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName2" + } ] +} \ No newline at end of file From 20fecc3876e4ec89bfb9c725662ef0f21491abd8 Mon Sep 17 00:00:00 2001 From: "CORP\\mmrzik" Date: Fri, 26 Jan 2024 09:54:32 +0100 Subject: [PATCH 34/47] #103: last small changes - removed this.paths.add(path) in method SystemPath.addPath() - linked new issue to TODO - added some java doc --- .../main/java/com/devonfw/tools/ide/common/SystemPath.java | 1 - .../com/devonfw/tools/security/BuildSecurityJsonFiles.java | 2 +- .../main/java/com/devonfw/tools/security/UrlAnalyzer.java | 7 +++++++ .../java/com/devonfw/tools/security/UrlFileFilter.java | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index 36f5dd5e4..85bac342c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -166,7 +166,6 @@ public Path retrievePath(String tool) { */ public void addPath(String tool, Path path) { - this.paths.add(path); this.tool2pathMap.put(tool, path); } 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 0653242d9..868eae377 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -274,7 +274,7 @@ private static void debugInfo(Vulnerability vulnerability, VersionRange versionR protected static BigDecimal getBigDecimalSeverity(Vulnerability vulnerability) { if (vulnerability.getCvssV2() == null && vulnerability.getCvssV3() == null) { - // TODO if this ever happens, add a case that handles this + // TODO if this ever happens, add a case that handles this. See https://github.com/devonfw/IDEasy/issues/190 throw new RuntimeException("Vulnerability without severity found: " + vulnerability.getName() + "\\n" + " Please contact https://github.com/devonfw/IDEasy and make a request to get this feature implemented."); } diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index a56d6fc67..58aaf1dea 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -40,6 +40,13 @@ public UrlAnalyzer(UpdateManager updateManager) { this.updateManager = updateManager; } + /** + * Analyzes the given {@link Dependency} and adds {@link Evidence} to it. Namely, CPE vendor, product, edition and + * version. + * + * @param dependency the dependency to analyze. + * @param engine not actually used here, but needs to match the signature of the super class. + */ @Override protected void analyzeDependency(Dependency dependency, Engine engine) { diff --git a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java index 121e8ab8e..112a2cb07 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlFileFilter.java @@ -12,6 +12,11 @@ */ public class UrlFileFilter implements FileFilter { + /** + * This method only accepts files with name {@link UrlStatusFile#STATUS_JSON}. + * + * @param pathname the {@link java.io.File} to check. + */ @Override public boolean accept(java.io.File pathname) { From ae522925cf807e026134b587ff6f9d95be63f0b9 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Mon, 19 Feb 2024 14:10:42 +0100 Subject: [PATCH 35/47] #103: code reformat & cleanup --- .../tools/ide/tool/ToolCommandlet.java | 44 ++++++++++++------- .../tools/ide/url/updater/UpdateManager.java | 4 +- 2 files changed, 28 insertions(+), 20 deletions(-) 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 243ce86ca..3a5b06255 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 @@ -1,5 +1,10 @@ package com.devonfw.tools.ide.tool; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.LATEST_SAFE; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.NEXT_SAFE; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.SAFE_LATEST; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.STAY; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -11,7 +16,6 @@ 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.Tag; @@ -26,12 +30,11 @@ 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.json.UrlSecurityWarning; import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.util.Pair; import com.devonfw.tools.ide.version.VersionIdentifier; -import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.*; - /** * {@link Commandlet} for a tool integrated into the IDE. */ @@ -113,7 +116,8 @@ public void runTool(boolean runInBackground, VersionIdentifier toolVersion, Stri } else { throw new UnsupportedOperationException("Not yet implemented!"); } - ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath).addArgs(args); + ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath) + .addArgs(args); pc.run(false, runInBackground); } @@ -249,7 +253,8 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi Map> options = new HashMap<>(); options.put(STAY, Pair.of("Stay with the current unsafe version (" + current + ").", configuredVersion)); options.put(LATEST_SAFE, Pair.of("Install the latest of all safe versions (" + latestSafe + ").", latestSafe)); - options.put(SAFE_LATEST, Pair.of("Install the latest version (" + latest + "). This version is save.", VersionIdentifier.LATEST)); + options.put(SAFE_LATEST, + Pair.of("Install the latest version (" + latest + "). This version is save.", VersionIdentifier.LATEST)); options.put(NEXT_SAFE, Pair.of("Install the next safe version (" + nextSafe + ").", nextSafe)); if (current.equals(latest)) { @@ -355,11 +360,12 @@ private Path getProperInstallationSubDirOf(Path path) { try (Stream stream = Files.list(path)) { Path[] subFiles = stream.toArray(Path[]::new); if (subFiles.length == 0) { - throw new CliException("The downloaded package for the tool " + this.tool + " seems to be empty as you can check in the extracted folder " + path); + throw new CliException("The downloaded package for the tool " + this.tool + + " seems to be empty as you can check in the extracted folder " + path); } else if (subFiles.length == 1) { String filename = subFiles[0].getFileName().toString(); - if (!filename.equals(IdeContext.FOLDER_BIN) && !filename.equals(IdeContext.FOLDER_CONTENTS) && !filename.endsWith(".app") - && Files.isDirectory(subFiles[0])) { + if (!filename.equals(IdeContext.FOLDER_BIN) && !filename.equals(IdeContext.FOLDER_CONTENTS) + && !filename.endsWith(".app") && Files.isDirectory(subFiles[0])) { return getProperInstallationSubDirOf(subFiles[0]); } } @@ -424,15 +430,15 @@ protected void extract(Path file, Path targetDir) { this.context.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", getName(), file); if (Files.isDirectory(file)) { - fileAccess.move(file, targetDir); + fileAccess.move(file, targetDir); } else { try { Files.createDirectories(targetDir); } catch (IOException e) { throw new IllegalStateException("Failed to create folder " + targetDir); - } + } fileAccess.move(file, targetDir.resolve(file.getFileName())); - } + } } } @@ -529,8 +535,10 @@ public String getInstalledEdition(Path toolPath) { } return edition; } catch (IOException e) { - throw new IllegalStateException("Couldn't determine the edition of " + getName() + " from the directory structure of its software path " + toolPath - + ", assuming the name of the parent directory of the real path of the software path to be the edition " + "of the tool.", e); + throw new IllegalStateException("Couldn't determine the edition of " + getName() + + " from the directory structure of its software path " + toolPath + + ", assuming the name of the parent directory of the real path of the software path to be the edition " + + "of the tool.", e); } } @@ -595,8 +603,9 @@ public void setVersion(VersionIdentifier version, boolean hint) { this.context.info("{}={} has been set in {}", name, version, settingsVariables.getSource()); EnvironmentVariables declaringVariables = variables.findVariable(name); if ((declaringVariables != null) && (declaringVariables != settingsVariables)) { - this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name, - declaringVariables.getSource()); + this.context.warning( + "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", + name, declaringVariables.getSource()); } if (hint) { this.context.info("To install that version call the following command:"); @@ -639,8 +648,9 @@ public void setEdition(String edition, boolean hint) { this.context.info("{}={} has been set in {}", name, edition, settingsVariables.getSource()); EnvironmentVariables declaringVariables = variables.findVariable(name); if ((declaringVariables != null) && (declaringVariables != settingsVariables)) { - this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name, - declaringVariables.getSource()); + this.context.warning( + "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", + name, declaringVariables.getSource()); } if (hint) { this.context.info("To install that edition call the following command:"); diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index ae3b42c13..8eaca3dfd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -5,8 +5,6 @@ import java.util.Arrays; import java.util.List; -import com.devonfw.tools.ide.tool.intellij.IntellijCommunityUrlUpdater; -import com.devonfw.tools.ide.tool.intellij.IntellijUltimateUrlUpdater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +26,7 @@ import com.devonfw.tools.ide.tool.gradle.GradleUrlUpdater; import com.devonfw.tools.ide.tool.helm.HelmUrlUpdater; import com.devonfw.tools.ide.tool.intellij.IntellijUrlUpdater; +import com.devonfw.tools.ide.tool.jasypt.JasyptUrlUpdater; import com.devonfw.tools.ide.tool.java.JavaUrlUpdater; import com.devonfw.tools.ide.tool.jenkins.JenkinsUrlUpdater; import com.devonfw.tools.ide.tool.jmc.JmcUrlUpdater; @@ -46,7 +45,6 @@ import com.devonfw.tools.ide.tool.tomcat.TomcatUrlUpdater; import com.devonfw.tools.ide.tool.vscode.VsCodeUrlUpdater; import com.devonfw.tools.ide.url.model.folder.UrlRepository; -import com.devonfw.tools.ide.tool.jasypt.JasyptUrlUpdater; /** * The {@code UpdateManager} class manages the update process for various tools by using a list of From f66c7ea60b010279e7f3b3272c33f12ef48cd98a Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Mon, 19 Feb 2024 14:14:33 +0100 Subject: [PATCH 36/47] #103: implemented requested changes removed default getEdition override from tools changed getEdition to non abstract made getIntellijJsonRelease public --- .../androidstudio/AndroidStudioUrlUpdater.java | 17 ++++++----------- .../tools/ide/tool/aws/AwsUrlUpdater.java | 6 ------ .../tools/ide/tool/az/AzureUrlUpdater.java | 6 ------ .../ide/tool/cobigen/CobigenUrlUpdater.java | 6 ------ .../tool/docker/DockerDesktopUrlUpdater.java | 6 ------ .../tools/ide/tool/dotnet/DotNetUrlUpdater.java | 6 ------ .../tools/ide/tool/gcloud/GCloudUrlUpdater.java | 6 ------ .../ide/tool/gcviewer/GcViewerUrlUpdater.java | 6 ------ .../devonfw/tools/ide/tool/gh/GhUrlUpdater.java | 6 ------ .../tools/ide/tool/gradle/GradleUrlUpdater.java | 6 ------ .../tools/ide/tool/helm/HelmUrlUpdater.java | 6 ------ .../intellij/IntellijCommunityUrlUpdater.java | 4 ++-- .../intellij/IntellijUltimateUrlUpdater.java | 2 +- .../ide/tool/intellij/IntellijUrlUpdater.java | 13 +++++++++++-- .../tools/ide/tool/jasypt/JasyptUrlUpdater.java | 6 ------ .../tools/ide/tool/java/JavaUrlUpdater.java | 6 ------ .../ide/tool/jenkins/JenkinsUrlUpdater.java | 6 ------ .../tools/ide/tool/jmc/JmcUrlUpdater.java | 6 ------ .../tool/kotlinc/KotlincNativeUrlUpdater.java | 6 ------ .../ide/tool/kotlinc/KotlincUrlUpdater.java | 6 ------ .../tool/lazydocker/LazyDockerUrlUpdater.java | 6 ------ .../tools/ide/tool/mvn/MvnUrlUpdater.java | 6 ------ .../tools/ide/tool/node/NodeUrlUpdater.java | 6 ------ .../tools/ide/tool/npm/NpmUrlUpdater.java | 6 ------ .../devonfw/tools/ide/tool/oc/OcUrlUpdater.java | 6 ------ .../tools/ide/tool/pip/PipUrlUpdater.java | 6 ------ .../tools/ide/tool/python/PythonUrlUpdater.java | 6 ------ .../ide/tool/quarkus/QuarkusUrlUpdater.java | 6 ------ .../tools/ide/tool/sonar/SonarUrlUpdater.java | 6 ------ .../ide/tool/terraform/TerraformUrlUpdater.java | 6 ------ .../tools/ide/tool/tomcat/TomcatUrlUpdater.java | 6 ------ .../tools/ide/tool/vscode/VsCodeUrlUpdater.java | 6 ------ .../ide/url/updater/AbstractUrlUpdater.java | 10 +++++++--- .../tool/intellij/IntellijUrlUpdaterMock.java | 2 +- 34 files changed, 28 insertions(+), 188 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java index fd705960d..7f6a39581 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/androidstudio/AndroidStudioUrlUpdater.java @@ -1,5 +1,11 @@ package com.devonfw.tools.ide.tool.androidstudio; +import java.util.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.devonfw.tools.ide.json.mapping.JsonMapping; import com.devonfw.tools.ide.url.model.folder.UrlEdition; import com.devonfw.tools.ide.url.model.folder.UrlRepository; @@ -7,11 +13,6 @@ import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.url.updater.JsonUrlUpdater; import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; -import java.util.List; /** * {@link JsonUrlUpdater} for Android Studio. @@ -40,12 +41,6 @@ protected String getTool() { return "android-studio"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override public void update(UrlRepository urlRepository) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java index 52cc4eb4a..7f9df6110 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "aws"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java index 2bb343633..9aca42153 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/az/AzureUrlUpdater.java @@ -18,12 +18,6 @@ protected String getTool() { return "az"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java index 85779f291..484118ee9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/cobigen/CobigenUrlUpdater.java @@ -12,12 +12,6 @@ protected String getTool() { return "cobigen"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getMavenGroupIdPath() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java index 36e09430b..6b57fc794 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/DockerDesktopUrlUpdater.java @@ -23,12 +23,6 @@ protected String getTool() { return "docker"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java index e68435479..d56dd5ecd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/dotnet/DotNetUrlUpdater.java @@ -13,12 +13,6 @@ protected String getTool() { return "dotnet"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java index fbd07a12a..cb61b4fae 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gcloud/GCloudUrlUpdater.java @@ -21,12 +21,6 @@ protected String getTool() { return "gcloud"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubRepository() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java index d0643c8d6..d940e85db 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gcviewer/GcViewerUrlUpdater.java @@ -13,12 +13,6 @@ protected String getTool() { return "gcviewer"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java index b063314df..bd8a30dbc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/GhUrlUpdater.java @@ -17,12 +17,6 @@ protected String getTool() { return "gh"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java index ad6338483..b971c371f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/GradleUrlUpdater.java @@ -44,12 +44,6 @@ protected String getTool() { return "gradle"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java index bae59bb62..6c001f779 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/HelmUrlUpdater.java @@ -17,12 +17,6 @@ protected String getTool() { return "helm"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java index c882582cd..1c582e885 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijCommunityUrlUpdater.java @@ -5,11 +5,11 @@ public class IntellijCommunityUrlUpdater extends IntellijUrlUpdater { @Override protected String getEdition() { - return getTool(); + return "community"; } @Override - IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + public IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { return releases[1]; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java index d7513091d..f0f2ea2d7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUltimateUrlUpdater.java @@ -9,7 +9,7 @@ protected String getEdition() { } @Override - IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + public IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { return releases[0]; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java index 9395968fc..e722f2c45 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java @@ -20,7 +20,7 @@ /** * {@link IntellijUrlUpdater} base class for IntelliJ. */ -public abstract class IntellijUrlUpdater extends JsonUrlUpdater { +public class IntellijUrlUpdater extends JsonUrlUpdater { private static final String VERSION_BASE_URL = "https://data.services.jetbrains.com"; @@ -56,7 +56,10 @@ public void update(UrlRepository urlRepository) { * @param releases Has 2 elements, 1. Ultimate Edition, 2. Community Edition * @return The release for the {@link #getEdition() edition}. */ - abstract IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases); + public IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + + return releases[1]; + } /** * Adds a version for the provided {@link UrlEdition} @@ -141,6 +144,12 @@ protected String getTool() { return "intellij"; } + @Override + protected String getEdition() { + + return "community"; + } + @Override protected String doGetVersionUrl() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java index 10be2c97a..5dd1629a3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jasypt/JasyptUrlUpdater.java @@ -12,12 +12,6 @@ protected String getTool() { return "jasypt"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getMavenGroupIdPath() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java index 905269bc9..4ab982093 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/JavaUrlUpdater.java @@ -16,12 +16,6 @@ protected String getTool() { return "java"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String mapVersion(String version) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java index d48d118ca..873648087 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jenkins/JenkinsUrlUpdater.java @@ -16,12 +16,6 @@ protected String getTool() { return "jenkins"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getVersionUrl() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java index 43bbbd0b4..ce6f085e4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/JmcUrlUpdater.java @@ -14,12 +14,6 @@ protected String getTool() { return "jmc"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java index 502845c8a..c9171cb5f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNativeUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "kotlinc-native"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java index a2dde36cc..8f42bdf68 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "kotlinc"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java index 32e019c09..168e35953 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/lazydocker/LazyDockerUrlUpdater.java @@ -19,12 +19,6 @@ protected String getTool() { return "lazydocker"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java index 4e81b8280..b22e9f2ae 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/MvnUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "mvn"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java index 952e068d3..8f0e40b78 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/node/NodeUrlUpdater.java @@ -21,12 +21,6 @@ protected String getTool() { return "node"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getVersionPrefixToRemove() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java index fc36e9da2..adc2c5308 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "npm"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java index 95c403b09..9a7ade068 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/OcUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "oc"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java index d4eb79f6f..ab7412176 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/pip/PipUrlUpdater.java @@ -15,12 +15,6 @@ protected String getTool() { return "pip"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java index aba4243c0..793ad52b2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/python/PythonUrlUpdater.java @@ -35,12 +35,6 @@ protected String getTool() { return "python"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java index 3c8346783..86faa1442 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/QuarkusUrlUpdater.java @@ -17,12 +17,6 @@ protected String getTool() { return "quarkus"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java index b6a88f47b..f7bbd91af 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/sonar/SonarUrlUpdater.java @@ -13,12 +13,6 @@ protected String getTool() { return "sonar"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java index 5c8a42cbc..623c4b824 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/TerraformUrlUpdater.java @@ -17,12 +17,6 @@ protected String getTool() { return "terraform"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java index 014671419..7a3381510 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/tomcat/TomcatUrlUpdater.java @@ -14,12 +14,6 @@ protected String getTool() { return "tomcat"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected void addVersion(UrlVersion urlVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java index 79fecc276..fc12b3e5b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/VsCodeUrlUpdater.java @@ -14,12 +14,6 @@ protected String getTool() { return "vscode"; } - @Override - protected String getEdition() { - - return getTool(); - } - @Override protected String getGithubOrganization() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index c8e797910..24e729649 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -16,6 +16,9 @@ import java.util.Objects; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.devonfw.tools.ide.os.OperatingSystem; import com.devonfw.tools.ide.os.SystemArchitecture; import com.devonfw.tools.ide.url.model.file.UrlChecksum; @@ -31,8 +34,6 @@ import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.util.DateTimeUtil; import com.devonfw.tools.ide.util.HexUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Abstract base implementation of {@link UrlUpdater}. Contains methods for retrieving response bodies from URLs, @@ -77,7 +78,10 @@ public abstract class AbstractUrlUpdater extends AbstractProcessorWithTimeout im /** * @return the name of the {@link UrlEdition edition} handled by this updater. */ - abstract protected String getEdition(); + protected String getEdition() { + + return getTool(); + } /** * @return the combination of {@link #getTool() tool} and {@link #getEdition() edition} but simplified if both are diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java index f7e28cc69..b7f04673f 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdaterMock.java @@ -19,7 +19,7 @@ protected String getEdition() { } @Override - IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { + public IntellijJsonObject getIntellijJsonRelease(IntellijJsonObject[] releases) { return releases[1]; } From 7628cc917bcae03ae725d0ecb3c1aac9dee27311 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Mon, 19 Feb 2024 16:14:12 +0100 Subject: [PATCH 37/47] #103: applied reformat --- pom.xml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 4a34e84f5..1d319afb5 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -100,74 +101,74 @@ Jörg Hohwiller hohwille@users.sourceforge.net Capgemini - + admin designer developer +1 - + trippl Thomas Rippl - + developer +1 - + markusschuh Markus Schuh Capgemini - + contributor +1 - + maybeec Malte Brunnlieb Capgemini - + contributor +1 - + ediekman Erik Diekmann Capgemini - + contributor +1 - + nricheton Nicolas Richeton Capgemini - + contributor +1 - + From db6e276f5af392575ab843ea1bbca2ec20b72783 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Mon, 19 Feb 2024 16:22:58 +0100 Subject: [PATCH 38/47] #103: implemented requested changes added dependencyManagement to root pom.xml added owasp version property to root pom.xml renamed security artifact to ide-security --- pom.xml | 11 +++++++++++ security/pom.xml | 12 ++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 1d319afb5..530986062 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,19 @@ IDEasy ${revision} + 9.0.9 + + + + org.owasp + dependency-check-core + ${owasp.version} + + + + org.junit.jupiter diff --git a/security/pom.xml b/security/pom.xml index 072218910..70a25f8b1 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -8,26 +8,18 @@ ide dev-SNAPSHOT + + ide-security - org.example - security - - - 17 - 9.0.9 - org.owasp dependency-check-core - ${owasp.version} com.devonfw.tools.IDEasy ide-cli - 2024.02.001-alpha-SNAPSHOT - compile From 2862e6b67324ce366b2be3957b3602167063f2a3 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Thu, 22 Feb 2024 12:18:48 +0100 Subject: [PATCH 39/47] #103: fixed merge issues added missing answers param to newContext --- .../com/devonfw/tools/ide/context/AbstractIdeContextTest.java | 3 ++- .../java/com/devonfw/tools/ide/tool/ToolCommandletTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index 2de274fc2..6ecfa1371 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -62,7 +62,8 @@ protected static IdeTestContext newContext(String projectTestCaseName, String pr * @param answers the answers to use for the {@link IdeTestContext}. * @return the {@link IdeTestContext} pointing to that project. */ - protected static IdeTestContext newContext(String projectTestCaseName, String projectPath, boolean copyForMutation) { + protected static IdeTestContext newContext(String projectTestCaseName, String projectPath, boolean copyForMutation, + String... answers) { Path sourceDir = PATH_PROJECTS.resolve(projectTestCaseName); Path userDir = sourceDir.resolve("project"); diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index c67e9fa74..36ec6b7a3 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -3,6 +3,8 @@ import java.nio.file.Path; import java.util.List; +import org.junit.jupiter.api.Test; + import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.context.IdeTestContext; @@ -11,7 +13,6 @@ import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -import org.junit.jupiter.api.Test; /** Test of {@link ToolCommandlet} */ public class ToolCommandletTest extends AbstractIdeContextTest { From be3ec96d7b257b6c26e5f1b24febf5aef8ddda68 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Thu, 22 Feb 2024 16:06:20 +0100 Subject: [PATCH 40/47] #103: some fixes fixed pom versions applied reformat --- cli/pom.xml | 2 - pom.xml | 17 +++++++ security/pom.xml | 51 ++++++++++++------- .../security/BuildSecurityJsonFiles.java | 30 ++++++----- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 384dd94ab..03f342f93 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -58,12 +58,10 @@ org.slf4j slf4j-api - 2.0.3 ch.qos.logback logback-classic - 1.4.7 diff --git a/pom.xml b/pom.xml index 530986062..7322df841 100644 --- a/pom.xml +++ b/pom.xml @@ -20,15 +20,32 @@ IDEasy ${revision} 9.0.9 + 2.0.3 + 1.4.7 + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + org.owasp dependency-check-core ${owasp.version} + + com.devonfw.tools.IDEasy + ide-cli + ${revision} + diff --git a/security/pom.xml b/security/pom.xml index 70a25f8b1..7fbd5ee29 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -2,25 +2,38 @@ - 4.0.0 - - com.devonfw.tools.IDEasy.dev - ide - dev-SNAPSHOT - - - ide-security + 4.0.0 + + com.devonfw.tools.IDEasy.dev + ide + dev-SNAPSHOT + - - - - org.owasp - dependency-check-core - - - com.devonfw.tools.IDEasy - ide-cli - - + ide-security + + + 17 + + + + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + org.owasp + dependency-check-core + + + com.devonfw.tools.IDEasy + ide-cli + compile + + \ No newline at end of file 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 868eae377..5640adf4a 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -12,19 +12,6 @@ import java.util.Set; import java.util.stream.Collectors; -import com.devonfw.tools.ide.context.AbstractIdeContext; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.context.IdeContextConsole; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; -import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; -import com.devonfw.tools.ide.url.model.folder.UrlVersion; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.UpdateManager; -import com.devonfw.tools.ide.util.MapUtil; -import com.devonfw.tools.ide.version.BoundaryType; -import com.devonfw.tools.ide.version.VersionIdentifier; -import com.devonfw.tools.ide.version.VersionRange; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -55,6 +42,20 @@ import org.owasp.dependencycheck.utils.Pair; import org.owasp.dependencycheck.utils.Settings; +import com.devonfw.tools.ide.context.AbstractIdeContext; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeContextConsole; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; +import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; +import com.devonfw.tools.ide.url.model.folder.UrlVersion; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; +import com.devonfw.tools.ide.util.MapUtil; +import com.devonfw.tools.ide.version.BoundaryType; +import com.devonfw.tools.ide.version.VersionIdentifier; +import com.devonfw.tools.ide.version.VersionRange; + /** * This class is used to build the {@link UrlSecurityJsonFile} files for IDEasy. It scans the * {@link AbstractIdeContext#getUrlsPath() ide-url} folder for all tools, editions and versions and checks for @@ -85,6 +86,7 @@ public class BuildSecurityJsonFiles { private static BigDecimal minV3Severity = new BigDecimal("0.0"); private static final Set actuallyIgnoredCves = new HashSet<>(); + private static final IdeContext context = new IdeContextConsole(IdeLogLevel.INFO, null, false);; /** @@ -342,7 +344,7 @@ private static String getUrlVersion(String cpeVersion, Map cpeTo String urlVersion = null; if (cpeVersion != null) { - if (cpeToUrlVersion!= null && cpeToUrlVersion.containsKey(cpeVersion)) { + if (cpeToUrlVersion != null && cpeToUrlVersion.containsKey(cpeVersion)) { urlVersion = cpeToUrlVersion.get(cpeVersion); } else { urlVersion = urlUpdater.mapCpeVersionToUrlVersion(cpeVersion); From a7d686cc3ea5d67339718e058cb077ddf65c42f0 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Sat, 24 Feb 2024 00:28:18 +0100 Subject: [PATCH 41/47] #103: implemented requested changes renamed retrievePath to getPath renamed addPath to setPath --- .../java/com/devonfw/tools/ide/common/SystemPath.java | 9 +++++---- .../com/devonfw/tools/ide/tool/LocalToolCommandlet.java | 2 +- .../com/devonfw/tools/ide/context/IdeContextTest.java | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index f293f78d2..6b17ad47f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -67,7 +67,8 @@ public SystemPath(String envPath, Path softwarePath, char pathSeparator, IdeCont } else { Path duplicate = this.tool2pathMap.putIfAbsent(tool, path); if (duplicate != null) { - context.warning("Duplicated tool path for tool: {} at path: {} with duplicated path: {}.", tool, path, duplicate); + context.warning("Duplicated tool path for tool: {} at path: {} with duplicated path: {}.", tool, path, + duplicate); } } } @@ -160,16 +161,16 @@ public Path findBinary(Path toolPath) { * @return the {@link Path} to the directory of the tool where the binaries can be found or {@code null} if the tool * is not installed. */ - public Path retrievePath(String tool) { + public Path getPath(String tool) { return this.tool2pathMap.get(tool); } /** * @param tool the name of the tool. - * @param path the new {@link #retrievePath(String) tool bin path}. + * @param path the new {@link #getPath(String) tool bin path}. */ - public void addPath(String tool, Path path) { + public void setPath(String tool, Path path) { this.tool2pathMap.put(tool, path); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 2cc6b3284..dbbd2bd6d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -81,7 +81,7 @@ protected boolean doInstall(boolean silent) { fileAccess.backup(toolPath); } fileAccess.symlink(installation.linkDir(), toolPath); - this.context.getPath().addPath(this.tool, installation.binDir()); + this.context.getPath().setPath(this.tool, installation.binDir()); if (installedVersion == null) { this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion); } else { diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java index 4a6405313..dc0d443a7 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java @@ -46,9 +46,9 @@ public void testBasicProjectEnvironment() { assertThat(systemPath.toString()).isNotEqualTo(envPath).endsWith(envPath); Path softwarePath = context.getSoftwarePath(); Path javaBin = softwarePath.resolve("java/bin"); - assertThat(systemPath.retrievePath("java")).isEqualTo(javaBin); + assertThat(systemPath.getPath("java")).isEqualTo(javaBin); Path mvnBin = softwarePath.resolve("mvn/bin"); - assertThat(systemPath.retrievePath("mvn")).isEqualTo(mvnBin); + assertThat(systemPath.getPath("mvn")).isEqualTo(mvnBin); assertThat(systemPath.toString()).contains(javaBin.toString(), mvnBin.toString()); assertThat(variables.getType()).isSameAs(EnvironmentVariablesType.RESOLVED); assertThat(variables.getByType(EnvironmentVariablesType.RESOLVED)).isSameAs(variables); From a2995047e495359bc52a520ae64b122bb1707a59 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Sat, 24 Feb 2024 00:38:11 +0100 Subject: [PATCH 42/47] #103: implemented requested changes added javadocs --- .../ide/tool/SecurityRiskInteractionAnswer.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java index 79c086969..9a421f18e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java @@ -1,13 +1,29 @@ package com.devonfw.tools.ide.tool; +/** + * User interaction answers when a security risk was found and the user can f.e. choose to stay on the current unsafe + * version, use the latest safe version, use the latest version or use the next safe version. + */ public enum SecurityRiskInteractionAnswer { + /** + * User answer to stay on the current unsafe version. + */ STAY, + /** + * User answer to install the latest of all safe versions. + */ LATEST_SAFE, + /** + * User answer to use the latest safe version. + */ SAFE_LATEST, + /** + * User answer to use the next safe version. + */ NEXT_SAFE, } From 0f3596ff07e7fcf1559e537c2ed845a8bd60cb5e Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Sat, 24 Feb 2024 00:39:21 +0100 Subject: [PATCH 43/47] #103: implemented requested changes removed warnings from security json --- .../url/model/file/UrlSecurityJsonFile.java | 37 ++++++++++++------- .../url/model/UrlSecurityJsonFileTest.java | 31 ++++++++-------- .../basic/_ide/urls/mvn/mvn/security.json | 31 ++++++++-------- 3 files changed, 55 insertions(+), 44 deletions(-) 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 c15dcae90..779d00c1f 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 @@ -5,6 +5,7 @@ import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -18,6 +19,7 @@ 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,7 +30,7 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { /** {@link #getName() Name} of security json file. */ public static final String FILENAME_SECURITY = "security.json"; - private UrlSecurityWarningsJson urlSecurityWarningsJson = new UrlSecurityWarningsJson(); + private Collection urlSecurityWarnings; /** * The constructor. @@ -38,17 +40,19 @@ public class UrlSecurityJsonFile extends AbstractUrlFile { public UrlSecurityJsonFile(UrlEdition parent) { super(parent, FILENAME_SECURITY); + this.urlSecurityWarnings = new HashSet<>(); } /** * A wrapper for {@link #addSecurityWarning(VersionRange, BigDecimal, String, String, String)} used in the unit tests. + * + * @param versionRange the {@link VersionRange}. */ - public boolean addSecurityWarning(VersionRange versionRange) { + public void addSecurityWarning(VersionRange versionRange) { UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, null, null, null, null); - boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); + boolean added = urlSecurityWarnings.add(newWarning); this.modified = this.modified || added; - return added; } /** @@ -65,7 +69,7 @@ public boolean addSecurityWarning(VersionRange versionRange, BigDecimal severity String nistUrl) { UrlSecurityWarning newWarning = new UrlSecurityWarning(versionRange, severity, cveName, description, nistUrl); - boolean added = this.urlSecurityWarningsJson.getWarnings().add(newWarning); + boolean added = urlSecurityWarnings.add(newWarning); this.modified = this.modified || added; return added; } @@ -93,7 +97,7 @@ public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAff edition.getName()); } - for (UrlSecurityWarning warning : this.urlSecurityWarningsJson.getWarnings()) { + for (UrlSecurityWarning warning : this.urlSecurityWarnings) { VersionRange versionRange = warning.getVersionRange(); if (ignoreWarningsThatAffectAllVersions) { boolean includesOldestVersion = versionRange.getMin() == null @@ -114,6 +118,9 @@ public boolean contains(VersionIdentifier version, boolean ignoreWarningsThatAff /** * 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. + * + * @param version the {@link VersionIdentifier}. + * @return {@code true} if there is a security risk for the given version, {@code false} otherwise. */ public boolean contains(VersionIdentifier version) { @@ -129,7 +136,7 @@ public boolean contains(VersionIdentifier version) { public Set getMatchingSecurityWarnings(VersionIdentifier version) { Set matchedWarnings = new HashSet<>(); - for (UrlSecurityWarning warning : this.urlSecurityWarningsJson.getWarnings()) { + for (UrlSecurityWarning warning : this.urlSecurityWarnings) { if (warning.getVersionRange().contains(version)) { matchedWarnings.add(warning); } @@ -140,7 +147,7 @@ public Set getMatchingSecurityWarnings(VersionIdentifier ver /** Clears all security warnings. */ public void clearSecurityWarnings() { - this.urlSecurityWarningsJson.getWarnings().clear(); + this.urlSecurityWarnings.clear(); this.modified = true; } @@ -152,7 +159,8 @@ protected void doLoad() { } ObjectMapper mapper = JsonMapping.create(); try { - this.urlSecurityWarningsJson = mapper.readValue(getPath().toFile(), UrlSecurityWarningsJson.class); + urlSecurityWarnings = mapper.readValue(getPath().toFile(), new TypeReference>() { + }); } catch (IOException e) { throw new IllegalStateException("Failed to load the UrlSecurityJsonFile " + getPath(), e); } @@ -163,13 +171,13 @@ protected void doSave() { ObjectMapper mapper = JsonMapping.create(); - if (this.urlSecurityWarningsJson.getWarnings().isEmpty() && !Files.exists(getPath())) { + if (this.urlSecurityWarnings.isEmpty() && !Files.exists(getPath())) { return; } String jsonString; try { - jsonString = mapper.writeValueAsString(this.urlSecurityWarningsJson); + jsonString = mapper.writeValueAsString(urlSecurityWarnings); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -182,8 +190,11 @@ protected void doSave() { } } - public UrlSecurityWarningsJson getUrlSecurityWarningsJson() { + /** + * @return Collection of {@link UrlSecurityWarning}. + */ + public Collection getUrlSecurityWarnings() { - return this.urlSecurityWarningsJson; + return this.urlSecurityWarnings; } } \ No newline at end of file diff --git a/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java b/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java index ab497b6b8..3700f376e 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java @@ -1,5 +1,12 @@ package com.devonfw.tools.ide.url.model; +import java.io.File; +import java.math.BigDecimal; +import java.nio.file.Path; +import java.util.Set; + +import org.junit.jupiter.api.Test; + import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; @@ -7,12 +14,6 @@ import com.devonfw.tools.ide.url.model.folder.UrlEdition; import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionRange; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.math.BigDecimal; -import java.nio.file.Path; -import java.util.Set; /** Test of {@link UrlSecurityJsonFile}. */ @@ -42,7 +43,7 @@ public void testUrlJsonSecurityFileLoad() { UrlSecurityJsonFile securityFile = context.getUrls().getEdition("mvn", "mvn").getSecurityJsonFile(); // assert - assertThat(securityFile.getUrlSecurityWarningsJson().getWarnings()).containsExactly(warning1, warning2); + assertThat(securityFile.getUrlSecurityWarnings()).containsExactly(warning1, warning2); } /** @@ -66,15 +67,13 @@ public void testUrlJsonSecurityFileAddAndSave() { // assert assertThat(new File(String.valueOf(securityFilePath))).hasContent(""" - { - "warnings" : [ { - "versionRange" : "[1,3)", - "severity" : 1.2, - "cveName" : "testName3", - "description" : "testDescription3", - "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName3" - } ] - } + [ { + "versionRange" : "[1,3)", + "severity" : 1.2, + "cveName" : "testName3", + "description" : "testDescription3", + "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName3" + } ] """); } diff --git a/cli/src/test/resources/ide-projects/basic/_ide/urls/mvn/mvn/security.json b/cli/src/test/resources/ide-projects/basic/_ide/urls/mvn/mvn/security.json index 57d9df8d5..f3086cf74 100644 --- a/cli/src/test/resources/ide-projects/basic/_ide/urls/mvn/mvn/security.json +++ b/cli/src/test/resources/ide-projects/basic/_ide/urls/mvn/mvn/security.json @@ -1,15 +1,16 @@ -{ - "warnings" : [ { - "versionRange" : "[3.0.6,3.2.1)", - "severity" : 5.8, - "cveName" : "testName1", - "description" : "testDescription1", - "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName1" - }, { - "versionRange" : "(,3.8.1)", - "severity" : 9.1, - "cveName" : "testName2", - "description" : "testDescription2", - "nistUrl" : "https://nvd.nist.gov/vuln/detail/testName2" - } ] -} \ No newline at end of file +[ + { + "versionRange": "[3.0.6,3.2.1)", + "severity": 5.8, + "cveName": "testName1", + "description": "testDescription1", + "nistUrl": "https://nvd.nist.gov/vuln/detail/testName1" + }, + { + "versionRange": "(,3.8.1)", + "severity": 9.1, + "cveName": "testName2", + "description": "testDescription2", + "nistUrl": "https://nvd.nist.gov/vuln/detail/testName2" + } +] \ No newline at end of file From 69e1fdddeeb4b139c3e7db821d06b7e132389033 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Thu, 29 Feb 2024 18:38:47 +0100 Subject: [PATCH 44/47] #103: fixed intellij and vscode added missing CPE vendors/products --- .../tools/ide/tool/intellij/IntellijUrlUpdater.java | 12 ++++++++++++ .../tools/ide/tool/vscode/VsCodeUrlUpdater.java | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java index e722f2c45..051d66da6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/IntellijUrlUpdater.java @@ -175,4 +175,16 @@ protected void collectVersionsFromJson(IntellijJsonObject jsonItem, Collection Date: Thu, 29 Feb 2024 18:42:39 +0100 Subject: [PATCH 45/47] #103: fixed NPEs and other issues adjusted getCpeVendor and getCpeProduct to return the tool name instead of an empty string removed unused urlEdition param from getCpeEdition added workaround for intellij #1378 fixed NPE's (added checks for missing UrlUpdaters) --- .../ide/url/updater/AbstractUrlUpdater.java | 9 ++++----- .../tools/ide/url/updater/UpdateManager.java | 12 ++++++++++-- .../tools/security/BuildSecurityJsonFiles.java | 16 ++++++++++++---- .../com/devonfw/tools/security/UrlAnalyzer.java | 17 +++++++++++------ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 24e729649..5f49c8e8f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -103,7 +103,7 @@ protected final String getToolWithEdition() { */ public String getCpeVendor() { - return ""; + return getTool(); } /** @@ -111,16 +111,15 @@ public String getCpeVendor() { */ public String getCpeProduct() { - return ""; + return getTool(); } /** - * @param urlEdition the {@link UrlEdition} to get the CPE (Common Platform Enumeration) edition for. * @return the edition as specified in the CPE. */ - public String getCpeEdition(String urlEdition) { + public String getCpeEdition() { - return ""; + return getTool(); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 8eaca3dfd..dad24b779 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -108,8 +108,16 @@ public void updateAll() { */ public AbstractUrlUpdater retrieveUrlUpdater(String tool, String edition) { - return updaters.stream().filter(updater -> updater.getTool().equals(tool) && updater.getEdition().equals(edition)) - .findFirst().orElse(null); + for (AbstractUrlUpdater updater : updaters) { + // TODO: fix this ugly hack for intellij see: https://github.com/devonfw/ide/issues/1378 + if (updater.getTool().equals(tool) && edition.equals("intellij")) { + return updater; + } + if (updater.getTool().equals(tool) && updater.getEdition().equals(edition)) { + return updater; + } + } + return null; } public UrlRepository getUrlRepository() { 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 5640adf4a..2aaf37854 100644 --- a/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java +++ b/security/src/main/java/com/devonfw/tools/security/BuildSecurityJsonFiles.java @@ -5,6 +5,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -111,7 +112,7 @@ private static void run() { initCvesToIgnore(); UpdateManager updateManager = new UpdateManager(context.getUrlsPath(), null); - Dependency[] dependencies = getDependenciesWithVulnerabilities(updateManager); + List dependencies = getDependenciesWithVulnerabilities(updateManager); Set> foundToolsAndEditions = new HashSet<>(); for (Dependency dependency : dependencies) { String filePath = dependency.getFilePath(); @@ -119,6 +120,9 @@ private static void run() { String tool = parent.getParent().getParent().getFileName().toString(); String edition = parent.getParent().getFileName().toString(); AbstractUrlUpdater urlUpdater = updateManager.retrieveUrlUpdater(tool, edition); + if (urlUpdater == null) { + continue; + } UrlSecurityJsonFile securityFile = context.getUrls().getEdition(tool, edition).getSecurityJsonFile(); boolean newlyAdded = foundToolsAndEditions.add(new Pair<>(tool, edition)); if (newlyAdded) { // to assure that the file is cleared only once per tool and edition @@ -153,6 +157,7 @@ private static Map buildCpeToUrlVersionMap(String tool, String e List sortedVersions = context.getUrls().getSortedVersions(tool, edition).stream() .map(VersionIdentifier::toString).toList(); + List sortedCpeVersions = sortedVersions.stream().map(urlUpdater::mapUrlVersionToCpeVersion) .collect(Collectors.toList()); Map cpeToUrlVersion = MapUtil.createMapfromLists(sortedCpeVersions, sortedVersions); @@ -163,13 +168,13 @@ private static Map buildCpeToUrlVersionMap(String tool, String e * Uses the {@link Engine OWASP engine} to scan the {@link AbstractIdeContext#getUrlsPath() ide-url} folder for * dependencies and then runs {@link Engine#analyzeDependencies() analyzes} them to get the {@link Vulnerability * vulnerabilities}. - * + * * @param updateManager the {@link UpdateManager} to use to get the {@link AbstractUrlUpdater} of the tool to get CPE * Vendor, CPE Product and CPE edition of the tool, as well as the * {@link AbstractUrlUpdater#mapCpeVersionToUrlVersion(String) CPE naming of its version} * @return the {@link Dependency dependencies} with associated {@link Vulnerability vulnerabilities}. */ - private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager updateManager) { + private static List getDependenciesWithVulnerabilities(UpdateManager updateManager) { Settings settings = new Settings(); Engine engine = new Engine(settings); @@ -189,8 +194,11 @@ private static Dependency[] getDependenciesWithVulnerabilities(UpdateManager upd throw new RuntimeException(e); } Dependency[] dependencies = engine.getDependencies(); + // remove dependencies without vulnerabilities + List dependenciesFiltered = Arrays.stream(dependencies) + .filter(dependency -> !dependency.getVulnerabilities().isEmpty()).toList(); engine.close(); - return dependencies; + return dependenciesFiltered; } /** diff --git a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java index 58aaf1dea..58ecb5c1f 100644 --- a/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java +++ b/security/src/main/java/com/devonfw/tools/security/UrlAnalyzer.java @@ -1,7 +1,9 @@ package com.devonfw.tools.security; -import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; -import com.devonfw.tools.ide.url.updater.UpdateManager; +import java.io.FileFilter; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.AbstractFileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.AnalysisPhase; @@ -11,9 +13,8 @@ import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; -import java.io.FileFilter; -import java.nio.file.Path; -import java.nio.file.Paths; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater; +import com.devonfw.tools.ide.url.updater.UpdateManager; /** * Analyzes file paths to detect tool, edition and version of software listed in a directory structure like this: @@ -56,9 +57,13 @@ protected void analyzeDependency(Dependency dependency, Engine engine) { AbstractUrlUpdater urlUpdater = this.updateManager.retrieveUrlUpdater(tool, edition); + if (urlUpdater == null) { + return; + } + String cpeVendor = urlUpdater.getCpeVendor(); String cpeProduct = urlUpdater.getCpeProduct(); - String cpeEdition = urlUpdater.getCpeEdition(edition); + String cpeEdition = urlUpdater.getCpeEdition(); String cpeVersion = urlUpdater.mapUrlVersionToCpeVersion(versionFolder.getFileName().toString()); if (cpeVendor.isBlank() || cpeProduct.isBlank()) { From ba4bc07216d96f876a8f26bfb15940b53678a44b Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Tue, 2 Apr 2024 10:22:45 +0200 Subject: [PATCH 46/47] #103 fixed tests added missing answers to IdeTestContext --- .../ide/context/AbstractIdeContextTest.java | 30 ++++++++++--------- .../tools/ide/tool/ToolCommandletTest.java | 2 +- .../url/model/UrlSecurityJsonFileTest.java | 10 +++---- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index 9318c6308..0aa47f799 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -1,5 +1,13 @@ package com.devonfw.tools.ide.context; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.Condition; +import org.assertj.core.api.ListAssert; + import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.FileAccessImpl; import com.devonfw.tools.ide.io.FileCopyMode; @@ -7,13 +15,6 @@ import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.log.IdeTestLogger; import com.devonfw.tools.ide.repo.ToolRepositoryMock; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.Condition; -import org.assertj.core.api.ListAssert; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; /** * Abstract base class for tests that need mocked instances of {@link IdeContext}. @@ -37,7 +38,7 @@ public abstract class AbstractIdeContextTest extends Assertions { /** * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * project in {@link #TEST_PROJECTS}. E.g. "basic". * @return the {@link IdeTestContext} pointing to that project. */ protected IdeTestContext newContext(String testProject) { @@ -47,7 +48,7 @@ protected IdeTestContext newContext(String testProject) { /** * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * project in {@link #TEST_PROJECTS}. E.g. "basic". * @param projectPath the relative path inside the test project where to create the context. * @return the {@link IdeTestContext} pointing to that project. */ @@ -58,15 +59,16 @@ protected static IdeTestContext newContext(String testProject, String projectPat /** * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * project in {@link #TEST_PROJECTS}. E.g. "basic". * @param projectPath the relative path inside the test project where to create the context. * @param copyForMutation - {@code true} to create a copy of the project that can be modified by the test, - * {@code false} otherwise (only to save resources if you are 100% sure that your test never modifies anything in that - * project.) + * {@code false} otherwise (only to save resources if you are 100% sure that your test never modifies anything + * in that project.) * @param answers the answers to use for the {@link IdeTestContext}. * @return the {@link IdeTestContext} pointing to that project. */ - protected static IdeTestContext newContext(String testProject, String projectPath, boolean copyForMutation) { + protected static IdeTestContext newContext(String testProject, String projectPath, boolean copyForMutation, + String... answers) { Path ideRoot = TEST_PROJECTS.resolve(testProject); if (copyForMutation) { @@ -87,7 +89,7 @@ protected static IdeTestContext newContext(String testProject, String projectPat if (Files.isDirectory(repositoryFolder)) { toolRepository = new ToolRepositoryMock(repositoryFolder); } - IdeTestContext context = new IdeTestContext(ideHome, toolRepository); + IdeTestContext context = new IdeTestContext(ideHome, toolRepository, answers); if (toolRepository != null) { toolRepository.setContext(context); } diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java index 36ec6b7a3..5deb8219b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/ToolCommandletTest.java @@ -314,7 +314,7 @@ public void testSecurityRiskInteractionCurrentVersionIsSafe() { */ private IdeContext getContextForSecurityJsonTests(Class dummyTool, String... answers) { - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; // if I don't pass answers here I get: End of answers reached! IdeContext context = newContext("basic", path, true, answers); ToolCommandlet toolCommandlet = context.getCommandletManager().getCommandlet(dummyTool); diff --git a/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java b/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java index 3700f376e..746fd7c61 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/url/model/UrlSecurityJsonFileTest.java @@ -24,7 +24,7 @@ public class UrlSecurityJsonFileTest extends AbstractIdeContextTest { public void testUrlJsonSecurityFileLoad() { // arrange - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; UrlSecurityWarning warning1 = new UrlSecurityWarning(); warning1.setVersionRange(VersionRange.of("[3.0.6,3.2.1)")); warning1.setSeverity(BigDecimal.valueOf(5.8)); @@ -54,7 +54,7 @@ public void testUrlJsonSecurityFileLoad() { public void testUrlJsonSecurityFileAddAndSave() { // arrange - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; IdeContext context = newContext("basic", path, true); UrlSecurityJsonFile securityFile = context.getUrls().getEdition("mvn", "mvn").getSecurityJsonFile(); Path securityFilePath = securityFile.getPath(); @@ -82,7 +82,7 @@ public void testUrlJsonSecurityFileAddAndSave() { public void testUrlSecurityJsonFileContains() { // arrange - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; IdeContext context = newContext("basic", path, true); UrlSecurityJsonFile securityFile = context.getUrls().getEdition("mvn", "mvn").getSecurityJsonFile(); @@ -99,7 +99,7 @@ public void testUrlSecurityJsonFileContains() { public void testUrlSecurityJsonFileContainsIgnoreWarningsThatAffectAllVersions() { // arrange - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; IdeContext context = newContext("basic", path, true); UrlEdition edition = context.getUrls().getEdition("mvn", "mvn"); UrlSecurityJsonFile securityFile = edition.getSecurityJsonFile(); @@ -114,7 +114,7 @@ public void testUrlSecurityJsonFileContainsIgnoreWarningsThatAffectAllVersions() public void testGetMatchingSecurityWarnings() { // arrange - String path = "workspaces/foo-test/my-git-repo"; + String path = "project/workspaces/foo-test/my-git-repo"; IdeContext context = newContext("basic", path, true); UrlEdition edition = context.getUrls().getEdition("mvn", "mvn"); UrlSecurityJsonFile securityFile = edition.getSecurityJsonFile(); From 998387d4a465281cf8c40bdbfc489e5d3b1820ed Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Tue, 2 Apr 2024 10:32:59 +0200 Subject: [PATCH 47/47] #103 implemented requested changes renamed SAFE_LATEST to LATEST --- .../tool/SecurityRiskInteractionAnswer.java | 4 +-- .../tools/ide/tool/ToolCommandlet.java | 26 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java index 9a421f18e..767f090fc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/SecurityRiskInteractionAnswer.java @@ -17,9 +17,9 @@ public enum SecurityRiskInteractionAnswer { LATEST_SAFE, /** - * User answer to use the latest safe version. + * User answer to use the latest version. */ - SAFE_LATEST, + LATEST, /** * User answer to use the next safe version. 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 ebedc8e51..12ca31f94 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 @@ -1,8 +1,8 @@ package com.devonfw.tools.ide.tool; +import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.LATEST; import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.LATEST_SAFE; import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.NEXT_SAFE; -import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.SAFE_LATEST; import static com.devonfw.tools.ide.tool.SecurityRiskInteractionAnswer.STAY; import java.io.IOException; @@ -14,9 +14,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.common.Tags; @@ -30,16 +28,9 @@ import com.devonfw.tools.ide.property.StringListProperty; import com.devonfw.tools.ide.url.model.file.UrlSecurityJsonFile; import com.devonfw.tools.ide.url.model.file.json.UrlSecurityWarning; -import com.devonfw.tools.ide.util.FilenameUtil; import com.devonfw.tools.ide.util.Pair; import com.devonfw.tools.ide.version.VersionIdentifier; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; - /** * {@link Commandlet} for a tool integrated into the IDE. */ @@ -268,7 +259,7 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi Map> options = new HashMap<>(); options.put(STAY, Pair.of("Stay with the current unsafe version (" + current + ").", configuredVersion)); options.put(LATEST_SAFE, Pair.of("Install the latest of all safe versions (" + latestSafe + ").", latestSafe)); - options.put(SAFE_LATEST, + options.put(LATEST, Pair.of("Install the latest version (" + latest + "). This version is save.", VersionIdentifier.LATEST)); options.put(NEXT_SAFE, Pair.of("Install the next safe version (" + nextSafe + ").", nextSafe)); @@ -278,13 +269,13 @@ private VersionIdentifier whichVersionDoYouWantToInstall(VersionIdentifier confi return getAnswer(options, currentIsUnsafe + "All newer versions are also not safe. " + ask, STAY, LATEST_SAFE); } else if (nextSafe.equals(latest)) { return getAnswer(options, currentIsUnsafe + "Of the newer versions, only the latest is safe. " + ask, STAY, - SAFE_LATEST); + LATEST); } else if (nextSafe.equals(latestSafe)) { return getAnswer(options, currentIsUnsafe + "Of the newer versions, only version " + nextSafe + " is safe, which is however not the latest. " + ask, STAY, NEXT_SAFE); } else { if (latestSafe.equals(latest)) { - return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, SAFE_LATEST); + return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, LATEST); } else { return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, LATEST_SAFE); } @@ -445,11 +436,10 @@ public String getInstalledEdition(Path toolPath) { } return edition; } catch (IOException e) { - throw new IllegalStateException( - "Couldn't determine the edition of " + getName() + " from the directory structure of its software path " - + toolPath - + ", assuming the name of the parent directory of the real path of the software path to be the edition " - + "of the tool.", e); + throw new IllegalStateException("Couldn't determine the edition of " + getName() + + " from the directory structure of its software path " + toolPath + + ", assuming the name of the parent directory of the real path of the software path to be the edition " + + "of the tool.", e); } }