From 5f062e34b7a7ac2bd637f1c332b0025e499d35c1 Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Thu, 12 Jun 2025 18:54:38 +0300 Subject: [PATCH 1/8] added WebviewObjectConverter --- .../ui/webview/WebviewObjectConverter.java | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java diff --git a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java new file mode 100644 index 0000000..4645e59 --- /dev/null +++ b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java @@ -0,0 +1,167 @@ +package com.jfrog.ide.eclipse.ui.webview; + +import com.jfrog.ide.common.nodes.*; +import com.jfrog.ide.common.nodes.subentities.*; +import com.jfrog.ide.common.parse.Applicability; +import com.jfrog.ide.common.webview.ApplicableDetails; +import com.jfrog.ide.common.webview.Cve; +import com.jfrog.ide.common.webview.DependencyPage; +import com.jfrog.ide.common.webview.ImpactGraph; +import com.jfrog.ide.common.webview.ImpactGraphNode; +import com.jfrog.ide.common.webview.IssuePage; +import com.jfrog.ide.common.webview.Location; +import com.jfrog.ide.common.webview.SastIssuePage; + +import java.nio.file.Paths; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +public class WebviewObjectConverter { + public static final int IMPACT_PATHS_LIMIT = 20; + public static DependencyPage convertScaIssueToDepPage(ScaIssueNode scaNode) { + return new DependencyPage() + .cve(new Cve(scaNode.getTitle(), null, null, null, null, new ApplicableDetails(Applicability.getWebviewIconName(scaNode.getApplicability()), null, null))) + .component(scaNode.getComponentName()) + .version(scaNode.getComponentVersion()) + .severity(scaNode.getSeverity().getSeverityName()) + .fixedVersion(scaNode.getFixedVersions()) + .summary(scaNode.getFullDescription()) + .impactGraph(toImpactGraph(scaNode.getImpactPaths())); + } + + public static IssuePage convertFileIssueToIssuePage(FileIssueNode fileIssueNode) { + return new IssuePage() + .header(fileIssueNode.getTitle()) + .type(ConvertPageType(fileIssueNode.getReporterType())) + .severity(fileIssueNode.getSeverity().name()) + .description(fileIssueNode.getFullDescription()) + .location(convertFileLocation(fileIssueNode)); + } + + public static IssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIssueNode) { + return new SastIssuePage(convertFileIssueToIssuePage(sastIssueNode)) + .setAnalysisSteps(convertCodeFlowsToLocations(sastIssueNode.getCodeFlows())) + .setRuleID(sastIssueNode.getRuleId()); + } + + private static Location[] convertCodeFlowsToLocations(FindingInfo[][] codeFlows) { + if (codeFlows != null && codeFlows.length > 0) { + Location[] locations = new Location[codeFlows[0].length]; + for (int i = 0; i < codeFlows[0].length; i++) { + FindingInfo codeFlow = codeFlows[0][i]; + locations[i] = new Location( + codeFlow.getFilePath(), + Paths.get(codeFlow.getFilePath()).getFileName().toString(), + codeFlow.getRowStart(), + codeFlow.getColStart(), + codeFlow.getRowEnd(), + codeFlow.getColEnd(), + codeFlow.getLineSnippet()); + } + return locations; + } + return null; + } + + private static String ConvertPageType(SourceCodeScanType reporterType) { + return switch (reporterType) { + case SECRETS -> "SECRETS"; + case IAC -> "IAC"; + case SAST -> "SAST"; + default -> "EMPTY"; + }; + } + + private static Location convertFileLocation(FileIssueNode fileIssueNodeNode) { + return new Location( + fileIssueNodeNode.getFilePath(), + Paths.get(fileIssueNodeNode.getFilePath()).getFileName().toString(), + fileIssueNodeNode.getRowStart() + 1, + fileIssueNodeNode.getColStart() + 1, + fileIssueNodeNode.getRowEnd() + 1, + fileIssueNodeNode.getColEnd() + 1, + fileIssueNodeNode.getLineSnippet()); + } + + /** + * Converts a list of impact paths to an ImpactGraph. + * Each path is a list of ImpactPath objects, representing a path from root to leaf. + * Node names are "name:version" (or just "name" if version is empty). + */ +// public static ImpactGraph toImpactGraph(List> impactPaths) { +// // Use a dummy root node +// ImpactTreeNode root = new ImpactTreeNode("root"); +// int maxDepth = 0; +// for (List path : impactPaths) { +// ImpactTreeNode current = root; +// int depth = 0; +// for (ImpactPath ip : path) { +// String nodeName = ip.getName() + (ip.getVersion() != null && !ip.getVersion().isEmpty() ? ":" + ip.getVersion() : ""); +// current.getChildren().add(new ImpactTreeNode(nodeName)); +// depth++; +// } +// if (depth > maxDepth) { +// maxDepth = depth; +// } +// } +// ImpactGraphNode rootGraphNode = toImpactGraphNode(root); +// // +1 to include root +// maxDepth += 1; +// // Set pathsLimit to the IMPACT_PATHS_LIMIT if maximum depth found exceeding the limit +// return new ImpactGraph(rootGraphNode, maxDepth > IMPACT_PATHS_LIMIT ? IMPACT_PATHS_LIMIT : -1 ); +// } TODO: improve implementation + + public static ImpactGraph toImpactGraph(List> impactPaths) { + if (impactPaths == null || impactPaths.isEmpty()) { + return new ImpactGraph(new ImpactGraphNode("", new ImpactGraphNode[0]), 0); + } + // Use the first element in each path as the root for that path + Map rootMap = new LinkedHashMap<>(); + int maxDepth = 0; + for (List path : impactPaths) { + if (path == null || path.isEmpty()) { + continue; + } + + String rootName = getNodeName(path.get(0)); + ImpactTreeNode root = rootMap.computeIfAbsent(rootName, ImpactTreeNode::new); + ImpactTreeNode current = root; + int depth = 1; + for (int i = 1; i < path.size(); i++) { + String nodeName = getNodeName(path.get(i)); + current = getOrAddChild(current, nodeName); + depth++; + } + if (depth > maxDepth) { + maxDepth = depth; + } + } + // Always use the first root in the map as the ImpactGraph root + ImpactGraphNode rootGraphNode = toImpactGraphNode(rootMap.values().iterator().next()); + return new ImpactGraph(rootGraphNode, maxDepth); + } + + private static String getNodeName(ImpactPath ip) { + return ip.getName() + (ip.getVersion() != null && !ip.getVersion().isEmpty() ? ":" + ip.getVersion() : ""); + } + + // Helper to find or add a child node by name + private static ImpactTreeNode getOrAddChild(ImpactTreeNode parent, String nodeName) { + for (ImpactTreeNode child : parent.getChildren()) { + if (child.getName().equals(nodeName)) { + return child; + } + } + ImpactTreeNode newChild = new ImpactTreeNode(nodeName); + parent.getChildren().add(newChild); + return newChild; + } + + // Convert ImpactTreeNode to ImpactGraphNode tree + private static ImpactGraphNode toImpactGraphNode(ImpactTreeNode impactTreeNode) { + ImpactGraphNode[] children = impactTreeNode.getChildren().stream().map(WebviewObjectConverter::toImpactGraphNode).toArray(ImpactGraphNode[]::new); + return new ImpactGraphNode(impactTreeNode.getName(), children); + } +} From 2d5d5f7911129cae53df5804ec36dedf43553837 Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Sun, 15 Jun 2025 15:57:38 +0300 Subject: [PATCH 2/8] improve imapctPath to impactGraph convert implementation --- .../ui/webview/WebviewObjectConverter.java | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java index 4645e59..728a474 100644 --- a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java +++ b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java @@ -90,28 +90,6 @@ private static Location convertFileLocation(FileIssueNode fileIssueNodeNode) { * Each path is a list of ImpactPath objects, representing a path from root to leaf. * Node names are "name:version" (or just "name" if version is empty). */ -// public static ImpactGraph toImpactGraph(List> impactPaths) { -// // Use a dummy root node -// ImpactTreeNode root = new ImpactTreeNode("root"); -// int maxDepth = 0; -// for (List path : impactPaths) { -// ImpactTreeNode current = root; -// int depth = 0; -// for (ImpactPath ip : path) { -// String nodeName = ip.getName() + (ip.getVersion() != null && !ip.getVersion().isEmpty() ? ":" + ip.getVersion() : ""); -// current.getChildren().add(new ImpactTreeNode(nodeName)); -// depth++; -// } -// if (depth > maxDepth) { -// maxDepth = depth; -// } -// } -// ImpactGraphNode rootGraphNode = toImpactGraphNode(root); -// // +1 to include root -// maxDepth += 1; -// // Set pathsLimit to the IMPACT_PATHS_LIMIT if maximum depth found exceeding the limit -// return new ImpactGraph(rootGraphNode, maxDepth > IMPACT_PATHS_LIMIT ? IMPACT_PATHS_LIMIT : -1 ); -// } TODO: improve implementation public static ImpactGraph toImpactGraph(List> impactPaths) { if (impactPaths == null || impactPaths.isEmpty()) { @@ -119,28 +97,32 @@ public static ImpactGraph toImpactGraph(List> impactPaths) { } // Use the first element in each path as the root for that path Map rootMap = new LinkedHashMap<>(); - int maxDepth = 0; - for (List path : impactPaths) { - if (path == null || path.isEmpty()) { + boolean isMaxLimitExceeded; + int pathsNumber = impactPaths.size(); + int pathIndex = 0; + + for (; pathIndex < pathsNumber && pathIndex < IMPACT_PATHS_LIMIT; pathIndex++) { + List currentPath = impactPaths.get(pathIndex); + if (currentPath == null || currentPath.isEmpty()) { continue; } - String rootName = getNodeName(path.get(0)); + String rootName = getNodeName(currentPath.get(0)); ImpactTreeNode root = rootMap.computeIfAbsent(rootName, ImpactTreeNode::new); - ImpactTreeNode current = root; - int depth = 1; - for (int i = 1; i < path.size(); i++) { - String nodeName = getNodeName(path.get(i)); - current = getOrAddChild(current, nodeName); - depth++; - } - if (depth > maxDepth) { - maxDepth = depth; + ImpactTreeNode currentNode = root; + int currentPathSize = currentPath.size(); + + for (int nodeIndex = 1; nodeIndex < currentPathSize; nodeIndex++) { + String nodeName = getNodeName(currentPath.get(nodeIndex)); + currentNode = getOrAddChild(currentNode, nodeName); } } - // Always use the first root in the map as the ImpactGraph root + + isMaxLimitExceeded = pathIndex >= IMPACT_PATHS_LIMIT ? true : false; + ImpactGraphNode rootGraphNode = toImpactGraphNode(rootMap.values().iterator().next()); - return new ImpactGraph(rootGraphNode, maxDepth); + // pass value for pathsLimit only if exceeded the defined IMPACT_PATHS_LIMIT, so a corresponding message will appear in the WebView UI + return new ImpactGraph(rootGraphNode, isMaxLimitExceeded ? IMPACT_PATHS_LIMIT : -1); } private static String getNodeName(ImpactPath ip) { From d2b544d383484dbdecea8dcea994121400644edd Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Mon, 16 Jun 2025 12:03:29 +0300 Subject: [PATCH 3/8] added tests --- bundle/.classpath | 8 +- bundle/META-INF/MANIFEST.MF | 7 +- .../webview/WebviewObjectConverterTest.java | 198 ++++++++++++++++++ 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java diff --git a/bundle/.classpath b/bundle/.classpath index 6187908..065de69 100644 --- a/bundle/.classpath +++ b/bundle/.classpath @@ -2,6 +2,7 @@ + @@ -11,6 +12,11 @@ - + + + + + + diff --git a/bundle/META-INF/MANIFEST.MF b/bundle/META-INF/MANIFEST.MF index 0ade212..5c27dca 100644 --- a/bundle/META-INF/MANIFEST.MF +++ b/bundle/META-INF/MANIFEST.MF @@ -22,7 +22,11 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.114.0", Bundle-ClassPath: ., lib/ide-plugins-common.jar Bundle-Vendor: JFrog -Export-Package: com.jfrog.ide.eclipse.configuration, +Export-Package: com.jfrog.ide.common.nodes, + com.jfrog.ide.common.nodes.subentities, + com.jfrog.ide.common.parse, + com.jfrog.ide.common.webview, + com.jfrog.ide.eclipse.configuration, com.jfrog.ide.eclipse.log, com.jfrog.ide.eclipse.npm, com.jfrog.ide.eclipse.scan, @@ -30,4 +34,5 @@ Export-Package: com.jfrog.ide.eclipse.configuration, com.jfrog.ide.eclipse.ui, com.jfrog.ide.eclipse.ui.actions, com.jfrog.ide.eclipse.ui.issues, + com.jfrog.ide.eclipse.ui.webview, com.jfrog.ide.eclipse.utils diff --git a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java new file mode 100644 index 0000000..726eba5 --- /dev/null +++ b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java @@ -0,0 +1,198 @@ +package com.jfrog.ide.eclipse.ui.webview; + +import com.jfrog.ide.common.nodes.FileIssueNode; +import com.jfrog.ide.common.nodes.ScaIssueNode; +import com.jfrog.ide.common.nodes.SastIssueNode; +import com.jfrog.ide.common.nodes.subentities.ImpactPath; +import com.jfrog.ide.common.nodes.subentities.Severity; +import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; +import com.jfrog.ide.common.parse.Applicability; +import com.jfrog.ide.common.webview.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +public class WebviewObjectConverterTest { + + private ScaIssueNode scaIssueNode; + private FileIssueNode secretIssueNode; + private SastIssueNode sastIssueNode; + + @Before + void setUp() { + // Setup SCA test data + String scaTitle = "CVE-2023-1234"; + String reason = "sca issue reason"; + Severity severity = Severity.High; + String ruleID = "CVE-2023-1234_test-component_1.0.0"; + Applicability applicability = Applicability.APPLICABLE; + List> impactPaths = createTestImpactPaths(); + String[] fixedVersions = {"[1.0.1]", "[1.0.2]"}; + String fullDescription = "Test vulnerability description"; + + scaIssueNode = new ScaIssueNode(scaTitle, reason, severity, ruleID, applicability, impactPaths, fixedVersions, fullDescription); + + // setup common data + String filePath = "/test/path/file.java"; + int rowStart = 10; + int colStart = 5; + int rowEnd = 20; + int colEnd = 10; + String lineSnippet = "vulnerable code line"; + + // setup secrets test data + String secretTitle = "Secret issue"; + String secretReason = "Hard coded secrets were found"; + Severity secretSeverity = Severity.Medium; + String secretRuleId = "SECRET-RULE"; + String secretFullDescription = "Test Secret issue description"; + + secretIssueNode = new FileIssueNode(secretTitle, filePath, rowStart, colStart, rowEnd, colEnd, secretReason, lineSnippet, SourceCodeScanType.SECRETS, secretSeverity, secretFullDescription); + + + // setup sast test data + String sastTitle = "SAST Issue"; + String sastReason = "SAST issue reason"; + Severity sastSeverity = Severity.Critical; + String sastRuleId = "SAST-RULE"; + String sastFullDescription = "Test SAST issue description"; + + sastIssueNode = new SastIssueNode(sastTitle, filePath, rowStart, colStart, rowEnd, colEnd, sastReason, lineSnippet, null, sastSeverity, sastRuleId, sastFullDescription); + } + + @Test + void testConvertScaIssueToDepPage() { + DependencyPage result = WebviewObjectConverter.convertScaIssueToDepPage(scaIssueNode); + + assertNotNull(result); + assertEquals(scaIssueNode.getComponentName(), result.getComponent()); + assertEquals(scaIssueNode.getComponentVersion(), result.getVersion()); + assertEquals(scaIssueNode.getSeverity().getSeverityName(), result.getSeverity()); + assertEquals(scaIssueNode.getFullDescription(), result.getSummary()); + assertEquals(scaIssueNode.getFixedVersions(), result.getFixedVersion()); + assertNotNull(result.getCve()); + assertEquals(scaIssueNode.getTitle(), result.getCve().getId()); + } + + @Test + void testConvertFileIssueToIssuePage() { + IssuePage result = WebviewObjectConverter.convertFileIssueToIssuePage(secretIssueNode); + + assertNotNull(result); + assertEquals(secretIssueNode.getTitle(), result.getHeader()); + assertEquals(secretIssueNode.getSeverity().name(), result.getSeverity()); + assertEquals(secretIssueNode.getFullDescription(), result.getDescription()); + assertNotNull(result.getLocation()); + assertEquals(secretIssueNode.getFilePath(), result.getLocation().getFile()); + assertEquals(secretIssueNode.getRowStart() + 1, result.getLocation().getStartRow()); + assertEquals(secretIssueNode.getColStart() + 1, result.getLocation().getStartColumn()); + } + + @Test + void testConvertSastIssueToSastIssuePage() { + IssuePage result = WebviewObjectConverter.convertSastIssueToSastIssuePage(sastIssueNode); + + assertNotNull(result); + assertTrue(result instanceof SastIssuePage); + SastIssuePage sastResult = (SastIssuePage) result; + assertEquals(sastIssueNode.getTitle(), sastResult.getHeader()); + assertEquals(sastIssueNode.getRuleId(), sastResult.getRuleId()); + assertEquals(sastIssueNode.getSeverity().name(), sastResult.getSeverity()); + assertEquals(sastIssueNode.getFullDescription(), sastResult.getDescription()); + } + + @Test + void testToImpactGraph_EmptyInput() { + ImpactGraph result = WebviewObjectConverter.toImpactGraph(null); + + assertNotNull(result); + assertNotNull(result.getRoot()); + assertEquals("", result.getRoot().getName()); + assertEquals(0, result.getRoot().getChildren().length); + } + + @Test + void testToImpactGraph_SinglePath() { + List> impactPaths = new ArrayList<>(); + List path = new ArrayList<>(); + path.add(new ImpactPath("root", "1.0")); + path.add(new ImpactPath("child1", "2.0")); + path.add(new ImpactPath("child2", "3.0")); + impactPaths.add(path); + + ImpactGraph result = WebviewObjectConverter.toImpactGraph(impactPaths); + + assertNotNull(result); + assertNotNull(result.getRoot()); + assertEquals("root:1.0", result.getRoot().getName()); + assertEquals(1, result.getRoot().getChildren().length); + assertEquals("child1:2.0", result.getRoot().getChildren()[0].getName()); + assertEquals(1, result.getRoot().getChildren()[0].getChildren().length); + assertEquals("child2:3.0", result.getRoot().getChildren()[0].getChildren()[0].getName()); + } + + @Test + void testToImpactGraph_MultiplePaths() { + List> impactPaths = new ArrayList<>(); + + // First path + List path1 = new ArrayList<>(); + path1.add(new ImpactPath("root", "1.0")); + path1.add(new ImpactPath("child1", "2.0")); + impactPaths.add(path1); + + // Second path + List path2 = new ArrayList<>(); + path2.add(new ImpactPath("root", "1.0")); + path2.add(new ImpactPath("child2", "3.0")); + impactPaths.add(path2); + + ImpactGraph result = WebviewObjectConverter.toImpactGraph(impactPaths); + + assertNotNull(result); + assertNotNull(result.getRoot()); + assertEquals("root:1.0", result.getRoot().getName()); + assertEquals(2, result.getRoot().getChildren().length); + + // Verify both children exist + boolean hasChild1 = false; + boolean hasChild2 = false; + for (ImpactGraphNode child : result.getRoot().getChildren()) { + if (child.getName().equals("child1:2.0")) hasChild1 = true; + if (child.getName().equals("child2:3.0")) hasChild2 = true; + } + assertTrue(hasChild1 && hasChild2); + } + + @Test + void testToImpactGraph_ExceedsLimit() { + List> impactPaths = new ArrayList<>(); + for (int i = 0; i < WebviewObjectConverter.IMPACT_PATHS_LIMIT + 5; i++) { + List path = new ArrayList<>(); + path.add(new ImpactPath("root" + i, "1.0")); + path.add(new ImpactPath("child" + i, "2.0")); + impactPaths.add(path); + } + + ImpactGraph result = WebviewObjectConverter.toImpactGraph(impactPaths); + + assertNotNull(result); + assertEquals(WebviewObjectConverter.IMPACT_PATHS_LIMIT, result.getPathsLimit()); + } + + private List> createTestImpactPaths() { + List> impactPaths = new ArrayList<>(); + List path = new ArrayList<>(); + path.add(new ImpactPath("root", "1.0")); + path.add(new ImpactPath("child1", "2.0")); + path.add(new ImpactPath("child2", "3.0")); + impactPaths.add(path); + return impactPaths; + } +} \ No newline at end of file From f0355ace49a4d16241f783fe56318458a30fc17f Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Mon, 16 Jun 2025 12:26:22 +0300 Subject: [PATCH 4/8] rearrange tests --- .../webview/WebviewObjectConverterTest.java | 90 ++++++++----------- 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java index 726eba5..3653f7d 100644 --- a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java +++ b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java @@ -8,25 +8,27 @@ import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.common.parse.Applicability; import com.jfrog.ide.common.webview.*; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import junit.framework.TestCase; import java.util.ArrayList; import java.util.List; -public class WebviewObjectConverterTest { +public class WebviewObjectConverterTest extends TestCase{ private ScaIssueNode scaIssueNode; private FileIssueNode secretIssueNode; private SastIssueNode sastIssueNode; - - @Before - void setUp() { - // Setup SCA test data + + // setup common data + String filePath = "/test/path/file.java"; + int rowStart = 10; + int colStart = 5; + int rowEnd = 20; + int colEnd = 10; + String lineSnippet = "vulnerable code line"; + + public void testConvertScaIssueToDepPage() { + // Setup SCA test data String scaTitle = "CVE-2023-1234"; String reason = "sca issue reason"; Severity severity = Severity.High; @@ -37,37 +39,6 @@ void setUp() { String fullDescription = "Test vulnerability description"; scaIssueNode = new ScaIssueNode(scaTitle, reason, severity, ruleID, applicability, impactPaths, fixedVersions, fullDescription); - - // setup common data - String filePath = "/test/path/file.java"; - int rowStart = 10; - int colStart = 5; - int rowEnd = 20; - int colEnd = 10; - String lineSnippet = "vulnerable code line"; - - // setup secrets test data - String secretTitle = "Secret issue"; - String secretReason = "Hard coded secrets were found"; - Severity secretSeverity = Severity.Medium; - String secretRuleId = "SECRET-RULE"; - String secretFullDescription = "Test Secret issue description"; - - secretIssueNode = new FileIssueNode(secretTitle, filePath, rowStart, colStart, rowEnd, colEnd, secretReason, lineSnippet, SourceCodeScanType.SECRETS, secretSeverity, secretFullDescription); - - - // setup sast test data - String sastTitle = "SAST Issue"; - String sastReason = "SAST issue reason"; - Severity sastSeverity = Severity.Critical; - String sastRuleId = "SAST-RULE"; - String sastFullDescription = "Test SAST issue description"; - - sastIssueNode = new SastIssueNode(sastTitle, filePath, rowStart, colStart, rowEnd, colEnd, sastReason, lineSnippet, null, sastSeverity, sastRuleId, sastFullDescription); - } - - @Test - void testConvertScaIssueToDepPage() { DependencyPage result = WebviewObjectConverter.convertScaIssueToDepPage(scaIssueNode); assertNotNull(result); @@ -80,8 +51,15 @@ void testConvertScaIssueToDepPage() { assertEquals(scaIssueNode.getTitle(), result.getCve().getId()); } - @Test - void testConvertFileIssueToIssuePage() { + public void testConvertFileIssueToIssuePage() { + // setup secrets test data + String secretTitle = "Secret issue"; + String secretReason = "Hard coded secrets were found"; + Severity secretSeverity = Severity.Medium; + String secretRuleId = "SECRET-RULE"; + String secretFullDescription = "Test Secret issue description"; + + secretIssueNode = new FileIssueNode(secretTitle, filePath, rowStart, colStart, rowEnd, colEnd, secretReason, lineSnippet, SourceCodeScanType.SECRETS, secretSeverity, secretRuleId, secretFullDescription); IssuePage result = WebviewObjectConverter.convertFileIssueToIssuePage(secretIssueNode); assertNotNull(result); @@ -94,8 +72,16 @@ void testConvertFileIssueToIssuePage() { assertEquals(secretIssueNode.getColStart() + 1, result.getLocation().getStartColumn()); } - @Test - void testConvertSastIssueToSastIssuePage() { + public void testConvertSastIssueToSastIssuePage() { + // setup SAST test data + String sastTitle = "SAST Issue"; + String sastReason = "SAST issue reason"; + Severity sastSeverity = Severity.Critical; + String sastRuleId = "SAST-RULE"; + String sastFullDescription = "Test SAST issue description"; + + sastIssueNode = new SastIssueNode(sastTitle, filePath, rowStart, colStart, rowEnd, colEnd, sastReason, lineSnippet, null, sastSeverity, sastRuleId, sastFullDescription); + IssuePage result = WebviewObjectConverter.convertSastIssueToSastIssuePage(sastIssueNode); assertNotNull(result); @@ -107,8 +93,7 @@ void testConvertSastIssueToSastIssuePage() { assertEquals(sastIssueNode.getFullDescription(), sastResult.getDescription()); } - @Test - void testToImpactGraph_EmptyInput() { + public void testToImpactGraph_EmptyInput() { ImpactGraph result = WebviewObjectConverter.toImpactGraph(null); assertNotNull(result); @@ -117,8 +102,7 @@ void testToImpactGraph_EmptyInput() { assertEquals(0, result.getRoot().getChildren().length); } - @Test - void testToImpactGraph_SinglePath() { + public void testToImpactGraph_SinglePath() { List> impactPaths = new ArrayList<>(); List path = new ArrayList<>(); path.add(new ImpactPath("root", "1.0")); @@ -137,8 +121,7 @@ void testToImpactGraph_SinglePath() { assertEquals("child2:3.0", result.getRoot().getChildren()[0].getChildren()[0].getName()); } - @Test - void testToImpactGraph_MultiplePaths() { + public void testToImpactGraph_MultiplePaths() { List> impactPaths = new ArrayList<>(); // First path @@ -170,8 +153,7 @@ void testToImpactGraph_MultiplePaths() { assertTrue(hasChild1 && hasChild2); } - @Test - void testToImpactGraph_ExceedsLimit() { + public void testToImpactGraph_ExceedsLimit() { List> impactPaths = new ArrayList<>(); for (int i = 0; i < WebviewObjectConverter.IMPACT_PATHS_LIMIT + 5; i++) { List path = new ArrayList<>(); From e6762d6370bf69146e854d7a3d5e68dfcea86856 Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Mon, 16 Jun 2025 12:34:01 +0300 Subject: [PATCH 5/8] fix --- bundle/.classpath | 7 +------ bundle/META-INF/MANIFEST.MF | 5 +---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/bundle/.classpath b/bundle/.classpath index 065de69..1442c33 100644 --- a/bundle/.classpath +++ b/bundle/.classpath @@ -12,11 +12,6 @@ - - - - - - + diff --git a/bundle/META-INF/MANIFEST.MF b/bundle/META-INF/MANIFEST.MF index 5c27dca..cedd5d2 100644 --- a/bundle/META-INF/MANIFEST.MF +++ b/bundle/META-INF/MANIFEST.MF @@ -22,10 +22,7 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.114.0", Bundle-ClassPath: ., lib/ide-plugins-common.jar Bundle-Vendor: JFrog -Export-Package: com.jfrog.ide.common.nodes, - com.jfrog.ide.common.nodes.subentities, - com.jfrog.ide.common.parse, - com.jfrog.ide.common.webview, +Export-Package: com.jfrog.ide.eclipse.configuration, com.jfrog.ide.eclipse.log, com.jfrog.ide.eclipse.npm, From f5578d72dbef4e6354d5095fe42b041cc4a9f6ba Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Mon, 16 Jun 2025 12:35:17 +0300 Subject: [PATCH 6/8] minor fix --- bundle/.classpath | 1 - bundle/META-INF/MANIFEST.MF | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bundle/.classpath b/bundle/.classpath index 1442c33..6187908 100644 --- a/bundle/.classpath +++ b/bundle/.classpath @@ -2,7 +2,6 @@ - diff --git a/bundle/META-INF/MANIFEST.MF b/bundle/META-INF/MANIFEST.MF index cedd5d2..6d890b7 100644 --- a/bundle/META-INF/MANIFEST.MF +++ b/bundle/META-INF/MANIFEST.MF @@ -22,8 +22,7 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.114.0", Bundle-ClassPath: ., lib/ide-plugins-common.jar Bundle-Vendor: JFrog -Export-Package: - com.jfrog.ide.eclipse.configuration, +Export-Package: com.jfrog.ide.eclipse.configuration, com.jfrog.ide.eclipse.log, com.jfrog.ide.eclipse.npm, com.jfrog.ide.eclipse.scan, From ffcf0439be343ad5bdb134c59a12136a4a96b5a4 Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Mon, 16 Jun 2025 15:42:28 +0300 Subject: [PATCH 7/8] fix type of sast issue page --- .../eclipse/ui/webview/WebviewObjectConverter.java | 2 +- .../ui/webview/WebviewObjectConverterTest.java | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java index 728a474..b7e9d3b 100644 --- a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java +++ b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java @@ -40,7 +40,7 @@ public static IssuePage convertFileIssueToIssuePage(FileIssueNode fileIssueNode) .location(convertFileLocation(fileIssueNode)); } - public static IssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIssueNode) { + public static SastIssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIssueNode) { return new SastIssuePage(convertFileIssueToIssuePage(sastIssueNode)) .setAnalysisSteps(convertCodeFlowsToLocations(sastIssueNode.getCodeFlows())) .setRuleID(sastIssueNode.getRuleId()); diff --git a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java index 3653f7d..71327b5 100644 --- a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java +++ b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java @@ -82,15 +82,13 @@ public void testConvertSastIssueToSastIssuePage() { sastIssueNode = new SastIssueNode(sastTitle, filePath, rowStart, colStart, rowEnd, colEnd, sastReason, lineSnippet, null, sastSeverity, sastRuleId, sastFullDescription); - IssuePage result = WebviewObjectConverter.convertSastIssueToSastIssuePage(sastIssueNode); + SastIssuePage result = WebviewObjectConverter.convertSastIssueToSastIssuePage(sastIssueNode); assertNotNull(result); - assertTrue(result instanceof SastIssuePage); - SastIssuePage sastResult = (SastIssuePage) result; - assertEquals(sastIssueNode.getTitle(), sastResult.getHeader()); - assertEquals(sastIssueNode.getRuleId(), sastResult.getRuleId()); - assertEquals(sastIssueNode.getSeverity().name(), sastResult.getSeverity()); - assertEquals(sastIssueNode.getFullDescription(), sastResult.getDescription()); + assertEquals(sastIssueNode.getTitle(), result.getHeader()); + assertEquals(sastIssueNode.getRuleId(), result.getRuleId()); + assertEquals(sastIssueNode.getSeverity().name(), result.getSeverity()); + assertEquals(sastIssueNode.getFullDescription(), result.getDescription()); } public void testToImpactGraph_EmptyInput() { From a9519c0686b8fabe70ff1b13a89f0e3d51fca0e7 Mon Sep 17 00:00:00 2001 From: kerenr-jfrog Date: Tue, 17 Jun 2025 13:55:12 +0300 Subject: [PATCH 8/8] fix CR comments --- .../ide/eclipse/ui/webview/WebviewObjectConverter.java | 9 +++++---- .../eclipse/ui/webview/WebviewObjectConverterTest.java | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java index b7e9d3b..45d6b9f 100644 --- a/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java +++ b/bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java @@ -48,8 +48,10 @@ public static SastIssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIs private static Location[] convertCodeFlowsToLocations(FindingInfo[][] codeFlows) { if (codeFlows != null && codeFlows.length > 0) { - Location[] locations = new Location[codeFlows[0].length]; - for (int i = 0; i < codeFlows[0].length; i++) { + FindingInfo[] codeFlowList = codeFlows[0]; + int codeFlowListSize = codeFlowList.length; + Location[] locations = new Location[codeFlowListSize]; + for (int i = 0; i < codeFlowListSize; i++) { FindingInfo codeFlow = codeFlows[0][i]; locations[i] = new Location( codeFlow.getFilePath(), @@ -70,7 +72,7 @@ private static String ConvertPageType(SourceCodeScanType reporterType) { case SECRETS -> "SECRETS"; case IAC -> "IAC"; case SAST -> "SAST"; - default -> "EMPTY"; + default -> "EMPTY"; // any other value passed will result the load of the default WebView page }; } @@ -90,7 +92,6 @@ private static Location convertFileLocation(FileIssueNode fileIssueNodeNode) { * Each path is a list of ImpactPath objects, representing a path from root to leaf. * Node names are "name:version" (or just "name" if version is empty). */ - public static ImpactGraph toImpactGraph(List> impactPaths) { if (impactPaths == null || impactPaths.isEmpty()) { return new ImpactGraph(new ImpactGraphNode("", new ImpactGraphNode[0]), 0); diff --git a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java index 71327b5..fda80e6 100644 --- a/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java +++ b/tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java @@ -109,7 +109,7 @@ public void testToImpactGraph_SinglePath() { impactPaths.add(path); ImpactGraph result = WebviewObjectConverter.toImpactGraph(impactPaths); - + assertNotNull(result); assertNotNull(result.getRoot()); assertEquals("root:1.0", result.getRoot().getName()); @@ -117,6 +117,8 @@ public void testToImpactGraph_SinglePath() { assertEquals("child1:2.0", result.getRoot().getChildren()[0].getName()); assertEquals(1, result.getRoot().getChildren()[0].getChildren().length); assertEquals("child2:3.0", result.getRoot().getChildren()[0].getChildren()[0].getName()); + // validate IMPACT_PATHS_LIMIT wasn't exceeded + assertEquals(-1, result.getPathsLimit()); } public void testToImpactGraph_MultiplePaths() {