Skip to content

Commit 9dbdb4e

Browse files
authored
[plugins] Enforce marketplace source policy at runtime (openai#29691)
## Summary - project effective marketplace/plugin config through the enterprise source policy so blocked installed plugins become inactive - filter plugin list/read/discovery and CLI marketplace source/snapshot reporting using the same policy - enforce source admission for background marketplace cache refreshes - continue refreshing/upgrading independent marketplaces and plugins when one entry fails, returning per-entry errors - include policy-projected plugin state in cache and refresh keys so requirement changes invalidate stale results ## Stack This is PR 2 of 2 and is based on openai#29690. Review the admission model and source matcher in openai#29690 first; this PR contains only runtime enforcement. ## Test plan - `just test -p codex-core-plugins` (287 tests) - `just test -p codex-cli plugin_list_ignores_implicit_system_marketplace_roots_without_manifests` - `cargo check -p codex-cli -p codex-app-server --tests`
1 parent e2398d0 commit 9dbdb4e

12 files changed

Lines changed: 893 additions & 140 deletions

codex-rs/cli/src/marketplace_cmd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ fn configured_marketplace_sources_by_root(
341341
codex_home: &Path,
342342
plugins_input: &PluginsConfigInput,
343343
) -> HashMap<PathBuf, JsonMarketplaceSource> {
344-
let marketplace_sources = configured_marketplace_sources(plugins_input);
344+
let marketplace_sources = configured_marketplace_sources(plugins_input, codex_home);
345345
let Some(user_config) = plugins_input.config_layer_stack.effective_user_config() else {
346346
return HashMap::new();
347347
};

codex-rs/cli/src/plugin_cmd.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use codex_core_plugins::PluginInstallOutcome;
1010
use codex_core_plugins::PluginInstallRequest;
1111
use codex_core_plugins::PluginsConfigInput;
1212
use codex_core_plugins::PluginsManager;
13+
use codex_core_plugins::allowed_configured_marketplace_names;
1314
use codex_core_plugins::installed_marketplaces::marketplace_install_root;
1415
use codex_core_plugins::installed_marketplaces::resolve_configured_marketplace_root;
1516
use codex_core_plugins::marketplace::MarketplaceListError;
@@ -228,7 +229,7 @@ pub async fn run_plugin_list(
228229
.is_none_or(|name| marketplace.name == *name)
229230
})
230231
.collect::<Vec<_>>();
231-
let marketplace_sources = configured_marketplace_sources(&plugins_input);
232+
let marketplace_sources = configured_marketplace_sources(&plugins_input, codex_home.as_path());
232233

233234
if args.json {
234235
let output = JsonPluginListOutput::from_marketplaces(
@@ -480,6 +481,7 @@ pub(crate) struct JsonMarketplaceSource {
480481

481482
pub(crate) fn configured_marketplace_sources(
482483
plugins_input: &PluginsConfigInput,
484+
codex_home: &Path,
483485
) -> HashMap<String, JsonMarketplaceSource> {
484486
let Some(user_config) = plugins_input.config_layer_stack.effective_user_config() else {
485487
return HashMap::new();
@@ -490,9 +492,12 @@ pub(crate) fn configured_marketplace_sources(
490492
else {
491493
return HashMap::new();
492494
};
495+
let allowed_marketplace_names =
496+
allowed_configured_marketplace_names(&plugins_input.config_layer_stack, codex_home);
493497

494498
marketplaces
495499
.iter()
500+
.filter(|(marketplace_name, _)| allowed_marketplace_names.contains(*marketplace_name))
496501
.filter_map(|(marketplace_name, marketplace)| {
497502
let source_type = marketplace
498503
.get("source_type")
@@ -746,11 +751,16 @@ pub(crate) fn configured_marketplace_snapshot_issues(
746751
else {
747752
return Vec::new();
748753
};
754+
let allowed_marketplace_names =
755+
allowed_configured_marketplace_names(&plugins_input.config_layer_stack, codex_home);
749756

750757
let default_install_root = marketplace_install_root(codex_home);
751758
let mut manifest_paths = Vec::new();
752759
let mut issues = Vec::new();
753760
for (configured_name, marketplace) in configured_marketplaces {
761+
if !allowed_marketplace_names.contains(configured_name) {
762+
continue;
763+
}
754764
if marketplace_name.is_some_and(|name| configured_name != name) {
755765
continue;
756766
}

codex-rs/cli/tests/plugin_cli.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ fn setup_local_marketplace_with_implicit_system_roots() -> Result<(TempDir, Temp
205205
let cache_home = TempDir::new()?;
206206
let runtime_root = cache_home
207207
.path()
208+
.join(".cache")
208209
.join("codex-runtimes")
209210
.join("codex-primary-runtime")
210211
.join("plugins")
@@ -844,7 +845,8 @@ async fn plugin_list_ignores_implicit_system_marketplace_roots_without_manifests
844845
let (codex_home, source, cache_home) = setup_local_marketplace_with_implicit_system_roots()?;
845846

846847
codex_command(codex_home.path())?
847-
.env("XDG_CACHE_HOME", cache_home.path())
848+
.env("HOME", cache_home.path())
849+
.env("USERPROFILE", cache_home.path())
848850
.args(["plugin", "list"])
849851
.assert()
850852
.success()

codex-rs/core-plugins/src/installed_marketplaces.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use codex_utils_absolute_path::AbsolutePathBuf;
77
use tracing::warn;
88

99
use crate::marketplace::find_marketplace_manifest_path;
10+
use crate::marketplace_policy::project_effective_user_config;
1011

1112
pub const INSTALLED_MARKETPLACES_DIR: &str = ".tmp/marketplaces";
1213

@@ -18,7 +19,7 @@ pub fn installed_marketplace_roots_from_layer_stack(
1819
config_layer_stack: &ConfigLayerStack,
1920
codex_home: &Path,
2021
) -> Vec<AbsolutePathBuf> {
21-
let Some(user_config) = config_layer_stack.effective_user_config() else {
22+
let Some(user_config) = project_effective_user_config(config_layer_stack, codex_home) else {
2223
return Vec::new();
2324
};
2425
let Some(marketplaces_value) = user_config.get("marketplaces") else {

codex-rs/core-plugins/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub use manager::PluginUninstallError;
5555
pub use manager::PluginsConfigInput;
5656
pub use manager::PluginsManager;
5757
pub use manager::RecommendedPluginCandidatesInput;
58+
pub use marketplace_policy::allowed_configured_marketplace_names;
5859
pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeError as PluginMarketplaceUpgradeError;
5960
pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome as PluginMarketplaceUpgradeOutcome;
6061
pub use provider::ExecutorPluginProvider;

0 commit comments

Comments
 (0)