mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
Compare commits
1 Commits
dh--stuff
...
dev/zhao/e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8832459f7f |
2
codex-rs/Cargo.lock
generated
2
codex-rs/Cargo.lock
generated
@@ -1254,6 +1254,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"clap",
|
"clap",
|
||||||
"codex-core",
|
"codex-core",
|
||||||
|
"codex-execpolicy",
|
||||||
"libc",
|
"libc",
|
||||||
"path-absolutize",
|
"path-absolutize",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
@@ -1263,6 +1264,7 @@ dependencies = [
|
|||||||
"shlex",
|
"shlex",
|
||||||
"socket2 0.6.0",
|
"socket2 0.6.0",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ mod codex_conversation;
|
|||||||
mod compact_remote;
|
mod compact_remote;
|
||||||
pub use codex_conversation::CodexConversation;
|
pub use codex_conversation::CodexConversation;
|
||||||
mod codex_delegate;
|
mod codex_delegate;
|
||||||
mod command_safety;
|
pub mod command_safety;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod config_loader;
|
pub mod config_loader;
|
||||||
mod context_manager;
|
mod context_manager;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ anyhow = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
codex-core = { workspace = true }
|
codex-core = { workspace = true }
|
||||||
|
codex-execpolicy = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
path-absolutize = { workspace = true }
|
path-absolutize = { workspace = true }
|
||||||
rmcp = { workspace = true, default-features = false, features = [
|
rmcp = { workspace = true, default-features = false, features = [
|
||||||
@@ -51,6 +52,7 @@ tokio = { workspace = true, features = [
|
|||||||
"signal",
|
"signal",
|
||||||
] }
|
] }
|
||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
|
||||||
|
|
||||||
|
|||||||
@@ -55,19 +55,21 @@
|
|||||||
//! | |
|
//! | |
|
||||||
//! o<-----x
|
//! o<-----x
|
||||||
//!
|
//!
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use tracing_subscriber::{self};
|
use tracing_subscriber::{self};
|
||||||
|
|
||||||
use crate::posix::mcp_escalation_policy::ExecPolicyOutcome;
|
use crate::posix::exec_policy::ExecPolicyEvaluator;
|
||||||
|
use crate::posix::exec_policy::load_policy_from_codex_home;
|
||||||
|
use crate::posix::mcp_escalation_policy::SharedExecPolicy;
|
||||||
|
|
||||||
mod escalate_client;
|
mod escalate_client;
|
||||||
mod escalate_protocol;
|
mod escalate_protocol;
|
||||||
mod escalate_server;
|
mod escalate_server;
|
||||||
mod escalation_policy;
|
mod escalation_policy;
|
||||||
|
mod exec_policy;
|
||||||
mod mcp;
|
mod mcp;
|
||||||
mod mcp_escalation_policy;
|
mod mcp_escalation_policy;
|
||||||
mod socket;
|
mod socket;
|
||||||
@@ -114,7 +116,10 @@ pub async fn main_mcp_server() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
tracing::info!("Starting MCP server");
|
tracing::info!("Starting MCP server");
|
||||||
let service = mcp::serve(bash_path, execve_wrapper, dummy_exec_policy)
|
let policy = load_policy_from_codex_home().await?;
|
||||||
|
let policy: SharedExecPolicy = std::sync::Arc::new(ExecPolicyEvaluator::new(policy));
|
||||||
|
|
||||||
|
let service = mcp::serve(bash_path, execve_wrapper, policy)
|
||||||
.await
|
.await
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
tracing::error!("serving error: {:?}", e);
|
tracing::error!("serving error: {:?}", e);
|
||||||
@@ -144,27 +149,3 @@ pub async fn main_execve_wrapper() -> anyhow::Result<()> {
|
|||||||
let exit_code = escalate_client::run(file, argv).await?;
|
let exit_code = escalate_client::run(file, argv).await?;
|
||||||
std::process::exit(exit_code);
|
std::process::exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace with execpolicy
|
|
||||||
|
|
||||||
fn dummy_exec_policy(file: &Path, argv: &[String], _workdir: &Path) -> ExecPolicyOutcome {
|
|
||||||
if file.ends_with("rm") {
|
|
||||||
ExecPolicyOutcome::Forbidden
|
|
||||||
} else if file.ends_with("git") {
|
|
||||||
ExecPolicyOutcome::Prompt {
|
|
||||||
run_with_escalated_permissions: false,
|
|
||||||
}
|
|
||||||
} else if file == Path::new("/opt/homebrew/bin/gh")
|
|
||||||
&& let [_, arg1, arg2, ..] = argv
|
|
||||||
&& arg1 == "issue"
|
|
||||||
&& arg2 == "list"
|
|
||||||
{
|
|
||||||
ExecPolicyOutcome::Allow {
|
|
||||||
run_with_escalated_permissions: true,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ExecPolicyOutcome::Allow {
|
|
||||||
run_with_escalated_permissions: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
266
codex-rs/exec-server/src/posix/exec_policy.rs
Normal file
266
codex-rs/exec-server/src/posix/exec_policy.rs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
use std::io::ErrorKind;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use codex_core::command_safety::is_dangerous_command::command_might_be_dangerous;
|
||||||
|
use codex_execpolicy::Decision;
|
||||||
|
use codex_execpolicy::Evaluation;
|
||||||
|
use codex_execpolicy::Policy;
|
||||||
|
use codex_execpolicy::PolicyParser;
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
use crate::posix::mcp_escalation_policy::ExecPolicy;
|
||||||
|
use crate::posix::mcp_escalation_policy::ExecPolicyOutcome;
|
||||||
|
|
||||||
|
const POLICY_DIR_NAME: &str = "policy";
|
||||||
|
const POLICY_EXTENSION: &str = "codexpolicy";
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum ExecPolicyError {
|
||||||
|
#[error("failed to resolve CODEX_HOME: {source}")]
|
||||||
|
CodexHome { source: std::io::Error },
|
||||||
|
|
||||||
|
#[error("failed to read execpolicy files from {dir}: {source}")]
|
||||||
|
ReadDir {
|
||||||
|
dir: PathBuf,
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("failed to read execpolicy file {path}: {source}")]
|
||||||
|
ReadFile {
|
||||||
|
path: PathBuf,
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("failed to parse execpolicy file {path}: {source}")]
|
||||||
|
ParsePolicy {
|
||||||
|
path: String,
|
||||||
|
source: codex_execpolicy::Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn load_policy_from_codex_home() -> Result<Arc<Policy>, ExecPolicyError> {
|
||||||
|
let codex_home = codex_core::config::find_codex_home()
|
||||||
|
.map_err(|source| ExecPolicyError::CodexHome { source })?;
|
||||||
|
|
||||||
|
let policy_dir = codex_home.join(POLICY_DIR_NAME);
|
||||||
|
let policy_paths = collect_policy_files(&policy_dir).await?;
|
||||||
|
|
||||||
|
let mut parser = PolicyParser::new();
|
||||||
|
for policy_path in &policy_paths {
|
||||||
|
let contents =
|
||||||
|
fs::read_to_string(policy_path)
|
||||||
|
.await
|
||||||
|
.map_err(|source| ExecPolicyError::ReadFile {
|
||||||
|
path: policy_path.clone(),
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
let identifier = policy_path.to_string_lossy().to_string();
|
||||||
|
parser
|
||||||
|
.parse(&identifier, &contents)
|
||||||
|
.map_err(|source| ExecPolicyError::ParsePolicy {
|
||||||
|
path: identifier,
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let policy = Arc::new(parser.build());
|
||||||
|
tracing::debug!(
|
||||||
|
"loaded execpolicy from {} files in {}",
|
||||||
|
policy_paths.len(),
|
||||||
|
policy_dir.display()
|
||||||
|
);
|
||||||
|
Ok(policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ExecPolicyEvaluator {
|
||||||
|
policy: Arc<Policy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecPolicyEvaluator {
|
||||||
|
pub(crate) fn new(policy: Arc<Policy>) -> Self {
|
||||||
|
Self { policy }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_for(file: &Path, argv: &[String]) -> Vec<String> {
|
||||||
|
let cmd0 = argv
|
||||||
|
.first()
|
||||||
|
.and_then(|s| Path::new(s).file_name())
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.or_else(|| {
|
||||||
|
file.file_name()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| file.display().to_string());
|
||||||
|
|
||||||
|
let mut command = Vec::with_capacity(argv.len().max(1));
|
||||||
|
command.push(cmd0);
|
||||||
|
if argv.len() > 1 {
|
||||||
|
command.extend(argv.iter().skip(1).cloned());
|
||||||
|
}
|
||||||
|
command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecPolicy for ExecPolicyEvaluator {
|
||||||
|
fn evaluate(&self, file: &Path, argv: &[String], _workdir: &Path) -> ExecPolicyOutcome {
|
||||||
|
let command = Self::command_for(file, argv);
|
||||||
|
|
||||||
|
match self.policy.check_multiple(std::iter::once(&command)) {
|
||||||
|
Evaluation::Match { decision, .. } => match decision {
|
||||||
|
Decision::Forbidden => ExecPolicyOutcome::Forbidden,
|
||||||
|
Decision::Prompt => ExecPolicyOutcome::Prompt {
|
||||||
|
run_with_escalated_permissions: true,
|
||||||
|
},
|
||||||
|
Decision::Allow => ExecPolicyOutcome::Allow {
|
||||||
|
run_with_escalated_permissions: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Evaluation::NoMatch { .. } => {
|
||||||
|
if command_might_be_dangerous(&command) {
|
||||||
|
ExecPolicyOutcome::Prompt {
|
||||||
|
run_with_escalated_permissions: false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ExecPolicyOutcome::Allow {
|
||||||
|
run_with_escalated_permissions: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn collect_policy_files(dir: &Path) -> Result<Vec<PathBuf>, ExecPolicyError> {
|
||||||
|
let mut read_dir = match fs::read_dir(dir).await {
|
||||||
|
Ok(read_dir) => read_dir,
|
||||||
|
Err(err) if err.kind() == ErrorKind::NotFound => return Ok(Vec::new()),
|
||||||
|
Err(source) => {
|
||||||
|
return Err(ExecPolicyError::ReadDir {
|
||||||
|
dir: dir.to_path_buf(),
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut policy_paths = Vec::new();
|
||||||
|
while let Some(entry) =
|
||||||
|
read_dir
|
||||||
|
.next_entry()
|
||||||
|
.await
|
||||||
|
.map_err(|source| ExecPolicyError::ReadDir {
|
||||||
|
dir: dir.to_path_buf(),
|
||||||
|
source,
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
let path = entry.path();
|
||||||
|
let file_type = entry
|
||||||
|
.file_type()
|
||||||
|
.await
|
||||||
|
.map_err(|source| ExecPolicyError::ReadDir {
|
||||||
|
dir: dir.to_path_buf(),
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if path
|
||||||
|
.extension()
|
||||||
|
.and_then(|ext| ext.to_str())
|
||||||
|
.is_some_and(|ext| ext == POLICY_EXTENSION)
|
||||||
|
&& file_type.is_file()
|
||||||
|
{
|
||||||
|
policy_paths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
policy_paths.sort();
|
||||||
|
Ok(policy_paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
struct EnvVarGuard {
|
||||||
|
key: &'static str,
|
||||||
|
original: Option<std::ffi::OsString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnvVarGuard {
|
||||||
|
fn set(key: &'static str, value: &std::ffi::OsStr) -> Self {
|
||||||
|
let original = std::env::var_os(key);
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var(key, value);
|
||||||
|
}
|
||||||
|
Self { key, original }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EnvVarGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
match &self.original {
|
||||||
|
Some(value) => std::env::set_var(self.key, value),
|
||||||
|
None => std::env::remove_var(self.key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allow_policy_bypasses_sandbox() {
|
||||||
|
let mut parser = PolicyParser::new();
|
||||||
|
parser
|
||||||
|
.parse(
|
||||||
|
"test.codexpolicy",
|
||||||
|
r#"prefix_rule(pattern=["echo"], decision="allow")"#,
|
||||||
|
)
|
||||||
|
.expect("parse policy");
|
||||||
|
let evaluator = ExecPolicyEvaluator::new(Arc::new(parser.build()));
|
||||||
|
|
||||||
|
let outcome = evaluator.evaluate(
|
||||||
|
Path::new("/bin/echo"),
|
||||||
|
&["echo".to_string()],
|
||||||
|
Path::new("/"),
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
outcome,
|
||||||
|
ExecPolicyOutcome::Allow {
|
||||||
|
run_with_escalated_permissions: true
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_match_dangerous_command_prompts() {
|
||||||
|
let evaluator = ExecPolicyEvaluator::new(Arc::new(Policy::empty()));
|
||||||
|
let outcome = evaluator.evaluate(
|
||||||
|
Path::new("/bin/rm"),
|
||||||
|
&["rm".to_string(), "-rf".to_string(), "/".to_string()],
|
||||||
|
Path::new("/"),
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
outcome,
|
||||||
|
ExecPolicyOutcome::Prompt {
|
||||||
|
run_with_escalated_permissions: false
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn missing_policy_dir_loads_empty() {
|
||||||
|
let dir = tempdir().expect("tempdir");
|
||||||
|
let _guard = EnvVarGuard::set("CODEX_HOME", dir.path().as_os_str());
|
||||||
|
let policy = load_policy_from_codex_home().await.expect("load policy");
|
||||||
|
assert!(matches!(
|
||||||
|
policy.check_multiple(std::iter::once(&vec!["rm".to_string()])),
|
||||||
|
Evaluation::NoMatch { .. }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,8 +27,8 @@ use tracing::debug;
|
|||||||
|
|
||||||
use crate::posix::escalate_server::EscalateServer;
|
use crate::posix::escalate_server::EscalateServer;
|
||||||
use crate::posix::escalate_server::{self};
|
use crate::posix::escalate_server::{self};
|
||||||
use crate::posix::mcp_escalation_policy::ExecPolicy;
|
|
||||||
use crate::posix::mcp_escalation_policy::McpEscalationPolicy;
|
use crate::posix::mcp_escalation_policy::McpEscalationPolicy;
|
||||||
|
use crate::posix::mcp_escalation_policy::SharedExecPolicy;
|
||||||
use crate::posix::stopwatch::Stopwatch;
|
use crate::posix::stopwatch::Stopwatch;
|
||||||
|
|
||||||
/// Path to our patched bash.
|
/// Path to our patched bash.
|
||||||
@@ -78,13 +78,13 @@ pub struct ExecTool {
|
|||||||
tool_router: ToolRouter<ExecTool>,
|
tool_router: ToolRouter<ExecTool>,
|
||||||
bash_path: PathBuf,
|
bash_path: PathBuf,
|
||||||
execve_wrapper: PathBuf,
|
execve_wrapper: PathBuf,
|
||||||
policy: ExecPolicy,
|
policy: SharedExecPolicy,
|
||||||
sandbox_state: Arc<RwLock<Option<SandboxState>>>,
|
sandbox_state: Arc<RwLock<Option<SandboxState>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool_router]
|
#[tool_router]
|
||||||
impl ExecTool {
|
impl ExecTool {
|
||||||
pub fn new(bash_path: PathBuf, execve_wrapper: PathBuf, policy: ExecPolicy) -> Self {
|
pub fn new(bash_path: PathBuf, execve_wrapper: PathBuf, policy: SharedExecPolicy) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tool_router: Self::tool_router(),
|
tool_router: Self::tool_router(),
|
||||||
bash_path,
|
bash_path,
|
||||||
@@ -121,7 +121,7 @@ impl ExecTool {
|
|||||||
let escalate_server = EscalateServer::new(
|
let escalate_server = EscalateServer::new(
|
||||||
self.bash_path.clone(),
|
self.bash_path.clone(),
|
||||||
self.execve_wrapper.clone(),
|
self.execve_wrapper.clone(),
|
||||||
McpEscalationPolicy::new(self.policy, context, stopwatch.clone()),
|
McpEscalationPolicy::new(self.policy.clone(), context, stopwatch.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = escalate_server
|
let result = escalate_server
|
||||||
@@ -198,7 +198,7 @@ impl ServerHandler for ExecTool {
|
|||||||
pub(crate) async fn serve(
|
pub(crate) async fn serve(
|
||||||
bash_path: PathBuf,
|
bash_path: PathBuf,
|
||||||
execve_wrapper: PathBuf,
|
execve_wrapper: PathBuf,
|
||||||
policy: ExecPolicy,
|
policy: SharedExecPolicy,
|
||||||
) -> Result<RunningService<RoleServer, ExecTool>, rmcp::service::ServerInitializeError> {
|
) -> Result<RunningService<RoleServer, ExecTool>, rmcp::service::ServerInitializeError> {
|
||||||
let tool = ExecTool::new(bash_path, execve_wrapper, policy);
|
let tool = ExecTool::new(bash_path, execve_wrapper, policy);
|
||||||
tool.serve(stdio()).await
|
tool.serve(stdio()).await
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rmcp::ErrorData as McpError;
|
use rmcp::ErrorData as McpError;
|
||||||
use rmcp::RoleServer;
|
use rmcp::RoleServer;
|
||||||
@@ -18,7 +19,11 @@ use crate::posix::stopwatch::Stopwatch;
|
|||||||
/// `argv` is the argv, including the program name (`argv[0]`).
|
/// `argv` is the argv, including the program name (`argv[0]`).
|
||||||
/// `workdir` is the absolute, canonical path to the working directory in which to execute the
|
/// `workdir` is the absolute, canonical path to the working directory in which to execute the
|
||||||
/// command.
|
/// command.
|
||||||
pub(crate) type ExecPolicy = fn(file: &Path, argv: &[String], workdir: &Path) -> ExecPolicyOutcome;
|
pub(crate) trait ExecPolicy: Send + Sync {
|
||||||
|
fn evaluate(&self, file: &Path, argv: &[String], workdir: &Path) -> ExecPolicyOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type SharedExecPolicy = Arc<dyn ExecPolicy>;
|
||||||
|
|
||||||
pub(crate) enum ExecPolicyOutcome {
|
pub(crate) enum ExecPolicyOutcome {
|
||||||
Allow {
|
Allow {
|
||||||
@@ -33,14 +38,14 @@ pub(crate) enum ExecPolicyOutcome {
|
|||||||
/// ExecPolicy with access to the MCP RequestContext so that it can leverage
|
/// ExecPolicy with access to the MCP RequestContext so that it can leverage
|
||||||
/// elicitations.
|
/// elicitations.
|
||||||
pub(crate) struct McpEscalationPolicy {
|
pub(crate) struct McpEscalationPolicy {
|
||||||
policy: ExecPolicy,
|
policy: SharedExecPolicy,
|
||||||
context: RequestContext<RoleServer>,
|
context: RequestContext<RoleServer>,
|
||||||
stopwatch: Stopwatch,
|
stopwatch: Stopwatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl McpEscalationPolicy {
|
impl McpEscalationPolicy {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
policy: ExecPolicy,
|
policy: SharedExecPolicy,
|
||||||
context: RequestContext<RoleServer>,
|
context: RequestContext<RoleServer>,
|
||||||
stopwatch: Stopwatch,
|
stopwatch: Stopwatch,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -103,7 +108,7 @@ impl EscalationPolicy for McpEscalationPolicy {
|
|||||||
argv: &[String],
|
argv: &[String],
|
||||||
workdir: &Path,
|
workdir: &Path,
|
||||||
) -> Result<EscalateAction, rmcp::ErrorData> {
|
) -> Result<EscalateAction, rmcp::ErrorData> {
|
||||||
let outcome = (self.policy)(file, argv, workdir);
|
let outcome = self.policy.evaluate(file, argv, workdir);
|
||||||
let action = match outcome {
|
let action = match outcome {
|
||||||
ExecPolicyOutcome::Allow {
|
ExecPolicyOutcome::Allow {
|
||||||
run_with_escalated_permissions,
|
run_with_escalated_permissions,
|
||||||
|
|||||||
Reference in New Issue
Block a user