Files
codex/codex-rs/codex-mcp/src/rmcp_client_credential_policy_tests.rs
T
rreichel3-oai c7e80f873f Preserve executor MCP credential boundaries across reconnects (#48143)
## Why

Cached executor MCP declarations must not gain access to host environment credentials when reconnecting to an executor that lacks environment credential resolution.

## What changed

- Retain credential policy through MCP registration, catalog materialization, and runtime setup. Derive selected HTTP plugin policy from the plugin's source environment.
- Reject host bearer-token fallback and transports requiring host environment headers or helpers for executor-owned declarations. Preserve host-configured credential resolution.
- Include credential policy in connection and tool catalog cache identities, and avoid reading host environment values for executor-only identities.

## Testing

Add regression coverage for legacy executors, cached declarations after a capability downgrade, policy preservation through catalog rebuilds, and connection and tool cache isolation.

GitOrigin-RevId: 2d5b85e4e98668c637c538cbe528914c936bd831
2026-09-25 17:15:29 +00:00

60 lines
2.1 KiB
Rust

//! Credential resolution must retain executor provenance when host fallback is considered.
use super::resolve_bearer_token;
use crate::server::McpCredentialPolicy;
use pretty_assertions::assert_eq;
#[test]
fn executor_credentials_reject_host_fallback_for_arbitrary_environment_names() {
for env_var in ["PATH", "MCP_UNPROTECTED_TEST_TOKEN"] {
let error = resolve_bearer_token("docs", Some(env_var), McpCredentialPolicy::ExecutorOnly)
.expect_err("executor credential names must never resolve on the host");
assert_eq!(
error.to_string(),
"MCP server 'docs' requires executor-side environment credential resolution; update the executor to a version that supports it (host fallback is disabled)",
);
}
}
#[test]
fn servers_without_environment_credentials_do_not_require_host_fallback() {
for policy in [
McpCredentialPolicy::ExecutorOnly,
McpCredentialPolicy::HostFallbackAllowed,
] {
assert_eq!(
resolve_bearer_token("docs", /*bearer_token_env_var*/ None, policy)
.expect("a server without an environment credential needs no resolution"),
None,
);
}
}
#[test]
fn configured_credentials_retain_host_environment_resolution() {
// PATH is nonsecret and read without changing the process environment.
let expected = match std::env::var("PATH") {
Ok(value) if value.is_empty() => {
Err("Environment variable PATH for MCP server 'docs' is empty".to_string())
}
Ok(value) => Ok(Some(value)),
Err(std::env::VarError::NotPresent) => {
Err("Environment variable PATH for MCP server 'docs' is not set".to_string())
}
Err(std::env::VarError::NotUnicode(_)) => Err(
"Environment variable PATH for MCP server 'docs' contains invalid Unicode".to_string(),
),
};
assert_eq!(
resolve_bearer_token(
"docs",
Some("PATH"),
McpCredentialPolicy::HostFallbackAllowed
)
.map_err(|error| error.to_string()),
expected,
);
}