-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Bug: Corrupted permission.yaml silently resets all permissions — denied tools become allowed #7432
Description
Bug Description
When permission.yaml contains malformed YAML (from a partial write, manual editing error, or disk corruption), PermissionManager::new silently replaces the entire permission map with an empty HashMap. No warning is logged. Tools that were explicitly set to never_allow become implicitly allowed.
Affected Code
File: crates/goose/src/config/permission.rs, line 48
pub fn new(config_dir: PathBuf) -> Self {
let permission_path = config_dir.join(PERMISSION_FILE);
let permission_map = if permission_path.exists() {
let file_contents =
fs::read_to_string(&permission_path).expect("Failed to read permission.yaml");
serde_yaml::from_str(&file_contents).unwrap_or_else(|_| HashMap::new())
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Parse error silently discarded — returns empty map, no log
} else {
fs::create_dir_all(&config_dir).expect("Failed to create config directory");
HashMap::new()
};
// ...
}Security Impact
- User configures
permission.yamlwithnever_allowentries for sensitive tools (e.g., file system access, shell execution) - The YAML file gets corrupted (partial write during crash, manual edit with syntax error, disk error)
- On next process start,
serde_yaml::from_strreturnsErr(...) unwrap_or_else(|_| HashMap::new())silently swallows the errorpermission_mapis now{}— allnever_allowentries are gone- Previously blocked tools are now permitted with zero user notification
This is a fail-open behavior where security configuration corruption leads to elevated permissions.
Steps to Reproduce
- Configure
permission.yamlwith restricted permissions:shell_execute: permission: never_allow file_write: permission: never_allow
- Corrupt the file (e.g., truncate it, add invalid YAML characters)
- Restart goose
- Observe: no error/warning in logs, all tools are now permitted
Expected Behavior
At minimum, a tracing::warn! or tracing::error! should be emitted when the parse fails, alerting the user that their permission configuration is being reset. Ideally, the process should refuse to start with corrupted security configuration (fail-closed).
Proposed Solution
Option A (recommended — fail-closed):
let permission_map: HashMap<String, PermissionConfig> = match serde_yaml::from_str(&file_contents) {
Ok(map) => map,
Err(e) => {
tracing::error!(
"Failed to parse permission.yaml: {}. Refusing to start with corrupted permission config.",
e
);
return Err(anyhow::anyhow!("Corrupted permission.yaml"));
}
};Option B (fail-open with loud warning):
serde_yaml::from_str(&file_contents).unwrap_or_else(|e| {
tracing::warn!(
"Failed to parse permission.yaml: {}. Starting with empty permissions — all tools permitted.",
e
);
HashMap::new()
})| Aspect | Option A | Option B |
|---|---|---|
| Security | Fail-closed — no silent degradation | Fail-open — degraded but warned |
| UX | Blocks startup (may frustrate users) | Allows startup with visible warning |
Happy to submit a PR for this fix.