CVE-2022-24441: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: The package snyk before 1.1064.0 are vulnerable to Code Injection when analyzing a project. An attacker who can convince a user to scan a malicious project can include commands in a build file such as build.gradle or gradle-wrapper.jar, which will be executed with the privileges of the application. This vulnerability may be triggered when running the the CLI tool directly, or when running a scan with one of the IDE plugins that invoke the Snyk CLI. Successful exploitation of this issue would likely require some level of social engineering - to coerce an untrusted project to be downloaded and analyzed via the Snyk CLI or opened in an IDE where a Snyk IDE plugin is installed and enabled. Additionally, if the IDE has a Trust feature then the target folder must be marked as ‘trusted’ in order to be vulnerable. **NOTE:** This issue is independent of the one reported in [CVE-2022-40764](https://security.snyk.io/vuln/SNYK-JS-SNYK-3037342), and upgrading to a fixed version for this addresses that issue as well. The affected IDE plugins and versions are: - VS Code - Affected: <=1.8.0, Fixed: 1.9.0 - IntelliJ - Affected: <=2.4.47, Fixed: 2.4.48 - Visual Studio - Affected: <=1.1.30, Fixed: 1.1.31 - Eclipse - Affected: <=v20221115.132308, Fixed: All subsequent versions - Language Server - Affected: <=v20221109.114426, Fixed: All subsequent versions
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2022-11-30T13:15Z
Repository: https://github.com/snyk/vscode-extension
Language: TypeScript
Files changed: CHANGELOG.md, package.json, src/snyk/base/modules/snykLib.ts, src/snyk/cli/services/cliService.ts, src/snyk/common/commands/commandController.ts, src/snyk/common/configuration/configuration.ts, src/snyk/common/configuration/trustedFolders.ts, src/snyk/common/constants/languageServer.ts, src/snyk/common/constants/settings.ts, src/snyk/common/languageServer/languageServer.ts, src/snyk/common/languageServer/settings.ts, src/snyk/common/messages/analysisMessages.ts, src/snyk/common/views/analysisTreeNodeProvider.ts, src/snyk/common/watchers/configurationWatcher.ts, src/snyk/snykCode/codeService.ts, src/snyk/snykCode/views/issueTreeProvider.ts, src/snyk/snykOss/services/ossService.ts, src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts, src/test/unit/cli/services/cliService.test.ts, src/test/unit/common/configuration/trustedFolders.test.ts, src/test/unit/common/languageServer/languageServer.test.ts, src/test/unit/common/languageServer/middleware.test.ts, src/test/unit/snykOss/services/ossService.test.ts
Commit date: 2022-11-29 13:24:41 +0100
Commit message: feat: workspace trust (#306)


Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0238cc..4e758bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,14 @@
 # Snyk Security - Code and Open Source Dependencies Changelog
 
-## [1.7.8]
+## [1.9.0]
+
+### Added
+
+- Added workspace trust feature.
+
+## [1.8.0]
 
 ### Added
 
 - Snyk LS: (Preview) Added IaC scans enabled by feature flag (`lsIacScan`).
 
diff --git a/package.json b/package.json
index c9e3205..73f7308 100644
--- a/package.json
+++ b/package.json
@@ -173,10 +173,15 @@
             },
             "additionalProperties": false,
             "description": "Severity issues to display.",
             "scope": "window"
           },
+          "snyk.trustedFolders": {
+            "type": "array",
+            "default": [],
+            "description": "Folders to trust for Snyk scans."
+          },
           "snyk.features.preview": {
             "type": "object",
             "default": {},
             "title": "Preview feature toggles",
             "description": "Preview features that are currently in development. Setting keys will be removed when features become stable.",
@@ -269,11 +274,11 @@
         "contents": "Snyk has encountered a problem. Please restart the extension: \n[Restart](command:snyk.start 'Restart Snyk')\nIf the error persists, please check your [settings](command:snyk.settings) and [contact us](https://snyk.io/contact-us/?utm_source=vsc)!",
         "when": "snyk:error == 'blocking'"
       },
       {
         "view": "snyk.views.welcome",
-        "contents": "Welcome to Snyk for Visual Studio Code. 👋\nLet's start by connecting VS Code with Snyk:\n[Connect VS Code with Snyk](command:snyk.initiateLogin 'Connect with Snyk')\n👉 Snyk's mission is to finds bugs, fast. Connect with Snyk to start your first analysis!\nBy connecting your account with Snyk, you agree to the Snyk [Privacy Policy](https://snyk.io/policies/privacy), and the Snyk [Terms of Service](https://snyk.io/policies/terms-of-service).",
+        "contents": "Welcome to Snyk for Visual Studio Code. 👋\n👉 Connect with Snyk to start your first analysis!\nWhen 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](https://docs.snyk.io/ide-tools/visual-studio-code-extension/workspace-trust)\n[Trust workspace and connect](command:snyk.initiateLogin 'Connect with Snyk')\nBy connecting your account with Snyk, you agree to the Snyk [Privacy Policy](https://snyk.io/policies/privacy), and the Snyk [Terms of Service](https://snyk.io/policies/terms-of-service).",
         "when": "!snyk:error && !snyk:loggedIn"
       },
       {
         "view": "snyk.views.welcome",
         "contents": "We are now redirecting you to our auth page, go ahead and log in. If a browser window doesn't open after a few seconds, please copy the url below and manually paste it in a browser.\n[Copy URL to clipboard](command:snyk.copyAuthLink 'Copy URL to clipboard')",
diff --git a/src/snyk/base/modules/snykLib.ts b/src/snyk/base/modules/snykLib.ts
index bf51bb4..457db24 100644
--- a/src/snyk/base/modules/snykLib.ts
+++ b/src/snyk/base/modules/snykLib.ts
@@ -124,18 +124,18 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib {
 
   private async startOssAnalysis(manual = false, reportTriggeredEvent = true): Promise<void> {
     if (!configuration.getFeaturesConfiguration()?.ossEnabled) return;
     if (!this.ossService) throw new Error('OSS service is not initialized.');
 
-    // wait until Snyk CLI is downloaded
+    // wait until Snyk Language Server is downloaded
     await firstValueFrom(this.downloadService.downloadReady$);
 
     try {
       const oldResult = this.ossService.getResult();
       const result = await this.ossService.test(manual, reportTriggeredEvent);
 
-      if (result instanceof CliError) {
+      if (result instanceof CliError || !result) {
         return;
       }
 
       if (oldResult) {
         await this.ossService.showBackgroundNotification(oldResult);
diff --git a/src/snyk/cli/services/cliService.ts b/src/snyk/cli/services/cliService.ts
index 77f1a38..b44ed5d 100644
--- a/src/snyk/cli/services/cliService.ts
+++ b/src/snyk/cli/services/cliService.ts
@@ -1,12 +1,14 @@
 import { firstValueFrom } from 'rxjs';
 import parseArgsStringToArgv from 'string-argv';
 import { AnalysisStatusProvider } from '../../common/analysis/statusProvider';
 import { IConfiguration } from '../../common/configuration/configuration';
+import { getTrustedFolders } from '../../common/configuration/trustedFolders';
 import { ErrorHandler } from '../../common/error/errorHandler';
 import { ILanguageServer } from '../../common/languageServer/languageServer';
 import { ILog } from '../../common/logger/interfaces';
+import { messages as analysisMessages } from '../../common/messages/analysisMessages';
 import { DownloadService } from '../../common/services/downloadService';
 import { ExtensionContext } from '../../common/vscode/extensionContext';
 import { IVSCodeWorkspace } from '../../common/vscode/workspace';
 import { CliExecutable } from '../cliExecutable';
 import { messages } from '../messages/messages';
@@ -21,10 +23,11 @@ export abstract class CliService<CliResult> extends AnalysisStatusProvider {
   protected result: CliResult | CliError | undefined;
 
   private cliProcess?: CliProcess;
   private _isLsDownloadSuccessful = true;
   private _isCliReady: boolean;
+  private _isAnyWorkspaceFolderTrusted = true;
 
   constructor(
     protected readonly extensionContext: ExtensionContext,
     protected readonly logger: ILog,
     protected readonly config: IConfiguration,
@@ -41,11 +44,15 @@ export abstract class CliService<CliResult> extends AnalysisStatusProvider {
 
   get isCliReady(): boolean {
     return this._isCliReady;
   }
 
-  async test(manualTrigger: boolean, reportTriggeredEvent: boolean): Promise<CliResult | CliError> {
+  get isAnyWorkspaceFolderTrusted(): boolean {
+    return this._isAnyWorkspaceFolderTrusted;
+  }
+
+  async test(manualTrigger: boolean, reportTriggeredEvent: boolean): Promise<CliResult | CliError | void> {
     this.ensureDependencies();
 
     const currentCliPath = CliExecutable.getPath(this.extensionContext.extensionPath, this.config.getCliPath());
     const currentCliPathExists = await CliExecutable.exists(
       this.extensionContext.extensionPath,
@@ -64,25 +71,33 @@ export abstract class CliService<CliResult> extends AnalysisStatusProvider {
 
     // Prevent from CLI scan until Language Server downloads the CLI.
     const cliPath = await firstValueFrom(this.languageServer.cliReady$);
     this._isCliReady = true;
 
+    let foldersToTest = this.workspace.getWorkspaceFolders();
+    if (foldersToTest.length == 0) {
+      throw new Error('No workspace was opened.');
+    }
+
+    foldersToTest = getTrustedFolders(this.config, foldersToTest);
+    if (foldersToTest.length == 0) {
+      this.handleNoTrustedFolders();
+      this.logger.info(`Skipping Open Source scan. ${analysisMessages.noWorkspaceTrustDescription}`);
+      return;
+    }
+    this._isAnyWorkspaceFolderTrusted = true;
+
     // Start test
     this.analysisStarted();
     this.beforeTest(manualTrigger, reportTriggeredEvent);
     this.result = undefined;
 
     if (this.cliProcess) {
       const killed = this.cliProcess.kill();
       if (!killed) this.logger.error('Failed to kill an already running CLI instance.');
     }
 
-    const foldersToTest = this.workspace.getWorkspaceFolders();
-    if (foldersToTest.length == 0) {
-      throw new Error('No workspace was opened.');
-    }
-
     this.cliProcess = new CliProcess(this.logger, this.config, this.workspace);
     const args = this.buildArguments(foldersToTest);
 
     let output: string;
     try {
@@ -128,10 +143,14 @@ export abstract class CliService<CliResult> extends AnalysisStatusProvider {
   handleLsDownloadFailure(error: Error | unknown): void {
     this.logger.error(`${messages.lsDownloadFailed} ${ErrorHandler.stringifyError(error)}`);
     this._isLsDownloadSuccessful = false;
   }
 
+  handleNoTrustedFolders() {
+    this._isAnyWorkspaceFolderTrusted = false;
+  }
+
   private buildArguments(foldersToTest: string[]): string[] {
     const args = [];
 
     args.push(...this.command);
     args.push(...foldersToTest);
diff --git a/src/snyk/common/commands/commandController.ts b/src/snyk/common/commands/commandController.ts
index e0e0455..7f540e0 100644
--- a/src/snyk/common/commands/commandController.ts
+++ b/src/snyk/common/commands/commandController.ts
@@ -18,11 +18,11 @@ import {
   SNYK_OPEN_BROWSER_COMMAND,
   SNYK_SET_TOKEN_COMMAND,
   VSCODE_GO_TO_SETTINGS_COMMAND,
 } from '../constants/commands';
 import { COMMAND_DEBOUNCE_INTERVAL, IDE_NAME, SNYK_NAME_EXTENSION, SNYK_PUBLISHER } from '../constants/general';
-import { SNYK_LOGIN_COMMAND } from '../constants/languageServer';
+import { SNYK_LOGIN_COMMAND, SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND } from '../constants/languageServer';
 import { ErrorHandler } from '../error/errorHandler';
 import { ILog } from '../logger/interfaces';
 import { IOpenerService } from '../services/openerService';
 import { IVSCodeCommands } from '../vscode/commands';
 import { Range, Uri } from '../vscode/types';
@@ -53,10 +53,11 @@ export class CommandController {
 
   async initiateLogin(): Promise<void> {
     this.logger.info('Initiating login');
     await this.executeCommand(SNYK_INITIATE_LOGIN_COMMAND, this.authService.initiateLogin.bind(this.authService));
     await this.commands.executeCommand(SNYK_LOGIN_COMMAND);
+    await this.commands.executeCommand(SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND);
   }
 
   async setToken(): Promise<void> {
     await this.executeCommand(SNYK_SET_TOKEN_COMMAND, this.authService.setToken.bind(this.authService));
   }
diff --git a/src/snyk/common/configuration/configuration.ts b/src/snyk/common/configuration/configuration.ts
index ec19d8a..f9612d2 100644
--- a/src/snyk/common/configuration/configuration.ts
+++ b/src/snyk/common/configuration/configuration.ts
@@ -15,10 +15,11 @@ import {
   CODE_SECURITY_ENABLED_SETTING,
   CONFIGURATION_IDENTIFIER,
   FEATURES_PREVIEW_SETTING,
   OSS_ENABLED_SETTING,
   SEVERITY_FILTER_SETTING,
+  TRUSTED_FOLDERS,
   YES_BACKGROUND_OSS_NOTIFICATION_SETTING,
   YES_CRASH_REPORT_SETTING,
   YES_TELEMETRY_SETTING,
   YES_WELCOME_NOTIFICATION_SETTING,
 } from '../constants/settings';
@@ -96,10 +97,14 @@ export interface IConfiguration {
   severityFilter: SeverityFilter;
 
   getSnykLanguageServerPath(): string | undefined;
 
   setShouldReportEvents(b: boolean): Promise<void>;
+
+  getTrustedFolders(): string[];
+
+  setTrustedFolders(trustedFolders: string[]): Promise<void>;
 }
 
 export class Configuration implements IConfiguration {
   // These attributes are used in tests
   private readonly defaultSnykCodeBaseURL = 'https://deeproxy.snyk.io';
@@ -422,10 +427,24 @@ export class Configuration implements IConfiguration {
 
   getCliPath(): string | undefined {
     return this.workspace.getConfiguration<string>(CONFIGURATION_IDENTIFIER, this.getConfigName(ADVANCED_CLI_PATH));
   }
 
+  getTrustedFolders(): string[] {
+    return (
+      this.workspace.getConfiguration<string[]>(CONFIGURATION_IDENTIFIER, this.getConfigName(TRUSTED_FOLDERS)) || []
+    );
+  }
+
+  async setTrustedFolders(trustedFolders: string[]): Promise<void> {
+    await this.workspace.updateConfiguration(
+      CONFIGURATION_IDENTIFIER,
+      this.getConfigName(TRUSTED_FOLDERS),
+      trustedFolders,
+      true,
+    );
+  }
   private getConfigName = (setting: string) => setting.replace(`${CONFIGURATION_IDENTIFIER}.`, '');
 
   private static isSingleTenant(url: URL): boolean {
     return url.host.startsWith('app') && url.host.endsWith('snyk.io');
   }
diff --git a/src/snyk/common/configuration/trustedFolders.ts b/src/snyk/common/configuration/trustedFolders.ts
new file mode 100644
index 0000000..41652a5
--- /dev/null
+++ b/src/snyk/common/configuration/trustedFolders.ts
@@ -0,0 +1,7 @@
+import { IConfiguration } from './configuration';
+
+export function getTrustedFolders(config: IConfiguration, workspaceFolders: string[]): string[] {
+  const trustedFolders = config.getTrustedFolders();
+
+  return workspaceFolders.filter(folder => trustedFolders.includes(folder));
+}
diff --git a/src/snyk/common/constants/languageServer.ts b/src/snyk/common/constants/languageServer.ts
index 4c7a768..3f7ee57 100644
--- a/src/snyk/common/constants/languageServer.ts
+++ b/src/snyk/common/constants/languageServer.ts
@@ -8,9 +8,11 @@ export const PROTOCOL_VERSION = 3;
 export const DID_CHANGE_CONFIGURATION_METHOD = 'workspace/didChangeConfiguration';
 
 // custom methods
 export const SNYK_HAS_AUTHENTICATED = '$/snyk.hasAuthenticated';
 export const SNYK_CLI_PATH = '$/snyk.isAvailableCli';
+export const SNYK_ADD_TRUSTED_FOLDERS = '$/snyk.addTrustedFolders';
 
 // commands
 export const SNYK_LOGIN_COMMAND = 'snyk.login';
 export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan';
+export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders';
diff --git a/src/snyk/common/constants/settings.ts b/src/snyk/common/constants/settings.ts
index d7d3750..11fd503 100644
--- a/src/snyk/common/constants/settings.ts
+++ b/src/snyk/common/constants/settings.ts
@@ -20,5 +20,6 @@ export const ADVANCED_ORGANIZATION = `${CONFIGURATION_IDENTIFIER}.advanced.organ
 export const ADVANCED_AUTOMATIC_DEPENDENCY_MANAGEMENT = `${CONFIGURATION_IDENTIFIER}.advanced.automaticDependencyManagement`;
 export const ADVANCED_CLI_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.cliPath`;
 export const ADVANCED_CUSTOM_LS_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.languageServerPath`;
 
 export const SEVERITY_FILTER_SETTING = `${CONFIGURATION_IDENTIFIER}.severity`;
+export const TRUSTED_FOLDERS = `${CONFIGURATION_IDENTIFIER}.trustedFolders`;
diff --git a/src/snyk/common/languageServer/languageServer.ts b/src/snyk/common/languageServer/languageServer.ts
index a368eae..825a348 100644
--- a/src/snyk/common/languageServer/languageServer.ts
+++ b/src/snyk/common/languageServer/languageServer.ts
@@ -1,10 +1,15 @@
 import { firstValueFrom, ReplaySubject } from 'rxjs';
 import { IAuthenticationService } from '../../base/services/authenticationService';
 import { CLI_INTEGRATION_NAME } from '../../cli/contants/integration';
 import { Configuration, IConfiguration } from '../configuration/configuration';
-import { SNYK_CLI_PATH, SNYK_HAS_AUTHENTICATED, SNYK_LANGUAGE_SERVER_NAME } from '../constants/languageServer';
+import {
+  SNYK_ADD_TRUSTED_FOLDERS,
+  SNYK_CLI_PATH,
+  SNYK_HAS_AUTHENTICATED,
+  SNYK_LANGUAGE_SERVER_NAME,
+} from '../constants/languageServer';
 import { CONFIGURATION_IDENTIFIER } from '../constants/settings';
 import { ErrorHandler } from '../error/errorHandler';
 import { ILog } from '../logger/interfaces';
 import { getProxyEnvVariable, getProxyOptions } from '../proxy';
 import { DownloadService } from '../services/downloadService';
@@ -89,51 +94,60 @@ export class LanguageServer implements ILanguageServer {
       outputChannel: this.client?.outputChannel ?? this.window.createOutputChannel(SNYK_LANGUAGE_SERVER_NAME),
     };
 
     // Create the language client and start the client.
     this.client = this.languageClientAdapter.create('Snyk LS', SNYK_LANGUAGE_SERVER_NAME, serverOptions, clientOptions);
-
     this.client
       .onReady()
       .then(() => {
-        this.client.onNotification(SNYK_HAS_AUTHENTICATED, ({ token }: { token: string }) => {
-          this.authenticationService.updateToken(token).catch((error: Error) => {
-            ErrorHandler.handle(error, this.logger, error.message);
-          });
-        });
-
-        this.client.onNotification(SNYK_CLI_PATH, ({ cliPath }: { cliPath: string }) => {
-          if (!cliPath) {
-            ErrorHandler.handle(
-              new Error("CLI path wasn't provided by language server on $/snyk.isAvailableCli notification " + cliPath),
-              this.logger,
-              "CLI path wasn't provided by language server on notification",
-            );
-            return;
-          }
-
-          const currentCliPath = this.configuration.getCliPath();
-          if (currentCliPath != cliPath) {
-            this.logger.info('Setting Snyk CLI path to: ' + cliPath);
-            void this.configuration
-              .setCliPath(cliPath)
-              .then(() => {
-                this.cliReady$.next(cliPath);
-              })
-              .catch((error: Error) => {
-                ErrorHandler.handle(error, this.logger, error.message);
-              });
-          }
-        });
+        this.registerListeners(this.client);
       })
       .catch((error: Error) => ErrorHandler.handle(error, this.logger, error.message));
 
     // Start the client. This will also launch the server
     this.client.start();
     this.logger.info('Snyk Language Server started');
   }
 
+  private registerListeners(client: LanguageClient): void {
+    client.onNotification(SNYK_HAS_AUTHENTICATED, ({ token }: { token: string }) => {
+      this.authenticationService.updateToken(token).catch((error: Error) => {
+        ErrorHandler.handle(error, this.logger, error.message);
+      });
+    });
+
+    client.onNotification(SNYK_CLI_PATH, ({ cliPath }: { cliPath: string }) => {
+      if (!cliPath) {
+        ErrorHandler.handle(
+          new Error("CLI path wasn't provided by language server on $/snyk.isAvailableCli notification " + cliPath),
+          this.logger,
+          "CLI path wasn't provided by language server on notification",
+        );
+        return;
+      }
+
+      const currentCliPath = this.configuration.getCliPath();
+      if (currentCliPath != cliPath) {
+        this.logger.info('Setting Snyk CLI path to: ' + cliPath);
+        void this.configuration
+          .setCliPath(cliPath)
+          .then(() => {
+            this.cliReady$.next(cliPath);
+          })
+          .catch((error: Error) => {
+            ErrorHandler.handle(error, this.logger, error.message);
+          });
+      }
+    });
+
+    client.onNotification(SNYK_ADD_TRUSTED_FOLDERS, ({ trustedFolders }: { trustedFolders: string[] }) => {
+      this.configuration.setTrustedFolders(trustedFolders).catch((error: Error) => {
+        ErrorHandler.handle(error, this.logger, error.message);
+      });
+    });
+  }
+
   // Initialization options are not semantically equal to server settings, thus separated here
   // https://github.com/microsoft/language-server-protocol/issues/567
   async getInitializationOptions(): Promise<InitializationOptions> {
     const settings = await LanguageServerSettings.fromConfiguration(this.configuration);
     return {
diff --git a/src/snyk/common/languageServer/settings.ts b/src/snyk/common/languageServer/settings.ts
index 02029ff..41736b1 100644
--- a/src/snyk/common/languageServer/settings.ts
+++ b/src/snyk/common/languageServer/settings.ts
@@ -18,10 +18,12 @@ export type ServerSettings = {
   organization?: string;
   enableTelemetry?: string;
   manageBinariesAutomatically?: string;
   cliPath?: string;
   token?: string;
+  enableTrustedFoldersFeature?: string;
+  trustedFolders?: string[];
 };
 
 export class LanguageServerSettings {
   static async fromConfiguration(configuration: IConfiguration): Promise<ServerSettings> {
     return {
@@ -34,8 +36,10 @@ export class LanguageServerSettings {
       endpoint: configuration.snykOssApiEndpoint,
       additionalParams: configuration.getAdditionalCliParameters(),
       organization: configuration.organization,
       token: await configuration.getToken(),
       manageBinariesAutomatically: `${configuration.isAutomaticDependencyManagementEnabled()}`,
+      enableTrustedFoldersFeature: 'true',
+      trustedFolders: configuration.getTrustedFolders(),
     };
   }
 }
diff --git a/src/snyk/common/messages/analysisMessages.ts b/src/snyk/common/messages/analysisMessages.ts
index 21fd2aa..8281edf 100644
--- a/src/snyk/common/messages/analysisMessages.ts
+++ b/src/snyk/common/messages/analysisMessages.ts
@@ -1,6 +1,9 @@
 export const messages = {
   scanFailed: 'Scan failed',
+  noWorkspaceTrust: 'No workspace folder was granted trust',
   clickToProblem: 'Click here to see the problem.',
   allSeverityFiltersDisabled: 'Please enable severity filters to see the results.',
   duration: (time: string, day: string): string => `Analysis finished at ${time}, ${day}`,
+  noWorkspaceTrustDescription:
+    'None of workspace folders were trusted. If you trust the workspace, you can add it to the list of trusted folders in the extension settings, or when prompted by the extension next time.',
 };
diff --git a/src/snyk/common/views/analysisTreeNodeProvider.ts b/src/snyk/common/views/analysisTreeNodeProvider.ts
index ecb4820..e070d3b 100644
--- a/src/snyk/common/views/analysisTreeNodeProvider.ts
+++ b/src/snyk/common/views/analysisTreeNodeProvider.ts
@@ -70,7 +70,17 @@ export abstract class AnalysisTreeNodeProvder extends TreeNodeProvider {
         title: '',
       },
     });
   }
 
+  protected getNoWorkspaceTrustTreeNode(): TreeNode {
+    return new TreeNode({
+      text: messages.noWorkspaceTrust,
+      command: {
+        command: SNYK_SHOW_OUTPUT_COMMAND,
+        title: '',
+      },
+    });
+  }
+
   protected abstract getFilteredIssues(issues: readonly unknown[]): readonly unknown[];
 }
diff --git a/src/snyk/common/watchers/configurationWatcher.ts b/src/snyk/common/watchers/configurationWatcher.ts
index 635cfc7..6c03d0c 100644
--- a/src/snyk/common/watchers/configurationWatcher.ts
+++ b/src/snyk/common/watchers/configurationWatcher.ts
@@ -11,10 +11,11 @@ import {
   ADVANCED_CUSTOM_LS_PATH,
   CODE_QUALITY_ENABLED_SETTING,
   CODE_SECURITY_ENABLED_SETTING,
   OSS_ENABLED_SETTING,
   SEVERITY_FILTER_SETTING,
+  TRUSTED_FOLDERS,
   YES_TELEMETRY_SETTING,
 } from '../constants/settings';
 import { ErrorHandler } from '../error/errorHandler';
 import { ILog } from '../logger/interfaces';
 import { errorsLogs } from '../messages/errors';
@@ -66,10 +67,11 @@ class ConfigurationWatcher implements IWatcher {
         CODE_SECURITY_ENABLED_SETTING,
         CODE_QUALITY_ENABLED_SETTING,
         SEVERITY_FILTER_SETTING,
         ADVANCED_CUSTOM_ENDPOINT,
         ADVANCED_CUSTOM_LS_PATH,
+        TRUSTED_FOLDERS,
       ].find(config => event.affectsConfiguration(config));
 
       if (change) {
         try {
           await this.onChangeConfiguration(extension, change);
diff --git a/src/snyk/snykCode/codeService.ts b/src/snyk/snykCode/codeService.ts
index c25caa2..d1c9a83 100644
--- a/src/snyk/snykCode/codeService.ts
+++ b/src/snyk/snykCode/codeService.ts
@@ -2,14 +2,16 @@ import { AnalysisSeverity, analyzeFolders, extendAnalysis, FileAnalysis } from '
 import { ConnectionOptions } from '@snyk/code-client/dist/http';
 import { v4 as uuidv4 } from 'uuid';
 import { AnalysisStatusProvider } from '../common/analysis/statusProvider';
 import { IAnalytics, SupportedAnalysisProperties } from '../common/analytics/itly';
 import { FeaturesConfiguration, IConfiguration } from '../common/configuration/configuration';
+import { getTrustedFolders } from '../common/configuration/trustedFolders';
 import { IDE_NAME } from '../common/constants/general';
 import { ErrorHandler } from '../common/error/errorHandler';
 import { ILog } from '../common/logger/interfaces';
 import { Logger } from '../common/logger/logger';
+import { messages as generalAnalysisMessages } from '../common/messages/analysisMessages';
 import { LearnService } from '../common/services/learnService';
 import { IViewManagerService } from '../common/services/viewManagerService';
 import { User } from '../common/user';
 import { IWebViewProvider } from '../common/views/webviewProvider';
 import { ExtensionContext } from '../common/vscode/extensionContext';
@@ -45,10 +47,11 @@ export interface ISnykCodeService extends AnalysisStatusProvider, Disposable {
   readonly remoteBundle: FileAnalysis | null;
   readonly suggestionProvider: ICodeSuggestionWebviewProvider;
   readonly falsePositiveProvider: IWebViewProvider<FalsePositiveWebviewModel>;
   hasError: boolean;
   hasTransientError: boolean;
+  isAnyWorkspaceFolderTrusted: boolean;
 
   startAnalysis(paths: string[], manual: boolean, reportTriggeredEvent: boolean): Promise<void>;
   clearBundle(): void;
   updateStatus(status: string, progress: string): void;
   errorEncountered(requestId: string): void;
@@ -72,10 +75,11 @@ export class SnykCodeService extends AnalysisStatusProvider implements ISnykCode
   private progress: Progress;
   private _analysisStatus = '';
   private _analysisProgress = '';
   private temporaryFailed = false;
   private failed = false;
+  private _isAnyWorkspaceFolderTrusted = true;
 
   constructor(
     readonly extensionContext: ExtensionContext,
     private readonly config: IConfiguration,
     private readonly viewManagerService: IViewManagerService,
@@ -132,10 +136,13 @@ export class SnykCodeService extends AnalysisStatusProvider implements ISnykCode
     return this.failed;
   }
   get hasTransientError(): boolean {
     return this.temporaryFailed;
   }
+  get isAnyWorkspaceFolderTrusted(): boolean {
+    return this._isAnyWorkspaceFolderTrusted;
+  }
 
   get analysisStatus(): string {
     return this._analysisStatus;
   }
   get analysisProgress(): string {
@@ -148,10 +155,19 @@ export class SnykCodeService extends AnalysisStatusProvider implements ISnykCode
     }
 
     const enabledFeatures = this.config.getFeaturesConfiguration();
     const requestId = uuidv4();
 
+    paths = getTrustedFolders(this.config, paths);
+    if (!paths.length) {
+      this._isAnyWorkspaceFolderTrusted = false;
+      this.viewManagerService.refreshCodeAnalysisViews(enabledFeatures);
+      this.logger.info(`Skipping Code scan. ${generalAnalysisMessages.noWorkspaceTrustDescription}`);
+      return;
+    }
+    this._isAnyWorkspaceFolderTrusted = true;
+
     try {
       Logger.info(analysisMessages.started);
 
       // reset error state
       this.temporaryFailed = false;
diff --git a/src/snyk/snykCode/views/issueTreeProvider.ts b/src/snyk/snykCode/views/issueTreeProvider.ts
index 419c0c0..8c3aeb7 100644
--- a/src/snyk/snykCode/views/issueTreeProvider.ts
+++ b/src/snyk/snykCode/views/issueTreeProvider.ts
@@ -49,10 +49,12 @@ export class IssueTreeProvider extends AnalysisTreeNodeProvder {
 
     if (this.snykCode.hasTransientError) {
       return this.getTransientErrorTreeNodes();
     } else if (this.snykCode.hasError) {
       return [this.getErrorEncounteredTreeNode()];
+    } else if (!this.snykCode.isAnyWorkspaceFolderTrusted) {
+      return [this.getNoWorkspaceTrustTreeNode()];
     }
 
     if (this.diagnosticCollection) {
       this.diagnosticCollection.forEach((uri: Uri, diagnostics: readonly Diagnostic[]): void => {
         const filePath = uri.path.split('/');
diff --git a/src/snyk/snykOss/services/ossService.ts b/src/snyk/snykOss/services/ossService.ts
index 0402270..2989147 100644
--- a/src/snyk/snykOss/services/ossService.ts
+++ b/src/snyk/snykOss/services/ossService.ts
@@ -104,10 +104,15 @@ export class OssService extends CliService<OssResult> {
   override handleLsDownloadFailure(error: Error | unknown): void {
     this.viewManagerService.refreshOssView();
     super.handleLsDownloadFailure(error);
   }
 
+  override handleNoTrustedFolders(): void {
+    super.handleNoTrustedFolders();
+    this.viewManagerService.refreshOssView();
+  }
+
   activateSuggestionProvider(): void {
     this.suggestionProvider.activate();
   }
 
   showSuggestionProvider(vulnerability: OssIssueCommandArg): Promise<void> {
diff --git a/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts b/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts
index 2733b55..6cd3e81 100644
--- a/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts
+++ b/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts
@@ -48,10 +48,12 @@ export class OssVulnerabilityTreeProvider extends AnalysisTreeNodeProvder {
       return [
         new TreeNode({
           text: messages.cookingDependencies,
         }),
       ];
+    } else if (!this.ossService.isAnyWorkspaceFolderTrusted) {
+      return [this.getNoWorkspaceTrustTreeNode()];
     }
 
     if (this.ossService.isAnalysisRunning) {
       return [
         new TreeNode({
diff --git a/src/test/unit/cli/services/cliService.test.ts b/src/test/unit/cli/services/cliService.test.ts
index 636ea6d..3169dcb 100644
--- a/src/test/unit/cli/services/cliService.test.ts
+++ b/src/test/unit/cli/services/cliService.test.ts
@@ -48,14 +48,16 @@ suite('CliService', () => {
     extensionContext = {
       extensionPath: extensionPath,
       getGlobalStateValue: () => undefined,
     } as unknown as ExtensionContext;
 
+    const testFolderPath = 'test-folder';
     configuration = {
       getAdditionalCliParameters: () => '',
       isAutomaticDependencyManagementEnabled: () => true,
       getCliPath: () => undefined,
+      getTrustedFolders: () => [testFolderPath],
     } as unknown as IConfiguration;
 
     downloadService = {
       download: () => false,
       isCliInstalled: () => true,
@@ -69,11 +71,11 @@ suite('CliService', () => {
     testCliService = new TestCliService(
       extensionContext,
       logger,
       configuration,
       {
-        getWorkspaceFolders: () => ['test-folder'],
+        getWorkspaceFolders: () => [testFolderPath],
       } as IVSCodeWorkspace,
       downloadService,
       ls,
     );
   });
diff --git a/src/test/unit/common/configuration/trustedFolders.test.ts b/src/test/unit/common/configuration/trustedFolders.test.ts
new file mode 100644
index 0000000..7254747
--- /dev/null
+++ b/src/test/unit/common/configuration/trustedFolders.test.ts
@@ -0,0 +1,38 @@
+import { deepStrictEqual } from 'assert';
+import { IConfiguration } from '../../../../snyk/common/configuration/configuration';
+import { getTrustedFolders } from '../../../../snyk/common/configuration/trustedFolders';
+
+suite('Trusted Folders', () => {
+  test('Single folder trusted', () => {
+    const config = {
+      getTrustedFolders: () => ['/test/workspace', '/test/workspace2'],
+    } as IConfiguration;
+    const workspaceFolders = ['/test/workspace'];
+
+    const trustedFolders = getTrustedFolders(config, workspaceFolders);
+
+    deepStrictEqual(trustedFolders, ['/test/workspace']);
+  });
+
+  test('Multiple folders trusted', () => {
+    const config = {
+      getTrustedFolders: () => ['/test/workspace', '/test/workspace2'],
+    } as IConfiguration;
+    const workspaceFolders = ['/test/workspace', '/test/workspace2'];
+
+    const trustedFolders = getTrustedFolders(config, workspaceFolders);
+
+    deepStrictEqual(trustedFolders, ['/test/workspace', '/test/workspace2']);
+  });
+
+  test('No folders trusted', () => {
+    const config = {
+      getTrustedFolders: (): string[] => [],
+    } as IConfiguration;
+    const workspaceFolders = ['/test/workspace', '/test/workspace2'];
+
+    const trustedFolders = getTrustedFolders(config, workspaceFolders);
+
+    deepStrictEqual(trustedFolders, []);
+  });
+});
diff --git a/src/test/unit/common/languageServer/languageServer.test.ts b/src/test/unit/common/languageServer/languageServer.test.ts
index 2f0681c..a99533d 100644
--- a/src/test/unit/common/languageServer/languageServer.test.ts
+++ b/src/test/unit/common/languageServer/languageServer.test.ts
@@ -47,10 +47,13 @@ suite('Language Server', () => {
           lsIacScan: false,
           advisor: false,
           reportFalsePositives: false,
         };
       },
+      getTrustedFolders(): string[] {
+        return ['/trusted/test/folder'];
+      },
     } as IConfiguration;
 
     downloadService = {
       downloadReady$: new ReplaySubject<void>(1),
     } as DownloadService;
@@ -85,10 +88,12 @@ suite('Language Server', () => {
       endpoint: undefined,
       organization: undefined,
       additionalParams: '--all-projects',
       manageBinariesAutomatically: 'true',
       deviceId: user.anonymousId,
+      enableTrustedFoldersFeature: 'true',
+      trustedFolders: ['/trusted/test/folder'],
     };
 
     deepStrictEqual(await languageServer.getInitializationOptions(), expectedInitializationOptions);
   });
 
diff --git a/src/test/unit/common/languageServer/middleware.test.ts b/src/test/unit/common/languageServer/middleware.test.ts
index f90ba4d..38d5400 100644
--- a/src/test/unit/common/languageServer/middleware.test.ts
+++ b/src/test/unit/common/languageServer/middleware.test.ts
@@ -29,10 +29,11 @@ suite('Language Server: Middleware', () => {
           lsIacScan: false,
           advisor: false,
           reportFalsePositives: false,
         };
       },
+      getTrustedFolders: () => ['/trusted/test/folder'],
     } as IConfiguration;
   });
 
   teardown(() => {
     sinon.restore();
@@ -76,10 +77,12 @@ suite('Language Server: Middleware', () => {
     );
     assert.strictEqual(
       serverResult.cliPath,
       CliExecutable.getPath(extensionContextMock.extensionPath, configuration.getCliPath()),
     );
+    assert.strictEqual(serverResult.enableTrustedFoldersFeature, 'true');
+    assert.deepStrictEqual(serverResult.trustedFolders, configuration.getTrustedFolders());
   });
 
   test('Configuration request should return an error', async () => {
     const middleware = new LanguageClientMiddleware(configuration);
     const params: ConfigurationParams = {
diff --git a/src/test/unit/snykOss/services/ossService.test.ts b/src/test/unit/snykOss/services/ossService.test.ts
index 6c6d172..927cae8 100644
--- a/src/test/unit/snykOss/services/ossService.test.ts
+++ b/src/test/unit/snykOss/services/ossService.test.ts
@@ -31,23 +31,25 @@ suite('OssService', () => {
     const ls = {
       cliReady$: new ReplaySubject<void>(1),
     } as unknown as ILanguageServer;
     ls.cliReady$.next('');
 
+    const testFolderPath = '';
     ossService = new OssService(
       {
         extensionPath,
       } as ExtensionContext,
       logger,
       {
         getAdditionalCliParameters: () => '',
         getCliPath: () => undefined,
         isAutomaticDependencyManagementEnabled: () => true,
-      } as IConfiguration,
+        getTrustedFolders: () => [testFolderPath],
+      } as unknown as IConfiguration,
       {} as IWebViewProvider<OssIssueCommandArg>,
       {
-        getWorkspaceFolders: () => [''],
+        getWorkspaceFolders: () => [testFolderPath],
       } as IVSCodeWorkspace,
       {
         refreshOssView: () => undefined,
       } as IViewManagerService,
       {} as DownloadService,

Vulnerable code: ## [1.7.8]
        "contents": "Welcome to Snyk for Visual Studio Code. 👋\nLet's start by connecting VS Code with Snyk:\n[Connect VS Code with Snyk](command:snyk.initiateLogin 'Connect with Snyk')\n👉 Snyk's mission is to finds bugs, fast. Connect with Snyk to start your first analysis!\nBy connecting your account with Snyk, you agree to the Snyk [Privacy Policy](https://snyk.io/policies/privacy), and the Snyk [Terms of Service](https://snyk.io/policies/terms-of-service).",
    // wait until Snyk CLI is downloaded
      if (result instanceof CliError) {
  async test(manualTrigger: boolean, reportTriggeredEvent: boolean): Promise<CliResult | CliError> {
    const foldersToTest = this.workspace.getWorkspaceFolders();
    if (foldersToTest.length == 0) {
      throw new Error('No workspace was opened.');
    }

import { SNYK_LOGIN_COMMAND } from '../constants/languageServer';
import { SNYK_CLI_PATH, SNYK_HAS_AUTHENTICATED, SNYK_LANGUAGE_SERVER_NAME } from '../constants/languageServer';

        this.client.onNotification(SNYK_HAS_AUTHENTICATED, ({ token }: { token: string }) => {
          this.authenticationService.updateToken(token).catch((error: Error) => {
            ErrorHandler.handle(error, this.logger, error.message);
          });
        });

        this.client.onNotification(SNYK_CLI_PATH, ({ cliPath }: { cliPath: string }) => {
          if (!cliPath) {
            ErrorHandler.handle(
              new Error("CLI path wasn't provided by language server on $/snyk.isAvailableCli notification " + cliPath),
              this.logger,
              "CLI path wasn't provided by language server on notification",
            );
            return;
          }

          const currentCliPath = this.configuration.getCliPath();
          if (currentCliPath != cliPath) {
            this.logger.info('Setting Snyk CLI path to: ' + cliPath);
            void this.configuration
              .setCliPath(cliPath)
              .then(() => {
                this.cliReady$.next(cliPath);
              })
              .catch((error: Error) => {
                ErrorHandler.handle(error, this.logger, error.message);
              });
          }
        });
        getWorkspaceFolders: () => ['test-folder'],
      } as IConfiguration,
        getWorkspaceFolders: () => [''],
Fixed code: ## [1.9.0]

### Added

- Added workspace trust feature.

## [1.8.0]
          "snyk.trustedFolders": {
            "type": "array",
            "default": [],
            "description": "Folders to trust for Snyk scans."
          },
        "contents": "Welcome to Snyk for Visual Studio Code. 👋\n👉 Connect with Snyk to start your first analysis!\nWhen 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](https://docs.snyk.io/ide-tools/visual-studio-code-extension/workspace-trust)\n[Trust workspace and connect](command:snyk.initiateLogin 'Connect with Snyk')\nBy connecting your account with Snyk, you agree to the Snyk [Privacy Policy](https://snyk.io/policies/privacy), and the Snyk [Terms of Service](https://snyk.io/policies/terms-of-service).",
    // wait until Snyk Language Server is downloaded
      if (result instanceof CliError || !result) {
import { getTrustedFolders } from '../../common/configuration/trustedFolders';
import { messages as analysisMessages } from '../../common/messages/analysisMessages';
  private _isAnyWorkspaceFolderTrusted = true;
  get isAnyWorkspaceFolderTrusted(): boolean {
    return this._isAnyWorkspaceFolderTrusted;
  }

  async test(manualTrigger: boolean, reportTriggeredEvent: boolean): Promise<CliResult | CliError | void> {
    let foldersToTest = this.workspace.getWorkspaceFolders();
    if (foldersToTest.length == 0) {
      throw new Error('No workspace was opened.');
    }

    foldersToTest = getTrustedFolders(this.config, foldersToTest);
    if (foldersToTest.length == 0) {
      this.handleNoTrustedFolders();
      this.logger.info(`Skipping Open Source scan. ${analysisMessages.noWorkspaceTrustDescription}`);
      return;
    }
    this._isAnyWorkspaceFolderTrusted = true;

  handleNoTrustedFolders() {
    this._isAnyWorkspaceFolderTrusted = false;
  }

import { SNYK_LOGIN_COMMAND, SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND } from '../constants/languageServer';
    await this.commands.executeCommand(SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND);
  TRUSTED_FOLDERS,

  getTrustedFolders(): string[];

  setTrustedFolders(trustedFolders: string[]): Promise<void>;
  getTrustedFolders(): string[] {
    return (
      this.workspace.getConfiguration<string[]>(CONFIGURATION_IDENTIFIER, this.getConfigName(TRUSTED_FOLDERS)) || []
    );
  }

  async setTrustedFolders(trustedFolders: string[]): Promise<void> {
    await this.workspace.updateConfiguration(
      CONFIGURATION_IDENTIFIER,
      this.getConfigName(TRUSTED_FOLDERS),
      trustedFolders,
      true,
    );
  }
import { IConfiguration } from './configuration';

export function getTrustedFolders(config: IConfiguration, workspaceFolders: string[]): string[] {
  const trustedFolders = config.getTrustedFolders();

  return workspaceFolders.filter(folder => trustedFolders.includes(folder));
}
export const SNYK_ADD_TRUSTED_FOLDERS = '$/snyk.addTrustedFolders';
export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders';
export const TRUSTED_FOLDERS = `${CONFIGURATION_IDENTIFIER}.trustedFolders`;
import {
  SNYK_ADD_TRUSTED_FOLDERS,
  SNYK_CLI_PATH,
  SNYK_HAS_AUTHENTICATED,
  SNYK_LANGUAGE_SERVER_NAME,
} from '../constants/languageServer';
        this.registerListeners(this.client);
  private registerListeners(client: LanguageClient): void {
    client.onNotification(SNYK_HAS_AUTHENTICATED, ({ token }: { token: string }) => {
      this.authenticationService.updateToken(token).catch((error: Error) => {
        ErrorHandler.handle(error, this.logger, error.message);
      });
    });

    client.onNotification(SNYK_CLI_PATH, ({ cliPath }: { cliPath: string }) => {
      if (!cliPath) {
        ErrorHandler.handle(
          new Error("CLI path wasn't provided by language server on $/snyk.isAvailableCli notification " + cliPath),
          this.logger,
          "CLI path wasn't provided by language server on notification",
        );
        return;
      }

      const currentCliPath = this.configuration.getCliPath();
      if (currentCliPath != cliPath) {
        this.logger.info('Setting Snyk CLI path to: ' + cliPath);
        void this.configuration
          .setCliPath(cliPath)
          .then(() => {
            this.cliReady$.next(cliPath);
          })
          .catch((error: Error) => {
            ErrorHandler.handle(error, this.logger, error.message);
          });
      }
    });

    client.onNotification(SNYK_ADD_TRUSTED_FOLDERS, ({ trustedFolders }: { trustedFolders: string[] }) => {
      this.configuration.setTrustedFolders(trustedFolders).catch((error: Error) => {
        ErrorHandler.handle(error, this.logger, error.message);
      });
    });
  }

  enableTrustedFoldersFeature?: string;
  trustedFolders?: string[];
      enableTrustedFoldersFeature: 'true',
      trustedFolders: configuration.getTrustedFolders(),
  noWorkspaceTrust: 'No workspace folder was granted trust',
  noWorkspaceTrustDescription:
    'None of workspace folders were trusted. If you trust the workspace, you can add it to the list of trusted folders in the extension settings, or when prompted by the extension next time.',
  protected getNoWorkspaceTrustTreeNode(): TreeNode {
    return new TreeNode({
      text: messages.noWorkspaceTrust,
      command: {
        command: SNYK_SHOW_OUTPUT_COMMAND,
        title: '',
      },
    });
  }

  TRUSTED_FOLDERS,
        TRUSTED_FOLDERS,
import { getTrustedFolders } from '../common/configuration/trustedFolders';
import { messages as generalAnalysisMessages } from '../common/messages/analysisMessages';
  isAnyWorkspaceFolderTrusted: boolean;
  private _isAnyWorkspaceFolderTrusted = true;
  get isAnyWorkspaceFolderTrusted(): boolean {
    return this._isAnyWorkspaceFolderTrusted;
  }
    paths = getTrustedFolders(this.config, paths);
    if (!paths.length) {
      this._isAnyWorkspaceFolderTrusted = false;
      this.viewManagerService.refreshCodeAnalysisViews(enabledFeatures);
      this.logger.info(`Skipping Code scan. ${generalAnalysisMessages.noWorkspaceTrustDescription}`);
      return;
    }
    this._isAnyWorkspaceFolderTrusted = true;

    } else if (!this.snykCode.isAnyWorkspaceFolderTrusted) {
      return [this.getNoWorkspaceTrustTreeNode()];
  override handleNoTrustedFolders(): void {
    super.handleNoTrustedFolders();
    this.viewManagerService.refreshOssView();
  }

    } else if (!this.ossService.isAnyWorkspaceFolderTrusted) {
      return [this.getNoWorkspaceTrustTreeNode()];
    const testFolderPath = 'test-folder';
      getTrustedFolders: () => [testFolderPath],
        getWorkspaceFolders: () => [testFolderPath],
import { deepStrictEqual } from 'assert';
import { IConfiguration } from '../../../../snyk/common/configuration/configuration';
import { getTrustedFolders } from '../../../../snyk/common/configuration/trustedFolders';

suite('Trusted Folders', () => {
  test('Single folder trusted', () => {
    const config = {
      getTrustedFolders: () => ['/test/workspace', '/test/workspace2'],
    } as IConfiguration;
    const workspaceFolders = ['/test/workspace'];

    const trustedFolders = getTrustedFolders(config, workspaceFolders);

    deepStrictEqual(trustedFolders, ['/test/workspace']);
  });

  test('Multiple folders trusted', () => {
    const config = {
      getTrustedFolders: () => ['/test/workspace', '/test/workspace2'],
    } as IConfiguration;
    const workspaceFolders = ['/test/workspace', '/test/workspace2'];

    const trustedFolders = getTrustedFolders(config, workspaceFolders);

    deepStrictEqual(trustedFolders, ['/test/workspace', '/test/workspace2']);
  });

  test('No folders trusted', () => {
    const config = {
      getTrustedFolders: (): string[] => [],
    } as IConfiguration;
    const workspaceFolders = ['/test/workspace', '/test/workspace2'];

    const trustedFolders = getTrustedFolders(config, workspaceFolders);

    deepStrictEqual(trustedFolders, []);
  });
});
      getTrustedFolders(): string[] {
        return ['/trusted/test/folder'];
      },
      enableTrustedFoldersFeature: 'true',
      trustedFolders: ['/trusted/test/folder'],
      getTrustedFolders: () => ['/trusted/test/folder'],
    assert.strictEqual(serverResult.enableTrustedFoldersFeature, 'true');
    assert.deepStrictEqual(serverResult.trustedFolders, configuration.getTrustedFolders());
    const testFolderPath = '';
        getTrustedFolders: () => [testFolderPath],
      } as unknown as IConfiguration,
        getWorkspaceFolders: () => [testFolderPath],