Add implementation for setting to disable Github extensions (#12838)

This commit is contained in:
kevinjwang1
2025-11-11 18:37:01 +00:00
committed by GitHub
parent cc2c48d59e
commit b248ec6dfb
5 changed files with 89 additions and 0 deletions

View File

@@ -490,6 +490,11 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.folderTrust.enabled`** (boolean):
- **Description:** Setting to track whether Folder trust is enabled.
- **Default:** `false`

View File

@@ -128,6 +128,15 @@ export class ExtensionManager extends ExtensionLoader {
installMetadata: ExtensionInstallMetadata,
previousExtensionConfig?: ExtensionConfig,
): Promise<GeminiCLIExtension> {
if (
(installMetadata.type === 'git' ||
installMetadata.type === 'github-release') &&
this.settings.security?.blockGitExtensions
) {
throw new Error(
'Installing extensions from remote sources is disallowed by your current settings.',
);
}
const isUpdate = !!previousExtensionConfig;
let newExtensionConfig: ExtensionConfig | null = null;
let localSourcePath: string | undefined;
@@ -449,6 +458,13 @@ export class ExtensionManager extends ExtensionLoader {
const installMetadata = loadInstallMetadata(extensionDir);
let effectiveExtensionPath = extensionDir;
if (
(installMetadata?.type === 'git' ||
installMetadata?.type === 'github-release') &&
this.settings.security?.blockGitExtensions
) {
return null;
}
if (installMetadata?.type === 'link') {
effectiveExtensionPath = installMetadata.source;

View File

@@ -594,6 +594,34 @@ describe('extension tests', () => {
consoleSpy.mockRestore();
});
it('should not load github extensions if blockGitExtensions is set', async () => {
createExtension({
extensionsDir: userExtensionsDir,
name: 'my-ext',
version: '1.0.0',
installMetadata: {
type: 'git',
source: 'http://somehost.com/foo/bar',
},
});
const blockGitExtensionsSetting = {
security: {
blockGitExtensions: true,
},
};
extensionManager = new ExtensionManager({
workspaceDir: tempWorkspaceDir,
requestConsent: mockRequestConsent,
requestSetting: mockPromptForSettings,
settings: blockGitExtensionsSetting,
});
const extensions = await extensionManager.loadExtensions();
const extension = extensions.find((e) => e.name === 'my-ext');
expect(extension).toBeUndefined();
});
describe('id generation', () => {
it('should generate id from source for non-github git urls', async () => {
createExtension({
@@ -876,6 +904,30 @@ describe('extension tests', () => {
fs.rmSync(targetExtDir, { recursive: true, force: true });
});
it('should not install a github extension if blockGitExtensions is set', async () => {
const gitUrl = 'https://somehost.com/somerepo.git';
const blockGitExtensionsSetting = {
security: {
blockGitExtensions: true,
},
};
extensionManager = new ExtensionManager({
workspaceDir: tempWorkspaceDir,
requestConsent: mockRequestConsent,
requestSetting: mockPromptForSettings,
settings: blockGitExtensionsSetting,
});
await extensionManager.loadExtensions();
await expect(
extensionManager.installOrUpdateExtension({
source: gitUrl,
type: 'git',
}),
).rejects.toThrow(
'Installing extensions from remote sources is disallowed by your current settings.',
);
});
it('should prompt for trust if workspace is not trusted', async () => {
vi.mocked(isWorkspaceTrusted).mockReturnValue({
isTrusted: false,

View File

@@ -1102,6 +1102,15 @@ const SETTINGS_SCHEMA = {
description: 'Disable YOLO mode, even if enabled by a flag.',
showInDialog: true,
},
blockGitExtensions: {
type: 'boolean',
label: 'Blocks extensions from Git',
category: 'Security',
requiresRestart: true,
default: false,
description: 'Blocks installing and loading extensions from Git.',
showInDialog: true,
},
folderTrust: {
type: 'object',
label: 'Folder Trust',

View File

@@ -997,6 +997,13 @@
"default": false,
"type": "boolean"
},
"blockGitExtensions": {
"title": "Blocks extensions from Git",
"description": "Blocks installing and loading extensions from Git.",
"markdownDescription": "Blocks installing and loading extensions from Git.\n\n- Category: `Security`\n- Requires restart: `yes`\n- Default: `false`",
"default": false,
"type": "boolean"
},
"folderTrust": {
"title": "Folder Trust",
"description": "Settings for folder trust.",