Skip to content

Commit ae0558b

Browse files
committed
devonfw#103: writing security json file
1 parent 34febf5 commit ae0558b

File tree

11 files changed

+440
-121
lines changed

11 files changed

+440
-121
lines changed

cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ protected boolean doInstall(boolean silent) {
6363
// install configured version of our tool in the software repository if not already installed
6464
ToolInstallation installation = installInRepo(configuredVersion);
6565

66+
VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion);
67+
68+
System.out.println("Selected version: " + selectedVersion);
69+
6670
// check if we already have this version installed (linked) locally in IDE_HOME/software
6771
VersionIdentifier installedVersion = getInstalledVersion();
6872
VersionIdentifier resolvedVersion = installation.resolvedVersion();

cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java

+24-18
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
import com.devonfw.tools.ide.environment.EnvironmentVariablesType;
1717
import com.devonfw.tools.ide.io.FileAccess;
1818
import com.devonfw.tools.ide.io.TarCompression;
19+
import com.devonfw.tools.ide.json.mapping.JsonMapping;
1920
import com.devonfw.tools.ide.os.MacOsHelper;
2021
import com.devonfw.tools.ide.process.ProcessContext;
2122
import com.devonfw.tools.ide.process.ProcessErrorHandling;
2223
import com.devonfw.tools.ide.property.StringListProperty;
2324
import com.devonfw.tools.ide.url.model.file.UrlSecurityFile;
2425
import com.devonfw.tools.ide.util.FilenameUtil;
2526
import com.devonfw.tools.ide.version.VersionIdentifier;
27+
import com.fasterxml.jackson.databind.ObjectMapper;
2628

2729
/**
2830
* {@link Commandlet} for a tool integrated into the IDE.
@@ -172,24 +174,28 @@ public boolean install(boolean silent) {
172174
return doInstall(silent);
173175
}
174176

175-
protected String question(String question, String ... options) {
177+
protected String question(String question, String... options) {
178+
176179
question += " Do you want to";
177180
for (int i = 0; i < options.length - 1; i++) {
178181
options[i] += " or";
179182
}
180183
options[options.length - 1] += "?";
181184
return this.context.question(question, options);
182185
}
186+
183187
protected VersionIdentifier securityRiskInteraction(VersionIdentifier configuredVersion) {
184188

185189
// TODO vielleicht security file auch neu als json file wenn 1.2 > 2.9 nicht ausreicht
186-
// TODO vielleicht auch zusätzlich das tool scannen sobald es installiert ist,
187-
188190
// TODO webpage:\nhttps://github.com/devonfw/ide/blob/master/documentation/vulnerabilities.asciidoc\n\n";
189191

192+
// TODO if no version is save, find a version that has lowest security risk, or suggest multiple ones, such that the
193+
// user can choose
194+
190195
UrlSecurityFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition()).getSecurityFile();
191-
VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(),
192-
configuredVersion);
196+
ObjectMapper mapper = JsonMapping.create();
197+
198+
VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion);
193199
if (!securityFile.contains(current)) {
194200
return configuredVersion;
195201
}
@@ -201,7 +207,8 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured
201207

202208
int currentVersionIndex = allVersions.indexOf(current);
203209

204-
// VersionIdentifier nextVersion = currentVersionIndex == 0 ? null : allVersions.get(allVersions.indexOf(currentVersion) - 1);
210+
// VersionIdentifier nextVersion = currentVersionIndex == 0 ? null :
211+
// allVersions.get(allVersions.indexOf(currentVersion) - 1);
205212
VersionIdentifier nextSafe = null;
206213
for (int i = currentVersionIndex - 1; i >= 0; i--) {
207214
if (!securityFile.contains(allVersions.get(i))) {
@@ -225,46 +232,45 @@ protected VersionIdentifier securityRiskInteraction(VersionIdentifier configured
225232
}
226233

227234
String currentIsUnsafe = "Currently, version " + current + " of " + this.getName() + " is installed, "
228-
+ "which is has a vulnerability.";
235+
+ "which is has a vulnerability:\n" + " TODOODODO" + "\n\n";
236+
229237
String stay = "stay with the current unsafe version (" + current + ")";
230238
String installLatestSafe = "install the latest safe version (" + latestSafe + ")";
231239
String installSafeLatest = "install the (safe) latest version (" + latestSafe + ")";
232-
String installNextSafe = "install the next safe version (" + nextSafe+ ")";
240+
String installNextSafe = "install the next safe version (" + nextSafe + ")";
233241

234242
if (current.equals(latest)) {
235243
String answer = question(currentIsUnsafe, stay, installLatestSafe);
236244
return answer.startsWith(stay) ? current : latestSafe;
237245

238246
} else if (nextSafe == null) {
239247
// TODO also allow selection of next or previous version, even if they are unsafe?
240-
String answer = question(currentIsUnsafe + " All newer versions are also not safe.",
241-
stay, installLatestSafe);
248+
String answer = question(currentIsUnsafe + " All newer versions are also not safe.", stay, installLatestSafe);
242249
return answer.startsWith(stay) ? current : latestSafe;
243250

244251
} else if (nextSafe.equals(latest)) {
245-
String answer = question( currentIsUnsafe + " Of the newer versions, only the latest is safe.",
246-
stay, installSafeLatest);
252+
String answer = question(currentIsUnsafe + " Of the newer versions, only the latest is safe.", stay,
253+
installSafeLatest);
247254
return answer.startsWith(stay) ? current : latestSafe;
248255

249256
} else if (nextSafe.equals(latestSafe)) {
250-
String answer = question(currentIsUnsafe +" Of the newer versions, only the version "
251-
+ nextSafe + " is safe.", stay, "Install the safe version (" + nextSafe + ")");
257+
String answer = question(currentIsUnsafe + " Of the newer versions, only the version " + nextSafe + " is safe.",
258+
stay, "Install the safe version (" + nextSafe + ")");
252259
return answer.startsWith(stay) ? current : nextSafe;
253260

254261
} else {
255262
if (latest.equals(latestSafe)) {
256263
String answer = question(currentIsUnsafe, stay, installNextSafe, installSafeLatest);
257-
return answer.startsWith(stay) ? current
258-
: answer.startsWith(installNextSafe) ? nextSafe : latestSafe;
264+
return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe;
259265

260266
} else {
261267
String answer = question(currentIsUnsafe, stay, installNextSafe, installLatestSafe);
262268
return answer.startsWith(stay) ? current : answer.startsWith(installNextSafe) ? nextSafe : latestSafe;
263269
}
264270
}
265271

266-
// VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion);
267-
// setVersion(chosenVersion, silent);
272+
// VersionIdentifier chosenVersion = securityRiskInteraction(configuredVersion);
273+
// setVersion(chosenVersion, silent);
268274
}
269275

270276
/**

cli/src/main/java/com/devonfw/tools/ide/url/model/file/SecurityEntry.java

-17
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package com.devonfw.tools.ide.url.model.file.json;
2+
3+
import java.io.BufferedWriter;
4+
import java.io.IOException;
5+
import java.nio.file.Files;
6+
import java.nio.file.Path;
7+
import java.nio.file.StandardOpenOption;
8+
import java.util.ArrayList;
9+
import java.util.HashSet;
10+
import java.util.List;
11+
import java.util.Set;
12+
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import com.devonfw.tools.ide.json.mapping.JsonMapping;
17+
import com.devonfw.tools.ide.url.model.file.AbstractUrlFile;
18+
import com.devonfw.tools.ide.url.model.folder.UrlEdition;
19+
import com.devonfw.tools.ide.version.VersionIdentifier;
20+
import com.devonfw.tools.ide.version.VersionRange;
21+
import com.fasterxml.jackson.core.JsonProcessingException;
22+
import com.fasterxml.jackson.core.type.TypeReference;
23+
import com.fasterxml.jackson.databind.ObjectMapper;
24+
25+
public class UrlSecurityJsonFile extends AbstractUrlFile<UrlEdition> {
26+
27+
/** {@link #getName() Name} of security json file. */
28+
public static final String FILENAME_SECURITY = "security.json";
29+
30+
private static final Logger LOG = LoggerFactory.getLogger(UrlSecurityJsonFile.class);
31+
32+
List<UrlSecurityMatch> matches;
33+
34+
/**
35+
* The constructor.
36+
*
37+
* @param parent the {@link #getParent() parent folder}.
38+
*/
39+
public UrlSecurityJsonFile(UrlEdition parent) {
40+
41+
super(parent, FILENAME_SECURITY);
42+
this.matches = new ArrayList<>();
43+
}
44+
45+
public boolean addSecurityMatch(VersionRange versionRange, double severity, String severityVersion, String cveName,
46+
String description, String nistUrl, List<String> referenceUrl) {
47+
48+
UrlSecurityWarning newWarning = new UrlSecurityWarning(severity, severityVersion, cveName, description, nistUrl,
49+
referenceUrl);
50+
for (UrlSecurityMatch match : matches) {
51+
if (match.getVersionRange().equals(versionRange)) {
52+
boolean added = match.addWarning(newWarning);
53+
this.modified = this.modified || added;
54+
return added;
55+
}
56+
}
57+
UrlSecurityMatch newMatch = new UrlSecurityMatch(versionRange);
58+
newMatch.addWarning(newWarning);
59+
this.modified = true;
60+
return matches.add(newMatch);
61+
}
62+
63+
public boolean removeSecurityMatch(VersionRange versionRange) {
64+
65+
for (UrlSecurityMatch match : matches) {
66+
if (match.getVersionRange().equals(versionRange)) {
67+
boolean removed = matches.remove(match);
68+
this.modified = this.modified || removed;
69+
return removed;
70+
}
71+
}
72+
return false;
73+
}
74+
75+
public boolean contains(VersionIdentifier version) {
76+
77+
for (UrlSecurityMatch match : matches) {
78+
if (match.getVersionRange().contains(version)) {
79+
return true;
80+
}
81+
}
82+
return false;
83+
}
84+
85+
public Set<UrlSecurityWarning> getSecurityWarnings(VersionIdentifier version) {
86+
87+
Set<UrlSecurityWarning> warnings = new HashSet<>();
88+
for (UrlSecurityMatch match : matches) {
89+
if (match.getVersionRange().contains(version)) {
90+
warnings.addAll(match.getWarnings());
91+
}
92+
}
93+
return warnings;
94+
}
95+
96+
public void clearSecurityMatches() {
97+
98+
this.matches.clear();
99+
}
100+
101+
@Override
102+
protected void doLoad() {
103+
104+
if (!Files.exists(getPath())) {
105+
return;
106+
}
107+
ObjectMapper mapper = JsonMapping.create();
108+
try {
109+
matches = mapper.readValue(getPath().toFile(), new TypeReference<List<UrlSecurityMatch>>() {
110+
});
111+
} catch (IOException e) {
112+
throw new IllegalStateException("The UrlSecurityJsonFile " + getPath() + " could not be parsed.", e);
113+
}
114+
}
115+
116+
@Override
117+
protected void doSave() {
118+
119+
Path path = getPath();
120+
ObjectMapper mapper = JsonMapping.create();
121+
122+
if (this.matches.isEmpty() && !Files.exists(path)) {
123+
return;
124+
}
125+
126+
String jsonString;
127+
try {
128+
jsonString = mapper.writeValueAsString(matches);
129+
} catch (JsonProcessingException e) {
130+
throw new RuntimeException(e);
131+
}
132+
133+
try (BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.TRUNCATE_EXISTING,
134+
StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
135+
bw.write(jsonString + "\n");
136+
} catch (IOException e) {
137+
throw new IllegalStateException("Failed to save file " + path, e);
138+
}
139+
}
140+
}
141+
142+
class UrlSecurityMatch {
143+
private final VersionRange versionRange;
144+
145+
private final Set<UrlSecurityWarning> warnings;
146+
147+
public UrlSecurityMatch() {
148+
149+
// this constructor is needed for jackson deserialization
150+
this.versionRange = null;
151+
this.warnings = new HashSet<>();
152+
}
153+
154+
public UrlSecurityMatch(VersionRange versionRange) {
155+
156+
this.versionRange = versionRange;
157+
this.warnings = new HashSet<>();
158+
}
159+
160+
public VersionRange getVersionRange() {
161+
162+
return versionRange;
163+
}
164+
165+
public Set<UrlSecurityWarning> getWarnings() {
166+
167+
return warnings;
168+
}
169+
170+
public boolean addWarning(UrlSecurityWarning warning) {
171+
172+
return this.warnings.add(warning);
173+
}
174+
175+
}
176+
177+
// severity could be java.math.BigDecimal; instead of double (unsing BigDecimal("123.4").setScale(1,
178+
// BigDecimal.ROUND_HALF_UP);)
179+
record UrlSecurityWarning(double severity, String severityVersion, String cveName, String description, String nistUrl,
180+
List<String> referenceUrl) {
181+
};

cli/src/main/java/com/devonfw/tools/ide/url/model/folder/UrlEdition.java

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.devonfw.tools.ide.url.model.AbstractUrlFolderWithParent;
44
import com.devonfw.tools.ide.url.model.file.UrlSecurityFile;
5+
import com.devonfw.tools.ide.url.model.file.json.UrlSecurityJsonFile;
56

67
/**
78
* 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<UrlTool, UrlVersion>
1213

1314
private UrlSecurityFile securityFile;
1415

16+
private UrlSecurityJsonFile securityJsonFile;
17+
1518
/**
1619
* The constructor.
1720
*
@@ -48,6 +51,15 @@ public UrlSecurityFile getSecurityFile() {
4851
return this.securityFile;
4952
}
5053

54+
public UrlSecurityJsonFile getSecurityJsonFile() {
55+
56+
if (this.securityJsonFile == null) {
57+
this.securityJsonFile = new UrlSecurityJsonFile(this);
58+
this.securityJsonFile.load(false);
59+
}
60+
return this.securityJsonFile;
61+
}
62+
5163
@Override
5264
public void save() {
5365

cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,17 @@ protected final String getToolWithEdition() {
9999

100100
protected String getCpeVendor() {
101101

102-
return "";
102+
return null;
103103
}
104104

105105
protected String getCpeProduct() {
106106

107-
return "";
107+
return null;
108+
}
109+
110+
protected String getCpeEdition() {
111+
112+
return null;
108113
}
109114

110115
protected String mapUrlVersionToCpeVersion(String version) {

cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java

+6
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ public String getCpeProduct(String tool) {
108108
.map(AbstractUrlUpdater::getCpeProduct).orElse(null);
109109
}
110110

111+
public String getCpeEdition(String tool) {
112+
113+
return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst()
114+
.map(AbstractUrlUpdater::getCpeEdition).orElse(null);
115+
}
116+
111117
public String mapUrlVersionToCpeVersion(String tool, String urlVersion) {
112118

113119
return updaters.stream().filter(updater -> updater.getTool().equals(tool)).findFirst()

0 commit comments

Comments
 (0)