diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9788b4..ce80102 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,8 @@ caffeine = "3.1.8" commons-compress = "1.21" commons-io = "2.16.1" -trustify-da-api-spec = "2.0.2" -trustify-da-java-client = "0.0.15" +trustify-da-api-spec = "2.0.10-SNAPSHOT" +trustify-da-java-client = "0.0.19-SNAPSHOT" github-api = "1.314" junit = "4.13.2" mockito = "4.11.0" diff --git a/src/main/java/org/jboss/tools/intellij/image/DockerfileAnnotator.java b/src/main/java/org/jboss/tools/intellij/image/DockerfileAnnotator.java index a333063..cb195be 100644 --- a/src/main/java/org/jboss/tools/intellij/image/DockerfileAnnotator.java +++ b/src/main/java/org/jboss/tools/intellij/image/DockerfileAnnotator.java @@ -28,6 +28,7 @@ import io.github.guacsec.trustifyda.api.v5.AnalysisReport; import io.github.guacsec.trustifyda.api.v5.DependencyReport; import io.github.guacsec.trustifyda.api.v5.ProviderReport; +import io.github.guacsec.trustifyda.api.v5.RecommendationReport; import io.github.guacsec.trustifyda.api.v5.Severity; import io.github.guacsec.trustifyda.api.v5.Source; import io.github.guacsec.trustifyda.image.ImageRef; @@ -93,7 +94,7 @@ static boolean isReportAvailable(AnalysisReport report) { .orElse(false); } - static String generateMessage(String image, AnalysisReport report, String recommendation) { + static String generateMessage(String image, AnalysisReport report, RecommendationInfo recommendation) { var messageBuilder = new StringBuilder(image); Optional.ofNullable(report.getProviders()) @@ -131,15 +132,19 @@ static String generateMessage(String image, AnalysisReport report, String recomm })); if (recommendation != null) { - messageBuilder.append(System.lineSeparator()) - .append("Replace your image with RedHat UBI: ") - .append(recommendation); + messageBuilder.append(System.lineSeparator()); + if (recommendation.isHardened()) { + messageBuilder.append("Switch to hardened image ").append(recommendation.imageRef()); + } else { + messageBuilder.append("Switch to Red Hat UBI ").append(recommendation.imageRef()) + .append(" for enhanced security and enterprise-grade stability"); + } } return messageBuilder.toString(); } - static String generateTooltip(String image, AnalysisReport report, String recommendation) { + static String generateTooltip(String image, AnalysisReport report, RecommendationInfo recommendation) { var tooltipBuilder = new StringBuilder("").append("
").append(image).append("
"); Optional.ofNullable(report.getProviders()) @@ -181,10 +186,16 @@ static String generateTooltip(String image, AnalysisReport report, String recomm })); if (recommendation != null) { - tooltipBuilder.append("") - .append("Replace your image with RedHat UBI: ") - .append(recommendation) - .append("
"); + tooltipBuilder.append(""); + if (recommendation.isHardened()) { + tooltipBuilder.append("Switch to hardened image ") + .append(recommendation.imageRef()) + .append("
"); + } else { + tooltipBuilder.append("Switch to Red Hat UBI ") + .append(recommendation.imageRef()) + .append(" for enhanced security and enterprise-grade stability
"); + } } return tooltipBuilder.toString(); @@ -208,7 +219,55 @@ static boolean hasIssue(AnalysisReport report) { .orElse(false); } - static String getRecommendation(AnalysisReport report, ImageRef imageRef) { + record RecommendationInfo(String imageRef, String sourceId) { + boolean isHardened() { + return "hardened".equals(sourceId); + } + } + + static String extractImageRefFromPurl(com.github.packageurl.PackageURL purl) { + var qualifiers = purl.getQualifiers(); + if (qualifiers == null) { + return null; + } + String repoUrl = qualifiers.get("repository_url"); + if (repoUrl == null || repoUrl.isBlank()) { + return null; + } + String tag = qualifiers.get("tag"); + if (tag != null && !tag.isBlank()) { + return repoUrl + ":" + tag; + } + return repoUrl; + } + + static RecommendationInfo getRecommendation(AnalysisReport report, ImageRef imageRef) { + // Check recommendations section first (Hummingbird hardened images, trusted-content) + var fromRecommendations = Optional.ofNullable(report.getProviders()) + .flatMap(providers -> providers.values().stream() + .filter(Objects::nonNull) + .filter(p -> p.getRecommendations() != null) + .flatMap(p -> p.getRecommendations().entrySet().stream()) + .flatMap(entry -> { + var sourceName = entry.getKey(); + var recSource = entry.getValue(); + if (recSource.getDependencies() == null) { + return java.util.stream.Stream.