mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
fix: harden plugin feature gating (#15104)
Resubmit https://github.com/openai/codex/pull/15020 with correct content. 1. Use requirement-resolved config.features as the plugin gate. 2. Guard plugin/list, plugin/read, and related flows behind that gate. 3. Skip bad marketplace.json files instead of failing the whole list. 4. Simplify plugin state and caching.
This commit is contained in:
@@ -4842,6 +4842,7 @@ impl CodexMessageProcessor {
|
||||
MarketplaceError::InvalidMarketplaceFile { .. }
|
||||
| MarketplaceError::PluginNotFound { .. }
|
||||
| MarketplaceError::PluginNotAvailable { .. }
|
||||
| MarketplaceError::PluginsDisabled
|
||||
| MarketplaceError::InvalidPlugin(_) => {
|
||||
self.send_invalid_request_error(request_id, err.to_string())
|
||||
.await;
|
||||
@@ -5363,6 +5364,13 @@ impl CodexMessageProcessor {
|
||||
.extend(valid_extra_roots);
|
||||
}
|
||||
|
||||
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
Ok(config) => config,
|
||||
Err(error) => {
|
||||
self.outgoing.send_error(request_id, error).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let skills_manager = self.thread_manager.skills_manager();
|
||||
let mut data = Vec::new();
|
||||
for cwd in cwds {
|
||||
@@ -5370,7 +5378,7 @@ impl CodexMessageProcessor {
|
||||
.get(&cwd)
|
||||
.map_or(&[][..], std::vec::Vec::as_slice);
|
||||
let outcome = skills_manager
|
||||
.skills_for_cwd_with_extra_user_roots(&cwd, force_reload, extra_roots)
|
||||
.skills_for_cwd_with_extra_user_roots(&cwd, &config, force_reload, extra_roots)
|
||||
.await;
|
||||
let errors = errors_to_info(&outcome.errors);
|
||||
let skills = skills_to_info(&outcome.skills, &outcome.disabled_paths);
|
||||
|
||||
@@ -28,12 +28,22 @@ use wiremock::matchers::path;
|
||||
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const TEST_CURATED_PLUGIN_SHA: &str = "0123456789abcdef0123456789abcdef01234567";
|
||||
|
||||
fn write_plugins_enabled_config(codex_home: &std::path::Path) -> std::io::Result<()> {
|
||||
std::fs::write(
|
||||
codex_home.join("config.toml"),
|
||||
r#"[features]
|
||||
plugins = true
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_list_returns_invalid_request_for_invalid_marketplace_file() -> Result<()> {
|
||||
async fn plugin_list_skips_invalid_marketplace_file() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let repo_root = TempDir::new()?;
|
||||
std::fs::create_dir_all(repo_root.path().join(".git"))?;
|
||||
std::fs::create_dir_all(repo_root.path().join(".agents/plugins"))?;
|
||||
write_plugins_enabled_config(codex_home.path())?;
|
||||
std::fs::write(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
"{not json",
|
||||
@@ -57,14 +67,23 @@ async fn plugin_list_returns_invalid_request_for_invalid_marketplace_file() -> R
|
||||
})
|
||||
.await?;
|
||||
|
||||
let err = timeout(
|
||||
let response: JSONRPCResponse = timeout(
|
||||
DEFAULT_TIMEOUT,
|
||||
mcp.read_stream_until_error_message(RequestId::Integer(request_id)),
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
||||
)
|
||||
.await??;
|
||||
let response: PluginListResponse = to_response(response)?;
|
||||
|
||||
assert_eq!(err.error.code, -32600);
|
||||
assert!(err.error.message.contains("invalid marketplace file"));
|
||||
assert!(
|
||||
response.marketplaces.iter().all(|marketplace| {
|
||||
marketplace.path
|
||||
!= AbsolutePathBuf::try_from(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
)
|
||||
.expect("absolute marketplace path")
|
||||
}),
|
||||
"invalid marketplace should be skipped"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -98,6 +117,7 @@ async fn plugin_list_rejects_relative_cwds() -> Result<()> {
|
||||
async fn plugin_list_accepts_omitted_cwds() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
std::fs::create_dir_all(codex_home.path().join(".agents/plugins"))?;
|
||||
write_plugins_enabled_config(codex_home.path())?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(".agents/plugins/marketplace.json"),
|
||||
r#"{
|
||||
@@ -385,6 +405,7 @@ async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Res
|
||||
std::fs::create_dir_all(repo_root.path().join(".git"))?;
|
||||
std::fs::create_dir_all(repo_root.path().join(".agents/plugins"))?;
|
||||
std::fs::create_dir_all(plugin_root.join(".codex-plugin"))?;
|
||||
write_plugins_enabled_config(codex_home.path())?;
|
||||
std::fs::write(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
r#"{
|
||||
@@ -518,6 +539,7 @@ async fn plugin_list_accepts_legacy_string_default_prompt() -> Result<()> {
|
||||
std::fs::create_dir_all(repo_root.path().join(".git"))?;
|
||||
std::fs::create_dir_all(repo_root.path().join(".agents/plugins"))?;
|
||||
std::fs::create_dir_all(plugin_root.join(".codex-plugin"))?;
|
||||
write_plugins_enabled_config(codex_home.path())?;
|
||||
std::fs::write(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
r#"{
|
||||
|
||||
@@ -232,6 +232,7 @@ async fn plugin_read_accepts_legacy_string_default_prompt() -> Result<()> {
|
||||
}
|
||||
}"##,
|
||||
)?;
|
||||
write_plugins_enabled_config(&codex_home)?;
|
||||
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
|
||||
@@ -285,6 +286,7 @@ async fn plugin_read_returns_invalid_request_when_plugin_is_missing() -> Result<
|
||||
]
|
||||
}"#,
|
||||
)?;
|
||||
write_plugins_enabled_config(&codex_home)?;
|
||||
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
|
||||
@@ -336,6 +338,7 @@ async fn plugin_read_returns_invalid_request_when_plugin_manifest_is_missing() -
|
||||
]
|
||||
}"#,
|
||||
)?;
|
||||
write_plugins_enabled_config(&codex_home)?;
|
||||
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
|
||||
@@ -382,3 +385,13 @@ fn write_installed_plugin(
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_plugins_enabled_config(codex_home: &TempDir) -> Result<()> {
|
||||
std::fs::write(
|
||||
codex_home.path().join("config.toml"),
|
||||
r#"[features]
|
||||
plugins = true
|
||||
"#,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user