mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
## Why Forwarded network policy decisions are audit telemetry and should not be written to the persistent state log. ## What changed - Emit forwarded `codex.network_proxy.policy_decision` events on the `codex_otel.log_only` target, keeping them available to OTEL log export while excluding them from persistent logs. - Assert the log-only target in the existing trusted-metadata audit test. GitOrigin-RevId: 16d07c8f390245036e4fa092a876012ef2b2fdba
82 lines
3.6 KiB
Rust
82 lines
3.6 KiB
Rust
use super::NetworkPolicyAuditContext;
|
|
use crate::protocol::ExecServerNetworkProtocol;
|
|
use crate::protocol::MAX_NETWORK_POLICY_HOST_BYTES;
|
|
use crate::protocol::MAX_NETWORK_POLICY_PROCESS_ID_BYTES;
|
|
use crate::protocol::MAX_NETWORK_POLICY_REASON_BYTES;
|
|
use crate::protocol::NetworkPolicyDecisionNotification;
|
|
|
|
const MAX_NETWORK_POLICY_METHOD_BYTES: usize = 32;
|
|
const MAX_NETWORK_POLICY_CLIENT_BYTES: usize = 256;
|
|
const MAX_NETWORK_POLICY_TIMESTAMP_BYTES: usize = 64;
|
|
|
|
pub(super) fn emit_network_policy_decision(
|
|
context: &NetworkPolicyAuditContext,
|
|
decision: &NetworkPolicyDecisionNotification,
|
|
) -> bool {
|
|
if decision.process_id.is_empty()
|
|
|| decision.process_id.len() > MAX_NETWORK_POLICY_PROCESS_ID_BYTES
|
|
|| decision.host.is_empty()
|
|
|| decision.host.len() > MAX_NETWORK_POLICY_HOST_BYTES
|
|
|| decision.host.chars().any(char::is_control)
|
|
|| decision.host.chars().any(char::is_whitespace)
|
|
|| decision.reason.len() > MAX_NETWORK_POLICY_REASON_BYTES
|
|
|| decision.reason.chars().any(char::is_control)
|
|
|| !matches!(decision.scope.as_str(), "domain" | "non_domain")
|
|
|| !matches!(decision.decision.as_str(), "allow" | "deny" | "ask")
|
|
|| !matches!(
|
|
decision.source.as_str(),
|
|
"baseline_policy" | "mode_guard" | "proxy_state" | "decider"
|
|
)
|
|
|| decision.timestamp.is_empty()
|
|
|| decision.timestamp.len() > MAX_NETWORK_POLICY_TIMESTAMP_BYTES
|
|
|| decision.timestamp.chars().any(char::is_control)
|
|
|| decision.method.as_ref().is_some_and(|method| {
|
|
method.len() > MAX_NETWORK_POLICY_METHOD_BYTES
|
|
|| method.chars().any(char::is_control)
|
|
|| method.chars().any(char::is_whitespace)
|
|
})
|
|
|| decision.client.as_ref().is_some_and(|client| {
|
|
client.len() > MAX_NETWORK_POLICY_CLIENT_BYTES
|
|
|| client.chars().any(char::is_control)
|
|
|| client.chars().any(char::is_whitespace)
|
|
})
|
|
{
|
|
return false;
|
|
}
|
|
|
|
let protocol = match decision.protocol {
|
|
ExecServerNetworkProtocol::Http => "http",
|
|
ExecServerNetworkProtocol::HttpsConnect => "https_connect",
|
|
ExecServerNetworkProtocol::Socks5Tcp => "socks5_tcp",
|
|
ExecServerNetworkProtocol::Socks5Udp => "socks5_udp",
|
|
};
|
|
let metadata = &context.metadata;
|
|
tracing::event!(
|
|
target: "codex_otel.log_only",
|
|
tracing::Level::INFO,
|
|
event.name = "codex.network_proxy.policy_decision",
|
|
event.timestamp = decision.timestamp,
|
|
conversation.id = metadata.conversation_id.as_deref(),
|
|
app.version = metadata.app_version.as_deref(),
|
|
auth_mode = metadata.auth_mode.as_deref(),
|
|
originator = metadata.originator.as_deref(),
|
|
user.account_id = metadata.user_account_id.as_deref(),
|
|
user.email = metadata.user_email.as_deref(),
|
|
terminal.type = metadata.terminal_type.as_deref(),
|
|
model = metadata.model.as_deref(),
|
|
slug = metadata.slug.as_deref(),
|
|
network.policy.scope = decision.scope,
|
|
network.policy.decision = decision.decision,
|
|
network.policy.source = decision.source,
|
|
network.policy.reason = decision.reason,
|
|
network.transport.protocol = protocol,
|
|
server.address = decision.host,
|
|
server.port = decision.port,
|
|
http.request.method = decision.method.as_deref().unwrap_or("none"),
|
|
client.address = decision.client.as_deref().unwrap_or("unknown"),
|
|
execution.id = context.execution_id.as_deref(),
|
|
network.policy.override = decision.policy_override,
|
|
);
|
|
true
|
|
}
|