allowlist blocks and shift SSE to warning (#2445)

This commit is contained in:
Michael Neale
2025-05-07 23:44:52 +10:00
committed by GitHub
parent cc2b8f715f
commit c0b79873b5
4 changed files with 90 additions and 22 deletions

View File

@@ -79,6 +79,11 @@ run-ui:
@echo "Running UI..."
cd ui/desktop && npm install && npm run start-gui
run-ui-only:
@echo "Running UI..."
cd ui/desktop && npm install && npm run start-gui
# Run UI with alpha changes
run-ui-alpha:
@just release-binary

View File

@@ -1,3 +1,5 @@
IMPORTANT: currently GOOSE_ALLOWLIST is used in main.ts in ui/desktop, and not in goose-server. The following is for reference in case it is used on the server side for launch time enforcement.
# Goose Extension Allowlist
The allowlist feature provides a security mechanism for controlling which MCP commands can be used by goose.
@@ -24,9 +26,11 @@ If this environment variable is not set, no allowlist restrictions will be appli
In certain development or testing scenarios, you may need to bypass the allowlist restrictions. You can do this by setting the `GOOSE_ALLOWLIST_BYPASS` environment variable to `true`:
```bash
export GOOSE_ALLOWLIST_BYPASS=true
# For the GUI, you can have it show a warning instead of blocking (but it will always show a warning):
export GOOSE_ALLOWLIST_WARNING=true
```
When this environment variable is set to `true` (case insensitive), the allowlist check will be bypassed and all commands will be allowed, even if the `GOOSE_ALLOWLIST` environment variable is set.
## Allowlist File Format

View File

@@ -498,7 +498,15 @@ export default function App() {
}
}, [view]);
// TODO: modify
// Configuration for extension security
const config = window.electron.getConfig();
// If GOOSE_ALLOWLIST_WARNING is true, use warning-only mode (STRICT_ALLOWLIST=false)
// If GOOSE_ALLOWLIST_WARNING is not set or false, use strict blocking mode (STRICT_ALLOWLIST=true)
const STRICT_ALLOWLIST = config.GOOSE_ALLOWLIST_WARNING === true ? false : true;
console.log(
`Extension security mode: ${STRICT_ALLOWLIST ? 'Strict' : 'Warning-only'} (GOOSE_ALLOWLIST_WARNING=${config.GOOSE_ALLOWLIST_WARNING})`
);
useEffect(() => {
console.log('Setting up extension handler');
const handleAddExtension = async (_event: IpcRendererEvent, link: string) => {
@@ -510,10 +518,18 @@ export default function App() {
window.electron.logInfo(`Adding extension from deep link ${link}`);
setPendingLink(link);
// Fetch the allowlist and check if the command is allowed
// Default values for confirmation dialog
let warningMessage = '';
let label = 'OK';
let title = 'Confirm Extension Installation';
let isBlocked = false;
let useDetailedMessage = false;
// For SSE extensions (with remoteUrl), always use detailed message
if (remoteUrl) {
useDetailedMessage = true;
} else {
// For command-based extensions, check against allowlist
try {
const allowedCommands = await window.electron.getAllowedExtensions();
@@ -524,22 +540,59 @@ export default function App() {
);
if (!isCommandAllowed) {
// Not in allowlist - use detailed message and show warning/block
useDetailedMessage = true;
title = '⛔️ Untrusted Extension ⛔️';
if (STRICT_ALLOWLIST) {
// Block installation completely unless override is active
isBlocked = true;
label = 'Extension Blocked';
warningMessage =
'\n\n⛔ BLOCKED: This extension command is not in the allowed list. ' +
'Installation is blocked by your administrator. ' +
'Please contact your administrator if you need this extension.';
} else {
// Allow override (either because STRICT_ALLOWLIST is false or secret key combo was used)
label = 'Override and install';
warningMessage =
'\n\n⚠ WARNING: This extension command is not in the allowed list. Installing extensions from untrusted sources may pose security risks. Please contact and admin if you are unsusure or want to allow this extension.';
'\n\n⚠ WARNING: This extension command is not in the allowed list. ' +
'Installing extensions from untrusted sources may pose security risks. ' +
'Please contact an admin if you are unsure or want to allow this extension.';
}
}
// If in allowlist, use simple message (useDetailedMessage remains false)
}
// If no allowlist, use simple message (useDetailedMessage remains false)
} catch (error) {
console.error('Error checking allowlist:', error);
}
}
const messageDetails = remoteUrl ? `Remote URL: ${remoteUrl}` : `Command: ${command}`;
// Set the appropriate message based on the extension type and allowlist status
if (useDetailedMessage) {
// Detailed message for SSE extensions or non-allowlisted command extensions
const detailedMessage = remoteUrl
? `You are about to install the ${extName} extension which connects to:\n\n${remoteUrl}\n\nThis extension will be able to access your conversations and provide additional functionality.`
: `You are about to install the ${extName} extension which runs the command:\n\n${command}\n\nThis extension will be able to access your conversations and provide additional functionality.`;
setModalMessage(`${detailedMessage}${warningMessage}`);
} else {
// Simple message for allowlisted command extensions or when no allowlist exists
const messageDetails = `Command: ${command}`;
setModalMessage(
`Are you sure you want to install the ${extName} extension?\n\n${messageDetails}${warningMessage}`
`Are you sure you want to install the ${extName} extension?\n\n${messageDetails}`
);
}
setExtensionConfirmLabel(label);
setExtensionConfirmTitle(title);
// If blocked, disable the confirmation button functionality by setting a special flag
if (isBlocked) {
setPendingLink(null); // Clear the pending link so confirmation does nothing
}
setModalVisible(true);
} catch (error) {
console.error('Error handling add-extension event:', error);
@@ -550,7 +603,7 @@ export default function App() {
return () => {
window.electron.off('add-extension', handleAddExtension);
};
}, []);
}, [STRICT_ALLOWLIST]);
// Focus the first found input field
useEffect(() => {
@@ -584,6 +637,10 @@ export default function App() {
} finally {
setPendingLink(null);
}
} else {
// This case happens when pendingLink was cleared due to blocking
console.log('Extension installation blocked by allowlist restrictions');
setModalVisible(false);
}
};

View File

@@ -272,6 +272,8 @@ let appConfig = {
GOOSE_API_HOST: 'http://127.0.0.1',
GOOSE_PORT: 0,
GOOSE_WORKING_DIR: '',
// If GOOSE_ALLOWLIST_WARNING env var is not set, defaults to false (strict blocking mode)
GOOSE_ALLOWLIST_WARNING: process.env.GOOSE_ALLOWLIST_WARNING === 'true',
secretKey: generateSecretKey(),
};