chore: Nest skill and protocol network permissions under network.enabled (#13427)

## Summary

Changes the permission profile shape from a bare network boolean to a
nested object.

Before:

```yaml
permissions:
  network: true
```

After:

```yaml
permissions:
  network:
    enabled: true
```

This also updates the shared Rust and app-server protocol types so
`PermissionProfile.network` is no longer `Option<bool>`, but
`Option<NetworkPermissions>` with `enabled: Option<bool>`.

## What Changed

- Updated `PermissionProfile` in `codex-rs/protocol/src/models.rs`:
- `pub network: Option<bool>` -> `pub network:
Option<NetworkPermissions>`
- Added `NetworkPermissions` with:
  - `pub enabled: Option<bool>`
- Changed emptiness semantics so `network` is only considered empty when
`enabled` is `None`
- Updated skill metadata parsing to accept `permissions.network.enabled`
- Updated core permission consumers to read
`network.enabled.unwrap_or(false)` where a concrete boolean is needed
- Updated app-server v2 protocol types and regenerated schema/TypeScript
outputs
- Updated docs to mention `additionalPermissions.network.enabled`
This commit is contained in:
Celia Chen
2026-03-03 20:57:29 -08:00
committed by GitHub
parent 2e154a35bc
commit d622bff384
19 changed files with 216 additions and 40 deletions
+19 -4
View File
@@ -193,7 +193,12 @@ fn merge_network_access(
base_network_access: bool,
additional_permissions: &PermissionProfile,
) -> bool {
base_network_access || matches!(additional_permissions.network, Some(true))
base_network_access
|| additional_permissions
.network
.as_ref()
.and_then(|network| network.enabled)
.unwrap_or(false)
}
fn sandbox_policy_with_additional_permissions(
@@ -431,6 +436,7 @@ mod tests {
use crate::tools::sandboxing::SandboxablePreference;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use dunce::canonicalize;
@@ -470,7 +476,9 @@ mod tests {
)
.expect("absolute temp dir");
let permissions = normalize_additional_permissions(PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
read: Some(vec![path.clone()]),
write: Some(vec![path.clone()]),
@@ -479,7 +487,12 @@ mod tests {
})
.expect("permissions");
assert_eq!(permissions.network, Some(true));
assert_eq!(
permissions.network,
Some(NetworkPermissions {
enabled: Some(true),
})
);
assert_eq!(
permissions.file_system,
Some(FileSystemPermissions {
@@ -505,7 +518,9 @@ mod tests {
network_access: false,
},
&PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
read: Some(vec![path.clone()]),
write: Some(Vec::new()),
+7 -2
View File
@@ -32,6 +32,8 @@ use tracing::error;
#[cfg(test)]
use crate::config::Config;
#[cfg(test)]
use codex_protocol::models::NetworkPermissions;
#[derive(Debug, Deserialize)]
struct SkillFrontmatter {
@@ -1387,7 +1389,8 @@ policy: {}
skill_dir,
r#"
permissions:
network: true
network:
enabled: true
file_system:
read:
- "./data"
@@ -1408,7 +1411,9 @@ permissions:
assert_eq!(
outcome.skills[0].permission_profile,
Some(PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
read: Some(vec![
AbsolutePathBuf::try_from(normalized(skill_dir.join("data").as_path()))
+11 -4
View File
@@ -44,7 +44,7 @@ pub(crate) fn compile_permission_profile(
file_system,
macos,
} = permissions?;
let network_access = network.unwrap_or_default();
let network_access = network.and_then(|value| value.enabled).unwrap_or_default();
let file_system = file_system.unwrap_or_default();
let fs_read = normalize_permission_paths(
file_system.read.as_deref().unwrap_or_default(),
@@ -232,6 +232,7 @@ mod tests {
use codex_protocol::models::MacOsPermissions;
#[cfg(target_os = "macos")]
use codex_protocol::models::MacOsPreferencesValue;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
@@ -251,7 +252,9 @@ mod tests {
fs::create_dir_all(&read_dir).expect("read dir");
let profile = compile_permission_profile(Some(PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
read: Some(vec![
absolute_path(&skill_dir.join("data")),
@@ -318,7 +321,9 @@ mod tests {
fs::create_dir_all(&skill_dir).expect("skill dir");
let profile = compile_permission_profile(Some(PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
..Default::default()
}))
.expect("profile");
@@ -353,7 +358,9 @@ mod tests {
fs::create_dir_all(&read_dir).expect("read dir");
let profile = compile_permission_profile(Some(PermissionProfile {
network: Some(true),
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
read: Some(vec![absolute_path(&skill_dir.join("data"))]),
write: Some(Vec::new()),