diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ca6eefd..79880dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Snyk Security Changelog +## [3.0.1] +### Changes +- New Summary Panel for scan information and net new issues filter. + ## [3.0.0] ### Changes - process api URL from hasAuthenticated message diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/analytics/TaskProcessor.java b/plugin/src/main/java/io/snyk/eclipse/plugin/analytics/TaskProcessor.java index 4449e448..3b4d2989 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/analytics/TaskProcessor.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/analytics/TaskProcessor.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; @@ -17,7 +18,7 @@ */ public class TaskProcessor { // left = taskToExecute, right = callback function - private final ConcurrentLinkedQueue, Consumer>> taskQueue = new ConcurrentLinkedQueue<>(); + private final Queue, Consumer>> taskQueue = new ConcurrentLinkedQueue<>(); private TaskProcessor() { CompletableFuture.runAsync(() -> { @@ -39,9 +40,11 @@ public static TaskProcessor getInstance() { } private void start() { + final List, Consumer>> copyForSending = new ArrayList<>(); + while (true) { String authToken = Preferences.getInstance().getAuthToken(); - var lc = SnykExtendedLanguageClient.getInstance(); + SnykExtendedLanguageClient lc = SnykExtendedLanguageClient.getInstance(); if (taskQueue.isEmpty() || authToken == null || authToken.isBlank() || lc == null) { try { Thread.sleep(1000); @@ -50,8 +53,9 @@ private void start() { } continue; } - List, Consumer>> copyForSending = new ArrayList<>( - taskQueue); + + copyForSending.clear(); // Clear the list before reuse + copyForSending.addAll(taskQueue); // Add all elements from taskQueue for (Pair, Consumer> event : copyForSending) { try { 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 9298bb4f..0a070b3f 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 @@ -20,33 +20,33 @@ public class BaseHtmlProvider { private final Random random = new Random(); private final Map colorCache = new HashMap<>(); private String nonce = ""; - - public String getCss() { - return ""; - } - - public String getJs() { - return ""; - } - - public String getInitScript() { - return ""; - } - - public String getNonce() { - if(!nonce.isEmpty()) { - return nonce; - } - String allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - StringBuilder nonceBuilder = new StringBuilder(32); - for (int i = 0; i < 32; i++) { - nonceBuilder.append(allowedChars.charAt(random.nextInt(allowedChars.length()))); - } - nonce = nonceBuilder.toString(); - return nonce; - } - - public String getNoDescriptionHtml() { + + public String getCss() { + return ""; + } + + public String getJs() { + return ""; + } + + public String getInitScript() { + return ""; + } + + public String getNonce() { + if (!nonce.isEmpty()) { + return nonce; + } + String allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder nonceBuilder = new StringBuilder(32); + for (int i = 0; i < 32; i++) { + nonceBuilder.append(allowedChars.charAt(random.nextInt(allowedChars.length()))); + } + nonce = nonceBuilder.toString(); + return nonce; + } + + public String getNoDescriptionHtml() { String snykWarningText = Platform.getResourceString(Platform.getBundle("io.snyk.eclipse.plugin"), "snyk.panel.auth.trust.warning.text"); @@ -75,7 +75,7 @@ public String getNoDescriptionHtml() { a { color: var(--link-color) } - + div { padding: 20px } @@ -94,116 +94,126 @@ public String getNoDescriptionHtml() { return html; } + public String replaceCssVariables(String html) { + // Build the CSS with the nonce + String nonce = getNonce(); + String css = ""; + String htmlStyled = html.replace("${ideStyle}", css); + htmlStyled = htmlStyled.replace("", css); + htmlStyled = htmlStyled.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 + htmlStyled = htmlStyled.replace("var(--text-color)", + getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR", "#000000")); + + htmlStyled = htmlStyled.replace("var(--ide-background-color)", + getColorAsHex("org.eclipse.ui.workbench.ACTIVE_NOFOCUS_TAB_BG_START", "#FFFFFF")); + htmlStyled = htmlStyled.replace("var(--background-color)", + getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END", "#FFFFFF")); + htmlStyled = htmlStyled.replace("var(--code-background-color)", + getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); + htmlStyled = htmlStyled.replace("var(--button-color)", + getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); + htmlStyled = htmlStyled.replace("var(--circle-color)", + getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); + + htmlStyled = htmlStyled.replace("var(--border-color)", + getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC")); + htmlStyled = htmlStyled.replace("var(--link-color)", getColorAsHex("ACTIVE_HYPERLINK_COLOR", "#0000FF")); + htmlStyled = htmlStyled.replace("var(--horizontal-border-color)", + getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC")); + + htmlStyled = htmlStyled.replace("${headerEnd}", ""); + htmlStyled = htmlStyled.replace("${nonce}", nonce); + htmlStyled = htmlStyled.replace("ideNonce", nonce); + htmlStyled = htmlStyled.replace("${ideScript}", ""); + + return htmlStyled; + } - 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_END", "#FFFFFF")); - html = html.replace("var(--code-background-color)", getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); - html = html.replace("var(--button-color)", getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); - html = html.replace("var(--circle-color)", getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0")); - - html = html.replace("var(--border-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC")); - html = html.replace("var(--link-color)", getColorAsHex("ACTIVE_HYPERLINK_COLOR", "#0000FF")); - html = html.replace("var(--horizontal-border-color)", getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC")); - - html = html.replace("${headerEnd}", ""); - html = html.replace("${nonce}", nonce); - html = html.replace("ideNonce", nonce); - html = html.replace("${ideScript}", ""); - - return html; - } - - public String getColorAsHex(String colorKey, String defaultColor) { - if(Preferences.getInstance().isTest()) { + public String getColorAsHex(String colorKey, String defaultColor) { + if (Preferences.getInstance().isTest()) { return ""; } - 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); - } - }); - } - - public Boolean isDarkTheme() { - var darkColor = getColorAsHex("org.eclipse.ui.workbench.DARK_BACKGROUND", ""); - return darkColor.equals("true"); - } - - private ColorRegistry colorRegistry; - private ColorRegistry getColorRegistry() { - if(colorRegistry != null) { - return colorRegistry; - } - ITheme currentTheme = getCurrentTheme(); - colorRegistry = currentTheme.getColorRegistry(); - return colorRegistry; - } - - - private ITheme currentTheme; - public ITheme getCurrentTheme() { - if(currentTheme != null) { - return currentTheme; - } - IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); - currentTheme = themeManager.getCurrentTheme(); - return currentTheme; - } - - public String getErrorHtml(String errorMessage, String path) { + 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); + } + }); + } + + public Boolean isDarkTheme() { + var darkColor = getColorAsHex("org.eclipse.ui.workbench.DARK_BACKGROUND", ""); + return Boolean.valueOf(darkColor); + } + + private ColorRegistry colorRegistry; + + private ColorRegistry getColorRegistry() { + if (colorRegistry != null) { + return colorRegistry; + } + ITheme currentTheme = getCurrentTheme(); + colorRegistry = currentTheme.getColorRegistry(); + return colorRegistry; + } + + private ITheme currentTheme; + + public ITheme getCurrentTheme() { + if (currentTheme != null) { + return currentTheme; + } + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + currentTheme = themeManager.getCurrentTheme(); + return currentTheme; + } + + public String getErrorHtml(String errorMessage, String path) { var html = """ - - - - - - Snyk for Eclipse - - - -
-
-

An error occurred:

-

- - - - -
Error message:%s
Path:%s
-

-
-
- - - """.formatted(errorMessage, path); + + + + + + Snyk for Eclipse + + + +
+
+

An error occurred:

+

+ + + + +
Error message:%s
Path:%s
+

+
+
+ + + """.formatted(errorMessage, path); return replaceCssVariables(html); } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/html/StaticPageHtmlProvider.java b/plugin/src/main/java/io/snyk/eclipse/plugin/html/StaticPageHtmlProvider.java index dd4204b0..6985fc0d 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/html/StaticPageHtmlProvider.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/html/StaticPageHtmlProvider.java @@ -1,12 +1,20 @@ package io.snyk.eclipse.plugin.html; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; +import io.snyk.eclipse.plugin.utils.SnykLogger; import io.snyk.eclipse.plugin.utils.ResourceUtils; public class StaticPageHtmlProvider extends BaseHtmlProvider { private static StaticPageHtmlProvider instance = new StaticPageHtmlProvider(); + private static ILog logger; public static StaticPageHtmlProvider getInstance() { synchronized (StaticPageHtmlProvider.class) { @@ -18,7 +26,7 @@ public static StaticPageHtmlProvider getInstance() { } return instance; } - + private String head = """ @@ -148,6 +156,31 @@ public String getInitHtml() { """.formatted(head, base64Image, snykWarningText); return replaceCssVariables(html); } - - + + public String getSummaryInitHtml() { + if (logger == null) { + logger = Platform.getLog(getClass()); + } + + StringBuilder content = new StringBuilder(); + try (InputStream inputStream = getClass().getResourceAsStream("/ui/html/ScanSummaryInit.html"); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + + String line = reader.readLine(); + while (line != null) { + // Process the line here + content.append(line).append(System.lineSeparator()); + line = reader.readLine(); + } + } catch (IOException e) { + SnykLogger.logError(e); + } + + return replaceCssVariables(content.toString()); + } + + public String getFormattedSummaryHtml(String summary) { + String summaryWithFunc = summary.replace("${ideFunc}", "window.enableDelta(isEnabled);"); + return replaceCssVariables(summaryWithFunc); + } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BaseBranchDialog.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BaseBranchDialog.java index 691fd48a..bd5018d1 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BaseBranchDialog.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BaseBranchDialog.java @@ -25,7 +25,7 @@ public class BaseBranchDialog { public BaseBranchDialog() { } - public void open(Display display, Path projectPath, String[] localBranches) { + public void open(Display display, Path projectPath, String... localBranches) { Shell shell = new Shell(display, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM); shell.setText("Choose base branch for net-new issues scanning"); shell.setLayout(new GridLayout(1, false)); @@ -50,7 +50,8 @@ public void widgetSelected(SelectionEvent e) { folderConfigs.setBaseBranch(projectPath, selectedBranch); shell.close(); CompletableFuture.runAsync(() -> { - SnykExtendedLanguageClient.getInstance().triggerScan(projectPath); + SnykExtendedLanguageClient lc = SnykExtendedLanguageClient.getInstance(); + lc.triggerScan(projectPath); }); } else { SnykLogger.logInfo("Branch is not a valid local branch for repository: " + projectPath); 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 f08dc8f8..5413af0e 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 @@ -5,11 +5,7 @@ import java.nio.file.Paths; import java.util.concurrent.CompletableFuture; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.core.commands.NotEnabledException; -import org.eclipse.core.commands.NotHandledException; import org.eclipse.core.commands.common.CommandException; -import org.eclipse.core.commands.common.NotDefinedException; import org.eclipse.jface.viewers.TreeNode; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4j.Location; @@ -38,6 +34,7 @@ @SuppressWarnings("restriction") public class BrowserHandler { + private static final int validNumberOfArguments = 5; private Browser browser; private String initScript = ""; @@ -49,7 +46,7 @@ public void initialize() { new BrowserFunction(browser, "openInEditor") { @Override public Object function(Object[] arguments) { - if (arguments.length != 5) { + if (arguments.length != validNumberOfArguments) { return null; } String filePath = (String) arguments[0]; @@ -169,7 +166,7 @@ public CompletableFuture updateBrowserContent(TreeNode node) { } private BaseHtmlProvider getHtmlProvider(TreeNode node) { - var product = ""; + String product; if (node instanceof IssueTreeNode) { product = ((ProductTreeNode) node.getParent().getParent()).getProduct(); } else if (node instanceof ProductTreeNode) { diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/ISnykToolView.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/ISnykToolView.java index c72ddd4b..5096a28f 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/ISnykToolView.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/ISnykToolView.java @@ -80,6 +80,11 @@ public interface ISnykToolView { */ abstract void refreshBrowser(String status); + /** + * Refreshes the SummaryPanel + */ + abstract void updateSummary(String status); + /** * Returns the tree root * 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 5b70bf7f..6bab41c3 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 @@ -58,6 +58,8 @@ * removed. */ public class SnykToolView extends ViewPart implements ISnykToolView { + public SnykToolView() { + } /** * The ID of the view as specified by the extension. @@ -67,16 +69,27 @@ public class SnykToolView extends ViewPart implements ISnykToolView { private TreeViewer treeViewer; private Browser browser; private BrowserHandler browserHandler; + private Browser summaryBrowser; + private SummaryBrowserHandler summaryBrowserHandler; private FolderConfigs folderConfigs = FolderConfigs.getInstance(); private TreeNode selectedNode; @Override public void createPartControl(Composite parent) { - SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL); - sashForm.setLayout(new FillLayout()); + SashForm horizontalSashForm = new SashForm(parent, SWT.HORIZONTAL); + horizontalSashForm.setLayout(new FillLayout()); + + // Create vertical SashForm for new Browser and TreeViewer + SashForm verticalSashForm = new SashForm(horizontalSashForm, SWT.VERTICAL); + verticalSashForm.setLayout(new FillLayout()); + + // Create new Browser (top quarter) + summaryBrowser = new Browser(verticalSashForm, SWT.EDGE); + summaryBrowserHandler = new SummaryBrowserHandler(summaryBrowser); + summaryBrowserHandler.initialize(); // Create TreeViewer - treeViewer = new TreeViewer(sashForm, SWT.BORDER); + treeViewer = new TreeViewer(verticalSashForm, SWT.BORDER); Tree tree = treeViewer.getTree(); tree.setHeaderVisible(false); tree.setLinesVisible(false); @@ -93,13 +106,15 @@ public void createPartControl(Composite parent) { registerTreeContextMenu(treeViewer.getControl()); + verticalSashForm.setWeights(new int[] { 1, 3 }); + // Create Browser // SWT.EDGE will be ignored if OS not windows and will be set to SWT.NONE. - browser = new Browser(sashForm, SWT.EDGE); + browser = new Browser(horizontalSashForm, SWT.EDGE); browserHandler = new BrowserHandler(browser); browserHandler.initialize(); // Set sash weights - sashForm.setWeights(new int[] { 1, 2 }); + horizontalSashForm.setWeights(new int[] { 1, 2 }); // Add selection listener to the tree treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @@ -133,7 +148,7 @@ public void selectionChanged(SelectionChangedEvent event) { if (Preferences.isDeltaEnabled()) this.enableDelta(); - + // initialize the filters TreeFilterManager.getInstance(); } @@ -164,7 +179,7 @@ public void run() { protected IStatus run(IProgressMonitor monitor) { try { var result = CommandHandler.getInstance().ignoreIssue(issue).get(10, TimeUnit.SECONDS); - outputCommandResult(result); + outputCommandResult(result); itn.setText("[ IGNORED ] " + itn.getText()); Display.getDefault().asyncExec(() -> { treeViewer.refresh(itn, true); @@ -318,6 +333,13 @@ public void refreshBrowser(String status) { }); }; + @Override + public void updateSummary(String summary) { + Display.getDefault().asyncExec(() -> { + this.summaryBrowserHandler.setBrowserText(summary); + }); + } + @Override public void resetNode(BaseTreeNode node) { if (node != null) @@ -450,4 +472,4 @@ protected void outputCommandResult(Object result) { } } -} +} \ No newline at end of file diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SummaryBrowserHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SummaryBrowserHandler.java new file mode 100644 index 00000000..304a0e9d --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/SummaryBrowserHandler.java @@ -0,0 +1,48 @@ +package io.snyk.eclipse.plugin.views.snyktoolview; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.BrowserFunction; + +import io.snyk.eclipse.plugin.html.StaticPageHtmlProvider; +import io.snyk.eclipse.plugin.preferences.Preferences; +import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient; + +public class SummaryBrowserHandler { + private Browser browser; + + public SummaryBrowserHandler(Browser browser) { + this.browser = browser; + } + + public void initialize() { + + new BrowserFunction(browser, "enableDelta") { + @Override + public Object function(Object[] arguments) { + boolean value = false; + if (arguments.length > 0 && arguments[0] instanceof Boolean) { + value = (Boolean) arguments[0]; + } + + Preferences.getInstance().store(Preferences.ENABLE_DELTA, Boolean.toString(value)); + + CompletableFuture.runAsync(() -> SnykExtendedLanguageClient.getInstance().updateConfiguration()); + + return null; + } + }; + + setDefaultBrowserText(); + } + + public void setDefaultBrowserText() { + browser.setText(StaticPageHtmlProvider.getInstance().getSummaryInitHtml()); + } + + public void setBrowserText(String summary) { + browser.setText(StaticPageHtmlProvider.getInstance().getFormattedSummaryHtml(summary)); + } + +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/BaseProductFilterHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/BaseProductFilterHandler.java index 4f98a1a4..b885538c 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/BaseProductFilterHandler.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/BaseProductFilterHandler.java @@ -22,10 +22,9 @@ public Object execute(ExecutionEvent event) throws ExecutionException { // now we can apply the filter new ProductFilter(TreeFilterManager.getInstance(), filterName).applyFilter(); - - final var lc = SnykExtendedLanguageClient.getInstance(); - lc.updateConfiguration(); - + + SnykExtendedLanguageClient.getInstance().updateConfiguration(); + return returnValue; } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/CollapseTreeHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/CollapseTreeHandler.java index a0e39318..98e7f971 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/CollapseTreeHandler.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/CollapseTreeHandler.java @@ -13,15 +13,15 @@ public Object execute(ExecutionEvent event) throws ExecutionException { String commandId = event.getCommand().getId(); - switch (commandId) { - case "io.snyk.eclipse.plugin.commands.TreeCollapse": + String treeCollapse = "io.snyk.eclipse.plugin.commands.TreeCollapse"; + String treeExpand = "io.snyk.eclipse.plugin.commands.TreeExpand"; + + if (commandId.equals(treeCollapse)) { SnykStartup.getView().getTreeViewer().collapseAll(); - break; - case "io.snyk.eclipse.plugin.commands.TreeExpand": + } else if (commandId.equals(treeExpand)) { SnykStartup.getView().getTreeViewer().expandAll(); - break; } return null; } -} \ No newline at end of file +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/FilterNetNewIssuesHandler.java b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/FilterNetNewIssuesHandler.java index da22748f..24e72d70 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/FilterNetNewIssuesHandler.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/handlers/FilterNetNewIssuesHandler.java @@ -32,7 +32,7 @@ public Object execute(ExecutionEvent event) throws ExecutionException { CompletableFuture.runAsync(() -> { // Update the Snyk Language Server configuration. - final var lc = SnykExtendedLanguageClient.getInstance(); + SnykExtendedLanguageClient lc = SnykExtendedLanguageClient.getInstance(); lc.updateConfiguration(); lc.triggerScan(null); }); diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardModel.java b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardModel.java index 42edeb6d..19e68a4f 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardModel.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardModel.java @@ -19,10 +19,10 @@ public void resetPreferences() { Preferences.getInstance().setIsInsecure(initialUnknownCerts); Preferences.getInstance().store(Preferences.AUTH_TOKEN_KEY, initialAuthToken); - SnykExtendedLanguageClient client = SnykExtendedLanguageClient.getInstance(); + SnykExtendedLanguageClient lc = SnykExtendedLanguageClient.getInstance(); // The language client may be null when the extension first loads, so only update configuration if we are able. - if (client != null) { - client.updateConfiguration(); + if (lc != null) { + lc.updateConfiguration(); }; } } diff --git a/plugin/src/main/java/io/snyk/languageserver/LsConstants.java b/plugin/src/main/java/io/snyk/languageserver/LsConstants.java index 72178334..243d8b18 100644 --- a/plugin/src/main/java/io/snyk/languageserver/LsConstants.java +++ b/plugin/src/main/java/io/snyk/languageserver/LsConstants.java @@ -1,22 +1,29 @@ package io.snyk.languageserver; -public interface LsConstants { - String COMMAND_LOGIN = "snyk.login"; - String COMMAND_GET_ACTIVE_USER = "snyk.getActiveUser"; - String COMMAND_WORKSPACE_SCAN = "snyk.workspace.scan"; - String COMMAND_WORKSPACE_FOLDER_SCAN = "snyk.workspaceFolder.scan"; - String COMMAND_TRUST_WORKSPACE_FOLDERS = "snyk.trustWorkspaceFolders"; - String COMMAND_GET_SETTINGS_SAST_ENABLED = "snyk.getSettingsSastEnabled"; - String COMMAND_GENERATE_ISSUE_DESCRIPTION = "snyk.generateIssueDescription"; - String COMMAND_REPORT_ANALYTICS = "snyk.reportAnalytics"; - String COMMAND_GET_FEATURE_FLAG_STATUS = "snyk.getFeatureFlagStatus"; - String COMMAND_CODE_FIX_DIFFS = "snyk.code.fixDiffs"; - String COMMAND_CODE_SUBMIT_FIX_FEEDBACK = "snyk.code.submitFixFeedback"; - String SNYK_HAS_AUTHENTICATED = "$/snyk.hasAuthenticated"; - String SNYK_IS_AVAILABLE_CLI = "$/snyk.isAvailableCli"; - String SNYK_ADD_TRUSTED_FOLDERS = "$/snyk.addTrustedFolders"; - String SNYK_SCAN = "$/snyk.scan"; - String SNYK_PUBLISH_DIAGNOSTICS_316 = "$/snyk.publishDiagnostics316"; - String SNYK_FOLDER_CONFIG = "$/snyk.folderConfigs"; - String COMMAND_SNYK_CLI = "snyk.executeCLI"; +public final class LsConstants { + + // Prevent instantiation of the class + private LsConstants() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static final String COMMAND_LOGIN = "snyk.login"; + public static final String COMMAND_GET_ACTIVE_USER = "snyk.getActiveUser"; + public static final String COMMAND_WORKSPACE_SCAN = "snyk.workspace.scan"; + public static final String COMMAND_WORKSPACE_FOLDER_SCAN = "snyk.workspaceFolder.scan"; + public static final String COMMAND_TRUST_WORKSPACE_FOLDERS = "snyk.trustWorkspaceFolders"; + public static final String COMMAND_GET_SETTINGS_SAST_ENABLED = "snyk.getSettingsSastEnabled"; + public static final String COMMAND_GENERATE_ISSUE_DESCRIPTION = "snyk.generateIssueDescription"; + public static final String COMMAND_REPORT_ANALYTICS = "snyk.reportAnalytics"; + public static final String COMMAND_GET_FEATURE_FLAG_STATUS = "snyk.getFeatureFlagStatus"; + public static final String COMMAND_CODE_FIX_DIFFS = "snyk.code.fixDiffs"; + public static final String COMMAND_CODE_SUBMIT_FIX_FEEDBACK = "snyk.code.submitFixFeedback"; + public static final String COMMAND_SNYK_CLI = "snyk.executeCLI"; + public static final String SNYK_HAS_AUTHENTICATED = "$/snyk.hasAuthenticated"; + public static final String SNYK_IS_AVAILABLE_CLI = "$/snyk.isAvailableCli"; + public static final String SNYK_ADD_TRUSTED_FOLDERS = "$/snyk.addTrustedFolders"; + public static final String SNYK_SCAN = "$/snyk.scan"; + public static final String SNYK_PUBLISH_DIAGNOSTICS_316 = "$/snyk.publishDiagnostics316"; + public static final String SNYK_FOLDER_CONFIG = "$/snyk.folderConfigs"; + public static final String SNYK_SCAN_SUMMARY = "$/snyk.scanSummary"; } \ No newline at end of file diff --git a/plugin/src/main/java/io/snyk/languageserver/ScanState.java b/plugin/src/main/java/io/snyk/languageserver/ScanState.java index 32985144..c36d28b1 100644 --- a/plugin/src/main/java/io/snyk/languageserver/ScanState.java +++ b/plugin/src/main/java/io/snyk/languageserver/ScanState.java @@ -1,5 +1,6 @@ package io.snyk.languageserver; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ScanState { @@ -7,7 +8,7 @@ private ScanState() { } - private final ConcurrentHashMap scanInProgress = new ConcurrentHashMap<>(); + private final Map scanInProgress = new ConcurrentHashMap<>(); private static ScanState instance = new ScanState(); public static ScanState getInstance() { 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 6365be2c..315976fb 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -76,7 +76,6 @@ import io.snyk.eclipse.plugin.views.snyktoolview.IssueTreeNode; import io.snyk.eclipse.plugin.views.snyktoolview.ProductTreeNode; import io.snyk.eclipse.plugin.views.snyktoolview.SnykToolView; -import io.snyk.eclipse.plugin.views.snyktoolview.handlers.IHandlerCommands; import io.snyk.eclipse.plugin.wizards.SnykWizard; import io.snyk.languageserver.CommandHandler; import io.snyk.languageserver.FeatureFlagConstants; @@ -97,6 +96,7 @@ import io.snyk.languageserver.protocolextension.messageObjects.SnykIsAvailableCliParams; import io.snyk.languageserver.protocolextension.messageObjects.SnykScanParam; import io.snyk.languageserver.protocolextension.messageObjects.SnykTrustedFoldersParams; +import io.snyk.languageserver.protocolextension.messageObjects.SummaryPanelParams; import io.snyk.languageserver.protocolextension.messageObjects.scanResults.Issue; @SuppressWarnings("restriction") @@ -112,11 +112,12 @@ public class SnykExtendedLanguageClient extends LanguageClientImpl { private Object chSyncObject = new Object(); private CommandHandler commandHandler; - private static SnykExtendedLanguageClient instance = null; + private static SnykExtendedLanguageClient instance; public SnykExtendedLanguageClient() { super(); - instance = this; + //TODO, fix this; Identifies a possible unsafe usage of a static field. + instance = this; //NOPMD om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); registerPluginInstalledEventTask(); registerRefreshFeatureFlagsTask(); @@ -132,7 +133,7 @@ public LanguageServer getConnectedLanguageServer() { } return super.getLanguageServer(); } - + public void updateConfiguration() { this.configurationUpdater.configurationChanged(); if (this.toolView != null) { @@ -367,7 +368,7 @@ public void snykScan(SnykScanParam param) { for (ProductTreeNode productTreeNode : affectedProductTreeNodes) { this.toolView.resetNode(productTreeNode); addInfoNodes(productTreeNode, param.getFolderPath(), issueCache); - populateFileAndIssueNodes(productTreeNode, param.getFolderPath(), issueCache); + populateFileAndIssueNodes(productTreeNode, issueCache); } break; case SCAN_STATE_ERROR: @@ -376,11 +377,19 @@ public void snykScan(SnykScanParam param) { productTreeNode.setErrorMessage(param.getErrorMessage()); } break; + default: + break; } setNodeState(param.getStatus(), affectedProductTreeNodes, issueCache); this.toolView.refreshBrowser(param.getStatus()); } + @JsonNotification(value = LsConstants.SNYK_SCAN_SUMMARY) + public void updateSummaryPanel(SummaryPanelParams summary) { + openToolView(); + this.toolView.updateSummary(summary.getSummary()); + } + @JsonNotification(value = LsConstants.SNYK_FOLDER_CONFIG) public void folderConfig(FolderConfigsParam folderConfigParam) { List folderConfigs = folderConfigParam != null ? folderConfigParam.getFolderConfigs() : List.of(); @@ -429,7 +438,7 @@ private void setNodeState(String status, Set affectedProductTre if (affectedProductTreeNodes.isEmpty()) { return; } - var nodeText = ""; + String nodeText; if (status.equals(SCAN_STATE_IN_PROGRESS)) { nodeText = NODE_TEXT_SCANNING; @@ -529,19 +538,21 @@ public void publishDiagnostics316(PublishDiagnostics316Param param) { populateIssueCache(param, filePath); } - private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, String folderPath, - SnykIssueCache issueCache) { + private void populateFileAndIssueNodes(ProductTreeNode productTreeNode, SnykIssueCache issueCache) { var cacheHashMap = issueCache.getCacheByDisplayProduct(productTreeNode.getProduct()); + List issuesList = new ArrayList<>(); for (var kv : cacheHashMap.entrySet()) { var fileName = kv.getKey(); - var issues = new ArrayList<>(kv.getValue()); + issuesList.clear(); // Clear the list instead of creating a new one + issuesList.addAll(kv.getValue()); - if (issues.isEmpty()) + if (issuesList.isEmpty()) continue; - FileTreeNode fileNode = new FileTreeNode(fileName); + + FileTreeNode fileNode = new FileTreeNode(fileName); //NOPMD toolView.addFileNode(productTreeNode, fileNode); - for (Issue issue : issues) { - toolView.addIssueNode(fileNode, new IssueTreeNode(issue)); + for (Issue issue : issuesList) { + toolView.addIssueNode(fileNode, new IssueTreeNode(issue)); //NOPMD } } } @@ -581,6 +592,8 @@ private void populateIssueCache(PublishDiagnostics316Param param, String filePat case SCAN_PARAMS_IAC: issueCache.addIacIssues(filePath, issueList); break; + default: + break; } } diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SummaryPanelParams.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SummaryPanelParams.java new file mode 100644 index 00000000..286251c6 --- /dev/null +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SummaryPanelParams.java @@ -0,0 +1,13 @@ +package io.snyk.languageserver.protocolextension.messageObjects; + +public class SummaryPanelParams { + private String scanSummary; + + public String getSummary() { + return scanSummary; + } + + public void setSummary(String scanSummary) { + this.scanSummary = scanSummary; + } +} diff --git a/plugin/src/main/resources/ui/html/ScanSummaryInit.html b/plugin/src/main/resources/ui/html/ScanSummaryInit.html new file mode 100644 index 00000000..01616e67 --- /dev/null +++ b/plugin/src/main/resources/ui/html/ScanSummaryInit.html @@ -0,0 +1,44 @@ + + + + + + + +
+

Snyk Security is loading...

+
+
+
+ +

Waiting for the Snyk CLI to be downloaded and the Language Server to be initialized.

+
+
+ +