use crate::Claude;
use crate::command::{ClaudeCommand, query::QueryCommand};
use crate::error::{Error, Result};
use crate::exec::CommandOutput;
#[allow(deprecated)]
use crate::types::PermissionMode;
pub const ALLOW_ENV: &str = "CLAUDE_WRAPPER_ALLOW_DANGEROUS";
#[derive(Debug, Clone)]
pub struct DangerousClient {
inner: Claude,
}
impl DangerousClient {
pub fn new(claude: Claude) -> Result<Self> {
if !is_allowed() {
return Err(Error::DangerousNotAllowed { env_var: ALLOW_ENV });
}
Ok(Self { inner: claude })
}
pub fn claude(&self) -> &Claude {
&self.inner
}
pub async fn query_bypass(&self, cmd: QueryCommand) -> Result<CommandOutput> {
#[allow(deprecated)]
let cmd = cmd.permission_mode(PermissionMode::BypassPermissions);
cmd.execute(&self.inner).await
}
}
fn is_allowed() -> bool {
std::env::var(ALLOW_ENV).as_deref() == Ok("1")
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static ENV_LOCK: Mutex<()> = Mutex::new(());
fn with_allow_env<T>(value: Option<&str>, f: impl FnOnce() -> T) -> T {
let _g = ENV_LOCK.lock().unwrap();
let prev = std::env::var(ALLOW_ENV).ok();
unsafe {
match value {
Some(v) => std::env::set_var(ALLOW_ENV, v),
None => std::env::remove_var(ALLOW_ENV),
}
}
let out = f();
unsafe {
match prev {
Some(v) => std::env::set_var(ALLOW_ENV, v),
None => std::env::remove_var(ALLOW_ENV),
}
}
out
}
#[test]
fn new_refuses_without_env() {
with_allow_env(None, || {
let claude = Claude::builder().binary("/usr/bin/true").build().unwrap();
let err = DangerousClient::new(claude).unwrap_err();
assert!(matches!(
err,
Error::DangerousNotAllowed { env_var } if env_var == ALLOW_ENV
));
});
}
#[test]
fn new_refuses_with_wrong_value() {
with_allow_env(Some("true"), || {
let claude = Claude::builder().binary("/usr/bin/true").build().unwrap();
assert!(matches!(
DangerousClient::new(claude).unwrap_err(),
Error::DangerousNotAllowed { .. }
));
});
with_allow_env(Some("yes"), || {
let claude = Claude::builder().binary("/usr/bin/true").build().unwrap();
assert!(matches!(
DangerousClient::new(claude).unwrap_err(),
Error::DangerousNotAllowed { .. }
));
});
}
#[test]
fn new_accepts_with_allow_env_set_to_one() {
with_allow_env(Some("1"), || {
let claude = Claude::builder().binary("/usr/bin/true").build().unwrap();
let d = DangerousClient::new(claude).unwrap();
assert_eq!(d.claude().binary(), std::path::Path::new("/usr/bin/true"));
});
}
#[test]
fn error_message_names_the_env_var() {
let e = Error::DangerousNotAllowed { env_var: ALLOW_ENV };
let s = e.to_string();
assert!(s.contains(ALLOW_ENV));
}
}