Skip to content

Commit

Permalink
devonfw#103: writing security json file
Browse files Browse the repository at this point in the history
  • Loading branch information
MattesMrzik committed Nov 24, 2023
1 parent 34febf5 commit ae0558b
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
42 changes: 24 additions & 18 deletions cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
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.util.FilenameUtil;
import com.devonfw.tools.ide.version.VersionIdentifier;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* {@link Commandlet} for a tool integrated into the IDE.
Expand Down Expand Up @@ -172,24 +174,28 @@ 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";
}
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;
}
Expand All @@ -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))) {
Expand All @@ -225,46 +232,45 @@ 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);
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 = 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);
return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe;
}
}

// VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion);
// setVersion(chosenVersion, silent);
// VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion);
// setVersion(chosenVersion, silent);
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<UrlEdition> {

/** {@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<UrlSecurityMatch> 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<String> 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<UrlSecurityWarning> getSecurityWarnings(VersionIdentifier version) {

Set<UrlSecurityWarning> 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<List<UrlSecurityMatch>>() {
});
} 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<UrlSecurityWarning> 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<UrlSecurityWarning> 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<String> referenceUrl) {
};
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -12,6 +13,8 @@ public class UrlEdition extends AbstractUrlFolderWithParent<UrlTool, UrlVersion>

private UrlSecurityFile securityFile;

private UrlSecurityJsonFile securityJsonFile;

/**
* The constructor.
*
Expand Down Expand Up @@ -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() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading

0 comments on commit ae0558b

Please sign in to comment.