[connectors] Track host-owned Codex Apps MCP provenance#20649
[connectors] Track host-owned Codex Apps MCP provenance#20649mzeng-openai wants to merge 11 commits intomainfrom
Conversation
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Flagging one security concern from Viyat's security review automation. The cached Codex Apps tool path appears to drop host-owned provenance. That matters because the exposure filtering treats host-owned Codex Apps tools differently: connector accessibility/enabled checks only apply when The fix I'd suggest: after deserializing the cache, set every cached Codex Apps |
|
Good catch. Cached Codex Apps tools now get restamped as |
…pps_mcp_declare # Conflicts: # codex-rs/core/src/mcp_tool_call.rs
| ); | ||
| match codex_apps_mcp_server_config(config) { | ||
| Ok(server_config) => { | ||
| servers.insert(CODEX_APPS_MCP_SERVER_NAME.to_string(), server_config); |
There was a problem hiding this comment.
Can/do we want to impose any restrictions on MCP server names read from config.toml so that "internal" MCP servers cannot run the risk of name collisions with user-defined ones?
You could also use https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.entry and ensure your insert() is not and overwrite and if so error?
| &config.chatgpt_base_url, | ||
| config.apps_mcp_path_override.as_deref(), | ||
| ) | ||
| #[cfg(test)] |
There was a problem hiding this comment.
Should this get moved to mod_tests.rs instead?
| if (base_url.starts_with("https://chatgpt.com") | ||
| || base_url.starts_with("https://chat.openai.com")) | ||
| && !base_url.contains("/backend-api") | ||
| #[derive(Debug, Clone, PartialEq, Eq)] |
There was a problem hiding this comment.
This is adding a lot of code to a file that is already fairly large: I would put this in a separate file (and move any tests to a parallel file, as appropriate).
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| struct TrustedCodexAppsMcpUrl(String); |
There was a problem hiding this comment.
You really want something like this to ensure the integrity of the String value:
mod trusted_codex_apps_mcp_url {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TrustedCodexAppsMcpUrl(String);
impl TrustedCodexAppsMcpUrl {
pub fn new(url: String) -> Option<Self> {
if is_trusted_codex_apps_mcp_url(&url) {
Some(Self(url))
} else {
None
}
}
pub fn as_str(&self) -> &str {
&self.0
}
}
fn is_trusted_codex_apps_mcp_url(url: &str) -> bool {
// your validation logic here
url.starts_with("https://")
}
}
pub use trusted_codex_apps_mcp_url::TrustedCodexAppsMcpUrl;All the more reason to move this to a separate file to restrict the public API!
Why
MCP server names are routing labels, not a trustworthy ownership boundary. Connector-gateway behavior should only apply to the built-in Codex Apps MCP instance created by the host, not to an MCP server that merely has the same name or declares the same capabilities.
How provenance is determined
McpServerProvenanceis host-assigned and runtime-only. MCP servers loaded from user/plugin config deserialize asUserConfigured; the only path that marks a serverHostOwnedCodexAppsis the host path that injects Codex Apps MCP atcodex_appsusing the ChatGPT backend/wham/appsendpoint. Because provenance is not serialized, cached Codex Apps tools are restamped as host-owned when loaded.What changed
McpServerProvenanceand mark the host-injected Apps MCP asHostOwnedCodexApps.ToolInfo.codex_appsserver name is not treated as host-owned.Testing
cargo test -p codex-mcpcargo test -p codex-core mcp_tool_call