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 + "