Files
tkmind_go/crates/goose/tests/adversary_inspector_tests.rs
T

173 lines
4.6 KiB
Rust

use goose::config::GooseMode;
use goose::conversation::message::{Message, MessageContent, ToolRequest};
use goose::security::adversary_inspector::AdversaryInspector;
use goose::tool_inspection::ToolInspector;
use rmcp::model::CallToolRequestParams;
use rmcp::object;
use std::sync::Arc;
use tokio::sync::Mutex;
fn make_request(
id: &str,
tool: &str,
args: serde_json::Map<String, serde_json::Value>,
) -> ToolRequest {
ToolRequest {
id: id.into(),
tool_call: Ok(CallToolRequestParams::new(tool.to_string()).with_arguments(args)),
metadata: None,
tool_meta: None,
}
}
fn write_adversary_md(dir: &std::path::Path, content: &str) {
std::fs::create_dir_all(dir).unwrap();
std::fs::write(dir.join("adversary.md"), content).unwrap();
}
#[tokio::test]
async fn test_adversary_disabled_without_config_file() {
let tmp = tempfile::tempdir().unwrap();
let provider = Arc::new(Mutex::new(None));
let inspector = AdversaryInspector::with_config_dir(provider, tmp.path().to_path_buf());
assert_eq!(inspector.name(), "adversary");
assert!(!inspector.is_enabled());
let results = inspector
.inspect(
"test-session",
&[make_request(
"r1",
"shell",
object!({"command": "rm -rf /"}),
)],
&[],
GooseMode::SmartApprove,
)
.await
.unwrap();
assert!(results.is_empty());
}
#[tokio::test]
async fn test_adversary_enabled_default_tools() {
let tmp = tempfile::tempdir().unwrap();
write_adversary_md(tmp.path(), "BLOCK everything for testing");
let provider = Arc::new(Mutex::new(None));
let inspector = AdversaryInspector::with_config_dir(provider, tmp.path().to_path_buf());
assert!(inspector.is_enabled());
let messages = vec![Message::new(
rmcp::model::Role::User,
chrono::Utc::now().timestamp(),
vec![MessageContent::text("build the project")],
)];
// shell is reviewed by default — no provider means fail-open (Allow)
let results = inspector
.inspect(
"test-session",
&[make_request(
"r1",
"shell",
object!({"command": "cargo build"}),
)],
&messages,
GooseMode::SmartApprove,
)
.await
.unwrap();
assert_eq!(results.len(), 1);
assert!(matches!(
results[0].action,
goose::tool_inspection::InspectionAction::Allow
));
// write is NOT reviewed by default — skipped entirely
let results = inspector
.inspect(
"test-session",
&[make_request(
"r1",
"write",
object!({"path": "foo.txt", "content": "hi"}),
)],
&messages,
GooseMode::SmartApprove,
)
.await
.unwrap();
assert!(results.is_empty());
}
#[tokio::test]
async fn test_adversary_custom_tool_filter() {
let tmp = tempfile::tempdir().unwrap();
write_adversary_md(
tmp.path(),
"tools: shell, computercontroller__automation_script\n---\nBLOCK bad stuff",
);
let provider = Arc::new(Mutex::new(None));
let inspector = AdversaryInspector::with_config_dir(provider, tmp.path().to_path_buf());
assert!(inspector.is_enabled());
let messages = vec![Message::new(
rmcp::model::Role::User,
chrono::Utc::now().timestamp(),
vec![MessageContent::text("do something")],
)];
// shell — reviewed
let results = inspector
.inspect(
"test",
&[make_request("r1", "shell", object!({"command": "ls"}))],
&messages,
GooseMode::Auto,
)
.await
.unwrap();
assert_eq!(results.len(), 1);
// automation_script — reviewed
let results = inspector
.inspect(
"test",
&[make_request(
"r2",
"computercontroller__automation_script",
object!({"script": "echo hi", "language": "shell"}),
)],
&messages,
GooseMode::Auto,
)
.await
.unwrap();
assert_eq!(results.len(), 1);
// write — NOT reviewed
let results = inspector
.inspect(
"test",
&[make_request(
"r3",
"write",
object!({"path": "x.txt", "content": "y"}),
)],
&messages,
GooseMode::Auto,
)
.await
.unwrap();
assert!(results.is_empty());
}