Skip to content

Commit 7abeef4

Browse files
authored
(feat) add workspace trust (#1086)
#1051 Not removing the the ls server path scope for now to ensure backwards compatibility. Lift that restriction and bump the minimum required VS Code version later.
1 parent 0e8f5b2 commit 7abeef4

File tree

9 files changed

+83
-5
lines changed

9 files changed

+83
-5
lines changed

packages/language-server/src/importPackage.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import * as svelte from 'svelte/compiler';
44
import sveltePreprocess from 'svelte-preprocess';
55
import { Logger } from './logger';
66

7+
/**
8+
* Whether or not the current workspace can be trusted.
9+
* TODO rework this to a class which depends on the LsConfigManager
10+
* and inject that class into all places where it's needed (Document etc.)
11+
*/
12+
let isTrusted = true;
13+
14+
export function setIsTrusted(_isTrusted: boolean) {
15+
isTrusted = _isTrusted;
16+
}
17+
718
/**
819
* This function encapsulates the require call in one place
920
* so we can replace its content inside rollup builds
@@ -15,8 +26,12 @@ function dynamicRequire(dynamicFileToRequire: string): any {
1526
}
1627

1728
export function getPackageInfo(packageName: string, fromPath: string) {
29+
const paths = [__dirname];
30+
if (isTrusted) {
31+
paths.unshift(fromPath);
32+
}
1833
const packageJSONPath = require.resolve(`${packageName}/package.json`, {
19-
paths: [fromPath, __dirname]
34+
paths
2035
});
2136
// eslint-disable-next-line @typescript-eslint/no-var-requires
2237
const { version } = dynamicRequire(packageJSONPath);

packages/language-server/src/lib/documents/configLoader.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class ConfigLoader {
5252
private configFiles = new Map<string, SvelteConfig>();
5353
private configFilesAsync = new Map<string, Promise<SvelteConfig>>();
5454
private filePathToConfigPath = new Map<string, string>();
55+
private disabled = false;
5556

5657
constructor(
5758
private globSync: typeof _glob.sync,
@@ -60,6 +61,13 @@ export class ConfigLoader {
6061
private dynamicImport: typeof _dynamicImport
6162
) {}
6263

64+
/**
65+
* Enable/disable loading of configs (for security reasons for example)
66+
*/
67+
setDisabled(disabled: boolean): void {
68+
this.disabled = disabled;
69+
}
70+
6371
/**
6472
* Tries to load all `svelte.config.js` files below given directory
6573
* and the first one found inside/above that directory.
@@ -141,7 +149,9 @@ export class ConfigLoader {
141149

142150
private async loadConfig(configPath: string, directory: string) {
143151
try {
144-
let config = (await this.dynamicImport(pathToFileURL(configPath)))?.default;
152+
let config = this.disabled
153+
? {}
154+
: (await this.dynamicImport(pathToFileURL(configPath)))?.default;
145155
config = {
146156
...config,
147157
compilerOptions: {

packages/language-server/src/ls-config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export class LSConfigManager {
252252
};
253253
private prettierConfig: any = {};
254254
private emmetConfig: VSCodeEmmetConfig = {};
255+
private isTrusted = true;
255256

256257
/**
257258
* Updates config.
@@ -324,6 +325,18 @@ export class LSConfigManager {
324325
});
325326
}
326327

328+
/**
329+
* Whether or not the current workspace can be trusted.
330+
* If not, certain operations should be disabled.
331+
*/
332+
getIsTrusted(): boolean {
333+
return this.isTrusted;
334+
}
335+
336+
updateIsTrusted(isTrusted: boolean): void {
337+
this.isTrusted = isTrusted;
338+
}
339+
327340
private _updateTsUserPreferences(lang: TsUserConfigLang, config: TSUserConfig) {
328341
this.tsUserPreferences[lang] = {
329342
...this.tsUserPreferences[lang],

packages/language-server/src/plugins/svelte/SveltePlugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class SveltePlugin
4747
constructor(private configManager: LSConfigManager) {}
4848

4949
async getDiagnostics(document: Document): Promise<Diagnostic[]> {
50-
if (!this.featureEnabled('diagnostics')) {
50+
if (!this.featureEnabled('diagnostics') || !this.configManager.getIsTrusted()) {
5151
return [];
5252
}
5353

packages/language-server/src/server.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import {
3535
} from './plugins';
3636
import { debounceThrottle, isNotNullOrUndefined, normalizeUri, urlToPath } from './utils';
3737
import { FallbackWatcher } from './lib/FallbackWatcher';
38+
import { configLoader } from './lib/documents/configLoader';
39+
import { setIsTrusted } from './importPackage';
3840

3941
namespace TagCloseRequest {
4042
export const type: RequestType<TextDocumentPositionParams, string | null, any> =
@@ -102,6 +104,14 @@ export function startServer(options?: LSOptions) {
102104
watcher.onDidChangeWatchedFiles(onDidChangeWatchedFiles);
103105
}
104106

107+
const isTrusted: boolean = evt.initializationOptions?.isTrusted ?? true;
108+
configLoader.setDisabled(!isTrusted);
109+
setIsTrusted(isTrusted);
110+
configManager.updateIsTrusted(isTrusted);
111+
if (!isTrusted) {
112+
Logger.log('Workspace is not trusted, running with reduced capabilities.');
113+
}
114+
105115
// Backwards-compatible way of setting initialization options (first `||` is the old style)
106116
configManager.update(
107117
evt.initializationOptions?.configuration?.svelte?.plugin ||

packages/language-server/test/lib/documents/configLoader.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ConfigLoader } from '../../../src/lib/documents/configLoader';
22
import path from 'path';
33
import { pathToFileURL, URL } from 'url';
44
import assert from 'assert';
5+
import { spy } from 'sinon';
56

67
describe('ConfigLoader', () => {
78
function configFrom(path: string) {
@@ -163,4 +164,17 @@ describe('ConfigLoader', () => {
163164
configFrom(normalizePath('some/svelte.config.js'))
164165
);
165166
});
167+
168+
it('should not load config when disabled', async () => {
169+
const moduleLoader = spy();
170+
const configLoader = new ConfigLoader(
171+
() => [],
172+
{ existsSync: () => true },
173+
path,
174+
moduleLoader
175+
);
176+
configLoader.setDisabled(true);
177+
await configLoader.awaitConfig(normalizePath('some/file.svelte'));
178+
assert.deepStrictEqual(moduleLoader.notCalled, true);
179+
});
166180
});

packages/language-server/test/plugins/svelte/SveltePlugin.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import * as importPackage from '../../../src/importPackage';
1212
import sinon from 'sinon';
1313

1414
describe('Svelte Plugin', () => {
15-
function setup(content: string, prettierConfig?: any) {
15+
function setup(content: string, prettierConfig?: any, trusted = true) {
1616
const document = new Document('file:///hello.svelte', content);
1717
const docManager = new DocumentManager(() => document);
1818
const pluginManager = new LSConfigManager();
19+
pluginManager.updateIsTrusted(trusted);
1920
pluginManager.updatePrettierConfig(prettierConfig);
2021
const plugin = new SveltePlugin(pluginManager);
2122
docManager.openDocument(<any>'some doc');
@@ -52,6 +53,14 @@ describe('Svelte Plugin', () => {
5253
assert.deepStrictEqual(diagnostics, [diagnostic]);
5354
});
5455

56+
it('provides no diagnostic errors when untrusted', async () => {
57+
const { plugin, document } = setup('<div bind:whatever></div>', {}, false);
58+
59+
const diagnostics = await plugin.getDiagnostics(document);
60+
61+
assert.deepStrictEqual(diagnostics, []);
62+
});
63+
5564
describe('#formatDocument', () => {
5665
function stubPrettier(config: any) {
5766
const formatStub = sinon.stub().returns('formatted');

packages/svelte-vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
"onLanguage:svelte",
4444
"onCommand:svelte.restartLanguageServer"
4545
],
46+
"capabilities": {
47+
"untrustedWorkspaces": {
48+
"supported": "limited",
49+
"description": "The extension requires workspace trust because it executes code specified by the workspace. Loading the user's node_modules and loading svelte config files is disabled when untrusted"
50+
}
51+
},
4652
"contributes": {
4753
"typescriptServerPlugins-disabled": [
4854
{

packages/svelte-vscode/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export function activate(context: ExtensionContext) {
100100
typescript: workspace.getConfiguration('typescript'),
101101
javascript: workspace.getConfiguration('javascript')
102102
},
103-
dontFilterIncompleteCompletions: true // VSCode filters client side and is smarter at it than us
103+
dontFilterIncompleteCompletions: true, // VSCode filters client side and is smarter at it than us
104+
isTrusted: (workspace as any).isTrusted
104105
}
105106
};
106107

0 commit comments

Comments
 (0)