Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 82 additions & 22 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,39 @@ fn profile_allows_configured_network_proxy(permission_profile: &PermissionProfil
}
}

fn build_network_proxy_spec(
configured_network_proxy_config: NetworkProxyConfig,
network_requirements: Option<Sourced<codex_config::NetworkConstraints>>,
permission_profile: &PermissionProfile,
) -> std::io::Result<Option<NetworkProxySpec>> {
let (network_requirements, network_requirements_source) = match network_requirements {
Some(Sourced { value, source }) => (Some(value), Some(source)),
None => (None, None),
};
let has_network_requirements = network_requirements.is_some();
let network = NetworkProxySpec::from_config_and_constraints(
configured_network_proxy_config,
network_requirements,
permission_profile,
)
.map_err(|err| {
if let Some(source) = network_requirements_source.as_ref() {
std::io::Error::new(
err.kind(),
format!("failed to build managed network proxy from {source}: {err}"),
)
} else {
err
}
})?;

Ok(if has_network_requirements {
Some(network)
} else {
network.enabled().then_some(network)
})
}

/// Configured thread persistence backend.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ThreadStoreConfig {
Expand Down Expand Up @@ -3320,32 +3353,12 @@ impl Config {
let mcp_servers = constrain_mcp_servers(cfg.mcp_servers.clone(), mcp_servers.as_ref())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{e}")))?;

let (network_requirements, network_requirements_source) = match network_requirements {
Some(Sourced { value, source }) => (Some(value), Some(source)),
None => (None, None),
};
let has_network_requirements = network_requirements.is_some();
let network_permission_profile = constrained_permission_profile.get().clone();
let network = NetworkProxySpec::from_config_and_constraints(
let network = build_network_proxy_spec(
configured_network_proxy_config,
network_requirements,
&network_permission_profile,
)
.map_err(|err| {
if let Some(source) = network_requirements_source.as_ref() {
std::io::Error::new(
err.kind(),
format!("failed to build managed network proxy from {source}: {err}"),
)
} else {
err
}
})?;
let network = if has_network_requirements {
Some(network)
} else {
network.enabled().then_some(network)
};
)?;
let helper_readable_roots = get_readable_roots_required_for_codex_runtime(
&codex_home,
zsh_path.as_ref(),
Expand Down Expand Up @@ -3700,6 +3713,53 @@ impl Config {
.is_some()
}

pub(crate) fn network_proxy_spec_for_active_permission_profile(
&self,
active_permission_profile: &ActivePermissionProfile,
permission_profile: &PermissionProfile,
) -> std::io::Result<Option<NetworkProxySpec>> {
let profile_allows_network_proxy =
profile_allows_configured_network_proxy(permission_profile);
let configured_network_proxy_config = if profile_allows_network_proxy {
let cfg: ConfigToml = self
.config_layer_stack
.effective_config()
.try_into()
.map_err(|err| {
std::io::Error::new(
ErrorKind::InvalidInput,
format!(
"failed to read effective config for selected permission profile: {err}"
),
)
})?;
let mut configured_network_proxy_config = network_proxy_config_for_profile_selection(
cfg.permissions.as_ref(),
active_permission_profile.id.as_str(),
)?;
if self.features.enabled(Feature::NetworkProxy)
&& permission_profile.network_sandbox_policy().is_enabled()
{
if let Some(network_proxy) = network_proxy_toml_config(cfg.features.as_ref()) {
apply_network_proxy_feature_config(
&mut configured_network_proxy_config,
network_proxy,
);
}
configured_network_proxy_config.network.enabled = true;
}
configured_network_proxy_config
} else {
NetworkProxyConfig::default()
};

build_network_proxy_spec(
configured_network_proxy_config,
self.config_layer_stack.requirements().network.clone(),
permission_profile,
)
}

pub fn bundled_skills_enabled(&self) -> bool {
crate::manager::bundled_skills_enabled_from_stack(&self.config_layer_stack)
}
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/core/src/guardian/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ pub(super) async fn run_guardian_review_session(
schema: serde_json::Value,
external_cancel: Option<CancellationToken>,
) -> (GuardianReviewOutcome, GuardianReviewAnalyticsResult) {
let live_network_config = match session.services.network_proxy.as_ref() {
let network_proxy = session.services.network_proxy.load_full();
let live_network_config = match network_proxy.as_ref() {
Some(network_proxy) => match network_proxy.proxy().current_cfg().await {
Ok(config) => Some(config),
Err(err) => {
Expand Down
39 changes: 33 additions & 6 deletions codex-rs/core/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,9 +952,6 @@ impl Session {
}

async fn refresh_managed_network_proxy_for_current_permission_profile(&self) {
let Some(started_proxy) = self.services.network_proxy.as_ref() else {
return;
};
let Ok(_refresh_guard) = self.managed_network_proxy_refresh_lock.acquire().await else {
error!("managed network proxy refresh semaphore closed");
return;
Expand All @@ -968,7 +965,9 @@ impl Session {
.permissions
.network
.as_ref()
.cloned()
else {
self.services.network_proxy.store(None);
return;
};

Expand All @@ -991,8 +990,36 @@ impl Session {
spec
}
};
if let Err(err) = spec.apply_to_started_proxy(started_proxy).await {
warn!("failed to refresh managed network proxy for sandbox change: {err}");
if let Some(started_proxy) = self.services.network_proxy.load_full() {
if let Err(err) = spec.apply_to_started_proxy(started_proxy.as_ref()).await {
warn!("failed to refresh managed network proxy for sandbox change: {err}");
}
return;
}

match Self::start_managed_network_proxy(
&spec,
current_exec_policy.as_ref(),
&session_configuration.permission_profile(),
/*network_policy_decider*/ None,
self.services
.managed_network_requirements_configured
.then(|| {
build_blocked_request_observer(Arc::clone(&self.services.network_approval))
}),
self.services.managed_network_requirements_configured,
self.services.network_proxy_audit_metadata.clone(),
)
.await
{
Ok((started_proxy, _session_network_proxy)) => {
self.services
.network_proxy
.store(Some(Arc::new(started_proxy)));
}
Err(err) => {
warn!("failed to start managed network proxy for sandbox change: {err}");
}
}
}

Expand Down Expand Up @@ -1886,7 +1913,7 @@ impl Session {
let execpolicy_amendment =
execpolicy_network_rule_amendment(amendment, network_approval_context, &host);

if let Some(started_network_proxy) = self.services.network_proxy.as_ref() {
if let Some(started_network_proxy) = self.services.network_proxy.load_full() {
let proxy = started_network_proxy.proxy();
match amendment.action {
NetworkPolicyRuleAction::Allow => proxy
Expand Down
34 changes: 32 additions & 2 deletions codex-rs/core/src/session/session.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::input_queue::InputQueue;
use super::*;
use crate::config::ConstraintError;
use crate::goals::GoalRuntimeState;
use crate::skills::SkillError;
use crate::state::ActiveTurn;
Expand Down Expand Up @@ -291,6 +292,33 @@ impl SessionConfiguration {
updates.profile_workspace_roots.clone().unwrap_or_default(),
Some(&current_file_system_sandbox_policy),
)?;
if let Some(active_permission_profile) = next_configuration.active_permission_profile()
{
let mut config = (*next_configuration.original_config_do_not_use).clone();
let permission_profile = next_configuration.permission_profile();
config.permissions.network = config
.network_proxy_spec_for_active_permission_profile(
&active_permission_profile,
&permission_profile,
)
.map_err(|err| ConstraintError::InvalidValue {
field_name: "default_permissions",
candidate: active_permission_profile.id.clone(),
allowed: format!(
"configured permission profile with valid network policy ({err})"
),
requirement_source: codex_config::RequirementSource::Unknown,
})?;
config
.permissions
.set_permission_profile_from_session_snapshot(
PermissionProfileSnapshot::active(
permission_profile,
active_permission_profile,
),
)?;
next_configuration.original_config_do_not_use = Arc::new(config);
}
} else if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries(
Expand Down Expand Up @@ -885,7 +913,7 @@ impl Session {
network_policy_decider.as_ref().map(Arc::clone),
blocked_request_observer.as_ref().map(Arc::clone),
managed_network_requirements_configured,
network_proxy_audit_metadata,
network_proxy_audit_metadata.clone(),
)
.instrument(info_span!(
"session_init.network_proxy",
Expand Down Expand Up @@ -977,7 +1005,9 @@ impl Session {
session_extension_data,
thread_extension_data,
agent_control,
network_proxy,
network_proxy: arc_swap::ArcSwapOption::from(network_proxy.map(Arc::new)),
network_proxy_audit_metadata,
managed_network_requirements_configured,
network_approval: Arc::clone(&network_approval),
state_db: state_db_ctx.clone(),
live_thread: live_thread_init.as_ref().cloned(),
Expand Down
Loading
Loading