Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { ipcBrowserViewChannelName } from '../../platform/browserView/common/bro
import { ipcBrowserViewGroupChannelName } from '../../platform/browserView/common/browserViewGroup.js';
import { BrowserViewMainService, IBrowserViewMainService } from '../../platform/browserView/electron-main/browserViewMainService.js';
import { BrowserViewGroupMainService, IBrowserViewGroupMainService } from '../../platform/browserView/electron-main/browserViewGroupMainService.js';
import { NativeParsedArgs } from '../../platform/environment/common/argv.js';
import { consumeInitialWindowArgs, NativeParsedArgs } from '../../platform/environment/common/argv.js';
import { IEnvironmentMainService } from '../../platform/environment/electron-main/environmentMainService.js';
import { isLaunchedFromCli } from '../../platform/environment/node/argvHelper.js';
import { getResolvedShellEnv } from '../../platform/shell/node/shellEnv.js';
Expand Down Expand Up @@ -1535,7 +1535,7 @@ export class CodeApplication extends Disposable {
this.auxiliaryWindowsMainService = accessor.get(IAuxiliaryWindowsMainService);

const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP;
const args = this.environmentMainService.args;
const args = consumeInitialWindowArgs(this.environmentMainService.args);

// Handle agents window first based on context
if (args['agents']) {
Expand Down
10 changes: 5 additions & 5 deletions src/vs/code/node/cliArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/

/**
* Rewrites `--folder-uri <uri>` / `--file-uri <uri>` pairs into a single
* `--flag=value` token so the URI is not a standalone argv entry. Used on
* Windows to avoid Chromium filtering URL-like tokens before main.js runs.
* See https://github.com/microsoft/vscode/issues/209072.
* Rewrites `--folder-uri <uri>` / `--file-uri <uri>` / `--trust-folder <uri>`
* pairs into a single `--flag=value` token so the URI is not a standalone argv
* entry. Used on Windows to avoid Chromium filtering URL-like tokens before
* main.js runs. See https://github.com/microsoft/vscode/issues/209072.
*/
export function combineUriFlags(args: string[]): string[] {
const result: string[] = [];
Expand All @@ -17,7 +17,7 @@ export function combineUriFlags(args: string[]): string[] {
result.push(...args.slice(i));
break;
}
if ((arg === '--folder-uri' || arg === '--file-uri') && i + 1 < args.length && !args[i + 1].startsWith('-')) {
if ((arg === '--folder-uri' || arg === '--file-uri' || arg === '--trust-folder') && i + 1 < args.length && !args[i + 1].startsWith('-')) {
result.push(`${arg}=${args[i + 1]}`);
i++; // skip the value, it's now part of the flag
} else {
Expand Down
15 changes: 15 additions & 0 deletions src/vs/code/test/node/cliArgs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,19 @@ suite('combineUriFlags', () => {
]
);
});

test('rewrites --trust-folder followed by a path or URI', () => {
assert.deepStrictEqual(
combineUriFlags([
'--trust-folder', 'vscode-remote://ssh-remote+host/workspace',
'--trust-folder', '/local/path',
'--wait',
]),
[
'--trust-folder=vscode-remote://ssh-remote+host/workspace',
'--trust-folder=/local/path',
'--wait',
]
);
});
});
14 changes: 14 additions & 0 deletions src/vs/platform/environment/common/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export interface NativeParsedArgs {
'use-inmemory-secretstorage'?: boolean;
'password-store'?: string;
'disable-workspace-trust'?: boolean;
'trust-folder'?: string[];
'disable-crash-reporter'?: boolean;
'crash-reporter-directory'?: string;
'crash-reporter-id'?: string;
Expand Down Expand Up @@ -181,3 +182,16 @@ export interface NativeParsedArgs {
'trace-startup-duration'?: string;
'xdg-portal-required-version'?: string;
}

/** Copies arguments for the initial window and removes one-shot values from the shared process arguments. */
export function consumeInitialWindowArgs(args: NativeParsedArgs): NativeParsedArgs {
const initialWindowArgs = { ...args };
removeOneShotWindowArgs(args);

return initialWindowArgs;
}

/** Removes arguments that must not be inherited by another load of a window. */
export function removeOneShotWindowArgs(args: NativeParsedArgs): void {
delete args['trust-folder'];
}
3 changes: 3 additions & 0 deletions src/vs/platform/environment/common/environmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
@memoize
get disableWorkspaceTrust(): boolean { return !!this.args['disable-workspace-trust']; }

@memoize
get trustedFolders(): string[] { return this.args['trust-folder'] || []; }

@memoize
get useInMemorySecretStorage(): boolean { return !!this.args['use-inmemory-secretstorage']; }

Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/environment/node/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'use-inmemory-secretstorage': { type: 'boolean', deprecates: ['disable-keytar'] },
'password-store': { type: 'string' },
'disable-workspace-trust': { type: 'boolean' },
'trust-folder': { type: 'string[]', cat: 'o', args: 'folder', description: localize('trustFolder', "Trust the given folder and its subfolders for Workspace Trust. Accepts a folder path or URI, can be repeated, and is persisted so the folder stays trusted when reopened.") },
Comment thread
sean-mcmanus marked this conversation as resolved.
'disable-crash-reporter': { type: 'boolean' },
'crash-reporter-directory': { type: 'string' },
'crash-reporter-id': { type: 'string' },
Expand Down
18 changes: 18 additions & 0 deletions src/vs/platform/environment/test/node/argv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { consumeInitialWindowArgs, NativeParsedArgs, removeOneShotWindowArgs } from '../../common/argv.js';
import { formatOptions, Option, OptionDescriptions, Subcommand, parseArgs, ErrorReporter } from '../../node/argv.js';
import { addArg } from '../../node/argvHelper.js';

Expand All @@ -19,6 +20,23 @@ function c(description: string, options: OptionDescriptions<any>): Subcommand<an
};
}

suite('consumeInitialWindowArgs', () => {
test('removes --trust-folder from shared process and reload arguments', () => {
const args: NativeParsedArgs = { _: ['/workspace'], wait: true, 'trust-folder': ['/trusted'] };
const initialWindowArgs = consumeInitialWindowArgs(args);
const reloadArgs = { ...initialWindowArgs };
removeOneShotWindowArgs(reloadArgs);

assert.deepStrictEqual({ initialWindowArgs, remainingProcessArgs: args, reloadArgs }, {
initialWindowArgs: { _: ['/workspace'], wait: true, 'trust-folder': ['/trusted'] },
remainingProcessArgs: { _: ['/workspace'], wait: true },
reloadArgs: { _: ['/workspace'], wait: true }
});
});

ensureNoDisposablesAreLeakedInTestSuite();
});

suite('formatOptions', () => {

test('Text should display small columns correctly', () => {
Expand Down
3 changes: 2 additions & 1 deletion src/vs/platform/windows/electron-main/windowImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ISerializableCommandAction } from '../../action/common/action.js';
import { IBackupMainService } from '../../backup/electron-main/backup.js';
import { IConfigurationChangeEvent, IConfigurationService } from '../../configuration/common/configuration.js';
import { IDialogMainService } from '../../dialogs/electron-main/dialogMainService.js';
import { NativeParsedArgs } from '../../environment/common/argv.js';
import { NativeParsedArgs, removeOneShotWindowArgs } from '../../environment/common/argv.js';
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
import { isLaunchedFromCli } from '../../environment/node/argvHelper.js';
import { IFileService } from '../../files/common/files.js';
Expand Down Expand Up @@ -1380,6 +1380,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
delete configuration.filesToDiff;
delete configuration.filesToMerge;
delete configuration.filesToWait;
removeOneShotWindowArgs(configuration);

// Some configuration things get inherited if the window is being reloaded and we are
// in extension development mode. These options are all development related.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi
@memoize
get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; }

@memoize
get trustedFolders(): string[] { return []; }

@memoize
get isSessionsWindow(): boolean { return this.payload?.get('isSessionsWindow') === 'true'; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService {
readonly skipReleaseNotes: boolean;
readonly skipWelcome: boolean;
readonly disableWorkspaceTrust: boolean;
readonly trustedFolders: string[];
readonly isSessionsWindow: boolean;
readonly webviewExternalEndpoint: string;

Expand Down
30 changes: 28 additions & 2 deletions src/vs/workbench/services/workspaces/common/workspaceTrust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
this._workspaceResolvedPromiseResolve();

if (!this.environmentService.remoteAuthority) {
this._workspaceTrustInitializedPromiseResolve();
// Apply `--trust-folder` before signalling workspace trust initialization.
this.addTrustedFoldersFromCli().finally(() => this._workspaceTrustInitializedPromiseResolve());
}
});

Expand All @@ -172,7 +173,8 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
await this.updateWorkspaceTrust();
})
.finally(() => {
this._workspaceTrustInitializedPromiseResolve();
// Apply remote `--trust-folder` values after resolver canonicalization is available.
this.addTrustedFoldersFromCli().finally(() => this._workspaceTrustInitializedPromiseResolve());
});
}

Expand Down Expand Up @@ -285,6 +287,30 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
await this.updateWorkspaceTrust();
}

private async addTrustedFoldersFromCli(): Promise<void> {
const folders = this.environmentService.trustedFolders;
Comment thread
sean-mcmanus marked this conversation as resolved.
if (!folders?.length) {
return;
}

for (const folder of folders) {
let uri: URI;
try {
uri = folder.includes('://') ? URI.parse(folder) : URI.file(folder);
uri = this.uriIdentityService.extUri.removeTrailingPathSeparator(uri);
} catch {
continue; // ignore a malformed --trust-folder value
}

try {
// Isolate resolution failures so valid `--trust-folder` entries are still applied.
await this.setUrisTrust([uri], true);
Comment thread
sean-mcmanus marked this conversation as resolved.
} catch {
// Never block workspace trust initialization on a bad --trust-folder value
}
}
}

private getWorkspaceUris(): URI[] {
const workspaceUris = this._canonicalWorkspace.folders.map(f => f.uri);
const workspaceConfiguration = this._canonicalWorkspace.configuration;
Expand Down
Loading
Loading