mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
Support configured credential providers in remote execution (#56049)
## Why Remote credential masking previously fell back to unmasked execution whenever custom credential providers were configured. ## What changed - Transport configured provider rules to the executor-local network proxy, where credential values and destination context are resolved. - Advertise `credentialMaskingCustomProviders` and negotiate masking support. Keep the warning and unmasked fallback for executors that support only built-in providers. - Discard provider rules when masking is disabled and omit their contents from debug output. ## Testing Extend coverage for capability compatibility, provider configuration round trips, and fallback on older executors. Exercise configured-provider masking in remote request history and verify that masked placeholders are restored to real credentials in upstream request headers. GitOrigin-RevId: b036f56900b343ddccc17b5293c82bc1f3426237
This commit is contained in:
@@ -8,6 +8,7 @@ pub use codex_file_system::WalkOutcome;
|
||||
use codex_file_system::WireFileSystemSandboxContext;
|
||||
use codex_install_context::InstallContext;
|
||||
use codex_network_proxy::ManagedNetworkSandboxContext;
|
||||
use codex_network_proxy::RemoteCredentialMasking;
|
||||
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
@@ -151,10 +152,13 @@ fn unknown_executor_version() -> String {
|
||||
pub struct EnvironmentCapabilities {
|
||||
/// Whether `exec` accepts instructions for launching an executor-local network proxy.
|
||||
#[serde(default)]
|
||||
pub network_proxy_launch: bool,
|
||||
network_proxy_launch: bool,
|
||||
/// Whether the selected shell can mask executor environment credentials.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub credential_masking: bool,
|
||||
credential_masking: bool,
|
||||
/// Whether credential masking also supports configured providers.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
credential_masking_custom_providers: bool,
|
||||
/// Whether capability discovery applies the filesystem sandbox sent with each root.
|
||||
#[serde(default)]
|
||||
pub capability_discovery_sandbox: bool,
|
||||
@@ -187,6 +191,31 @@ pub struct EnvironmentCapabilities {
|
||||
pub linux_approved_root_write_preserves_restrictions: bool,
|
||||
}
|
||||
|
||||
impl EnvironmentCapabilities {
|
||||
/// Network proxy features available through this executor.
|
||||
pub fn network_proxy(&self) -> NetworkProxyCapabilities {
|
||||
NetworkProxyCapabilities {
|
||||
launch: self.network_proxy_launch,
|
||||
credential_masking: if !self.credential_masking {
|
||||
RemoteCredentialMasking::Disabled
|
||||
} else if self.credential_masking_custom_providers {
|
||||
RemoteCredentialMasking::WithConfiguredProviders
|
||||
} else {
|
||||
RemoteCredentialMasking::BuiltInOnly
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Features supported by an executor-local network proxy.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct NetworkProxyCapabilities {
|
||||
/// Whether `exec` accepts instructions for launching an executor-local network proxy.
|
||||
pub launch: bool,
|
||||
/// Credential masking support available through the selected shell.
|
||||
pub credential_masking: RemoteCredentialMasking,
|
||||
}
|
||||
|
||||
/// Status returned by an initialized exec-server connection.
|
||||
///
|
||||
/// The response is intentionally small today. New status details can be added
|
||||
@@ -286,6 +315,7 @@ impl EnvironmentInfo {
|
||||
capabilities: EnvironmentCapabilities {
|
||||
network_proxy_launch: true,
|
||||
credential_masking,
|
||||
credential_masking_custom_providers: credential_masking,
|
||||
capability_discovery_sandbox: true,
|
||||
capability_discovery_v2: true,
|
||||
environment_config_read: true,
|
||||
@@ -1260,6 +1290,7 @@ mod tests {
|
||||
use super::FsOpenParams;
|
||||
use super::FsReadFileParams;
|
||||
use super::HttpRequestParams;
|
||||
use super::NetworkProxyCapabilities;
|
||||
use super::ProcessId;
|
||||
use super::ProcessSandboxType;
|
||||
use super::ShellInfo;
|
||||
@@ -1270,6 +1301,7 @@ mod tests {
|
||||
use codex_network_proxy::ManagedNetworkSandboxContext;
|
||||
use codex_network_proxy::NetworkProxyAuditMetadata;
|
||||
use codex_network_proxy::NetworkProxyConfig;
|
||||
use codex_network_proxy::RemoteCredentialMasking;
|
||||
use codex_network_proxy::RemoteNetworkProxyConfig;
|
||||
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
|
||||
use codex_protocol::config_types::WindowsSandboxProxySettingsMode;
|
||||
@@ -1508,6 +1540,7 @@ mod tests {
|
||||
EnvironmentCapabilities {
|
||||
network_proxy_launch: true,
|
||||
credential_masking: false,
|
||||
credential_masking_custom_providers: false,
|
||||
capability_discovery_sandbox: true,
|
||||
capability_discovery_v2: false,
|
||||
environment_config_read: false,
|
||||
@@ -1520,6 +1553,88 @@ mod tests {
|
||||
linux_approved_root_write_preserves_restrictions: false,
|
||||
}
|
||||
);
|
||||
|
||||
for (input_masking, expected_masking, output_masking) in [
|
||||
(
|
||||
serde_json::json!({}),
|
||||
RemoteCredentialMasking::Disabled,
|
||||
serde_json::json!({}),
|
||||
),
|
||||
(
|
||||
serde_json::json!({ "credentialMasking": true }),
|
||||
RemoteCredentialMasking::BuiltInOnly,
|
||||
serde_json::json!({ "credentialMasking": true }),
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"credentialMasking": true,
|
||||
"credentialMaskingCustomProviders": false,
|
||||
}),
|
||||
RemoteCredentialMasking::BuiltInOnly,
|
||||
serde_json::json!({ "credentialMasking": true }),
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"credentialMasking": true,
|
||||
"credentialMaskingCustomProviders": true,
|
||||
}),
|
||||
RemoteCredentialMasking::WithConfiguredProviders,
|
||||
serde_json::json!({
|
||||
"credentialMasking": true,
|
||||
"credentialMaskingCustomProviders": true,
|
||||
}),
|
||||
),
|
||||
(
|
||||
serde_json::json!({ "credentialMaskingCustomProviders": true }),
|
||||
RemoteCredentialMasking::Disabled,
|
||||
serde_json::json!({ "credentialMaskingCustomProviders": true }),
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"credentialMasking": false,
|
||||
"credentialMaskingCustomProviders": true,
|
||||
}),
|
||||
RemoteCredentialMasking::Disabled,
|
||||
serde_json::json!({ "credentialMaskingCustomProviders": true }),
|
||||
),
|
||||
] {
|
||||
for launch in [None, Some(false), Some(true)] {
|
||||
let mut input = serde_json::json!({
|
||||
"capabilityDiscoverySandbox": true,
|
||||
});
|
||||
if let Some(launch) = launch {
|
||||
input["networkProxyLaunch"] = launch.into();
|
||||
}
|
||||
input
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.extend(input_masking.as_object().unwrap().clone());
|
||||
let decoded: EnvironmentCapabilities = serde_json::from_value(input).unwrap();
|
||||
assert_eq!(
|
||||
decoded.network_proxy(),
|
||||
NetworkProxyCapabilities {
|
||||
launch: launch.unwrap_or_default(),
|
||||
credential_masking: expected_masking,
|
||||
}
|
||||
);
|
||||
let mut expected_wire = serde_json::json!({
|
||||
"networkProxyLaunch": launch.unwrap_or_default(),
|
||||
"capabilityDiscoverySandbox": true,
|
||||
"capabilityDiscoveryV2": false,
|
||||
"environmentConfigRead": false,
|
||||
"httpHeaderEnvVars": false,
|
||||
"sandboxedFileStreaming": false,
|
||||
"fileWriteStreaming": false,
|
||||
"shellSnapshotV2": false,
|
||||
"windowsMxc": false,
|
||||
});
|
||||
expected_wire
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.extend(output_masking.as_object().unwrap().clone());
|
||||
assert_eq!(serde_json::to_value(decoded).unwrap(), expected_wire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user