fix: allowlist bypass for developers (#2149)

This commit is contained in:
Michael Neale
2025-04-12 04:01:22 +10:00
committed by GitHub
parent 54f794bc32
commit add681ef05
2 changed files with 97 additions and 0 deletions
+10
View File
@@ -19,6 +19,16 @@ export GOOSE_ALLOWLIST=https://example.com/goose-allowlist.yaml
If this environment variable is not set, no allowlist restrictions will be applied (all commands will be allowed). If this environment variable is not set, no allowlist restrictions will be applied (all commands will be allowed).
## Bypassing the Allowlist
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
```
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 ## Allowlist File Format
The allowlist file should be a YAML file with the following structure: The allowlist file should be a YAML file with the following structure:
@@ -396,6 +396,16 @@ fn get_allowed_extensions() -> &'static Option<AllowedExtensions> {
/// Checks if a command is allowed based on the allowlist /// Checks if a command is allowed based on the allowlist
fn is_command_allowed(cmd: &str, args: &[String]) -> bool { fn is_command_allowed(cmd: &str, args: &[String]) -> bool {
// Check if bypass is enabled
if let Ok(bypass_value) = env::var("GOOSE_ALLOWLIST_BYPASS") {
if bypass_value.to_lowercase() == "true" {
// Bypass the allowlist check
println!("Allowlist check bypassed due to GOOSE_ALLOWLIST_BYPASS=true");
return true;
}
}
// Proceed with normal allowlist check
is_command_allowed_with_allowlist(&make_full_cmd(cmd, args), get_allowed_extensions()) is_command_allowed_with_allowlist(&make_full_cmd(cmd, args), get_allowed_extensions())
} }
@@ -1075,4 +1085,81 @@ mod tests {
// Wait for the server thread to complete // Wait for the server thread to complete
handle.join().unwrap(); handle.join().unwrap();
} }
#[test]
fn test_allowlist_bypass() {
// We need to directly test is_command_allowed_with_allowlist with our test allowlist
// since get_allowed_extensions() might return None in the test environment
// Create a restrictive allowlist
let allowlist = create_test_allowlist(&["uvx mcp_slack"]);
// Command not in allowlist
let cmd = "uvx unauthorized_command";
// Without bypass, command should be denied with our test allowlist
assert!(!is_command_allowed_with_allowlist(cmd, &allowlist));
// Set the bypass environment variable
env::set_var("GOOSE_ALLOWLIST_BYPASS", "true");
// With bypass enabled, any command should be allowed regardless of allowlist
assert!(is_command_allowed(
"uvx",
&vec!["unauthorized_command".to_string()]
));
// Test case insensitivity
env::set_var("GOOSE_ALLOWLIST_BYPASS", "TRUE");
assert!(is_command_allowed(
"uvx",
&vec!["unauthorized_command".to_string()]
));
// Clean up
env::remove_var("GOOSE_ALLOWLIST_BYPASS");
// Create a mock function to test with allowlist and bypass
let test_with_allowlist_and_bypass = |bypass_value: &str, expected: bool| {
if bypass_value.is_empty() {
env::remove_var("GOOSE_ALLOWLIST_BYPASS");
} else {
env::set_var("GOOSE_ALLOWLIST_BYPASS", bypass_value);
}
// This is what we're testing - a direct call that simulates what happens in is_command_allowed
let result = if let Ok(bypass) = env::var("GOOSE_ALLOWLIST_BYPASS") {
if bypass.to_lowercase() == "true" {
true
} else {
is_command_allowed_with_allowlist(cmd, &allowlist)
}
} else {
is_command_allowed_with_allowlist(cmd, &allowlist)
};
assert_eq!(
result,
expected,
"With GOOSE_ALLOWLIST_BYPASS={}, expected allowed={}",
if bypass_value.is_empty() {
"not set"
} else {
bypass_value
},
expected
);
};
// Test various bypass values
test_with_allowlist_and_bypass("true", true);
test_with_allowlist_and_bypass("TRUE", true);
test_with_allowlist_and_bypass("True", true);
test_with_allowlist_and_bypass("false", false);
test_with_allowlist_and_bypass("0", false);
test_with_allowlist_and_bypass("", false);
// Final cleanup
env::remove_var("GOOSE_ALLOWLIST_BYPASS");
}
} }