mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Respect filesystem permissions during capability discovery (#36124)
## Why Executor capability discovery can traverse plugin and skill roots, including symlinks. Under restricted filesystem permissions, discovery must not expose files outside the permitted paths. ## What changed - Pass each environment's filesystem sandbox context through capability root discovery and apply it to metadata, directory walks, and file reads. - Enable discovery for restricted sessions so permitted executor skills remain available while inaccessible roots and symlink targets are omitted. - Key discovery caches by sandbox context and reject sandboxed discovery on executors that do not advertise support for it. - Split requests with more than 128 roots into supported-size batches. ## Testing - Cover permitted and denied external symlink targets. - Verify restricted skill listing excludes inaccessible skills. - Verify cache separation across permission contexts and discovery of 129 roots. GitOrigin-RevId: 44d16468ca003403bdb8b71a04ae8c9ff94ed494
This commit is contained in:
1 parent
3d805abdf0
commit
5decb399ae
14 files changed
+510
-70
No files matched your search
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||
use codex_core_skills::HostSkillsSnapshot;
|
||||
use codex_core_skills::injection::HostSkillsCatalogInWorldState;
|
||||
use codex_core_skills::injection::InjectedHostSkillPrompts;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
@@ -457,7 +458,9 @@ where
|
||||
include_bundled_skills: false,
|
||||
include_orchestrator_skills: false,
|
||||
mcp_resources: None,
|
||||
executor_capability_discovery: None,
|
||||
executor_capability_discovery: step_store
|
||||
.get::<ExecutorCapabilityDiscoverySnapshot>()
|
||||
.map(|discovery| discovery.as_ref().clone()),
|
||||
});
|
||||
self.build_skill_tools(
|
||||
session_store,
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_extension_api::ExtensionMetrics;
|
||||
use codex_mcp::McpResourceClient;
|
||||
use codex_mcp::McpResourceClientCacheKey;
|
||||
@@ -138,12 +139,19 @@ impl SkillsThreadState {
|
||||
providers: &SkillProviders,
|
||||
query: SkillListQuery,
|
||||
) -> SkillCatalog {
|
||||
let sandbox_contexts = query
|
||||
.executor_capability_discovery
|
||||
.as_ref()
|
||||
.map(|discovery| discovery.sandbox_contexts().clone())
|
||||
.unwrap_or_default();
|
||||
if let Some(cached) = self
|
||||
.executor_discovery_cache
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.as_ref()
|
||||
.filter(|cached| cached.roots == query.executor_roots)
|
||||
.filter(|cached| {
|
||||
cached.roots == query.executor_roots && cached.sandbox_contexts == sandbox_contexts
|
||||
})
|
||||
{
|
||||
return cached.catalog.clone();
|
||||
}
|
||||
@@ -153,11 +161,15 @@ impl SkillsThreadState {
|
||||
.executor_discovery_cache
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
if let Some(cached) = cache.as_ref().filter(|cached| cached.roots == roots) {
|
||||
if let Some(cached) = cache
|
||||
.as_ref()
|
||||
.filter(|cached| cached.roots == roots && cached.sandbox_contexts == sandbox_contexts)
|
||||
{
|
||||
return cached.catalog.clone();
|
||||
}
|
||||
*cache = Some(CachedExecutorDiscoveryCatalog {
|
||||
roots,
|
||||
sandbox_contexts,
|
||||
catalog: discovered.clone(),
|
||||
});
|
||||
discovered
|
||||
@@ -282,6 +294,7 @@ struct CachedExecutorCatalog {
|
||||
|
||||
struct CachedExecutorDiscoveryCatalog {
|
||||
roots: Vec<SelectedCapabilityRoot>,
|
||||
sandbox_contexts: HashMap<String, FileSystemSandboxContext>,
|
||||
catalog: SkillCatalog,
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user