Skip to main content

bn/commands/
trust.rs

1use anyhow::{anyhow, Result};
2use std::path::Path;
3
4use crate::hooks::{create_trust, is_trusted, revoke_trust};
5
6/// Manage hook trust status.
7///
8/// By default, hooks are disabled (not trusted). Users must explicitly run
9/// `bn trust` to enable hook execution. This is a security measure to ensure
10/// users review .beans/hooks/ scripts before allowing execution.
11///
12/// # Arguments
13///
14/// * `beans_dir` - The .beans/ directory path
15/// * `revoke` - If true, disable hooks (remove trust file)
16/// * `check` - If true, display current trust status without changing it
17///
18/// # Returns
19///
20/// * `Ok(())` on success
21/// * `Err` if file operations fail
22pub fn cmd_trust(beans_dir: &Path, revoke: bool, check: bool) -> Result<()> {
23    // hooks functions expect the project root (parent of .beans/)
24    let project_dir = beans_dir
25        .parent()
26        .ok_or_else(|| anyhow!("Cannot determine project root from beans dir"))?;
27
28    // If --check: print current status
29    if check {
30        if is_trusted(project_dir) {
31            println!("Hooks are enabled");
32        } else {
33            println!("Hooks are disabled");
34        }
35        return Ok(());
36    }
37
38    // If --revoke: disable hooks
39    if revoke {
40        revoke_trust(project_dir)?;
41        println!("Hooks disabled");
42        return Ok(());
43    }
44
45    // Otherwise: enable hooks
46    create_trust(project_dir)?;
47    println!("Hooks enabled. Review .beans/hooks before running commands");
48    Ok(())
49}
50
51// ---------------------------------------------------------------------------
52// Tests
53// ---------------------------------------------------------------------------
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use std::fs;
59    use tempfile::TempDir;
60
61    fn create_test_dir() -> TempDir {
62        TempDir::new().unwrap()
63    }
64
65    #[test]
66    fn test_cmd_trust_enables_hooks() {
67        let temp_dir = create_test_dir();
68        let project_dir = temp_dir.path();
69        let beans_dir = project_dir.join(".beans");
70
71        // Ensure .beans directory exists
72        fs::create_dir_all(&beans_dir).unwrap();
73
74        // Trust is not enabled by default
75        assert!(!is_trusted(project_dir));
76
77        // Enable trust (cmd_trust receives .beans/ path, like main.rs)
78        cmd_trust(&beans_dir, false, false).unwrap();
79
80        // Verify trust is now enabled
81        assert!(is_trusted(project_dir));
82    }
83
84    #[test]
85    fn test_cmd_trust_check_reports_disabled() {
86        let temp_dir = create_test_dir();
87        let project_dir = temp_dir.path();
88        let beans_dir = project_dir.join(".beans");
89
90        // Ensure .beans directory exists
91        fs::create_dir_all(&beans_dir).unwrap();
92
93        // Check status when disabled - should not error
94        let result = cmd_trust(&beans_dir, false, true);
95        assert!(result.is_ok());
96    }
97
98    #[test]
99    fn test_cmd_trust_check_reports_enabled() {
100        let temp_dir = create_test_dir();
101        let project_dir = temp_dir.path();
102        let beans_dir = project_dir.join(".beans");
103
104        // Ensure .beans directory exists
105        fs::create_dir_all(&beans_dir).unwrap();
106
107        // Enable trust first
108        cmd_trust(&beans_dir, false, false).unwrap();
109
110        // Check status when enabled - should not error
111        let result = cmd_trust(&beans_dir, false, true);
112        assert!(result.is_ok());
113    }
114
115    #[test]
116    fn test_cmd_trust_revoke_disables_hooks() {
117        let temp_dir = create_test_dir();
118        let project_dir = temp_dir.path();
119        let beans_dir = project_dir.join(".beans");
120
121        // Ensure .beans directory exists
122        fs::create_dir_all(&beans_dir).unwrap();
123
124        // Enable trust first
125        cmd_trust(&beans_dir, false, false).unwrap();
126        assert!(is_trusted(project_dir));
127
128        // Revoke trust
129        cmd_trust(&beans_dir, true, false).unwrap();
130
131        // Verify trust is disabled
132        assert!(!is_trusted(project_dir));
133    }
134
135    #[test]
136    fn test_cmd_trust_revoke_with_check() {
137        let temp_dir = create_test_dir();
138        let project_dir = temp_dir.path();
139        let beans_dir = project_dir.join(".beans");
140
141        // Ensure .beans directory exists
142        fs::create_dir_all(&beans_dir).unwrap();
143
144        // Enable trust first
145        cmd_trust(&beans_dir, false, false).unwrap();
146
147        // Revoke with check - should report disabled
148        let result = cmd_trust(&beans_dir, true, true);
149        assert!(result.is_ok());
150    }
151}