Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#103: security warning for CVEs in file tool/edition/security #119

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2237158
#103: security warning for CVEs in file tool/edition/security
MattesMrzik Oct 25, 2023
897b800
promting the user for vulnerabilities and start of Main for OWASP check
MattesMrzik Nov 8, 2023
59f6a1b
merged main into feature 103
MattesMrzik Nov 8, 2023
5f162e2
moved owasp dep from idecli pom to security pom, mor work in Main
MattesMrzik Nov 14, 2023
e5c70e7
#103: url analyzer works and vulnerabilities are retrieved
MattesMrzik Nov 15, 2023
34febf5
#103: Security Entry and determination of version Range for vulnerabi…
MattesMrzik Nov 16, 2023
ae0558b
#103: writing security json file
MattesMrzik Nov 24, 2023
ba87b95
#103: test interaction and getVersionRangeFromInterval
MattesMrzik Dec 5, 2023
ba694ab
#103: refinements
MattesMrzik Dec 6, 2023
81b8586
#103: more refinement
MattesMrzik Dec 14, 2023
9574f8d
#158: VersionRange with open boundaries
MattesMrzik Dec 18, 2023
7e2023e
Merge remote-tracking branch 'upstream/main' into feature/#103-implem…
MattesMrzik Dec 19, 2023
64c8454
Merge remote-tracking branch 'origin/feature/#158-version-range-with-…
MattesMrzik Dec 19, 2023
5518138
#103: removed duplicate VersionRange.equals
MattesMrzik Dec 19, 2023
4fbef6e
#103: versionRange with open interval
MattesMrzik Dec 19, 2023
1b9224b
#103: updated urlSecJsonFile.contains
MattesMrzik Dec 19, 2023
9a86e34
#103: rephrase interaction, mapUtil, LICENCE
MattesMrzik Dec 21, 2023
fe9109f
#103: moved urlSecurityJson to its own class
MattesMrzik Dec 22, 2023
b19b877
#103: fixed write json bug, and more
MattesMrzik Dec 22, 2023
fd64100
#103: some final cleanup
MattesMrzik Jan 2, 2024
312afdd
#103: updated to be in line with #158
MattesMrzik Jan 2, 2024
37122ff
Merge branch 'main' of https://github.com/devonfw/IDEasy into feature…
MattesMrzik Jan 20, 2024
9b28679
#103: fixed small bug due to merged main
MattesMrzik Jan 20, 2024
1389057
#103: fixed bugs
MattesMrzik Jan 20, 2024
98b3da3
#103: fixed bug
MattesMrzik Jan 20, 2024
06cc433
#103: added logging test
MattesMrzik Jan 21, 2024
80ab231
#103: refactored code
MattesMrzik Jan 21, 2024
a26df56
103: first half of team review
MattesMrzik Jan 21, 2024
dbee293
#103: more change requests from team review and bug fix
MattesMrzik Jan 21, 2024
ea1bb26
Update cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
MattesMrzik Jan 22, 2024
b02bfcf
#103: checkpoint to not accidentally lose progress
MattesMrzik Jan 23, 2024
fb6b842
Merge branch 'feature/#103-implement-version-security-checks' of http…
MattesMrzik Jan 23, 2024
0c54094
#103: fixed intellij updater test
MattesMrzik Jan 23, 2024
55f139c
#103: done with change requests
MattesMrzik Jan 24, 2024
d69bea7
Merge branch 'main' of https://www.github.com/devonfw/IDEasy into fea…
MattesMrzik Jan 25, 2024
f34fc22
Update security/src/main/java/com/devonfw/tools/security/BuildSecurit…
MattesMrzik Jan 25, 2024
1c66c77
#103: small fix
MattesMrzik Jan 25, 2024
c78aad4
Merge branch 'feature/#103-implement-version-security-checks' of http…
MattesMrzik Jan 25, 2024
6a20d3c
#103: added tests
MattesMrzik Jan 25, 2024
cbe086d
#103: test for UrlSecurityJson
MattesMrzik Jan 25, 2024
20fecc3
#103: last small changes
MattesMrzik Jan 26, 2024
47ae5b7
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Feb 19, 2024
ae52292
#103: code reformat & cleanup
jan-vcapgemini Feb 19, 2024
f66c7ea
#103: implemented requested changes
jan-vcapgemini Feb 19, 2024
7628cc9
#103: applied reformat
jan-vcapgemini Feb 19, 2024
db6e276
#103: implemented requested changes
jan-vcapgemini Feb 19, 2024
6da9066
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Feb 22, 2024
2862e6b
#103: fixed merge issues
jan-vcapgemini Feb 22, 2024
be3ec96
#103: some fixes
jan-vcapgemini Feb 22, 2024
097bbdc
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Feb 23, 2024
a7d686c
#103: implemented requested changes
jan-vcapgemini Feb 23, 2024
a299504
#103: implemented requested changes
jan-vcapgemini Feb 23, 2024
0f3596f
#103: implemented requested changes
jan-vcapgemini Feb 23, 2024
30d5bf2
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Feb 26, 2024
69e1fdd
#103: fixed intellij and vscode
jan-vcapgemini Feb 29, 2024
4d6766c
#103: fixed NPEs and other issues
jan-vcapgemini Feb 29, 2024
f162e09
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Feb 29, 2024
3834ce8
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Mar 28, 2024
ba4bc07
#103 fixed tests
jan-vcapgemini Apr 2, 2024
d794e67
Merge branch 'main' into feature/#103-implement-version-security-checks
jan-vcapgemini Apr 2, 2024
998387d
#103 implemented requested changes
jan-vcapgemini Apr 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,18 @@ 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see the reason for this change: It does set the path for a tool. If the path for that tool is already set, it will be updated/set to the new path value. The name addPath would in my opinion imply that the path is added (so calling this method with java as tool if the path for Java is already set it would be added so we have it twice in the path?), what is actually not the case. In most cases this method will actually update the path - only if we installed a tool initially that was not present before, we add a new entry to the path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey. Makes sense. I was talking to @jan-vcapgemini and we both came to the conclusion that it could be nice to only use the prefixes set and get with actual getters and setters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted, renamed back to get and set.


this.paths.add(path);
this.tool2pathMap.put(tool, path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ 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);
setVersion(selectedVersion, silent);
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ public Path getToolBinPath() {
protected boolean doInstall(boolean silent) {

VersionIdentifier configuredVersion = getConfiguredVersion();
VersionIdentifier selectedVersion = securityRiskInteraction(configuredVersion);
setVersion(selectedVersion, silent);
// install configured version of our tool in the software repository if not already installed
ToolInstallation installation = installInRepo(configuredVersion);

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();
Expand All @@ -79,7 +80,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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.devonfw.tools.ide.tool;

public enum SecurityRiskInteractionAnswer {

STAY,

LATEST_SAFE,

SAFE_LATEST,

NEXT_SAFE,

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc missing.

149 changes: 147 additions & 2 deletions cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
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;

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;
Expand All @@ -21,9 +26,13 @@
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.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.
*/
Expand Down Expand Up @@ -89,7 +98,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.
*/
Expand Down Expand Up @@ -171,6 +180,141 @@ public boolean install(boolean silent) {
return doInstall(silent);
}

/**
* 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) {
jan-vcapgemini marked this conversation as resolved.
Show resolved Hide resolved

UrlSecurityJsonFile securityFile = this.context.getUrls().getEdition(this.tool, this.getEdition())
.getSecurityJsonFile();

VersionIdentifier current = this.context.getUrls().getVersion(this.tool, this.getEdition(), configuredVersion);

if (!securityFile.contains(current, true, this.context, securityFile.getParent())) {
return configuredVersion;
}

List<VersionIdentifier> allVersions = this.context.getUrls().getSortedVersions(this.tool, this.getEdition());
VersionIdentifier latest = allVersions.get(0);

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 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?";

// enum id, option message, version that is returned if this option is selected
Map<SecurityRiskInteractionAnswer, Pair<String, VersionIdentifier>> 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(NEXT_SAFE, Pair.of("Install the next safe version (" + nextSafe + ").", nextSafe));

if (current.equals(latest)) {
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
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);
} 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);
} else {
return getAnswer(options, currentIsUnsafe + ask, STAY, NEXT_SAFE, LATEST_SAFE);
}
}
}

private VersionIdentifier getAnswer(Map<SecurityRiskInteractionAnswer, Pair<String, VersionIdentifier>> 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);
}

/**
* 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<VersionIdentifier> 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<VersionIdentifier> 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}.
*
Expand All @@ -181,7 +325,8 @@ public boolean install(boolean silent) {
protected abstract boolean doInstall(boolean silent);

/**
* This method is called after the tool has been newly installed or updated to a new version.
* This method is called after the tool has been newly installed or updated to a new version. Override it to add
* custom post installation logic.
*/
protected void postInstall() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ protected String getTool() {
return "android-studio";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
public void update(UrlRepository urlRepository) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ protected String getTool() {
return "aws";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current commit of this branch, there is no implementation in the abstract class. I thought we can use this to force that every UrlUpdater that inherits from AbstractUrlUpdater must implement this method to achieve that every UrlUpdater manages one and only one edition of a tool. Or did I understand something wrong in our discussion?

@Override
protected String getGithubOrganization() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ protected String getTool() {
return "az";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected void addVersion(UrlVersion urlVersion) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ protected String getTool() {
return "cobigen";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected String getMavenGroupIdPath() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ protected String getTool() {
return "docker";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected void addVersion(UrlVersion urlVersion) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ protected String getTool() {
return "dotnet";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected String getVersionPrefixToRemove() {

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected String getGithubRepository() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ protected String getTool() {
return "gcviewer";
}

@Override
protected String getEdition() {

return getTool();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Override
protected String getEdition() {
return getTool();
}

What is the point in overriding this method with the same implementation as the super-class (AbstractUrlUpdater)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted.

@Override
protected void addVersion(UrlVersion urlVersion) {

Expand Down
Loading
Loading