From c85ca00fea966254b4f8675709000861ff7c3a5c Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Mon, 10 Oct 2022 15:04:17 +0530 Subject: [PATCH 1/7] feat: add workspace trust feature for scanning --- CHANGELOG.md | 4 + plugin/META-INF/MANIFEST.MF | 2 +- .../plugin/properties/PreferencesPage.java | 198 +++++----- .../properties/preferences/Preferences.java | 1 + .../eclipse/plugin/runner/SnykCliRunner.java | 10 +- .../UntrustedScanRequestedException.java | 27 ++ .../LsConfigurationUpdater.java | 360 +++++++++--------- .../languageserver/download/LsBinaries.java | 2 +- .../SnykExtendedLanguageClient.java | 33 +- .../SnykTrustedFoldersParams.java | 13 + .../plugin/runner/SnykCliRunnerTest.java | 38 ++ .../SnykLanguageServerTest.java | 32 ++ .../SnykExtendedLanguageClientTest.java | 46 +++ 13 files changed, 488 insertions(+), 278 deletions(-) create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/runner/UntrustedScanRequestedException.java create mode 100644 plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SnykTrustedFoldersParams.java create mode 100644 tests/src/test/java/io/snyk/eclipse/plugin/runner/SnykCliRunnerTest.java create mode 100644 tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a82455bc..7b6ce1a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [2.0.0] - Unreleased ### Changes +- add workspace trust feature + +## [2.0.0] - v20220927.182222 +### Changes - add support for insecure and custom CAs to download and API checks ## [2.0.0] - v20220905.164345 diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 3d751edc..f5794159 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.jdt.core, org.eclipse.core.resources, - org.eclipse.lsp4e;bundle-version="0.13.9", + org.eclipse.lsp4e;bundle-version="[0.13.9,0.14.0.qualifier]", org.eclipse.lsp4e.jdt;bundle-version="0.10.1", org.eclipse.equinox.security, org.eclipse.equinox.security.ui, diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/PreferencesPage.java b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/PreferencesPage.java index 29606927..a3f79434 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/PreferencesPage.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/PreferencesPage.java @@ -8,6 +8,9 @@ import io.snyk.languageserver.LsRuntimeEnvironment; import io.snyk.languageserver.download.HttpClientFactory; import io.snyk.languageserver.download.LsBinaries; + +import java.io.File; + import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditor; @@ -19,103 +22,114 @@ import org.eclipse.ui.IWorkbenchPreferencePage; public class PreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { - private BooleanFieldEditor snykCodeCheckbox; - - public PreferencesPage() { - super(GRID); - } - - @Override - public void init(IWorkbench workbench) { - setPreferenceStore(io.snyk.eclipse.plugin.properties.preferences.Preferences.getInstance().getStore()); - setMessage("Snyk Preferences"); - } - - @Override - protected void createFieldEditors() { - TokenFieldEditor tokenField = new TokenFieldEditor( - io.snyk.eclipse.plugin.properties.preferences.Preferences.getInstance(), - io.snyk.eclipse.plugin.properties.preferences.Preferences.AUTH_TOKEN_KEY, "Snyk API Token:", - getFieldEditorParent()); - addField(tokenField); - addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.PATH_KEY, "Path:", - getFieldEditorParent())); - addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ENDPOINT_KEY, - "Custom Endpoint:", getFieldEditorParent())); - addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.INSECURE_KEY, - "Allow unknown certificate authorities", getFieldEditorParent())); - - addField(space()); + private BooleanFieldEditor snykCodeCheckbox; + + public PreferencesPage() { + super(GRID); + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(io.snyk.eclipse.plugin.properties.preferences.Preferences.getInstance().getStore()); + setMessage("Snyk Preferences"); + } + + @Override + protected void createFieldEditors() { + TokenFieldEditor tokenField = new TokenFieldEditor( + io.snyk.eclipse.plugin.properties.preferences.Preferences.getInstance(), + io.snyk.eclipse.plugin.properties.preferences.Preferences.AUTH_TOKEN_KEY, "Snyk API Token:", + getFieldEditorParent()); + addField(tokenField); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.PATH_KEY, "Path:", + getFieldEditorParent())); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ENDPOINT_KEY, + "Custom Endpoint:", getFieldEditorParent())); + addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.INSECURE_KEY, + "Allow unknown certificate authorities", getFieldEditorParent())); + + addField(space()); addField(new LabelFieldEditor("The following options involve the Snyk Language Server.", getFieldEditorParent())); - addField(new LabelFieldEditor( - "Activating Snyk Code will cause upload of source code to Snyk or the given endpoint address.", - getFieldEditorParent())); - addField(space()); + addField(new LabelFieldEditor( + "Activating Snyk Code will cause upload of source code to Snyk or the given endpoint address.", + getFieldEditorParent())); + addField(space()); addField(new BooleanFieldEditor( io.snyk.eclipse.plugin.properties.preferences.Preferences.ACTIVATE_SNYK_OPEN_SOURCE, - "Snyk Open Source enabled", getFieldEditorParent())); - snykCodeCheckbox = new BooleanFieldEditor( - io.snyk.eclipse.plugin.properties.preferences.Preferences.ACTIVATE_SNYK_CODE, "Snyk Code enable" + "d", - getFieldEditorParent()); - - addField(snykCodeCheckbox); - addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ACTIVATE_SNYK_IAC, - "Snyk Infrastructure-as-Code enabled", getFieldEditorParent())); - - addField(space()); - addField(new LabelFieldEditor("Advanced options:", getFieldEditorParent())); - addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ORGANIZATION_KEY, - "Organization:", getFieldEditorParent())); - addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ADDITIONAL_PARAMETERS, - "Additional Parameters:", getFieldEditorParent())); - addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ADDITIONAL_ENVIRONMENT, - "Additional Environment:", getFieldEditorParent())); - - addField(space()); - BooleanFieldEditor manageBinaries = new BooleanFieldEditor(Preferences.MANAGE_BINARIES_AUTOMATICALLY, - "Update and install Snyk binaries automatically", getFieldEditorParent()); - manageBinaries.setPropertyChangeListener((PropertyChangeEvent propertyChangeEvent) -> { - System.out.println("managed bionaries changed"); - }); - addField(manageBinaries); - addField(new FileFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.LS_BINARY_KEY, - "Snyk Language Server:", getFieldEditorParent())); - addField(new FileFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.CLI_PATH, "Snyk CLI:", - getFieldEditorParent())); - - addField(space()); - - addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.SEND_ERROR_REPORTS, - "Send error reports to Snyk", getFieldEditorParent())); + "Snyk Open Source enabled", getFieldEditorParent())); + snykCodeCheckbox = new BooleanFieldEditor( + io.snyk.eclipse.plugin.properties.preferences.Preferences.ACTIVATE_SNYK_CODE, "Snyk Code enable" + "d", + getFieldEditorParent()); + + addField(snykCodeCheckbox); + addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ACTIVATE_SNYK_IAC, + "Snyk Infrastructure-as-Code enabled", getFieldEditorParent())); + + addField(space()); + addField(new LabelFieldEditor("Advanced options:", getFieldEditorParent())); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ORGANIZATION_KEY, + "Organization:", getFieldEditorParent())); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ADDITIONAL_PARAMETERS, + "Additional Parameters:", getFieldEditorParent())); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.ADDITIONAL_ENVIRONMENT, + "Additional Environment:", getFieldEditorParent())); + + addField(space()); + BooleanFieldEditor manageBinaries = new BooleanFieldEditor(Preferences.MANAGE_BINARIES_AUTOMATICALLY, + "Update and install Snyk binaries automatically", getFieldEditorParent()); + manageBinaries.setPropertyChangeListener((PropertyChangeEvent propertyChangeEvent) -> { + System.out.println("managed bionaries changed"); + }); + addField(manageBinaries); + addField(new FileFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.LS_BINARY_KEY, + "Snyk Language Server:", getFieldEditorParent())); + addField(new FileFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.CLI_PATH, "Snyk CLI:", + getFieldEditorParent())); + + addField(space()); + + addField(new BooleanFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.SEND_ERROR_REPORTS, + "Send error reports to Snyk", getFieldEditorParent())); addField(new BooleanFieldEditor(Preferences.ENABLE_TELEMETRY, "Send usage statistics to Snyk", getFieldEditorParent())); - disableSnykCodeIfOrgDisabled(); - } - - private FieldEditor space() { - return new LabelFieldEditor("", getFieldEditorParent()); - } - - @Override - public boolean performOk() { - boolean superOK = super.performOk(); - var snykView = SnykStartup.getSnykView(); - snykView.disableRunAbortActions(); - snykView.toggleRunActionEnablement(); - disableSnykCodeIfOrgDisabled(); - - new LsConfigurationUpdater().configurationChanged(); - return superOK; - } - - private void disableSnykCodeIfOrgDisabled() { - var apiClient = new ApiClient(); - if (snykCodeCheckbox.getBooleanValue() && !apiClient.checkSnykCodeEnablement()) { - String message = "Snyk Code disabled, because it is not enabled for your organization. After you close this preference page, it will stay disabled."; - snykCodeCheckbox.setLabelText(snykCodeCheckbox.getLabelText()+" ("+message+")"); - SnykLogger.logInfo(message); - } - } + + addField(space()); + + addField(new LabelFieldEditor( + "Only trusted paths are scanned by Snyk. The Trusted Folders setting allows to specify, which \n" + + "paths are safe to scan. Every path below a given path is considered safe to scan. \n" + + "Please separate entries with \"" + File.pathSeparator + "\".", + getFieldEditorParent())); + addField(new StringFieldEditor(io.snyk.eclipse.plugin.properties.preferences.Preferences.TRUSTED_FOLDERS, + "Trusted Folders:", getFieldEditorParent())); + + disableSnykCodeIfOrgDisabled(); + } + + private FieldEditor space() { + return new LabelFieldEditor("", getFieldEditorParent()); + } + + @Override + public boolean performOk() { + boolean superOK = super.performOk(); + var snykView = SnykStartup.getSnykView(); + snykView.disableRunAbortActions(); + snykView.toggleRunActionEnablement(); + disableSnykCodeIfOrgDisabled(); + + new LsConfigurationUpdater().configurationChanged(); + return superOK; + } + + private void disableSnykCodeIfOrgDisabled() { + var apiClient = new ApiClient(); + if (snykCodeCheckbox.getBooleanValue() && !apiClient.checkSnykCodeEnablement()) { + String message = "Snyk Code disabled, because it is not enabled for your organization. After you close this preference page, it will stay disabled."; + snykCodeCheckbox.setLabelText(snykCodeCheckbox.getLabelText() + " (" + message + ")"); + SnykLogger.logInfo(message); + } + } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java index 59e8322d..9ff9bfc3 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java @@ -26,6 +26,7 @@ public static synchronized Preferences getInstance(PreferenceStore store) { return preferences; } + public static final String TRUSTED_FOLDERS = "trustSettings"; public static final String AUTH_TOKEN_KEY = "authtoken"; public static final String PATH_KEY = "path"; public static final String ENDPOINT_KEY = "endpoint"; diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/runner/SnykCliRunner.java b/plugin/src/main/java/io/snyk/eclipse/plugin/runner/SnykCliRunner.java index a1ed528d..df05e489 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/runner/SnykCliRunner.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/runner/SnykCliRunner.java @@ -17,8 +17,6 @@ public class SnykCliRunner { private static final String TEST_PARAMS = "test"; private static final String FILE_PARAM = "--file="; - private static final String INSECURE = "--insecure"; - private static final String MONITOR_PARAM = "monitor"; // private static final String AUTH_PARAM = "auth"; @@ -59,12 +57,20 @@ private ProcessResult snykRun(List arguments) { private ProcessResult snykRun(List arguments, Optional navigatePath) { try { + checkIfTrusted(navigatePath.get()); ProcessBuilder processBuilder = createProcessBuilderByOS(arguments, Preferences.getInstance().getPath()); return processRunner.run(processBuilder, navigatePath); } catch (Exception e) { return ProcessResult.error(e.getMessage()); } } + + private void checkIfTrusted(File file) { + var trustedPaths = Preferences.getInstance().getPref(Preferences.TRUSTED_FOLDERS, ""); + if (!trustedPaths.contains(file.getAbsolutePath())) { + throw new UntrustedScanRequestedException(file.getAbsolutePath() + " is not trusted."); + } + } private ProcessBuilder createProcessBuilderByOS(List params, Optional path) throws Exception { ProcessBuilder processbuilder; diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/runner/UntrustedScanRequestedException.java b/plugin/src/main/java/io/snyk/eclipse/plugin/runner/UntrustedScanRequestedException.java new file mode 100644 index 00000000..2bc9afb6 --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/runner/UntrustedScanRequestedException.java @@ -0,0 +1,27 @@ +package io.snyk.eclipse.plugin.runner; + +public class UntrustedScanRequestedException extends RuntimeException { + + private static final long serialVersionUID = 4849361078384083852L; + + public UntrustedScanRequestedException() { + } + + public UntrustedScanRequestedException(String message) { + super(message); + } + + public UntrustedScanRequestedException(Throwable cause) { + super(cause); + } + + public UntrustedScanRequestedException(String message, Throwable cause) { + super(message, cause); + } + + public UntrustedScanRequestedException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java b/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java index 207b5162..c9bd10d5 100644 --- a/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java +++ b/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java @@ -4,6 +4,7 @@ import io.snyk.eclipse.plugin.properties.preferences.Preferences; import io.snyk.eclipse.plugin.utils.SnykLogger; +import java.io.File; import java.util.Collections; import org.eclipse.core.resources.IProject; @@ -17,185 +18,182 @@ @SuppressWarnings("restriction") public class LsConfigurationUpdater { - public void configurationChanged() { - var params = new DidChangeConfigurationParams(); - params.setSettings(getCurrentSettings()); - - var definition = LanguageServersRegistry.getInstance().getDefinition(SnykLanguageServer.LANGUAGE_SERVER_ID); - - if (definition == null) { - return; - } - for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { - if (project.isAccessible()) { - try { - var servers = Collections.unmodifiableCollection(LanguageServiceAccessor.getLanguageServers(project, null)); - for (LanguageServer ls : servers) { - var currentDefinition = LanguageServiceAccessor.resolveServerDefinition(ls); - if (currentDefinition.isEmpty() || !currentDefinition.get().id.equals(definition.id)) continue; - WorkspaceService workspaceService = ls.getWorkspaceService(); - workspaceService.didChangeConfiguration(params); - } - } catch (Exception e) { - SnykLogger.logError(e); - } - } - } - } - - Settings getCurrentSettings() { - Preferences preferences = Preferences.getInstance(); - String activateSnykOpenSource = preferences.getPref(Preferences.ACTIVATE_SNYK_OPEN_SOURCE, "true"); - String activateSnykCode = preferences.getPref(Preferences.ACTIVATE_SNYK_CODE, "false"); - String activateSnykIac = preferences.getPref(Preferences.ACTIVATE_SNYK_IAC, "true"); - String insecure = preferences.getPref(Preferences.INSECURE_KEY, "false"); - String endpoint = preferences.getPref(Preferences.ENDPOINT_KEY, ""); - String additionalParams = preferences.getPref(Preferences.ADDITIONAL_PARAMETERS, ""); - String additionalEnv = preferences.getPref(Preferences.ADDITIONAL_ENVIRONMENT, ""); - String path = preferences.getPref(Preferences.PATH_KEY, ""); - String sendErrorReports = preferences.getPref(Preferences.SEND_ERROR_REPORTS, ""); - String enableTelemetry = preferences.getPref(Preferences.ENABLE_TELEMETRY, "false"); - String organization = preferences.getPref(Preferences.ORGANIZATION_KEY, ""); - String manageBinariesAutomatically = preferences.getPref(Preferences.MANAGE_BINARIES_AUTOMATICALLY, "true"); - String cliPath = preferences.getPref(Preferences.CLI_PATH, ""); - String token = preferences.getPref(Preferences.AUTH_TOKEN_KEY, ""); - String integrationName = Activator.INTEGRATION_NAME; - String integrationVersion = Activator.PLUGIN_VERSION; - return new Settings(activateSnykOpenSource, - activateSnykCode, - activateSnykIac, - insecure, - endpoint, - additionalParams, - additionalEnv, - path, - sendErrorReports, - enableTelemetry, - organization, - manageBinariesAutomatically, - cliPath, - token, - integrationName, - integrationVersion - ); - } - - static class Settings { - private final String activateSnykOpenSource; - private final String activateSnykCode; - private final String activateSnykIac; - - private final String insecure; - private final String endpoint; - private final String additionalParams; - private final String additionalEnv; - private final String path; - private final String sendErrorReports; - private final String enableTelemetry; - private final String organization; - private final String manageBinariesAutomatically; - private final String cliPath; - private final String token; - private final String integrationName; - private final String integrationVersion; - - public Settings(String activateSnykOpenSource, - String activateSnykCode, - String activateSnykIac, - String insecure, - String endpoint, - String additionalParams, - String additionalEnv, - String path, - String sendErrorReports, - String enableTelemetry, - String organization, - String manageBinariesAutomatically, - String cliPath, - String token, - String integrationName, - String integrationVersion - ) { - this.activateSnykOpenSource = activateSnykOpenSource; - this.activateSnykCode = activateSnykCode; - this.activateSnykIac = activateSnykIac; - this.insecure = insecure; - this.endpoint = endpoint; - this.additionalParams = additionalParams; - this.additionalEnv = additionalEnv; - this.path = path; - this.sendErrorReports = sendErrorReports; - this.enableTelemetry = enableTelemetry; - this.organization = organization; - this.manageBinariesAutomatically = manageBinariesAutomatically; - this.cliPath = cliPath; - this.token = token; - this.integrationName = integrationName; - this.integrationVersion = integrationVersion; - } - - public String getPath() { - return path; - } - - public String getActivateSnykOpenSource() { - return activateSnykOpenSource; - } - - public String getActivateSnykCode() { - return activateSnykCode; - } - - public String getActivateSnykIac() { - return activateSnykIac; - } - - public String getInsecure() { - return insecure; - } - - public String getEndpoint() { - return endpoint; - } - - public String getAdditionalParams() { - return additionalParams; - } - - public String getAdditionalEnv() { - return additionalEnv; - } - - public String getSendErrorReports() { - return this.sendErrorReports; - } - - public String getEnableTelemetry() { - return this.enableTelemetry; - } - - public String getOrganization() { - return this.organization; - } - - public String getManageBinariesAutomatically() { - return this.manageBinariesAutomatically; - } - - public String getCliPath() { - return cliPath; - } - - public String getToken() { - return token; - } - - public String getIntegrationName() { - return integrationName; - } - - public String getIntegrationVersion() { - return integrationVersion; - } - } + public void configurationChanged() { + var params = new DidChangeConfigurationParams(); + params.setSettings(getCurrentSettings()); + + var definition = LanguageServersRegistry.getInstance().getDefinition(SnykLanguageServer.LANGUAGE_SERVER_ID); + + if (definition == null) { + return; + } + for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { + if (project.isAccessible()) { + try { + var servers = Collections + .unmodifiableCollection(LanguageServiceAccessor.getLanguageServers(project, null)); + for (LanguageServer ls : servers) { + var currentDefinition = LanguageServiceAccessor.resolveServerDefinition(ls); + if (currentDefinition.isEmpty() || !currentDefinition.get().id.equals(definition.id)) + continue; + WorkspaceService workspaceService = ls.getWorkspaceService(); + workspaceService.didChangeConfiguration(params); + } + } catch (Exception e) { + SnykLogger.logError(e); + } + } + } + } + + Settings getCurrentSettings() { + Preferences preferences = Preferences.getInstance(); + String activateSnykOpenSource = preferences.getPref(Preferences.ACTIVATE_SNYK_OPEN_SOURCE, + Boolean.TRUE.toString()); + String activateSnykCode = preferences.getPref(Preferences.ACTIVATE_SNYK_CODE, Boolean.FALSE.toString()); + String activateSnykIac = preferences.getPref(Preferences.ACTIVATE_SNYK_IAC, Boolean.TRUE.toString()); + String insecure = preferences.getPref(Preferences.INSECURE_KEY, Boolean.FALSE.toString()); + String endpoint = preferences.getPref(Preferences.ENDPOINT_KEY, ""); + String additionalParams = preferences.getPref(Preferences.ADDITIONAL_PARAMETERS, ""); + String additionalEnv = preferences.getPref(Preferences.ADDITIONAL_ENVIRONMENT, ""); + String path = preferences.getPref(Preferences.PATH_KEY, ""); + String sendErrorReports = preferences.getPref(Preferences.SEND_ERROR_REPORTS, ""); + String enableTelemetry = preferences.getPref(Preferences.ENABLE_TELEMETRY, Boolean.FALSE.toString()); + String organization = preferences.getPref(Preferences.ORGANIZATION_KEY, ""); + String manageBinariesAutomatically = preferences.getPref(Preferences.MANAGE_BINARIES_AUTOMATICALLY, + Boolean.TRUE.toString()); + String cliPath = preferences.getPref(Preferences.CLI_PATH, ""); + String token = preferences.getPref(Preferences.AUTH_TOKEN_KEY, ""); + String integrationName = Activator.INTEGRATION_NAME; + String integrationVersion = Activator.PLUGIN_VERSION; + String trustedFoldersString = preferences.getPref(Preferences.TRUSTED_FOLDERS); + String[] trustedFolders = new String[0]; + if (trustedFoldersString != null && !trustedFoldersString.isBlank()) { + trustedFolders = trustedFoldersString.split(File.pathSeparator); + } + String enableTrustedFolderFeature = Boolean.TRUE.toString(); + return new Settings(activateSnykOpenSource, activateSnykCode, activateSnykIac, insecure, endpoint, + additionalParams, additionalEnv, path, sendErrorReports, enableTelemetry, organization, + manageBinariesAutomatically, cliPath, token, integrationName, integrationVersion, trustedFolders, + enableTrustedFolderFeature); + } + + static class Settings { + private final String activateSnykOpenSource; + private final String activateSnykCode; + private final String activateSnykIac; + + private final String insecure; + private final String endpoint; + private final String additionalParams; + private final String additionalEnv; + private final String path; + private final String sendErrorReports; + private final String enableTelemetry; + private final String organization; + private final String manageBinariesAutomatically; + private final String cliPath; + private final String token; + private final String integrationName; + private final String integrationVersion; + private final String[] trustedFolders; + private final String enableTrustedFoldersFeature; + + public Settings(String activateSnykOpenSource, String activateSnykCode, String activateSnykIac, String insecure, + String endpoint, String additionalParams, String additionalEnv, String path, String sendErrorReports, + String enableTelemetry, String organization, String manageBinariesAutomatically, String cliPath, + String token, String integrationName, String integrationVersion, String[] trustedFolders, + String enableTrustedFoldersFeature) { + this.activateSnykOpenSource = activateSnykOpenSource; + this.activateSnykCode = activateSnykCode; + this.activateSnykIac = activateSnykIac; + this.insecure = insecure; + this.endpoint = endpoint; + this.additionalParams = additionalParams; + this.additionalEnv = additionalEnv; + this.path = path; + this.sendErrorReports = sendErrorReports; + this.enableTelemetry = enableTelemetry; + this.organization = organization; + this.manageBinariesAutomatically = manageBinariesAutomatically; + this.cliPath = cliPath; + this.token = token; + this.integrationName = integrationName; + this.integrationVersion = integrationVersion; + this.trustedFolders = trustedFolders; + this.enableTrustedFoldersFeature = enableTrustedFoldersFeature; + } + + public String getPath() { + return path; + } + + public String getActivateSnykOpenSource() { + return activateSnykOpenSource; + } + + public String getActivateSnykCode() { + return activateSnykCode; + } + + public String getActivateSnykIac() { + return activateSnykIac; + } + + public String getInsecure() { + return insecure; + } + + public String getEndpoint() { + return endpoint; + } + + public String getAdditionalParams() { + return additionalParams; + } + + public String getAdditionalEnv() { + return additionalEnv; + } + + public String getSendErrorReports() { + return this.sendErrorReports; + } + + public String getEnableTelemetry() { + return this.enableTelemetry; + } + + public String getOrganization() { + return this.organization; + } + + public String getManageBinariesAutomatically() { + return this.manageBinariesAutomatically; + } + + public String getCliPath() { + return cliPath; + } + + public String getToken() { + return token; + } + + public String getIntegrationName() { + return integrationName; + } + + public String getIntegrationVersion() { + return integrationVersion; + } + + public String[] getTrustedFolders() { + return trustedFolders; + } + + public String getEnableTrustedFoldersFeature() { + return enableTrustedFoldersFeature; + } + } } diff --git a/plugin/src/main/java/io/snyk/languageserver/download/LsBinaries.java b/plugin/src/main/java/io/snyk/languageserver/download/LsBinaries.java index 86b9d22a..f0971158 100644 --- a/plugin/src/main/java/io/snyk/languageserver/download/LsBinaries.java +++ b/plugin/src/main/java/io/snyk/languageserver/download/LsBinaries.java @@ -4,7 +4,7 @@ public class LsBinaries { private static final String LS_DOWNLOAD_BASE_URL = "https://static.snyk.io/snyk-ls"; - public static final String REQUIRED_LS_PROTOCOL_VERSION = "3"; + public static final String REQUIRED_LS_PROTOCOL_VERSION = "4"; public static URI getBaseUri() { return URI.create(String.format("%s/%s", LS_DOWNLOAD_BASE_URL, REQUIRED_LS_PROTOCOL_VERSION)); 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 bb4bb7df..81533684 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -1,20 +1,31 @@ package io.snyk.languageserver.protocolextension; +import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageClientImpl; +import org.eclipse.lsp4e.LanguageServerWrapper; import org.eclipse.lsp4e.ServerMessageHandler; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.MessageActionItem; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.ProgressParams; import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.ShowDocumentResult; +import org.eclipse.lsp4j.ShowMessageRequestParams; import org.eclipse.lsp4j.WorkDoneProgressCreateParams; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import io.snyk.eclipse.plugin.SnykStartup; @@ -22,6 +33,7 @@ import io.snyk.eclipse.plugin.utils.SnykLogger; import io.snyk.languageserver.protocolextension.messageObjects.HasAuthenticatedParam; import io.snyk.languageserver.protocolextension.messageObjects.SnykIsAvailableCliParams; +import io.snyk.languageserver.protocolextension.messageObjects.SnykTrustedFoldersParams; @SuppressWarnings("restriction") public class SnykExtendedLanguageClient extends LanguageClientImpl { @@ -60,6 +72,21 @@ public void isAvailableCli(SnykIsAvailableCliParams param) { enableSnykViewRunActions(); } + @JsonNotification(value = "$/snyk.addTrustedFolders") + public void addTrustedPaths(SnykTrustedFoldersParams param) { + var prefs = Preferences.getInstance(); + var storedTrustedPaths = prefs.getPref(Preferences.TRUSTED_FOLDERS, ""); + var trustedPaths = storedTrustedPaths.split(File.pathSeparator); + var pathSet = new HashSet<>(Arrays.asList(trustedPaths)); + pathSet.addAll(Arrays.asList(param.getTrustedFolders())); + Preferences.getInstance().store(Preferences.TRUSTED_FOLDERS, + pathSet.stream() + .filter(s -> !s.isBlank()) + .map(s -> s.trim()) + .distinct() + .collect(Collectors.joining(File.pathSeparator))); + } + @Override public CompletableFuture createProgress(WorkDoneProgressCreateParams params) { return progressMgr.createProgress(params); @@ -85,7 +112,8 @@ private void showAuthenticatedMessage() { ServerMessageHandler.showMessage("Authentication with Snyk successful", messageParams); } - // TODO: remove once LSP4e supports `showDocument` in its next release (it's been merged to it already) + // TODO: remove once LSP4e supports `showDocument` in its next release (it's + // been merged to it already) @Override public CompletableFuture showDocument(ShowDocumentParams params) { return CompletableFuture.supplyAsync(() -> { @@ -100,4 +128,7 @@ public CompletableFuture showDocument(ShowDocumentParams par return new ShowDocumentResult(true); }); } + + + } diff --git a/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SnykTrustedFoldersParams.java b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SnykTrustedFoldersParams.java new file mode 100644 index 00000000..b7001598 --- /dev/null +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/messageObjects/SnykTrustedFoldersParams.java @@ -0,0 +1,13 @@ +package io.snyk.languageserver.protocolextension.messageObjects; + +public class SnykTrustedFoldersParams { + private String[] trustedFolders; + + public String[] getTrustedFolders() { + return trustedFolders; + } + + public void setTrustedFolders(String[] trustedFolders) { + this.trustedFolders = trustedFolders; + } +} diff --git a/tests/src/test/java/io/snyk/eclipse/plugin/runner/SnykCliRunnerTest.java b/tests/src/test/java/io/snyk/eclipse/plugin/runner/SnykCliRunnerTest.java new file mode 100644 index 00000000..f07b6a91 --- /dev/null +++ b/tests/src/test/java/io/snyk/eclipse/plugin/runner/SnykCliRunnerTest.java @@ -0,0 +1,38 @@ +package io.snyk.eclipse.plugin.runner; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +import io.snyk.eclipse.plugin.properties.preferences.InMemoryPreferenceStore; +import io.snyk.eclipse.plugin.properties.preferences.Preferences; +import io.snyk.eclipse.plugin.properties.preferences.PreferencesUtils; + +class SnykCliRunnerTest { + + @Test + void testRunDoesntAllowScansOfUntrustedPath() { + PreferencesUtils.setPreferences(Preferences.getInstance(new InMemoryPreferenceStore())); + SnykCliRunner cut = new SnykCliRunner(); + File navigatePath = new File("untrusted/path"); + + ProcessResult result = cut.snykTest(navigatePath); + + assertTrue(result.getError().endsWith(navigatePath.getAbsolutePath() + " is not trusted.")); + } + + @Test + void testRunAllowsScanOfTrustedPath() { + File navigatePath = new File("trusted/path"); + InMemoryPreferenceStore store = new InMemoryPreferenceStore(); + store.put(Preferences.TRUSTED_FOLDERS, "a" + File.pathSeparator + navigatePath.getAbsolutePath() + File.pathSeparator + "b"); + PreferencesUtils.setPreferences(Preferences.getInstance(store)); + + SnykCliRunner cut = new SnykCliRunner(); + ProcessResult result = cut.snykTest(navigatePath); + + assertFalse(result.getError().endsWith(navigatePath.getAbsolutePath() + " is not trusted.")); + } +} diff --git a/tests/src/test/java/io/snyk/languageserver/SnykLanguageServerTest.java b/tests/src/test/java/io/snyk/languageserver/SnykLanguageServerTest.java index 04bcce7f..2ba746c6 100644 --- a/tests/src/test/java/io/snyk/languageserver/SnykLanguageServerTest.java +++ b/tests/src/test/java/io/snyk/languageserver/SnykLanguageServerTest.java @@ -7,6 +7,8 @@ import static org.junit.jupiter.api.Assertions.*; +import java.io.File; + class SnykLanguageServerTest { @Test @@ -18,4 +20,34 @@ void getInitializationOptions() { assertInstanceOf(LsConfigurationUpdater.Settings.class, output); } + + @Test + void getInitializationOptionsContainsTrustedPaths() { + InMemoryPreferenceStore store = new InMemoryPreferenceStore(); + String trustedPaths = "a" + File.pathSeparatorChar + "b/c"; + store.put(Preferences.TRUSTED_FOLDERS, trustedPaths); + PreferencesUtils.setPreferences(Preferences.getInstance(store)); + SnykLanguageServer snykStreamConnectionProvider = new SnykLanguageServer(); + + Object output = snykStreamConnectionProvider.getInitializationOptions(null); + + assertInstanceOf(LsConfigurationUpdater.Settings.class, output); + LsConfigurationUpdater.Settings settings = (LsConfigurationUpdater.Settings) output; + assertEquals("a", settings.getTrustedFolders()[0]); + assertEquals("b/c", settings.getTrustedFolders()[1]); + } + + @Test + void getInitializationOptionsDoesNotContainsTrustedPathsIfNoneKnown() { + InMemoryPreferenceStore store = new InMemoryPreferenceStore(); + PreferencesUtils.setPreferences(Preferences.getInstance(store)); + SnykLanguageServer snykStreamConnectionProvider = new SnykLanguageServer(); + + Object output = snykStreamConnectionProvider.getInitializationOptions(null); + + assertInstanceOf(LsConfigurationUpdater.Settings.class, output); + LsConfigurationUpdater.Settings settings = (LsConfigurationUpdater.Settings) output; + assertNotNull(settings.getTrustedFolders()); + assertEquals(0, settings.getTrustedFolders().length); + } } diff --git a/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java b/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java new file mode 100644 index 00000000..2c89899d --- /dev/null +++ b/tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java @@ -0,0 +1,46 @@ +package io.snyk.languageserver.protocolextension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.languageserver.protocolextension.messageObjects.HasAuthenticatedParam; +import io.snyk.languageserver.protocolextension.messageObjects.SnykIsAvailableCliParams; +import io.snyk.languageserver.protocolextension.messageObjects.SnykTrustedFoldersParams; + +class SnykExtendedLanguageClientTest { + private InMemoryPreferenceStore store = new InMemoryPreferenceStore(); + private SnykExtendedLanguageClient cut = new SnykExtendedLanguageClient(); + + @BeforeEach + void setUp() { + store = new InMemoryPreferenceStore(); + PreferencesUtils.setPreferences(Preferences.getInstance(store)); + } + + @Test + void testAddTrustedPathsAddsPathToPreferenceStore() { + SnykTrustedFoldersParams param = new SnykTrustedFoldersParams(); + param.setTrustedFolders(new String[] {"trusted/path "}); + + cut.addTrustedPaths(param); + + assertEquals("trusted/path", store.getString(Preferences.TRUSTED_FOLDERS, "")); + } + + @Test + void testAddTrustedPathsDeduplicatesAndTrims() { + SnykTrustedFoldersParams param = new SnykTrustedFoldersParams(); + param.setTrustedFolders(new String[] {"trusted/path", "trusted/path", " trusted/path "}); + + cut.addTrustedPaths(param); + + assertEquals("trusted/path", store.getString(Preferences.TRUSTED_FOLDERS, "")); + } + +} From d55cf37fd237efd439d349fb071f440f27fb358b Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Mon, 10 Oct 2022 20:07:55 +0530 Subject: [PATCH 2/7] chore: rename `trustSettings` to `trustedFolders` --- .../snyk/eclipse/plugin/properties/preferences/Preferences.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java index 9ff9bfc3..281d72f1 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java @@ -26,7 +26,7 @@ public static synchronized Preferences getInstance(PreferenceStore store) { return preferences; } - public static final String TRUSTED_FOLDERS = "trustSettings"; + public static final String TRUSTED_FOLDERS = "trustedFolders"; public static final String AUTH_TOKEN_KEY = "authtoken"; public static final String PATH_KEY = "path"; public static final String ENDPOINT_KEY = "endpoint"; From 637f5d70966066618de9819f3d1a8c48d532b5e5 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Tue, 11 Oct 2022 12:33:11 +0530 Subject: [PATCH 3/7] chore: display error message dialog when secure preferences cannot be initialized --- .../preferences/SecurePreferenceStore.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/SecurePreferenceStore.java b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/SecurePreferenceStore.java index 724600f9..7839f635 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/SecurePreferenceStore.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/SecurePreferenceStore.java @@ -4,7 +4,12 @@ import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.preferences.ScopedPreferenceStore; public class SecurePreferenceStore extends ScopedPreferenceStore implements PreferenceStore { @@ -14,7 +19,21 @@ public class SecurePreferenceStore extends ScopedPreferenceStore implements Pref public SecurePreferenceStore() { super(InstanceScope.INSTANCE, QUALIFIER); - node = SecurePreferencesFactory.getDefault().node(QUALIFIER); + ISecurePreferences secureStorage = SecurePreferencesFactory.getDefault(); + if (secureStorage == null) { + PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { + Display display = PlatformUI.getWorkbench().getDisplay(); + Shell activeShell = display.getActiveShell(); + String message = "Eclipse was unable to create or access the Secure Storage mechanism. " + + "Please check your Secure Storage in Eclipse preferences under " + + "General -> Security -> Secure Storage. " + + "The Snyk plugin will not be able to work reliably and save preferences " + + "or the authentication token until Secure Storage can be used."; + String title = "Error accessing Eclipse Secure Storage (Snyk)"; + MessageDialog.openError(activeShell, title, message); + }); + } + node = secureStorage.node(QUALIFIER); } @Override From f9530e0f1748a7959b9d12f74dc67f9f4a09bcda Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Mon, 17 Oct 2022 08:34:25 +0200 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=E2=AC=86=EF=B8=8F=20update=20javkso?= =?UTF-8?q?n-databind=20to=202.13.4.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/META-INF/MANIFEST.MF | 2 +- plugin/build.properties | 2 +- plugin/io.snyk.eclipse.plugin.eml | 14 +++++++------- plugin/pom.xml | 2 +- target-platform/target-platform.target | 2 +- tests/pom.xml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index f5794159..7e32b939 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -33,5 +33,5 @@ Bundle-ClassPath: ., target/dependency/httpcore-4.4.15.jar, target/dependency/jackson-annotations-2.13.4.jar, target/dependency/jackson-core-2.13.4.jar, - target/dependency/jackson-databind-2.13.4.jar, + target/dependency/jackson-databind-2.13.4.2.jar, target/dependency/javax.inject-1.jar diff --git a/plugin/build.properties b/plugin/build.properties index d38ca1ba..44c1bbde 100644 --- a/plugin/build.properties +++ b/plugin/build.properties @@ -14,7 +14,7 @@ bin.includes = plugin.xml,\ target/dependency/httpcore-4.4.15.jar,\ target/dependency/jackson-annotations-2.13.4.jar,\ target/dependency/jackson-core-2.13.4.jar,\ - target/dependency/jackson-databind-2.13.4.jar,\ + target/dependency/jackson-databind-2.13.4.2.jar,\ target/dependency/javax.inject-1.jar src.includes =src/,\ icons/ diff --git a/plugin/io.snyk.eclipse.plugin.eml b/plugin/io.snyk.eclipse.plugin.eml index 5ef4faad..ecca7d7b 100644 --- a/plugin/io.snyk.eclipse.plugin.eml +++ b/plugin/io.snyk.eclipse.plugin.eml @@ -19,22 +19,22 @@ - + - + - - + + - - - + + + diff --git a/plugin/pom.xml b/plugin/pom.xml index 55845846..6870cb6b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -29,7 +29,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4 + 2.13.4.2 jar diff --git a/target-platform/target-platform.target b/target-platform/target-platform.target index e115f9dc..01e61fb5 100644 --- a/target-platform/target-platform.target +++ b/target-platform/target-platform.target @@ -43,7 +43,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4 + 2.13.4.2 jar diff --git a/tests/pom.xml b/tests/pom.xml index 6804377a..836cf5e6 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -25,7 +25,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4 + 2.13.4.2 jar From 67aa3c23fb8352d03a2e99294cee8d3e6e4e47d3 Mon Sep 17 00:00:00 2001 From: michelkaporin Date: Mon, 31 Oct 2022 09:15:19 +0100 Subject: [PATCH 5/7] fix: merge --- .../LsConfigurationUpdater.java | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java b/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java index 0747255d..35b1871c 100644 --- a/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java +++ b/plugin/src/main/java/io/snyk/languageserver/LsConfigurationUpdater.java @@ -4,6 +4,7 @@ import io.snyk.eclipse.plugin.properties.preferences.Preferences; import io.snyk.eclipse.plugin.utils.SnykLogger; +import java.io.File; import java.util.Collections; import org.eclipse.core.resources.IProject; @@ -45,23 +46,30 @@ public void configurationChanged() { Settings getCurrentSettings() { Preferences preferences = Preferences.getInstance(); - String activateSnykOpenSource = preferences.getPref(Preferences.ACTIVATE_SNYK_OPEN_SOURCE, "true"); - String activateSnykCode = preferences.getPref(Preferences.ACTIVATE_SNYK_CODE, "false"); - String activateSnykIac = preferences.getPref(Preferences.ACTIVATE_SNYK_IAC, "true"); - String insecure = preferences.getPref(Preferences.INSECURE_KEY, "false"); + String activateSnykOpenSource = preferences.getPref(Preferences.ACTIVATE_SNYK_OPEN_SOURCE, + Boolean.TRUE.toString()); + String activateSnykCode = preferences.getPref(Preferences.ACTIVATE_SNYK_CODE, Boolean.FALSE.toString()); + String activateSnykIac = preferences.getPref(Preferences.ACTIVATE_SNYK_IAC, Boolean.TRUE.toString()); + String insecure = preferences.getPref(Preferences.INSECURE_KEY, Boolean.FALSE.toString()); String endpoint = preferences.getPref(Preferences.ENDPOINT_KEY, ""); String additionalParams = preferences.getPref(Preferences.ADDITIONAL_PARAMETERS, ""); String additionalEnv = preferences.getPref(Preferences.ADDITIONAL_ENVIRONMENT, ""); String path = preferences.getPref(Preferences.PATH_KEY, ""); String sendErrorReports = preferences.getPref(Preferences.SEND_ERROR_REPORTS, ""); - String enableTelemetry = preferences.getPref(Preferences.ENABLE_TELEMETRY, "false"); + String enableTelemetry = preferences.getPref(Preferences.ENABLE_TELEMETRY, Boolean.FALSE.toString()); String organization = preferences.getPref(Preferences.ORGANIZATION_KEY, ""); - String manageBinariesAutomatically = preferences.getPref(Preferences.MANAGE_BINARIES_AUTOMATICALLY, "true"); + String manageBinariesAutomatically = preferences.getPref(Preferences.MANAGE_BINARIES_AUTOMATICALLY, Boolean.TRUE.toString()); String cliPath = preferences.getPref(Preferences.CLI_PATH, ""); String token = preferences.getPref(Preferences.AUTH_TOKEN_KEY, ""); String integrationName = Activator.INTEGRATION_NAME; String integrationVersion = Activator.PLUGIN_VERSION; String automaticAuthentication = "false"; + String trustedFoldersString = preferences.getPref(Preferences.TRUSTED_FOLDERS); + String[] trustedFolders = new String[0]; + if (trustedFoldersString != null && !trustedFoldersString.isBlank()) { + trustedFolders = trustedFoldersString.split(File.pathSeparator); + } + String enableTrustedFolderFeature = Boolean.TRUE.toString(); return new Settings(activateSnykOpenSource, activateSnykCode, activateSnykIac, @@ -78,7 +86,9 @@ Settings getCurrentSettings() { token, integrationName, integrationVersion, - automaticAuthentication + automaticAuthentication, + trustedFolders, + enableTrustedFolderFeature ); } @@ -101,6 +111,8 @@ static class Settings { private final String integrationName; private final String integrationVersion; private final String automaticAuthentication; + private final String[] trustedFolders; + private final String enableTrustedFoldersFeature; public Settings(String activateSnykOpenSource, String activateSnykCode, @@ -118,7 +130,9 @@ public Settings(String activateSnykOpenSource, String token, String integrationName, String integrationVersion, - String automaticAuthentication + String automaticAuthentication, + String[] trustedFolders, + String enableTrustedFoldersFeature ) { this.activateSnykOpenSource = activateSnykOpenSource; this.activateSnykCode = activateSnykCode; @@ -137,6 +151,8 @@ public Settings(String activateSnykOpenSource, this.integrationName = integrationName; this.integrationVersion = integrationVersion; this.automaticAuthentication = automaticAuthentication; + this.trustedFolders = trustedFolders; + this.enableTrustedFoldersFeature = enableTrustedFoldersFeature; } public String getPath() { @@ -206,5 +222,13 @@ public String getIntegrationVersion() { public String getAutomaticAuthentication() { return automaticAuthentication; } + + public String[] getTrustedFolders() { + return trustedFolders; + } + + public String getEnableTrustedFoldersFeature() { + return enableTrustedFoldersFeature; + } } } From f9aebfd4ff4b58b66f336f571a8ead8a1a398cb2 Mon Sep 17 00:00:00 2001 From: michelkaporin Date: Mon, 31 Oct 2022 09:19:48 +0100 Subject: [PATCH 6/7] feat: onboarding wizard --- .../plugin/wizards/SWTWidgetHelper.java | 91 +++++++++++++++++++ .../eclipse/plugin/wizards/SnykWizard.java | 21 +++-- .../wizards/SnykWizardAuthenticatePage.java | 61 +++++++------ .../SnykExtendedLanguageClient.java | 25 +++-- 4 files changed, 153 insertions(+), 45 deletions(-) create mode 100644 plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SWTWidgetHelper.java diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SWTWidgetHelper.java b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SWTWidgetHelper.java new file mode 100644 index 00000000..97b6bb44 --- /dev/null +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SWTWidgetHelper.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2005-2008 VecTrace (Zingo Andersen) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * bastian implementation + *******************************************************************************/ + + +package io.snyk.eclipse.plugin.wizards; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; + +/** + * @author bastian + * https://foss.heptapod.net/mercurial/mercurialeclipse/-/blob/branch/default/plugin/src/com/vectrace/MercurialEclipse/ui/SWTWidgetHelper.java + */ +public final class SWTWidgetHelper { + public static final int LABEL_WIDTH_HINT = 400; + public static final int LABEL_INDENT_WIDTH = 32; + public static final int LIST_HEIGHT_HINT = 100; + public static final int SPACER_HEIGHT = 8; + + private SWTWidgetHelper() { + // hide constructor of utility class. + } + + /** + * Creates a group that has and spans the given number of columns in its parent and which has the + * given style. + * + * @param parent + * the parent control. + * @param text + * the title of the group. + * @param span + * the number of columns (in the parent's layout) that this group will span, which is also the number of + * columns that this group has. + * @param style + * the chosen style of the grid layout for this group. + * @return a new group + */ + public static Group createGroup(Composite parent, String text, int span, int style) { + Group group = new Group(parent, SWT.NULL); + group.setText(text); + GridData data = new GridData(style); + data.horizontalSpan = span; + // data.widthHint = GROUP_WIDTH; + + group.setLayoutData(data); + GridLayout layout = new GridLayout(); + layout.numColumns = span; + group.setLayout(layout); + return group; + } + + /** + * Creates a group that spans two columns. + * + * @param parent + * the parent control + * @param text + * the title of the group + * @param style + * the chosen style for this group + * @return a new group + */ + public static Group createGroup(Composite parent, String text, int style) { + return createGroup(parent, text, 2, style); + } + + /** + * Creates a group that has two columns and which style is horizontal fill. + * + * @param parent + * the parent control + * @param text + * the title of the group + * @return a new group + */ + public static Group createGroup(Composite parent, String text) { + return createGroup(parent, text, GridData.FILL_HORIZONTAL); + } +} diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizard.java b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizard.java index c8ae8cde..80cda0f7 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizard.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizard.java @@ -11,9 +11,9 @@ public class SnykWizard extends Wizard implements INewWizard { protected SnykWizardConfigureAPIPage configureAPIPage; protected SnykWizardAuthenticatePage authenticatePage; - + protected SnykWizardModel model; - + protected IWorkbench workbench; protected IStructuredSelection selection; @@ -22,18 +22,18 @@ public SnykWizard() { model = new SnykWizardModel(); setNeedsProgressMonitor(true); } - + @Override public String getWindowTitle() { return "Snyk Wizard"; } - + @Override public void addPages() { - configureAPIPage = new SnykWizardConfigureAPIPage(); + configureAPIPage = new SnykWizardConfigureAPIPage(); addPage(configureAPIPage); - - authenticatePage = new SnykWizardAuthenticatePage(); + + authenticatePage = new SnykWizardAuthenticatePage(); addPage(authenticatePage); } @@ -41,22 +41,23 @@ public void init(IWorkbench workbench, IStructuredSelection selection) { this.workbench = workbench; this.selection = selection; } - + public boolean canFinish() { if (this.getContainer().getCurrentPage() == authenticatePage) { return true; } return false; } - + public boolean performCancel() { model.resetPreferences(); return true; } - public boolean performFinish() { + public boolean performFinish() { new LsConfigurationUpdater().configurationChanged(); SnykExtendedLanguageClient.getInstance().triggerAuthentication(); + SnykExtendedLanguageClient.getInstance().trustWorkspaceFolders(); return true; } } diff --git a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardAuthenticatePage.java b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardAuthenticatePage.java index c0a7bf67..f4e12c36 100644 --- a/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardAuthenticatePage.java +++ b/plugin/src/main/java/io/snyk/eclipse/plugin/wizards/SnykWizardAuthenticatePage.java @@ -3,21 +3,27 @@ import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Text; import io.snyk.eclipse.plugin.properties.preferences.Preferences; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Listener; public class SnykWizardAuthenticatePage extends WizardPage implements Listener { private Text endpoint; private Button unknownCerts; + private String trustMessage = "⚠️ When scanning folder files, Snyk may automatically execute code such as invoking the package manager to get dependency information. " + + "You should only scan projects you trust. More Info" + + "\n\nOn finishing the wizard, the plugin will open a browser to authenticate you, trust the current workspace projects and trigger a scan."; public SnykWizardAuthenticatePage() { super("Snyk Wizard"); @@ -27,37 +33,45 @@ public SnykWizardAuthenticatePage() { @Override public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - GridLayout gl = new GridLayout(); - GridData gd = new GridData(GridData.FILL_HORIZONTAL); + Composite composite = new Composite(parent, SWT.NONE); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); - int ncol = 2; - gl.numColumns = ncol; - composite.setLayout(gl); + GridLayout gl = new GridLayout(); + int ncol = 2; + gl.numColumns = ncol; + composite.setLayout(gl); - Label endpointLabel = new Label(composite, SWT.NONE); - endpointLabel.setText("Endpoint:"); + Group endpointGroup = SWTWidgetHelper.createGroup(composite, ""); - endpoint = new Text(composite, SWT.BORDER | SWT.READ_ONLY); - endpoint.setLayoutData(gd); - - createLine(composite, ncol); + Label endpointLabel = new Label(endpointGroup, SWT.NONE); + endpointLabel.setText("Endpoint:"); + endpoint = new Text(endpointGroup, SWT.BORDER | SWT.READ_ONLY); + endpoint.setLayoutData(gd); - Label unknownCertsLabel = new Label(composite, SWT.NONE); - unknownCertsLabel.setText("Allow unknown certificate authorities:"); + Label unknownCertsLabel = new Label(endpointGroup, SWT.NONE); + unknownCertsLabel.setText("Allow unknown certificate authorities:"); - unknownCerts = new Button(composite, SWT.CHECK); - unknownCerts.setLayoutData(gd); + unknownCerts = new Button(endpointGroup, SWT.CHECK); + unknownCerts.setLayoutData(gd); - // required to avoid an error in the system - setControl(composite); - setPageComplete(false); + Group trustGroup = SWTWidgetHelper.createGroup(composite, ""); + + Link trustText = new Link(trustGroup, SWT.NONE); + trustText.setText(trustMessage); + gd = new GridData(GridData.FILL_BOTH); + trustText.setLayoutData(gd); + trustText.setBackground(new Color(0, 0, 0, 0)); + trustText.addListener(SWT.Selection, event -> org.eclipse.swt.program.Program.launch(event.text)); + + // required to avoid an error in the system + setControl(composite); + setPageComplete(false); } public void handleEvent(Event e) { getWizard().getContainer().updateButtons(); } - + public boolean isPageComplete() { return true; } @@ -66,11 +80,4 @@ void onEnterPage() { endpoint.setText(Preferences.getInstance().getEndpoint()); unknownCerts.setSelection(Preferences.getInstance().getBooleanPref(Preferences.INSECURE_KEY)); } - - private void createLine(Composite parent, int ncol) { - Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.BOLD); - GridData gridData = new GridData(GridData.FILL_HORIZONTAL); - gridData.horizontalSpan = ncol; - line.setLayoutData(gridData); - } } 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 5564bdb9..119d9c60 100644 --- a/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java +++ b/plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -23,6 +24,7 @@ import org.eclipse.lsp4j.ShowDocumentResult; import org.eclipse.lsp4j.WorkDoneProgressCreateParams; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; @@ -55,9 +57,8 @@ public void triggerScan(IWorkbenchWindow window) { if (Preferences.getInstance().getAuthToken().isBlank()) { runSnykWizard(); } else { - ExecuteCommandParams params = new ExecuteCommandParams("snyk.workspace.scan", new ArrayList<>()); try { - getLanguageServer().getWorkspaceService().executeCommand(params); + executeCommand("snyk.workspace.scan", new ArrayList<>()); if (window == null) { window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); @@ -87,12 +88,11 @@ public void triggerScan(IWorkbenchWindow window) { } public void triggerAuthentication() { - ExecuteCommandParams params = new ExecuteCommandParams("snyk.login", new ArrayList<>()); - try { - getLanguageServer().getWorkspaceService().executeCommand(params); - } catch (Exception e) { - SnykLogger.logError(e); - } + executeCommand("snyk.login", new ArrayList<>()); + } + + public void trustWorkspaceFolders() { + executeCommand("snyk.trustWorkspaceFolders", new ArrayList<>()); } @JsonNotification(value = "$/snyk.hasAuthenticated") @@ -167,6 +167,15 @@ private void runForProject(String projectName) { } } + private void executeCommand(@NonNull String command, List arguments) { + ExecuteCommandParams params = new ExecuteCommandParams(command, arguments); + try { + getLanguageServer().getWorkspaceService().executeCommand(params); + } catch (Exception e) { + SnykLogger.logError(e); + } + } + // TODO: remove once LSP4e supports `showDocument` in its next release (it's // been merged to it already) @Override From 9e12c982ddfc092ab0a0b1caba771f4e37d02818 Mon Sep 17 00:00:00 2001 From: michelkaporin Date: Tue, 29 Nov 2022 11:15:07 +0100 Subject: [PATCH 7/7] docs: update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eda27ddf..7d580479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,10 @@ ## [2.0.0] - Unreleased ### Changes -- add workspace trust feature +- add folder trust feature + +## [2.0.0] - v20221115.132308 +### Changes - adds configuration wizard for custom endpoints ## [2.0.0] - v20221007.135736