mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Freeze plugin roots in MCP tool attribution (#41117)
## Why Plugin MCP trust decisions must describe the exact host-discovered server registration. Looking up a plugin again by ID can resolve a different root. ## What changed - Retain the host plugin root in MCP catalog attribution and connection identity. - Reconnect an MCP server when its attributed plugin root changes. - Pass the retained root through tool lifecycle attribution and use it directly when building trusted tool context. ## Testing Add coverage for catalog identity, connection reconciliation, lifecycle attribution, and rejecting a different root associated with the same plugin ID. GitOrigin-RevId: bc92743dd40ab44065a07c3ea972b4956ef8351f
This commit is contained in:
1 parent
6c59264b14
commit
aa89cf62bb
11 files changed
+279
-38
No files matched your search
@@ -68,13 +68,13 @@ pub(crate) async fn trusted_tool_context(
|
||||
) -> Option<GuardianTrustedToolFragment> {
|
||||
let codex_home = config.codex_home.as_path().canonicalize().ok()?;
|
||||
let plugins = match source {
|
||||
McpToolSource::Connector | McpToolSource::Plugin { .. } => Some(
|
||||
McpToolSource::Connector => Some(
|
||||
manager
|
||||
.plugins_manager()
|
||||
.plugins_for_config(&config.plugins_config_input())
|
||||
.await,
|
||||
),
|
||||
McpToolSource::Config => None,
|
||||
McpToolSource::Config | McpToolSource::Plugin { .. } => None,
|
||||
McpToolSource::SelectedPlugin | McpToolSource::Other => return None,
|
||||
};
|
||||
|
||||
@@ -98,18 +98,16 @@ pub(crate) async fn trusted_tool_context(
|
||||
trusted_user_config_source(config, "apps", connector_id, &codex_home)?
|
||||
}
|
||||
}
|
||||
McpToolSource::Plugin { id: plugin_id } => {
|
||||
let plugin = plugins.as_ref()?.plugins().iter().find(|plugin| {
|
||||
plugin.is_active()
|
||||
&& plugin.config_name == plugin_id.as_str()
|
||||
&& plugin.mcp_servers.contains_key(&tool.server_name)
|
||||
&& is_home_owned_plugin_capability(
|
||||
plugin.root.as_path(),
|
||||
&codex_home,
|
||||
PluginCapability::Mcp,
|
||||
)
|
||||
})?;
|
||||
plugin.root.as_path().display().to_string()
|
||||
McpToolSource::Plugin { root, .. } => {
|
||||
let plugin_root = root.to_abs_path().ok()?;
|
||||
if !is_home_owned_plugin_capability(
|
||||
plugin_root.as_path(),
|
||||
&codex_home,
|
||||
PluginCapability::Mcp,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
plugin_root.as_path().display().to_string()
|
||||
}
|
||||
McpToolSource::Config => {
|
||||
trusted_user_config_source(config, "mcp_servers", &tool.server_name, &codex_home)?
|
||||
|
||||
@@ -149,6 +149,11 @@ async fn trusts_connector_declared_by_home_owned_plugin() -> Result<()> {
|
||||
&mcp,
|
||||
&McpToolSource::Plugin {
|
||||
id: "trusted@test".to_string(),
|
||||
root: test
|
||||
.config
|
||||
.codex_home
|
||||
.join("plugins/cache/test/trusted/local")
|
||||
.into(),
|
||||
},
|
||||
&test.thread_manager,
|
||||
&test.config,
|
||||
@@ -156,6 +161,20 @@ async fn trusts_connector_declared_by_home_owned_plugin() -> Result<()> {
|
||||
.await
|
||||
.expect("home-owned plugin MCP server should be trusted");
|
||||
assert_eq!(mcp_context.metadata, expected_context(&mcp, &plugin_root));
|
||||
// A trusted cached plugin with the same ID must not replace the frozen outside-home root.
|
||||
assert_eq!(
|
||||
trusted_tool_context(
|
||||
&mcp,
|
||||
&McpToolSource::Plugin {
|
||||
id: "trusted@test".to_string(),
|
||||
root: test.config.cwd.clone().into(),
|
||||
},
|
||||
&test.thread_manager,
|
||||
&test.config,
|
||||
)
|
||||
.await,
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
trusted_tool_context(
|
||||
&mcp,
|
||||
|
||||
Reference in new issue
Block a user