From f50b944ff470e3464342091f20f0768ee8b50e34 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Wed, 20 Nov 2024 18:45:56 +0100 Subject: [PATCH 1/9] wip: html provider --- .../eclipse/plugin/html/BaseHtmlProvider.java | 121 ++++++++++++++ .../eclipse/plugin/html/CodeHtmlProvider.java | 157 ++++++++++++++++++ .../plugin/html/HtmlProviderFactory.java | 20 +++ .../eclipse/plugin/html/IacHtmlProvider.java | 15 ++ .../eclipse/plugin/html/OssHtmlProvider.java | 15 ++ .../views/snyktoolview/SnykToolView.java | 81 ++++++++- 6 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/html/HtmlProviderFactory.java create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/html/IacHtmlProvider.java create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java new file mode 100644 index 00000000..45765d2b --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java @@ -0,0 +1,121 @@ +package io.snyk.eclipse.plugin.html; + +import java.util.Random; + +import org.eclipse.jface.resource.ColorRegistry; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.themes.ITheme; +import org.eclipse.ui.themes.IThemeManager; + +public class BaseHtmlProvider { + public String getCss() { + return """ + html, body { + height: 100%; + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + } + + body { + background-color: var(--background-color); + color: var(--text-color); + font-weight: 400; + } + + section { + padding: 20px; + } + + .font-light { + font-weight: bold; + } + + a, + .link { + color: var(--link-color); + } + + .delimiter-top { + border-top: 1px solid var(--horizontal-border-color); + } + code { + background-color: var(--code-background-color); + padding: 1px 3px; + border-radius: 4px; + } + """; + } + + public String getJs() { + return ""; + } + + public String getInitScript() { + return ""; + } + + public String getNonce() { + String allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder nonceBuilder = new StringBuilder(32); + for (int i = 0; i < 32; i++) { + nonceBuilder.append(allowedChars.charAt(random.nextInt(allowedChars.length()))); + } + return nonceBuilder.toString(); + } + + public String replaceCssVariables(String html) { + // Build the CSS with the nonce + String nonce = getNonce(); + String css = ""; + html = html.replace("${ideStyle}", css); + html = html.replace("", css); + html = html.replace("var(--default-font)", " ui-sans-serif, \"SF Pro Text\", \"Segoe UI\", \"Ubuntu\", Tahoma, Geneva, Verdana, sans-serif;"); + + + // Replace CSS variables with actual color values + html = html.replace("var(--text-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR", "#000000")); + html = html.replace("var(--background-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START", "#FFFFFF")); + html = html.replace("var(--border-color)", getColorAsHex( "org.eclipse.ui.workbench.ACTIVE_TAB_BORDER_COLOR", "#CCCCCC")); + html = html.replace("var(--link-color)", getColorAsHex("org.eclipse.ui.workbench.HYPERLINK_COLOR", "#0000FF")); + html = html.replace("var(--horizontal-border-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_HIGHLIGHT_BORDER_COLOR", "#CCCCCC")); + html = html.replace("var(--code-background-color)", getColorAsHex("org.eclipse.ui.workbench.CODE_BACKGROUND_COLOR", "#F0F0F0")); + + // Update the HTML head + String ideHeaders = """ + + + + """; + html = html.replace("", ideHeaders); + html = html.replace("${headerEnd}", ""); + + // Replace nonce placeholders + html = html.replace("${nonce}", nonce); + html = html.replace("ideNonce", nonce); + html = html.replace("${ideScript}", ""); + + return html; + } + + public String getColorAsHex(String colorKey, String defaultColor) { + ColorRegistry colorRegistry = getColorRegistry(); + Color color = colorRegistry.get(colorKey); + if (color == null) { + return defaultColor; + } else { + RGB rgb = color.getRGB(); + return String.format("#%02x%02x%02x", rgb.red, rgb.green, rgb.blue); + } + } + + private ColorRegistry getColorRegistry() { + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + ITheme currentTheme = themeManager.getCurrentTheme(); + return currentTheme.getColorRegistry(); + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java new file mode 100644 index 00000000..a5351d8a --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java @@ -0,0 +1,157 @@ +package io.snyk.eclipse.plugin.html; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.themes.ITheme; +import org.eclipse.ui.themes.IThemeManager; + +public class CodeHtmlProvider extends BaseHtmlProvider { + private static CodeHtmlProvider instance = new CodeHtmlProvider(); + + public static CodeHtmlProvider getInstance() { + if (instance == null) { + synchronized (CodeHtmlProvider.class) { + if (instance == null) { + instance = new CodeHtmlProvider(); + } + } + } + return instance; + } + + @Override + public String getCss() { + return super.getCss() + "\n" + """ + .identifiers { + padding-bottom: 20px; + } + .data-flow-table { + background-color: var(--code-background-color); + border: 1px solid transparent; + } + .tabs-nav { + margin: 21px 0 -21px; + } + .light .dark-only, + .high-contrast.high-contrast-light .dark-only { + display: none; + } + + .dark .light-only, + .high-contrast:not(.high-contrast-light) .light-only { + display: none; + } + .tab-item { + cursor: pointer; + display: inline-block; + padding: 5px 10px; + border-bottom: 1px solid transparent; + color: var(--text-color); + text-transform: uppercase; + } + + .tab-item:hover { + /* Add hover styles if needed */ + } + + .tab-item.is-selected { + border-bottom: 3px solid var(--link-color); + } + + .tab-content { + display: none; + } + + .tab-content.is-selected { + display: block; + } + .removed { + background-color: var(--line-removed); + color: #fff; + } + .lesson-link { + margin-left: 3px; + } + .added { + background-color: var(--line-added); + color: #fff; + } + .arrow { + cursor: pointer; + width: 20px; + height: 20px; + padding: 4px; + border-radius: 4px; + text-align: center; + line-height: 1; + } + .example { + background-color: var(--container-background-color); + } + """; + } + + @Override + public String getInitScript() { + String themeScript = getThemeScript(); + String initScript = super.getInitScript(); + return initScript + "\n" + """ + function navigateToIssue(e, target) { + e.preventDefault(); + var filePath = target.getAttribute('file-path'); + var startLine = target.getAttribute('start-line'); + var endLine = target.getAttribute('end-line'); + var startCharacter = target.getAttribute('start-character'); + var endCharacter = target.getAttribute('end-character'); + window.openInEditor(filePath, startLine, endLine, startCharacter, endCharacter); + } + var navigatableLines = document.getElementsByClassName('data-flow-clickable-row'); + for(var i = 0; i < navigatableLines.length; i++) { + navigatableLines[i].onclick = function(e) { + navigateToIssue(e, this); + return false; + }; + } + if(document.getElementById('position-line')) { + document.getElementById('position-line').onclick = function(e) { + var target = navigatableLines[0]; + if(target) { + navigateToIssue(e, target); + } + } + } + // Disable Autofix and ignores + if(document.getElementById('ai-fix-wrapper') && document.getElementById('no-ai-fix-wrapper')){ + document.getElementById('ai-fix-wrapper').className = 'hidden'; + document.getElementById('no-ai-fix-wrapper').className = ''; + } + if(document.getElementsByClassName('ignore-action-container') && document.getElementsByClassName('ignore-action-container')[0]){ + document.getElementsByClassName('ignore-action-container')[0].className = 'hidden'; + } + """ + themeScript; + } + + private String getThemeScript() { + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + ITheme currentTheme = themeManager.getCurrentTheme(); + String themeId = currentTheme.getId().toLowerCase(); + + boolean isDarkTheme = themeId.contains("dark"); + boolean isHighContrast = themeId.contains("highcontrast") || themeId.contains("high-contrast"); + + String themeScript = "var isDarkTheme = " + isDarkTheme + ";\n" + + "var isHighContrast = " + isHighContrast + ";\n" + + "document.body.classList.add(isHighContrast ? 'high-contrast' : (isDarkTheme ? 'dark' : 'light'));"; + return themeScript; + } + + @Override + public String replaceCssVariables(String html) { + html = super.replaceCssVariables(html); + + // Replace CSS variables with actual color values + html = html.replace("var(--line-removed)", super.getColorAsHex("org.eclipse.ui.workbench.lineRemovedColor", "#ff0000")); + html = html.replace("var(--line-added)", super.getColorAsHex("org.eclipse.ui.workbench.lineAddedColor", "#00ff00")); + + return html; + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/HtmlProviderFactory.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/HtmlProviderFactory.java new file mode 100644 index 00000000..3db5d826 --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/HtmlProviderFactory.java @@ -0,0 +1,20 @@ +package io.snyk.eclipse.plugin.html; + +import io.snyk.eclipse.plugin.domain.ProductConstants; + +public class HtmlProviderFactory { + + public static BaseHtmlProvider GetHtmlProvider(String product) + { + switch (product) { + case ProductConstants.DISPLAYED_CODE_SECURITY: + case ProductConstants.DISPLAYED_CODE_QUALITY: + return CodeHtmlProvider.getInstance(); + case ProductConstants.DISPLAYED_OSS: + return OssHtmlProvider.getInstance(); + case ProductConstants.DISPLAYED_IAC: + return IacHtmlProvider.getInstance(); + } + return null; + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/IacHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/IacHtmlProvider.java new file mode 100644 index 00000000..f54f32c0 --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/IacHtmlProvider.java @@ -0,0 +1,15 @@ +package io.snyk.eclipse.plugin.html; + +public class IacHtmlProvider extends BaseHtmlProvider { + private static IacHtmlProvider instance = new IacHtmlProvider(); + public static IacHtmlProvider getInstance() { + if (instance == null) { + synchronized (IacHtmlProvider.class) { + if (instance == null) { + instance = new IacHtmlProvider(); + } + } + } + return instance; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java new file mode 100644 index 00000000..b6d4e9be --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java @@ -0,0 +1,15 @@ +package io.snyk.eclipse.plugin.html; + +public class OssHtmlProvider extends BaseHtmlProvider { + private static OssHtmlProvider instance = new OssHtmlProvider(); + public static OssHtmlProvider getInstance() { + if (instance == null) { + synchronized (OssHtmlProvider.class) { + if (instance == null) { + instance = new OssHtmlProvider(); + } + } + } + return instance; + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java index 6a10928e..451ebb74 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java @@ -9,21 +9,31 @@ import java.util.List; import java.util.stream.Collectors; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeNode; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.BrowserFunction; +import org.eclipse.swt.browser.LocationEvent; +import org.eclipse.swt.browser.LocationListener; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; @@ -32,13 +42,27 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.menus.CommandContributionItemParameter; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; +import org.eclipse.ui.texteditor.ITextEditor; import org.osgi.framework.Bundle; import io.snyk.eclipse.plugin.properties.preferences.Preferences; import io.snyk.eclipse.plugin.utils.ResourceUtils; import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeContentProvider; import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeLabelProvider; +import io.snyk.eclipse.plugin.html.CodeHtmlProvider; +import io.snyk.eclipse.plugin.html.HtmlProviderFactory; +import io.snyk.eclipse.plugin.utils.ResourceUtils; +import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeContentProvider; +import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeLabelProvider; +import io.snyk.languageserver.protocolextension.FileTreeNode; +import io.snyk.languageserver.protocolextension.messageObjects.scanResults.LineRange; + +import java.io.File; +import java.nio.file.Path; /** * TODO This view will replace the old SnykView. Move the snyktoolview classes @@ -86,6 +110,53 @@ public void createPartControl(Composite parent) { // Create Browser // SWT.EDGE will be ignored if OS not windows and will be set to SWT.NONE. browser = new Browser(sashForm, SWT.EDGE); + // Register the Java function as an anonymous class + + new BrowserFunction(browser, "openInEditor") { + @SuppressWarnings("restriction") + @Override + public Object function(Object[] arguments) { + if (arguments.length != 5) { + return null; + } + String filePath = (String) arguments[0]; + var fileUri = Paths.get(filePath).toUri().toASCIIString(); + int startLine = Integer.parseInt(arguments[1].toString()); + int endLine = Integer.parseInt(arguments[2].toString()); + int startCharacter = Integer.parseInt(arguments[3].toString()); + int endCharacter = Integer.parseInt(arguments[4].toString()); + + Display.getDefault().asyncExec(() -> { + try { + Position startPosition = new Position(startLine, startCharacter); + Position endPosition = new Position(endLine, endCharacter); + Range range = new Range(startPosition, endPosition); + + var location = new Location(fileUri, range); + LSPEclipseUtils.openInEditor(location); + + } catch (Exception e) { + e.printStackTrace(); + } + }); + return null; + } + }; + + browser.addLocationListener(new LocationListener() { + @Override + public void changing(LocationEvent event) { + String url = event.location; + if(url.startsWith("http")) { + event.doit = false; + Program.launch(url); + } + } + + @Override + public void changed(LocationEvent event) { + } + }); initBrowserText(); // Set sash weights @@ -121,7 +192,13 @@ private void registerTreeContextMeny(Composite parent) { private void updateBrowserContent(TreeNode node) { // Generate HTML content based on the selected node String htmlContent = generateHtmlContent(node); - browser.setText(htmlContent); + if (node instanceof IssueTreeNode) { + var product = ((ProductTreeNode) node.getParent().getParent()).getProduct(); + var htmlProvider = HtmlProviderFactory.GetHtmlProvider(product); + htmlContent = htmlProvider.replaceCssVariables(htmlContent); + browser.setText(htmlContent); + browser.execute(htmlProvider.getInitScript()); + } } private void updateBrowserContent(String text) { @@ -327,4 +404,4 @@ private void addCommandIfNotPresent(IMenuManager menu, String commandId) { } } -} \ No newline at end of file +} From 0a4d042f0ecbcf1ae362819bad9543319e4c7e0e Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Thu, 21 Nov 2024 15:05:16 +0100 Subject: [PATCH 2/9] fix: populate tree after snyk.Scan success --- .../views/snyktoolview/SnykToolView.java | 24 +----------- .../snyk/languageserver/SnykIssueCache.java | 2 +- .../SnykExtendedLanguageClient.java | 38 ++++++++++--------- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java index 451ebb74..91b0fd0d 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java @@ -267,34 +267,14 @@ public void addFileNode(ProductTreeNode parent, FileTreeNode toBeAdded) { @Override public void addInfoNode(ProductTreeNode parent, InfoTreeNode toBeAdded) { - List list = new ArrayList<>(); - var children = parent.getChildren(); - if (children != null) { - list = Arrays.stream(children).map(it -> (BaseTreeNode) it).collect(Collectors.toList()); - } - toBeAdded.setParent(parent); - int insertIndex = GetLastInfoNodeIndex(list); - list.add(insertIndex, toBeAdded); - parent.setChildren(list.toArray(new BaseTreeNode[0])); - + parent.addChild(toBeAdded); + Display.getDefault().asyncExec(() -> { this.treeViewer.refresh(parent, true); }); } - private int GetLastInfoNodeIndex(List list) { - int insertIndex = 0; - for (int i = 0; i < list.size(); i++) { - if (list.get(i) instanceof InfoTreeNode) { - insertIndex += 1; - } else { - break; - } - } - return insertIndex; - } - @Override public ProductTreeNode getProductNode(String product, String folderPath) { if (product == null || folderPath == null) { diff --git a/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java b/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java index 1658f75a..b8048f67 100644 --- a/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java +++ b/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java @@ -208,7 +208,7 @@ public long getTotalCount(String product) { return getCacheByDisplayProduct(product).values().stream().flatMap(Collection::stream).count(); } - private Map> getCacheByDisplayProduct(String displayProduct) { + public Map> getCacheByDisplayProduct(String displayProduct) { switch (displayProduct) { case ProductConstants.DISPLAYED_OSS: return ossIssues; diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java index 79ed4fab..97d9a2e4 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -28,7 +28,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -323,12 +325,12 @@ public void hasAuthenticated(HasAuthenticatedParam param) { if (differentToken) { prefs.store(Preferences.AUTH_TOKEN_KEY, newToken); } - + if (!Preferences.getInstance().isTest()) { configurationUpdater.configurationChanged(); refreshFeatureFlags(); } - + if (!newToken.isBlank() && PlatformUI.isWorkbenchRunning()) { enableSnykViewRunActions(); } @@ -379,7 +381,9 @@ public void snykScan(SnykScanParam param) { case SCAN_STATE_SUCCESS: scanState.setScanInProgress(inProgressKey, false); for (ProductTreeNode productTreeNode : affectedProductTreeNodes) { + productTreeNode.reset(); addInfoNodes(productTreeNode, param.getFolderPath(), issueCache); + populateFileAndIssueNodes(productTreeNode, param.getFolderPath(), issueCache); } break; case SCAN_STATE_ERROR: @@ -502,7 +506,7 @@ private void addInfoNodes(ProductTreeNode productNode, String folderPath, SnykIs } if (totalCount > 0 && ignoredCount == totalCount - && pref.getBooleanPref(Preferences.IS_GLOBAL_IGNORES_FEATURE_ENABLED) + && pref.getBooleanPref(Preferences.IS_GLOBAL_IGNORES_FEATURE_ENABLED) && pref.getBooleanPref(FILTER_IGNORES_SHOW_OPEN_ISSUES)) { toolView.addInfoNode(productNode, new InfoTreeNode(ISnykToolView.IGNORED_ISSUES_FILTERED_BUT_AVAILABLE)); @@ -529,20 +533,19 @@ public CompletableFuture publishDiagnostics316(PublishDiagnostics316Param return; } - var productTreeNodes = populateIssueCache(param, filePath); - populateFileAndIssueNodes(filePath, productTreeNodes); + populateIssueCache(param, filePath); }); } - private void populateFileAndIssueNodes(String filePath, Set nodes) { - for (ProductTreeNode productTreeNode : nodes) { - var issueCache = IssueCacheHolder.getInstance().getCacheInstance(filePath); - var issues = issueCache.getIssues(filePath, productTreeNode.getProduct()); - issues = IssueSorter.sortIssuesBySeverity(issues); - issues = filterIgnoredIssues(issues); - if (issues.isEmpty()) + private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String folderPath, SnykIssueCache issueCache) { + var cacheHashMap = Collections.unmodifiableMap(issueCache.getCacheByDisplayProduct(productTreeNode.getProduct())); + for (var kv : cacheHashMap.entrySet()) { + var fileName = kv.getKey(); + var issues = kv.getValue(); + if(issues.isEmpty()) continue; - FileTreeNode fileNode = new FileTreeNode(filePath); + issues = IssueSorter.sortIssuesBySeverity(issues); + FileTreeNode fileNode = new FileTreeNode(fileName); toolView.addFileNode(productTreeNode, fileNode); for (Issue issue : issues) { toolView.addIssueNode(fileNode, new IssueTreeNode(issue)); @@ -568,17 +571,17 @@ private Collection filterIgnoredIssues(Collection issueList) .toList(); } - - private Set populateIssueCache(PublishDiagnostics316Param param, String filePath) { + + private void populateIssueCache(PublishDiagnostics316Param param, String filePath) { var issueCache = getIssueCache(filePath); Diagnostic316[] diagnostics = param.getDiagnostics(); if (diagnostics.length == 0) { issueCache.removeAllIssuesForPath(filePath); - return Set.of(); + return; } var source = diagnostics[0].getSource(); if (StringUtils.isEmpty(source)) { - return Set.of(); + return; } var snykProduct = LSP_SOURCE_TO_SCAN_PARAMS.get(source); List issueList = new ArrayList<>(); @@ -601,7 +604,6 @@ private Set populateIssueCache(PublishDiagnostics316Param param issueCache.addIacIssues(filePath, issueList); break; } - return getAffectedProductNodes(snykProduct, filePath); } public void reportAnalytics(AbstractTask event) { From 3f2426ccecc826c511ff234cdbb320b7cf50acca Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Thu, 21 Nov 2024 17:41:05 +0100 Subject: [PATCH 3/9] refactor: use css from LS --- .../eclipse/plugin/html/BaseHtmlProvider.java | 38 +--------- .../eclipse/plugin/html/CodeHtmlProvider.java | 76 +------------------ .../eclipse/plugin/html/OssHtmlProvider.java | 7 ++ 3 files changed, 10 insertions(+), 111 deletions(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java index 45765d2b..44ee5961 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java @@ -11,43 +11,7 @@ public class BaseHtmlProvider { public String getCss() { - return """ - html, body { - height: 100%; - display: flex; - flex-direction: column; - margin: 0; - padding: 0; - } - - body { - background-color: var(--background-color); - color: var(--text-color); - font-weight: 400; - } - - section { - padding: 20px; - } - - .font-light { - font-weight: bold; - } - - a, - .link { - color: var(--link-color); - } - - .delimiter-top { - border-top: 1px solid var(--horizontal-border-color); - } - code { - background-color: var(--code-background-color); - padding: 1px 3px; - border-radius: 4px; - } - """; + return ""; } public String getJs() { diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java index a5351d8a..8a892343 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java @@ -18,78 +18,6 @@ public static CodeHtmlProvider getInstance() { return instance; } - @Override - public String getCss() { - return super.getCss() + "\n" + """ - .identifiers { - padding-bottom: 20px; - } - .data-flow-table { - background-color: var(--code-background-color); - border: 1px solid transparent; - } - .tabs-nav { - margin: 21px 0 -21px; - } - .light .dark-only, - .high-contrast.high-contrast-light .dark-only { - display: none; - } - - .dark .light-only, - .high-contrast:not(.high-contrast-light) .light-only { - display: none; - } - .tab-item { - cursor: pointer; - display: inline-block; - padding: 5px 10px; - border-bottom: 1px solid transparent; - color: var(--text-color); - text-transform: uppercase; - } - - .tab-item:hover { - /* Add hover styles if needed */ - } - - .tab-item.is-selected { - border-bottom: 3px solid var(--link-color); - } - - .tab-content { - display: none; - } - - .tab-content.is-selected { - display: block; - } - .removed { - background-color: var(--line-removed); - color: #fff; - } - .lesson-link { - margin-left: 3px; - } - .added { - background-color: var(--line-added); - color: #fff; - } - .arrow { - cursor: pointer; - width: 20px; - height: 20px; - padding: 4px; - border-radius: 4px; - text-align: center; - line-height: 1; - } - .example { - background-color: var(--container-background-color); - } - """; - } - @Override public String getInitScript() { String themeScript = getThemeScript(); @@ -149,8 +77,8 @@ public String replaceCssVariables(String html) { html = super.replaceCssVariables(html); // Replace CSS variables with actual color values - html = html.replace("var(--line-removed)", super.getColorAsHex("org.eclipse.ui.workbench.lineRemovedColor", "#ff0000")); - html = html.replace("var(--line-added)", super.getColorAsHex("org.eclipse.ui.workbench.lineAddedColor", "#00ff00")); + html = html.replace("var(--example-line-removed-color)", super.getColorAsHex("org.eclipse.ui.workbench.lineRemovedColor", "#ff0000")); + html = html.replace("var(--example-line-added-color)", super.getColorAsHex("org.eclipse.ui.workbench.lineAddedColor", "#00ff00")); return html; } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java index b6d4e9be..93e43d23 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java @@ -12,4 +12,11 @@ public static OssHtmlProvider getInstance() { } return instance; } + @Override + public String replaceCssVariables(String html) { + html = super.replaceCssVariables(html); + html = html.replace("var(--container-background-color)", super.getColorAsHex("org.eclipse.ui.workbench.CODE_BACKGROUND_COLOR", "#F0F0F0")); + + return html; + } } From 1affa1b04ab37ab08052495bb0d2f6a966ff7a41 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Wed, 20 Nov 2024 18:45:56 +0100 Subject: [PATCH 4/9] wip: html provider --- .../java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java | 2 +- .../java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java | 4 ++-- .../java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java index 44ee5961..ba2ec123 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java @@ -76,7 +76,7 @@ public String getColorAsHex(String colorKey, String defaultColor) { return String.format("#%02x%02x%02x", rgb.red, rgb.green, rgb.blue); } } - + private ColorRegistry getColorRegistry() { IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); ITheme currentTheme = themeManager.getCurrentTheme(); diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java index 8a892343..c2e88b71 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java @@ -17,7 +17,7 @@ public static CodeHtmlProvider getInstance() { } return instance; } - + @Override public String getInitScript() { String themeScript = getThemeScript(); @@ -42,7 +42,7 @@ function navigateToIssue(e, target) { if(document.getElementById('position-line')) { document.getElementById('position-line').onclick = function(e) { var target = navigatableLines[0]; - if(target) { + if(target) { navigateToIssue(e, target); } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java index 93e43d23..b943853c 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/OssHtmlProvider.java @@ -18,5 +18,5 @@ public String replaceCssVariables(String html) { html = html.replace("var(--container-background-color)", super.getColorAsHex("org.eclipse.ui.workbench.CODE_BACKGROUND_COLOR", "#F0F0F0")); return html; - } + } } From f54fd1c663e5c740a75b25b4418e82ce604a9502 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Fri, 22 Nov 2024 12:48:23 +0100 Subject: [PATCH 5/9] fix: merge conflicts --- .../views/snyktoolview/SnykToolView.java | 22 +------------------ .../SnykExtendedLanguageClient.java | 5 ++--- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java index 91b0fd0d..f0c1c65a 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java @@ -4,17 +4,10 @@ package io.snyk.eclipse.plugin.views.snyktoolview; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; @@ -42,27 +35,14 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.menus.CommandContributionItemParameter; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; -import org.eclipse.ui.texteditor.ITextEditor; import org.osgi.framework.Bundle; -import io.snyk.eclipse.plugin.properties.preferences.Preferences; -import io.snyk.eclipse.plugin.utils.ResourceUtils; -import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeContentProvider; -import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeLabelProvider; -import io.snyk.eclipse.plugin.html.CodeHtmlProvider; import io.snyk.eclipse.plugin.html.HtmlProviderFactory; +import io.snyk.eclipse.plugin.properties.preferences.Preferences; import io.snyk.eclipse.plugin.utils.ResourceUtils; import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeContentProvider; import io.snyk.eclipse.plugin.views.snyktoolview.providers.TreeLabelProvider; -import io.snyk.languageserver.protocolextension.FileTreeNode; -import io.snyk.languageserver.protocolextension.messageObjects.scanResults.LineRange; - -import java.io.File; -import java.nio.file.Path; /** * TODO This view will replace the old SnykView. Move the snyktoolview classes diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java index 97d9a2e4..a70f4461 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -30,7 +30,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -105,7 +104,6 @@ @SuppressWarnings("restriction") public class SnykExtendedLanguageClient extends LanguageClientImpl { - private static final String SNYK_CODE_CONSISTENT_IGNORES = "snykCodeConsistentIgnores"; private ProgressManager progressManager = new ProgressManager(this); private final ObjectMapper om = new ObjectMapper(); private TaskProcessor taskProcessor; @@ -542,9 +540,10 @@ private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String f for (var kv : cacheHashMap.entrySet()) { var fileName = kv.getKey(); var issues = kv.getValue(); + issues = IssueSorter.sortIssuesBySeverity(issues); + issues = filterIgnoredIssues(issues); if(issues.isEmpty()) continue; - issues = IssueSorter.sortIssuesBySeverity(issues); FileTreeNode fileNode = new FileTreeNode(fileName); toolView.addFileNode(productTreeNode, fileNode); for (Issue issue : issues) { From 88f03c8061377f83372a8cee384014ee9c5a32a4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Fri, 22 Nov 2024 15:30:40 +0100 Subject: [PATCH 6/9] fix: added HtmlProviderFactory tests --- .../eclipse/plugin/html/BaseHtmlProvider.java | 20 +- .../eclipse/plugin/html/CodeHtmlProvider.java | 5 +- .../SnykExtendedLanguageClient.java | 4 +- .../plugin/html/HtmlProviderFactoryTest.java | 54 + .../resources/code_issue_description.html | 1411 +++++++++++++++++ 5 files changed, 1475 insertions(+), 19 deletions(-) create mode 100644 tests/src/test/java/io/snyk/eclipse/plugin/html/HtmlProviderFactoryTest.java create mode 100644 tests/src/test/resources/code_issue_description.html diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java index ba2ec123..2a7ac239 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java @@ -48,17 +48,8 @@ public String replaceCssVariables(String html) { html = html.replace("var(--link-color)", getColorAsHex("org.eclipse.ui.workbench.HYPERLINK_COLOR", "#0000FF")); html = html.replace("var(--horizontal-border-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_HIGHLIGHT_BORDER_COLOR", "#CCCCCC")); html = html.replace("var(--code-background-color)", getColorAsHex("org.eclipse.ui.workbench.CODE_BACKGROUND_COLOR", "#F0F0F0")); - - // Update the HTML head - String ideHeaders = """ - - - - """; - html = html.replace("", ideHeaders); + html = html.replace("${headerEnd}", ""); - - // Replace nonce placeholders html = html.replace("${nonce}", nonce); html = html.replace("ideNonce", nonce); html = html.replace("${ideScript}", ""); @@ -76,10 +67,15 @@ public String getColorAsHex(String colorKey, String defaultColor) { return String.format("#%02x%02x%02x", rgb.red, rgb.green, rgb.blue); } } - + + private ColorRegistry colorRegistry; private ColorRegistry getColorRegistry() { + if(colorRegistry != null) { + return colorRegistry; + } IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); ITheme currentTheme = themeManager.getCurrentTheme(); - return currentTheme.getColorRegistry(); + colorRegistry = currentTheme.getColorRegistry(); + return colorRegistry; } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java index c2e88b71..52efb7eb 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java @@ -47,14 +47,11 @@ function navigateToIssue(e, target) { } } } - // Disable Autofix and ignores + // Disable AIfix if(document.getElementById('ai-fix-wrapper') && document.getElementById('no-ai-fix-wrapper')){ document.getElementById('ai-fix-wrapper').className = 'hidden'; document.getElementById('no-ai-fix-wrapper').className = ''; } - if(document.getElementsByClassName('ignore-action-container') && document.getElementsByClassName('ignore-action-container')[0]){ - document.getElementsByClassName('ignore-action-container')[0].className = 'hidden'; - } """ + themeScript; } diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java index a70f4461..81746d49 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -552,8 +552,7 @@ private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String f } } - private Collection filterIgnoredIssues(Collection issueList) - { + private Collection filterIgnoredIssues(Collection issueList) { final boolean includeIgnoredIssues; final boolean includeOpenedIssues; @@ -568,7 +567,6 @@ private Collection filterIgnoredIssues(Collection issueList) return issueList.stream() .filter(it -> it.isVisible(includeIgnoredIssues, includeOpenedIssues)) .toList(); - } private void populateIssueCache(PublishDiagnostics316Param param, String filePath) { diff --git a/tests/src/test/java/io/snyk/eclipse/plugin/html/HtmlProviderFactoryTest.java b/tests/src/test/java/io/snyk/eclipse/plugin/html/HtmlProviderFactoryTest.java new file mode 100644 index 00000000..5880503c --- /dev/null +++ b/tests/src/test/java/io/snyk/eclipse/plugin/html/HtmlProviderFactoryTest.java @@ -0,0 +1,54 @@ +package io.snyk.eclipse.plugin.html; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +import io.snyk.eclipse.plugin.domain.ProductConstants; +import io.snyk.languageserver.LsBaseTest; + +public class HtmlProviderFactoryTest extends LsBaseTest { + @Test + void htmlProviderFactoryReturnsCorrectType() throws Exception { + var cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_CODE_SECURITY); + assertTrue(cut instanceof CodeHtmlProvider); + cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_CODE_QUALITY); + assertTrue(cut instanceof CodeHtmlProvider); + cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_OSS); + assertTrue(cut instanceof OssHtmlProvider); + cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_IAC); + assertTrue(cut instanceof IacHtmlProvider); + } + + @Test + void testHtmlProviderReplacesPlaceholders() throws Exception { + var htmlContent = Files.readString(Path.of("src/test/resources/code_issue_description.html")); + + var cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_CODE_SECURITY); + htmlContent = cut.replaceCssVariables(htmlContent); + assertTrue(!htmlContent.contains("${headerEnd})")); + assertTrue(!htmlContent.contains("${nonce})")); + assertTrue(!htmlContent.contains("${ideScript})")); + assertTrue(!htmlContent.contains("ideNonce")); + assertTrue(!htmlContent.contains("var(--text-color)")); + assertTrue(!htmlContent.contains("var(----background-color)")); + assertTrue(!htmlContent.contains("var(--border-color)")); + assertTrue(!htmlContent.contains("var(--link-color)")); + assertTrue(!htmlContent.contains("var(--text-color)")); + assertTrue(!htmlContent.contains("var(--horizontal-border-color)")); + assertTrue(!htmlContent.contains("var(--code-background-color)")); + assertTrue(!htmlContent.contains("var(--example-line-removed-color)")); + assertTrue(!htmlContent.contains("var(--example-line-added-color)")); + } + + @Test + void testHtmlProviderGeneratesInitScript() throws Exception { + var cut = HtmlProviderFactory.GetHtmlProvider(ProductConstants.DISPLAYED_CODE_SECURITY); + var initScript = cut.getInitScript(); + assertTrue(initScript.contains("window.openInEditor")); + assertTrue(initScript.contains("document.body.classList.add(isHighContrast")); + } +} diff --git a/tests/src/test/resources/code_issue_description.html b/tests/src/test/resources/code_issue_description.html new file mode 100644 index 00000000..24d1d321 --- /dev/null +++ b/tests/src/test/resources/code_issue_description.html @@ -0,0 +1,1411 @@ + + + + + + + + + + + + + ${ideStyle} + + + + +
+ + +
+
+ + + + Ignored. This issue is currently ignored. +
+
+ +
+ + + + +

NoSQL Injection

+
+
+
Issue
+ + | + + CWE-943 + + + + + | +
Position line: 39
+ + + | +
Priority score: 818
+ +
IGNORED
+
+ + + +
+ +
+ +
+
+ + + Ignore Details + + + + Fix Analysis + + + + Issue Overview + + +
+ + +
+ + +
+
+ +
+
Category
+
Ignored temporarily
+
+ +
+
Expiration
+
27 days
+
+ +
+
Ignored On
+
November 14, 2024
+
+ +
+
Ignored By
+
Abdelrahman Shawki
+
+ +
+
Reason
+
test test test
+
+ +
+ Ignores are currently managed in the Snyk web app. + To edit or remove the ignore please go to: + https://app.snyk.io. +
+
+
+ + + + +
+
+
Unsanitized input from the HTTP request body flows into find, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.
+ +
+ + + + + +
+ +
+

+ Data Flow - 1 step +

+
+ +
+ + index.js +
+ + + + + + + + + + + +
1 + line:39 + | + User.find({ username: req.body.username, password: req.body.password }, function (err, users) { +
+ +
+
+ + +
+

Fixed Code Examples

+ +

+ This type of vulnerability was fixed in 30 open source projects. +

+
+
+ + + + + + afuh/pinstagram + + +
+
+ + + + + + + + + + Example 1/3 + + + + + + + + + + +
+
+ +
+ +
+
+ +
+ const user = await User.findOne({ slug: req.params.user }).populate('likes') + +
+ +
+ const user = await User.findOne({ _id: req.user._id }).populate('likes') + +
+ +
+
+ + + + + +
+ +
+
+
+ + + +
+ +
+
+

Details

+ +

In an NoSQL injection attack, the user can submit an NoSQL query directly to the database, gaining access without providing appropriate credentials. Attackers can then view, export, modify, and delete confidential information; change passwords and other authentication information; and possibly gain access to other systems within the network. This is one of the most commonly exploited categories of vulnerability, but can largely be avoided through good coding practices.

+ +
+
+
+ +
+
+
+ + + + + + + ${ideScript} + + + From d75a346c1c1e4b3a0c4f304164a13cc6b9ac462f Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Fri, 22 Nov 2024 16:25:59 +0100 Subject: [PATCH 7/9] fix: use comperator for sorting issues --- .../snyk/languageserver/SnykIssueCache.java | 10 ++- .../SnykExtendedLanguageClient.java | 25 +++--- .../scanResults/IssueComparator.java | 77 +++++++++++++++++++ .../scanResults/IssueSorter.java | 31 -------- .../SnykExtendedLanguageClientTest.java | 8 -- 5 files changed, 95 insertions(+), 56 deletions(-) create mode 100644 plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueComparator.java delete mode 100644 plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueSorter.java diff --git a/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java b/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java index b8048f67..013dec62 100644 --- a/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java +++ b/plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java @@ -4,11 +4,13 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import io.snyk.eclipse.plugin.domain.ProductConstants; import io.snyk.languageserver.protocolextension.messageObjects.scanResults.Issue; +import io.snyk.languageserver.protocolextension.messageObjects.scanResults.IssueComparator; public class SnykIssueCache { private final Map> codeSecurityIssues = new ConcurrentHashMap<>(); @@ -75,8 +77,8 @@ public Collection getIssues(String path, String displayProduct) { * @param issues The collection of issues to add */ public void addCodeIssues(String path, Collection issues) { - var qualityIssues = new HashSet(issues.size()); - var securityIssues = new HashSet(issues.size()); + var qualityIssues = new TreeSet(new IssueComparator(issues)); + var securityIssues = new TreeSet(new IssueComparator(issues)); for (Issue issue : issues) { if (issue.additionalData().isSecurityType()) { securityIssues.add(issue); @@ -138,7 +140,7 @@ public void removeCodeIssuesForPath(String path) { */ public void addOssIssues(String path, Collection issues) { if (issues.size() > 0) { - ossIssues.put(path, issues); + ossIssues.put(path, new TreeSet(new IssueComparator(issues))); } else { ossIssues.remove(path); } @@ -173,7 +175,7 @@ public void removeOssIssuesForPath(String path) { */ public void addIacIssues(String path, Collection issues) { if (issues.size() > 0) { - iacIssues.put(path, issues); + iacIssues.put(path, new TreeSet(new IssueComparator(issues))); } else { iacIssues.remove(path); } diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java index 81746d49..dcf94a27 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -27,7 +27,6 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -100,7 +99,6 @@ import io.snyk.languageserver.protocolextension.messageObjects.SnykScanParam; import io.snyk.languageserver.protocolextension.messageObjects.SnykTrustedFoldersParams; import io.snyk.languageserver.protocolextension.messageObjects.scanResults.Issue; -import io.snyk.languageserver.protocolextension.messageObjects.scanResults.IssueSorter; @SuppressWarnings("restriction") public class SnykExtendedLanguageClient extends LanguageClientImpl { @@ -133,19 +131,17 @@ private void registerRefreshFeatureFlagsTask() { public void refreshFeatureFlags() { boolean enableConsistentIgnores = getFeatureFlagStatus(FeatureFlagConstants.SNYK_CODE_CONSISTENT_IGNORES); - Preferences.getInstance().store(Preferences.IS_GLOBAL_IGNORES_FEATURE_ENABLED, - Boolean.valueOf(enableConsistentIgnores).toString()); - - updateIgnoresButtons(); + toggleIgnores(enableConsistentIgnores); } - private void updateIgnoresButtons() { + private void toggleIgnores(Boolean enableConsistentIgnores) { + Preferences.getInstance().store(Preferences.IS_GLOBAL_IGNORES_FEATURE_ENABLED, + Boolean.valueOf(enableConsistentIgnores).toString()); PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { var snykToolView = SnykStartup.getView(); if (snykToolView != null) snykToolView.toggleIgnoresButtons(); }); - } private void createIssueCaches() { @@ -539,8 +535,7 @@ private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String f var cacheHashMap = Collections.unmodifiableMap(issueCache.getCacheByDisplayProduct(productTreeNode.getProduct())); for (var kv : cacheHashMap.entrySet()) { var fileName = kv.getKey(); - var issues = kv.getValue(); - issues = IssueSorter.sortIssuesBySeverity(issues); + var issues = new ArrayList<>(kv.getValue()); issues = filterIgnoredIssues(issues); if(issues.isEmpty()) continue; @@ -552,7 +547,7 @@ private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String f } } - private Collection filterIgnoredIssues(Collection issueList) { + private ArrayList filterIgnoredIssues(ArrayList issueList) { final boolean includeIgnoredIssues; final boolean includeOpenedIssues; @@ -566,7 +561,7 @@ private Collection filterIgnoredIssues(Collection issueList) { return issueList.stream() .filter(it -> it.isVisible(includeIgnoredIssues, includeOpenedIssues)) - .toList(); + .collect(Collectors.toCollection(ArrayList::new)); } private void populateIssueCache(PublishDiagnostics316Param param, String filePath) { @@ -582,11 +577,15 @@ private void populateIssueCache(PublishDiagnostics316Param param, String filePat } var snykProduct = LSP_SOURCE_TO_SCAN_PARAMS.get(source); List issueList = new ArrayList<>(); - + var isIgnoresEnabled = Preferences.getInstance().getBooleanPref(Preferences.IS_GLOBAL_IGNORES_FEATURE_ENABLED); for (var diagnostic : diagnostics) { if (diagnostic.getData() == null) { continue; } + if(!isIgnoresEnabled && diagnostic.getData().isIgnored()) { + toggleIgnores(true); + isIgnoresEnabled = true; + } issueList.add(diagnostic.getData()); } diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueComparator.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueComparator.java new file mode 100644 index 00000000..7bac6392 --- /dev/null +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueComparator.java @@ -0,0 +1,77 @@ +package io.snyk.languageserver.protocolextension.messageObjects.scanResults; + +import java.util.*; +import io.snyk.eclipse.plugin.domain.ProductConstants; + +public class IssueComparator implements Comparator { + + private final Map> issuesGrouped; + + public IssueComparator() { + this.issuesGrouped = new HashMap<>(); + } + + public IssueComparator(Collection issues) { + this.issuesGrouped = new HashMap<>(); + for (Issue issue : issues) { + this.issuesGrouped.computeIfAbsent(issue, k -> new ArrayList<>()).add(issue); + } + } + + @Override + public int compare(Issue o1, Issue o2) { + Map severityOrder = getSeverityOrderHashMap(); + + // Get ranks for the severities of the two issues + int rank1 = severityOrder.getOrDefault(o1.severity().toLowerCase(), Integer.MAX_VALUE); + int rank2 = severityOrder.getOrDefault(o2.severity().toLowerCase(), Integer.MAX_VALUE); + + // Compare based on severity rank (lower rank = higher severity) + int severityComparison = Integer.compare(rank1, rank2); + if (severityComparison != 0) { + return severityComparison; + } + + // Fallback: Compare by issue counts grouped by severity (cascading) + int o1Criticals = getCount(o1, ProductConstants.SEVERITY_CRITICAL); + int o2Criticals = getCount(o2, ProductConstants.SEVERITY_CRITICAL); + + int o1Highs = getCount(o1, ProductConstants.SEVERITY_HIGH); + int o2Highs = getCount(o2, ProductConstants.SEVERITY_HIGH); + + int o1Mediums = getCount(o1, ProductConstants.SEVERITY_MEDIUM); + int o2Mediums = getCount(o2, ProductConstants.SEVERITY_MEDIUM); + + int o1Lows = getCount(o1, ProductConstants.SEVERITY_LOW); + int o2Lows = getCount(o2, ProductConstants.SEVERITY_LOW); + + if (o1Criticals != o2Criticals) { + return Integer.compare(o2Criticals, o1Criticals); + } else if (o1Highs != o2Highs) { + return Integer.compare(o2Highs, o1Highs); + } else if (o1Mediums != o2Mediums) { + return Integer.compare(o2Mediums, o1Mediums); + } else if (o1Lows != o2Lows) { + return Integer.compare(o2Lows, o1Lows); + } + + // Fallback to comparing by hash codes if everything else is equal + return Integer.compare(o1.hashCode(), o2.hashCode()); + } + + private int getCount(Issue issue, String severity) { + List issuesForType = issuesGrouped.getOrDefault(issue, Collections.emptyList()); + return (int) issuesForType.stream() + .filter(i -> severity.equalsIgnoreCase(i.severity())) + .count(); + } + + private static Map getSeverityOrderHashMap() { + Map severityOrder = new HashMap<>(); + severityOrder.put(ProductConstants.SEVERITY_CRITICAL.toLowerCase(), 1); + severityOrder.put(ProductConstants.SEVERITY_HIGH.toLowerCase(), 2); + severityOrder.put(ProductConstants.SEVERITY_MEDIUM.toLowerCase(), 3); + severityOrder.put(ProductConstants.SEVERITY_LOW.toLowerCase(), 4); + return severityOrder; + } +} diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueSorter.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueSorter.java deleted file mode 100644 index 511d0153..00000000 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/scanResults/IssueSorter.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.snyk.languageserver.protocolextension.messageObjects.scanResults; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.snyk.eclipse.plugin.domain.ProductConstants; - -public class IssueSorter { - public static List sortIssuesBySeverity(Collection issues) { - List result = new ArrayList<>(issues); - Map severityOrder = getSeverityOrderHashMap(); - result.sort((i1, i2) -> { - Integer rank1 = severityOrder.getOrDefault(i1.severity().toLowerCase(), Integer.MAX_VALUE); - Integer rank2 = severityOrder.getOrDefault(i2.severity().toLowerCase(), Integer.MAX_VALUE); - return rank1.compareTo(rank2); - }); - return result; - } - - private static Map getSeverityOrderHashMap() { - Map severityOrder = new HashMap<>(); - severityOrder.put(ProductConstants.SEVERITY_CRITICAL, 1); - severityOrder.put(ProductConstants.SEVERITY_HIGH, 2); - severityOrder.put(ProductConstants.SEVERITY_MEDIUM, 3); - severityOrder.put(ProductConstants.SEVERITY_LOW, 4); - return severityOrder; - } -} diff --git a/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java b/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java index c3cb5efe..bc6ab3e6 100644 --- a/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java +++ b/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java @@ -11,14 +11,11 @@ import static io.snyk.eclipse.plugin.views.snyktoolview.ISnykToolView.CONGRATS_NO_ISSUES_FOUND; import static io.snyk.eclipse.plugin.views.snyktoolview.ISnykToolView.getPlural; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -29,7 +26,6 @@ import java.nio.file.Paths; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -51,12 +47,8 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import io.snyk.eclipse.plugin.analytics.AbstractTask; -import io.snyk.eclipse.plugin.analytics.AnalyticsEventTask; import io.snyk.eclipse.plugin.analytics.TaskProcessor; -import io.snyk.eclipse.plugin.properties.preferences.InMemoryPreferenceStore; import io.snyk.eclipse.plugin.properties.preferences.Preferences; -import io.snyk.eclipse.plugin.properties.preferences.PreferencesUtils; import io.snyk.eclipse.plugin.views.snyktoolview.ISnykToolView; import io.snyk.eclipse.plugin.views.snyktoolview.InfoTreeNode; import io.snyk.eclipse.plugin.views.snyktoolview.ProductTreeNode; From 9082725d0d7d74bcf19063e4639f7ad502b7af85 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Fri, 22 Nov 2024 17:24:18 +0100 Subject: [PATCH 8/9] refactor: create BrowserHandler --- .../eclipse/plugin/html/BaseHtmlProvider.java | 31 +++-- .../views/snyktoolview/BrowserHandler.java | 131 ++++++++++++++++++ .../views/snyktoolview/SnykToolView.java | 115 ++------------- 3 files changed, 163 insertions(+), 114 deletions(-) create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java index 2a7ac239..be9e4309 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java @@ -1,5 +1,7 @@ package io.snyk.eclipse.plugin.html; +import java.util.HashMap; +import java.util.Map; import java.util.Random; import org.eclipse.jface.resource.ColorRegistry; @@ -10,6 +12,10 @@ import org.eclipse.ui.themes.IThemeManager; public class BaseHtmlProvider { + private final Random random = new Random(); + private final Map colorCache = new HashMap<>(); + private String nonce = ""; + public String getCss() { return ""; } @@ -23,13 +29,16 @@ public String getInitScript() { } public String getNonce() { + if(!nonce.isEmpty()) { + return nonce; + } String allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - Random random = new Random(); StringBuilder nonceBuilder = new StringBuilder(32); for (int i = 0; i < 32; i++) { nonceBuilder.append(allowedChars.charAt(random.nextInt(allowedChars.length()))); } - return nonceBuilder.toString(); + nonce = nonceBuilder.toString(); + return nonce; } public String replaceCssVariables(String html) { @@ -58,14 +67,16 @@ public String replaceCssVariables(String html) { } public String getColorAsHex(String colorKey, String defaultColor) { - ColorRegistry colorRegistry = getColorRegistry(); - Color color = colorRegistry.get(colorKey); - if (color == null) { - return defaultColor; - } else { - RGB rgb = color.getRGB(); - return String.format("#%02x%02x%02x", rgb.red, rgb.green, rgb.blue); - } + return colorCache.computeIfAbsent(colorKey, key -> { + ColorRegistry colorRegistry = getColorRegistry(); + Color color = colorRegistry.get(colorKey); + if (color == null) { + return defaultColor; + } else { + RGB rgb = color.getRGB(); + return String.format("#%02x%02x%02x", rgb.red, rgb.green, rgb.blue); + } + }); } private ColorRegistry colorRegistry; diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java new file mode 100644 index 00000000..27867094 --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java @@ -0,0 +1,131 @@ +package io.snyk.eclipse.plugin.views.snyktoolview; + +import java.nio.file.Paths; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreeNode; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.BrowserFunction; +import org.eclipse.swt.browser.LocationEvent; +import org.eclipse.swt.browser.LocationListener; +import org.eclipse.swt.program.Program; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.Bundle; + +import io.snyk.eclipse.plugin.html.HtmlProviderFactory; +import io.snyk.eclipse.plugin.utils.ResourceUtils; + +public class BrowserHandler { + private Browser browser; + public BrowserHandler(Browser browser) { + this.browser = browser; + } + + public void initialize() { + new BrowserFunction(browser, "openInEditor") { + @SuppressWarnings("restriction") + @Override + public Object function(Object[] arguments) { + if (arguments.length != 5) { + return null; + } + String filePath = (String) arguments[0]; + var fileUri = Paths.get(filePath).toUri().toASCIIString(); + int startLine = Integer.parseInt(arguments[1].toString()); + int endLine = Integer.parseInt(arguments[2].toString()); + int startCharacter = Integer.parseInt(arguments[3].toString()); + int endCharacter = Integer.parseInt(arguments[4].toString()); + + Display.getDefault().asyncExec(() -> { + try { + Position startPosition = new Position(startLine, startCharacter); + Position endPosition = new Position(endLine, endCharacter); + Range range = new Range(startPosition, endPosition); + + var location = new Location(fileUri, range); + LSPEclipseUtils.openInEditor(location); + + } catch (Exception e) { + e.printStackTrace(); + } + }); + return null; + } + }; + + browser.addLocationListener(new LocationListener() { + @Override + public void changing(LocationEvent event) { + String url = event.location; + if(url.startsWith("http")) { + event.doit = false; + Program.launch(url); + } + } + + @Override + public void changed(LocationEvent event) { + } + }); + + initBrowserText(); + } + + public void updateBrowserContent(String text) { + String htmlContent = generateHtmlContent(text); + browser.setText(htmlContent); + } + + public CompletableFuture updateBrowserContent(TreeNode node) { + // Generate HTML content based on the selected node + return CompletableFuture.supplyAsync(() -> { + return generateHtmlContent(node); + }) + .thenAccept(htmlContent -> { + if (!(node instanceof IssueTreeNode)) return; + Display.getDefault().asyncExec(() -> { + var product = ((ProductTreeNode) node.getParent().getParent()).getProduct(); + var htmlProvider = HtmlProviderFactory.GetHtmlProvider(product); + var content = htmlProvider.replaceCssVariables(htmlContent); + browser.setText(content); + browser.execute(htmlProvider.getInitScript()); + }); + }); + + } + + public String generateHtmlContent(TreeNode node) { + if (node instanceof BaseTreeNode) { + return ((BaseTreeNode) node).getDetails(); + } + return ""; + } + + public String generateHtmlContent(String text) { + return "" + text + "

"; + } + + public void initBrowserText() { + String snykWarningText = Platform.getResourceString(Platform.getBundle("io.snyk.eclipse.plugin"), + "%snyk.trust.dialog.warning.text"); + + Bundle bundle = Platform.getBundle("io.snyk.eclipse.plugin"); + String base64Image = ResourceUtils.getBase64Image(bundle, "logo_snyk.png"); + + browser.setText(" " + + " " + + "Snyk for Eclipse
" + "Snyk Logo" + "

Welcome to Snyk for Eclipse

" + + "

\n" + snykWarningText + "\n" + ""); + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java index f0c1c65a..6b8aa95b 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SnykToolView.java @@ -1,6 +1,3 @@ -/** - * - */ package io.snyk.eclipse.plugin.views.snyktoolview; import java.nio.file.Paths; @@ -61,7 +58,7 @@ public class SnykToolView extends ViewPart implements ISnykToolView { private TreeViewer treeViewer; private Browser browser; private BaseTreeNode rootObject = new RootNode(); - + private BrowserHandler browserHandler; private final static Shell SHELL = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); @Override @@ -90,55 +87,8 @@ public void createPartControl(Composite parent) { // Create Browser // SWT.EDGE will be ignored if OS not windows and will be set to SWT.NONE. browser = new Browser(sashForm, SWT.EDGE); - // Register the Java function as an anonymous class - - new BrowserFunction(browser, "openInEditor") { - @SuppressWarnings("restriction") - @Override - public Object function(Object[] arguments) { - if (arguments.length != 5) { - return null; - } - String filePath = (String) arguments[0]; - var fileUri = Paths.get(filePath).toUri().toASCIIString(); - int startLine = Integer.parseInt(arguments[1].toString()); - int endLine = Integer.parseInt(arguments[2].toString()); - int startCharacter = Integer.parseInt(arguments[3].toString()); - int endCharacter = Integer.parseInt(arguments[4].toString()); - - Display.getDefault().asyncExec(() -> { - try { - Position startPosition = new Position(startLine, startCharacter); - Position endPosition = new Position(endLine, endCharacter); - Range range = new Range(startPosition, endPosition); - - var location = new Location(fileUri, range); - LSPEclipseUtils.openInEditor(location); - - } catch (Exception e) { - e.printStackTrace(); - } - }); - return null; - } - }; - - browser.addLocationListener(new LocationListener() { - @Override - public void changing(LocationEvent event) { - String url = event.location; - if(url.startsWith("http")) { - event.doit = false; - Program.launch(url); - } - } - - @Override - public void changed(LocationEvent event) { - } - }); - initBrowserText(); - + browserHandler = new BrowserHandler(browser); + browserHandler.initialize(); // Set sash weights sashForm.setWeights(new int[] { 1, 2 }); @@ -147,19 +97,19 @@ public void changed(LocationEvent event) { @SuppressWarnings("restriction") @Override public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - if (!selection.isEmpty()) { + Display.getDefault().asyncExec(() -> { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if (selection.isEmpty()) return; TreeNode node = (TreeNode) selection.getFirstElement(); - updateBrowserContent(node); + browserHandler.updateBrowserContent(node); if (node instanceof IssueTreeNode) { IssueTreeNode issueTreeNode = (IssueTreeNode) node; FileTreeNode fileNode = (FileTreeNode) issueTreeNode.getParent(); LSPEclipseUtils.open(fileNode.getPath().toUri().toASCIIString(), issueTreeNode.getIssue().getLSP4JRange()); - } - } - } - }); + } + }); + }}); } private void registerTreeContextMeny(Composite parent) { @@ -169,49 +119,6 @@ private void registerTreeContextMeny(Composite parent) { parent.setMenu(menu); } - private void updateBrowserContent(TreeNode node) { - // Generate HTML content based on the selected node - String htmlContent = generateHtmlContent(node); - if (node instanceof IssueTreeNode) { - var product = ((ProductTreeNode) node.getParent().getParent()).getProduct(); - var htmlProvider = HtmlProviderFactory.GetHtmlProvider(product); - htmlContent = htmlProvider.replaceCssVariables(htmlContent); - browser.setText(htmlContent); - browser.execute(htmlProvider.getInitScript()); - } - } - - private void updateBrowserContent(String text) { - String htmlContent = generateHtmlContent(text); - browser.setText(htmlContent); - } - - private String generateHtmlContent(TreeNode node) { - if (node instanceof BaseTreeNode) { - return ((BaseTreeNode) node).getDetails(); - } - return ""; - } - - private String generateHtmlContent(String text) { - return "" + text + "

"; - } - - private void initBrowserText() { - String snykWarningText = Platform.getResourceString(Platform.getBundle("io.snyk.eclipse.plugin"), - "%snyk.trust.dialog.warning.text"); - - Bundle bundle = Platform.getBundle("io.snyk.eclipse.plugin"); - String base64Image = ResourceUtils.getBase64Image(bundle, "logo_snyk.png"); - - browser.setText(" " - + " " - + "Snyk for Eclipse
" + "Snyk Logo" + "

Welcome to Snyk for Eclipse

" - + "

\n" + snykWarningText + "\n" + ""); - } - @Override public void setFocus() { treeViewer.getControl().setFocus(); @@ -249,7 +156,7 @@ public void addFileNode(ProductTreeNode parent, FileTreeNode toBeAdded) { public void addInfoNode(ProductTreeNode parent, InfoTreeNode toBeAdded) { toBeAdded.setParent(parent); parent.addChild(toBeAdded); - + Display.getDefault().asyncExec(() -> { this.treeViewer.refresh(parent, true); }); From d43b45641da57d3f60baed5948b4ea420eb53d01 Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Fri, 22 Nov 2024 17:33:11 +0100 Subject: [PATCH 9/9] fix: cache theme instance --- .../snyk/eclipse/plugin/html/CodeHtmlProvider.java | 14 +++++++++++--- .../plugin/views/snyktoolview/BrowserHandler.java | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java index 52efb7eb..ebd4e23b 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java @@ -54,10 +54,18 @@ function navigateToIssue(e, target) { } """ + themeScript; } - + + private ITheme currentTheme; + private ITheme getCurrentTheme() { + if(currentTheme != null) { + return currentTheme; + } + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + currentTheme = themeManager.getCurrentTheme(); + return currentTheme; + } private String getThemeScript() { - IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); - ITheme currentTheme = themeManager.getCurrentTheme(); + ITheme currentTheme = getCurrentTheme(); String themeId = currentTheme.getId().toLowerCase(); boolean isDarkTheme = themeId.contains("dark"); diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java index 27867094..fbc49c7d 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java @@ -87,11 +87,13 @@ public void updateBrowserContent(String text) { public CompletableFuture updateBrowserContent(TreeNode node) { // Generate HTML content based on the selected node + if (!(node instanceof IssueTreeNode)) return CompletableFuture.completedFuture(null); + browser.setText("Loading..."); + return CompletableFuture.supplyAsync(() -> { return generateHtmlContent(node); }) .thenAccept(htmlContent -> { - if (!(node instanceof IssueTreeNode)) return; Display.getDefault().asyncExec(() -> { var product = ((ProductTreeNode) node.getParent().getParent()).getProduct(); var htmlProvider = HtmlProviderFactory.GetHtmlProvider(product);