-
Notifications
You must be signed in to change notification settings - Fork 8
Added WebviewObjectConverter #40
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
Merged
kerenr-jfrog
merged 8 commits into
jfrog:dev
from
kerenr-jfrog:add_webview_object_converter
Jun 17, 2025
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
5f062e3
added WebviewObjectConverter
kerenr-jfrog 2d5d5f7
improve imapctPath to impactGraph convert implementation
kerenr-jfrog d2b544d
added tests
kerenr-jfrog f0355ac
rearrange tests
kerenr-jfrog e6762d6
fix
kerenr-jfrog f5578d7
minor fix
kerenr-jfrog ffcf043
fix type of sast issue page
kerenr-jfrog a9519c0
fix CR comments
kerenr-jfrog File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
bundle/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| 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 SastIssuePage 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) { | ||
kerenr-jfrog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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(), | ||
| 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"; // any other value passed will result the load of the default WebView page | ||
| }; | ||
| } | ||
|
|
||
| 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<List<ImpactPath>> 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<String, ImpactTreeNode> rootMap = new LinkedHashMap<>(); | ||
| boolean isMaxLimitExceeded; | ||
| int pathsNumber = impactPaths.size(); | ||
| int pathIndex = 0; | ||
|
|
||
| for (; pathIndex < pathsNumber && pathIndex < IMPACT_PATHS_LIMIT; pathIndex++) { | ||
| List<ImpactPath> currentPath = impactPaths.get(pathIndex); | ||
| if (currentPath == null || currentPath.isEmpty()) { | ||
| continue; | ||
| } | ||
|
|
||
| String rootName = getNodeName(currentPath.get(0)); | ||
| ImpactTreeNode root = rootMap.computeIfAbsent(rootName, ImpactTreeNode::new); | ||
| ImpactTreeNode currentNode = root; | ||
| int currentPathSize = currentPath.size(); | ||
|
|
||
| for (int nodeIndex = 1; nodeIndex < currentPathSize; nodeIndex++) { | ||
| String nodeName = getNodeName(currentPath.get(nodeIndex)); | ||
| currentNode = getOrAddChild(currentNode, nodeName); | ||
| } | ||
| } | ||
|
|
||
| isMaxLimitExceeded = pathIndex >= IMPACT_PATHS_LIMIT ? true : false; | ||
|
|
||
| ImpactGraphNode rootGraphNode = toImpactGraphNode(rootMap.values().iterator().next()); | ||
| // 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); | ||
kerenr-jfrog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| 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); | ||
kerenr-jfrog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
| } | ||
| } | ||
180 changes: 180 additions & 0 deletions
180
tests/src/main/java/com/jfrog/ide/eclipse/ui/webview/WebviewObjectConverterTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| 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 junit.framework.TestCase; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class WebviewObjectConverterTest extends TestCase{ | ||
|
|
||
| private ScaIssueNode scaIssueNode; | ||
| private FileIssueNode secretIssueNode; | ||
| private SastIssueNode sastIssueNode; | ||
|
|
||
| // 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; | ||
| String ruleID = "CVE-2023-1234_test-component_1.0.0"; | ||
| Applicability applicability = Applicability.APPLICABLE; | ||
| List<List<ImpactPath>> 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); | ||
| 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()); | ||
| } | ||
|
|
||
| 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); | ||
| 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()); | ||
| } | ||
|
|
||
| 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); | ||
|
|
||
| SastIssuePage result = WebviewObjectConverter.convertSastIssueToSastIssuePage(sastIssueNode); | ||
|
|
||
| assertNotNull(result); | ||
| 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() { | ||
| ImpactGraph result = WebviewObjectConverter.toImpactGraph(null); | ||
|
|
||
| assertNotNull(result); | ||
| assertNotNull(result.getRoot()); | ||
| assertEquals("", result.getRoot().getName()); | ||
| assertEquals(0, result.getRoot().getChildren().length); | ||
| } | ||
|
|
||
| public void testToImpactGraph_SinglePath() { | ||
| List<List<ImpactPath>> impactPaths = new ArrayList<>(); | ||
| List<ImpactPath> 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()); | ||
| // validate IMPACT_PATHS_LIMIT wasn't exceeded | ||
| assertEquals(-1, result.getPathsLimit()); | ||
| } | ||
|
|
||
| public void testToImpactGraph_MultiplePaths() { | ||
| List<List<ImpactPath>> impactPaths = new ArrayList<>(); | ||
|
|
||
| // First path | ||
| List<ImpactPath> path1 = new ArrayList<>(); | ||
| path1.add(new ImpactPath("root", "1.0")); | ||
| path1.add(new ImpactPath("child1", "2.0")); | ||
| impactPaths.add(path1); | ||
|
|
||
| // Second path | ||
| List<ImpactPath> 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); | ||
kerenr-jfrog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public void testToImpactGraph_ExceedsLimit() { | ||
| List<List<ImpactPath>> impactPaths = new ArrayList<>(); | ||
| for (int i = 0; i < WebviewObjectConverter.IMPACT_PATHS_LIMIT + 5; i++) { | ||
| List<ImpactPath> 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<List<ImpactPath>> createTestImpactPaths() { | ||
| List<List<ImpactPath>> impactPaths = new ArrayList<>(); | ||
| List<ImpactPath> 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; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.