permissions: make profiles represent enforcement

This commit is contained in:
Michael Bolin
2026-04-23 16:46:10 -07:00
parent a9f75e5cda
commit 766bd872cc
70 changed files with 1907 additions and 834 deletions
+4 -4
View File
@@ -23,7 +23,7 @@ use codex_app_server_protocol::CodexErrorInfo;
use codex_login::default_client::originator;
use codex_plugin::PluginTelemetryMetadata;
use codex_protocol::approvals::NetworkApprovalProtocol;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::SandboxPermissions;
use codex_protocol::protocol::GuardianAssessmentOutcome;
use codex_protocol::protocol::GuardianCommandSource;
@@ -180,17 +180,17 @@ pub enum GuardianApprovalRequestSource {
pub enum GuardianReviewedAction {
Shell {
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
},
UnifiedExec {
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
tty: bool,
},
Execve {
source: GuardianCommandSource,
program: String,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
},
ApplyPatch {},
NetworkAccess {
+109 -38
View File
@@ -1892,61 +1892,132 @@
"type": "string"
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -78,7 +78,8 @@
{
"type": "null"
}
]
],
"description": "Partial overlay used for per-command permission requests."
}
},
"type": "object"
+2 -1
View File
@@ -78,7 +78,8 @@
{
"type": "null"
}
]
],
"description": "Partial overlay used for per-command permission requests."
}
},
"type": "object"
@@ -25,7 +25,8 @@
{
"type": "null"
}
]
],
"description": "Partial overlay used for per-command permission requests."
}
},
"type": "object"
@@ -11269,61 +11270,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/v2/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/v2/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/v2/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/v2/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/v2/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -14544,7 +14616,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -15987,7 +16059,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -16314,7 +16386,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -7984,61 +7984,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -12431,7 +12502,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -13874,7 +13945,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -14201,7 +14272,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -246,61 +246,132 @@
"type": "string"
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"ReadOnlyAccess": {
@@ -276,61 +276,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"SandboxMode": {
@@ -900,61 +900,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"ReadOnlyAccess": {
@@ -2506,7 +2577,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -542,61 +542,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -900,61 +900,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"ReadOnlyAccess": {
@@ -2506,7 +2577,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
@@ -302,61 +302,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -900,61 +900,132 @@
]
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"ReadOnlyAccess": {
@@ -2506,7 +2577,7 @@
}
],
"default": null,
"description": "Canonical active permissions view for this thread when representable. This is `null` for external sandbox policies because external enforcement cannot be round-tripped as a `PermissionProfile`."
"description": "Canonical active permissions view for this thread."
},
"reasoningEffort": {
"anyOf": [
+109 -38
View File
@@ -326,61 +326,132 @@
"type": "string"
},
"PermissionProfile": {
"properties": {
"fileSystem": {
"anyOf": [
{
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
{
"type": "null"
}
]
},
"network": {
"anyOf": [
{
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
{
"type": "null"
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
]
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
},
"type": "object"
]
},
"PermissionProfileFileSystemPermissions": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"type": "array"
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
},
"required": [
"entries"
],
"type": "object"
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": [
"boolean",
"null"
]
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"Personality": {
@@ -4,4 +4,8 @@
import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions";
import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions";
export type AdditionalPermissionProfile = { network: AdditionalNetworkPermissions | null, fileSystem: AdditionalFileSystemPermissions | null, };
export type AdditionalPermissionProfile = {
/**
* Partial overlay used for per-command permission requests.
*/
network: AdditionalNetworkPermissions | null, fileSystem: AdditionalFileSystemPermissions | null, };
@@ -4,4 +4,4 @@
import type { PermissionProfileFileSystemPermissions } from "./PermissionProfileFileSystemPermissions";
import type { PermissionProfileNetworkPermissions } from "./PermissionProfileNetworkPermissions";
export type PermissionProfile = { network: PermissionProfileNetworkPermissions | null, fileSystem: PermissionProfileFileSystemPermissions | null, };
export type PermissionProfile = { "type": "managed", network: PermissionProfileNetworkPermissions, fileSystem: PermissionProfileFileSystemPermissions, } | { "type": "disabled" } | { "type": "external", network: PermissionProfileNetworkPermissions, };
@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry";
export type PermissionProfileFileSystemPermissions = { entries: Array<FileSystemSandboxEntry>, globScanMaxDepth?: number, };
export type PermissionProfileFileSystemPermissions = { "type": "restricted", entries: Array<FileSystemSandboxEntry>, globScanMaxDepth?: number, } | { "type": "unrestricted" };
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PermissionProfileNetworkPermissions = { enabled: boolean | null, };
export type PermissionProfileNetworkPermissions = { enabled: boolean, };
@@ -26,8 +26,6 @@ approvalsReviewer: ApprovalsReviewer,
*/
sandbox: SandboxPolicy,
/**
* Canonical active permissions view for this thread when representable.
* This is `null` for external sandbox policies because external
* enforcement cannot be round-tripped as a `PermissionProfile`.
* Canonical active permissions view for this thread.
*/
permissionProfile: PermissionProfile | null, reasoningEffort: ReasoningEffort | null, };
@@ -26,8 +26,6 @@ approvalsReviewer: ApprovalsReviewer,
*/
sandbox: SandboxPolicy,
/**
* Canonical active permissions view for this thread when representable.
* This is `null` for external sandbox policies because external
* enforcement cannot be round-tripped as a `PermissionProfile`.
* Canonical active permissions view for this thread.
*/
permissionProfile: PermissionProfile | null, reasoningEffort: ReasoningEffort | null, };
@@ -26,8 +26,6 @@ approvalsReviewer: ApprovalsReviewer,
*/
sandbox: SandboxPolicy,
/**
* Canonical active permissions view for this thread when representable.
* This is `null` for external sandbox policies because external
* enforcement cannot be round-tripped as a `PermissionProfile`.
* Canonical active permissions view for this thread.
*/
permissionProfile: PermissionProfile | null, reasoningEffort: ReasoningEffort | null, };
+108 -61
View File
@@ -37,7 +37,9 @@ use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
use codex_protocol::mcp::Tool as McpTool;
use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation;
use codex_protocol::memory_citation::MemoryCitationEntry as CoreMemoryCitationEntry;
use codex_protocol::models::AdditionalPermissionProfile as CoreAdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions as CoreFileSystemPermissions;
use codex_protocol::models::ManagedFileSystemPermissions as CoreManagedFileSystemPermissions;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::NetworkPermissions as CoreNetworkPermissions;
use codex_protocol::models::PermissionProfile as CorePermissionProfile;
@@ -51,6 +53,7 @@ use codex_protocol::permissions::FileSystemAccessMode as CoreFileSystemAccessMod
use codex_protocol::permissions::FileSystemPath as CoreFileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry as CoreFileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSpecialPath as CoreFileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy as CoreNetworkSandboxPolicy;
use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg;
use codex_protocol::plan_tool::StepStatus as CorePlanStepStatus;
use codex_protocol::protocol::AgentStatus as CoreAgentStatus;
@@ -1355,7 +1358,7 @@ pub struct AdditionalNetworkPermissions {
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct PermissionProfileNetworkPermissions {
pub enabled: Option<bool>,
pub enabled: bool,
}
impl From<CoreNetworkPermissions> for AdditionalNetworkPermissions {
@@ -1374,18 +1377,20 @@ impl From<AdditionalNetworkPermissions> for CoreNetworkPermissions {
}
}
impl From<CoreNetworkPermissions> for PermissionProfileNetworkPermissions {
fn from(value: CoreNetworkPermissions) -> Self {
impl From<CoreNetworkSandboxPolicy> for PermissionProfileNetworkPermissions {
fn from(value: CoreNetworkSandboxPolicy) -> Self {
Self {
enabled: value.enabled,
enabled: value.is_enabled(),
}
}
}
impl From<PermissionProfileNetworkPermissions> for CoreNetworkPermissions {
impl From<PermissionProfileNetworkPermissions> for CoreNetworkSandboxPolicy {
fn from(value: PermissionProfileNetworkPermissions) -> Self {
Self {
enabled: value.enabled,
if value.enabled {
Self::Enabled
} else {
Self::Restricted
}
}
}
@@ -1533,65 +1538,111 @@ impl From<FileSystemSandboxEntry> for CoreFileSystemSandboxEntry {
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type", rename_all = "camelCase")]
#[ts(tag = "type")]
#[ts(export_to = "v2/")]
pub struct PermissionProfileFileSystemPermissions {
pub entries: Vec<FileSystemSandboxEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub glob_scan_max_depth: Option<NonZeroUsize>,
pub enum PermissionProfileFileSystemPermissions {
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Restricted {
entries: Vec<FileSystemSandboxEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
glob_scan_max_depth: Option<NonZeroUsize>,
},
Unrestricted,
}
impl From<CoreFileSystemPermissions> for PermissionProfileFileSystemPermissions {
fn from(value: CoreFileSystemPermissions) -> Self {
Self {
entries: value
.entries
.into_iter()
.map(FileSystemSandboxEntry::from)
.collect(),
glob_scan_max_depth: value.glob_scan_max_depth,
impl From<CoreManagedFileSystemPermissions> for PermissionProfileFileSystemPermissions {
fn from(value: CoreManagedFileSystemPermissions) -> Self {
match value {
CoreManagedFileSystemPermissions::Restricted {
entries,
glob_scan_max_depth,
} => Self::Restricted {
entries: entries
.into_iter()
.map(FileSystemSandboxEntry::from)
.collect(),
glob_scan_max_depth,
},
CoreManagedFileSystemPermissions::Unrestricted => Self::Unrestricted,
}
}
}
impl From<PermissionProfileFileSystemPermissions> for CoreFileSystemPermissions {
impl From<PermissionProfileFileSystemPermissions> for CoreManagedFileSystemPermissions {
fn from(value: PermissionProfileFileSystemPermissions) -> Self {
Self {
entries: value
.entries
.into_iter()
.map(CoreFileSystemSandboxEntry::from)
.collect(),
glob_scan_max_depth: value.glob_scan_max_depth,
match value {
PermissionProfileFileSystemPermissions::Restricted {
entries,
glob_scan_max_depth,
} => Self::Restricted {
entries: entries
.into_iter()
.map(CoreFileSystemSandboxEntry::from)
.collect(),
glob_scan_max_depth,
},
PermissionProfileFileSystemPermissions::Unrestricted => Self::Unrestricted,
}
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
#[ts(tag = "type")]
#[ts(export_to = "v2/")]
pub struct PermissionProfile {
pub network: Option<PermissionProfileNetworkPermissions>,
pub file_system: Option<PermissionProfileFileSystemPermissions>,
pub enum PermissionProfile {
/// Codex owns sandbox construction for this profile.
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
Managed {
network: PermissionProfileNetworkPermissions,
file_system: PermissionProfileFileSystemPermissions,
},
/// Do not apply an outer sandbox.
Disabled,
/// Filesystem isolation is enforced by an external caller.
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
External {
network: PermissionProfileNetworkPermissions,
},
}
impl From<CorePermissionProfile> for PermissionProfile {
fn from(value: CorePermissionProfile) -> Self {
Self {
network: value.network.map(PermissionProfileNetworkPermissions::from),
file_system: value
.file_system
.map(PermissionProfileFileSystemPermissions::from),
match value {
CorePermissionProfile::Managed {
file_system,
network,
} => Self::Managed {
network: network.into(),
file_system: file_system.into(),
},
CorePermissionProfile::Disabled => Self::Disabled,
CorePermissionProfile::External { network } => Self::External {
network: network.into(),
},
}
}
}
impl From<PermissionProfile> for CorePermissionProfile {
fn from(value: PermissionProfile) -> Self {
Self {
network: value.network.map(CoreNetworkPermissions::from),
file_system: value.file_system.map(CoreFileSystemPermissions::from),
match value {
PermissionProfile::Managed {
file_system,
network,
} => Self::Managed {
file_system: file_system.into(),
network: network.into(),
},
PermissionProfile::Disabled => Self::Disabled,
PermissionProfile::External { network } => Self::External {
network: network.into(),
},
}
}
}
@@ -1600,12 +1651,13 @@ impl From<PermissionProfile> for CorePermissionProfile {
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct AdditionalPermissionProfile {
/// Partial overlay used for per-command permission requests.
pub network: Option<AdditionalNetworkPermissions>,
pub file_system: Option<AdditionalFileSystemPermissions>,
}
impl From<CorePermissionProfile> for AdditionalPermissionProfile {
fn from(value: CorePermissionProfile) -> Self {
impl From<CoreAdditionalPermissionProfile> for AdditionalPermissionProfile {
fn from(value: CoreAdditionalPermissionProfile) -> Self {
Self {
network: value.network.map(AdditionalNetworkPermissions::from),
file_system: value.file_system.map(AdditionalFileSystemPermissions::from),
@@ -1613,7 +1665,7 @@ impl From<CorePermissionProfile> for AdditionalPermissionProfile {
}
}
impl From<AdditionalPermissionProfile> for CorePermissionProfile {
impl From<AdditionalPermissionProfile> for CoreAdditionalPermissionProfile {
fn from(value: AdditionalPermissionProfile) -> Self {
Self {
network: value.network.map(CoreNetworkPermissions::from),
@@ -1634,7 +1686,7 @@ pub struct GrantedPermissionProfile {
pub file_system: Option<AdditionalFileSystemPermissions>,
}
impl From<GrantedPermissionProfile> for CorePermissionProfile {
impl From<GrantedPermissionProfile> for CoreAdditionalPermissionProfile {
fn from(value: GrantedPermissionProfile) -> Self {
Self {
network: value.network.map(CoreNetworkPermissions::from),
@@ -3355,9 +3407,7 @@ pub struct ThreadStartResponse {
/// `permissionProfile` when present as the canonical active permissions
/// view.
pub sandbox: SandboxPolicy,
/// Canonical active permissions view for this thread when representable.
/// This is `null` for external sandbox policies because external
/// enforcement cannot be round-tripped as a `PermissionProfile`.
/// Canonical active permissions view for this thread.
#[serde(default)]
pub permission_profile: Option<PermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
@@ -3461,9 +3511,7 @@ pub struct ThreadResumeResponse {
/// `permissionProfile` when present as the canonical active permissions
/// view.
pub sandbox: SandboxPolicy,
/// Canonical active permissions view for this thread when representable.
/// This is `null` for external sandbox policies because external
/// enforcement cannot be round-tripped as a `PermissionProfile`.
/// Canonical active permissions view for this thread.
#[serde(default)]
pub permission_profile: Option<PermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
@@ -3558,9 +3606,7 @@ pub struct ThreadForkResponse {
/// `permissionProfile` when present as the canonical active permissions
/// view.
pub sandbox: SandboxPolicy,
/// Canonical active permissions view for this thread when representable.
/// This is `null` for external sandbox policies because external
/// enforcement cannot be round-tripped as a `PermissionProfile`.
/// Canonical active permissions view for this thread.
#[serde(default)]
pub permission_profile: Option<PermissionProfile>,
pub reasoning_effort: Option<ReasoningEffort>,
@@ -7868,7 +7914,7 @@ mod tests {
#[test]
fn permission_profile_file_system_permissions_preserves_glob_scan_depth() {
let core_permissions = CoreFileSystemPermissions {
let core_permissions = CoreManagedFileSystemPermissions::Restricted {
entries: vec![CoreFileSystemSandboxEntry {
path: CoreFileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
@@ -7882,7 +7928,7 @@ mod tests {
assert_eq!(
permissions,
PermissionProfileFileSystemPermissions {
PermissionProfileFileSystemPermissions::Restricted {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
@@ -7893,7 +7939,7 @@ mod tests {
}
);
assert_eq!(
CoreFileSystemPermissions::from(permissions),
CoreManagedFileSystemPermissions::from(permissions),
core_permissions
);
}
@@ -7901,6 +7947,7 @@ mod tests {
#[test]
fn permission_profile_file_system_permissions_rejects_zero_glob_scan_depth() {
serde_json::from_value::<PermissionProfileFileSystemPermissions>(json!({
"type": "restricted",
"entries": [],
"globScanMaxDepth": 0,
}))
@@ -7954,8 +8001,8 @@ mod tests {
);
assert_eq!(
CorePermissionProfile::from(response.permissions),
CorePermissionProfile {
CoreAdditionalPermissionProfile::from(response.permissions),
CoreAdditionalPermissionProfile {
network: Some(CoreNetworkPermissions {
enabled: Some(true),
}),
@@ -122,7 +122,7 @@ use codex_protocol::ThreadId;
use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem;
use codex_protocol::dynamic_tools::DynamicToolResponse as CoreDynamicToolResponse;
use codex_protocol::items::parse_hook_prompt_message;
use codex_protocol::models::PermissionProfile as CorePermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile as CoreAdditionalPermissionProfile;
use codex_protocol::plan_tool::UpdatePlanArgs;
use codex_protocol::protocol::CodexErrorInfo as CoreCodexErrorInfo;
use codex_protocol::protocol::Event;
@@ -2719,7 +2719,7 @@ fn request_permissions_response_from_client_result(
strict_auto_review: false,
});
}
let granted_permissions: CorePermissionProfile = response.permissions.into();
let granted_permissions: CoreAdditionalPermissionProfile = response.permissions.into();
let permissions = if granted_permissions.is_empty() {
CoreRequestPermissionProfile::default()
} else {
@@ -2800,10 +2800,8 @@ impl CodexMessageProcessor {
/*has_in_progress_turn*/ false,
);
let permission_profile = thread_response_permission_profile(
&config_snapshot.sandbox_policy,
config_snapshot.permission_profile,
);
let permission_profile =
thread_response_permission_profile(config_snapshot.permission_profile);
let response = ThreadStartResponse {
thread: thread.clone(),
@@ -4608,7 +4606,6 @@ impl CodexMessageProcessor {
/*has_live_in_progress_turn*/ false,
);
let permission_profile = thread_response_permission_profile(
&session_configured.sandbox_policy,
codex_thread.config_snapshot().await.permission_profile,
);
@@ -5279,7 +5276,6 @@ impl CodexMessageProcessor {
/*has_in_progress_turn*/ false,
);
let permission_profile = thread_response_permission_profile(
&session_configured.sandbox_policy,
forked_thread.config_snapshot().await.permission_profile,
);
@@ -8794,8 +8790,7 @@ async fn handle_pending_thread_resume_request(
..
} = pending.config_snapshot;
let instruction_sources = pending.instruction_sources;
let permission_profile =
thread_response_permission_profile(&sandbox_policy, permission_profile);
let permission_profile = thread_response_permission_profile(permission_profile);
let response = ThreadResumeResponse {
thread,
@@ -9925,17 +9920,9 @@ fn with_thread_spawn_agent_metadata(
}
fn thread_response_permission_profile(
sandbox_policy: &codex_protocol::protocol::SandboxPolicy,
permission_profile: codex_protocol::models::PermissionProfile,
) -> Option<codex_app_server_protocol::PermissionProfile> {
match sandbox_policy {
codex_protocol::protocol::SandboxPolicy::DangerFullAccess
| codex_protocol::protocol::SandboxPolicy::ReadOnly { .. }
| codex_protocol::protocol::SandboxPolicy::WorkspaceWrite { .. } => {
Some(permission_profile.into())
}
codex_protocol::protocol::SandboxPolicy::ExternalSandbox { .. } => None,
}
Some(permission_profile.into())
}
fn requested_permissions_trust_project(overrides: &ConfigOverrides, cwd: &Path) -> bool {
@@ -10457,25 +10444,28 @@ mod tests {
}
#[test]
fn thread_response_permission_profile_omits_external_sandbox() {
fn thread_response_permission_profile_preserves_enforcement() {
let cwd = test_path_buf("/tmp").abs();
let profile = codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::DangerFullAccess,
cwd.as_path(),
);
assert_eq!(
thread_response_permission_profile(
let full_access_profile =
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::DangerFullAccess,
cwd.as_path(),
);
let external_profile =
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::ExternalSandbox {
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
},
profile.clone(),
),
None
cwd.as_path(),
);
assert_eq!(
thread_response_permission_profile(external_profile.clone()),
Some(external_profile.into())
);
assert_eq!(
thread_response_permission_profile(&SandboxPolicy::DangerFullAccess, profile.clone()),
Some(profile.into())
thread_response_permission_profile(full_access_profile.clone()),
Some(full_access_profile.into())
);
}
@@ -256,17 +256,18 @@ async fn command_exec_permission_profile_cwd_uses_command_cwd() -> Result<()> {
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let mut permission_profile = root_read_only_permission_profile();
permission_profile
.file_system
.as_mut()
.expect("root read-only helper should include filesystem permissions")
.entries
.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::CurrentWorkingDirectory,
},
access: FileSystemAccessMode::Write,
});
let PermissionProfile::Managed { file_system, .. } = &mut permission_profile else {
panic!("root read-only helper should use managed permissions");
};
let PermissionProfileFileSystemPermissions::Restricted { entries, .. } = file_system else {
panic!("root read-only helper should use restricted filesystem permissions");
};
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::CurrentWorkingDirectory,
},
access: FileSystemAccessMode::Write,
});
let command_request_id = mcp
.send_command_exec_request(CommandExecParams {
@@ -1061,11 +1062,9 @@ fn decode_delta_notification(
}
fn root_read_only_permission_profile() -> PermissionProfile {
PermissionProfile {
network: Some(PermissionProfileNetworkPermissions {
enabled: Some(false),
}),
file_system: Some(PermissionProfileFileSystemPermissions {
PermissionProfile::Managed {
network: PermissionProfileNetworkPermissions { enabled: false },
file_system: PermissionProfileFileSystemPermissions::Restricted {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
@@ -1073,7 +1072,7 @@ fn root_read_only_permission_profile() -> PermissionProfile {
access: FileSystemAccessMode::Read,
}],
glob_scan_max_depth: None,
}),
},
}
}
@@ -39,6 +39,7 @@ use codex_app_server_protocol::PatchApplyStatus;
use codex_app_server_protocol::PatchChangeKind;
use codex_app_server_protocol::PermissionProfile;
use codex_app_server_protocol::PermissionProfileFileSystemPermissions;
use codex_app_server_protocol::PermissionProfileNetworkPermissions;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerRequest;
use codex_app_server_protocol::ServerRequestResolvedNotification;
@@ -779,9 +780,9 @@ async fn turn_start_rejects_invalid_permission_profile_before_starting_turn() ->
text: "Hello".to_string(),
text_elements: Vec::new(),
}],
permission_profile: Some(PermissionProfile {
network: None,
file_system: Some(PermissionProfileFileSystemPermissions {
permission_profile: Some(PermissionProfile::Managed {
network: PermissionProfileNetworkPermissions { enabled: false },
file_system: PermissionProfileFileSystemPermissions::Restricted {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: unsupported_write_root,
@@ -789,7 +790,7 @@ async fn turn_start_rejects_invalid_permission_profile_before_starting_turn() ->
access: FileSystemAccessMode::Write,
}],
glob_scan_max_depth: None,
}),
},
}),
..Default::default()
})
+2 -30
View File
@@ -55,8 +55,6 @@ use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
use codex_model_provider_info::OLLAMA_OSS_PROVIDER_ID;
use codex_model_provider_info::WireApi;
use codex_models_manager::bundled_models_response;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
@@ -810,20 +808,7 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::
async fn permission_profile_override_populates_runtime_permissions() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
let permission_profile = PermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
}],
glob_scan_max_depth: None,
}),
};
let permission_profile = PermissionProfile::Disabled;
let config = Config::load_from_base_config_with_overrides(
ConfigToml::default(),
@@ -848,20 +833,7 @@ async fn permission_profile_override_populates_runtime_permissions() -> std::io:
async fn permission_profile_override_preserves_configured_network_proxy() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
let permission_profile = PermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
file_system: Some(FileSystemPermissions {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
}],
glob_scan_max_depth: None,
}),
};
let permission_profile = PermissionProfile::Disabled;
let config = Config::load_from_base_config_with_overrides(
ConfigToml {
+6 -4
View File
@@ -79,6 +79,7 @@ use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::permissions::FileSystemSandboxPolicy;
@@ -221,7 +222,8 @@ impl Permissions {
/// Effective runtime permissions after config requirements and runtime
/// readable-root additions have been applied.
pub fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(self.sandbox_policy.get()),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
@@ -1773,9 +1775,9 @@ impl Config {
})?;
let profile = resolve_permission_profile(permissions, default_permissions)?;
// PermissionProfile only carries the network enabled bit today. Keep the
// configured proxy/allowlist policy so active profiles can round-trip without
// broadening network behavior.
// PermissionProfile carries the active network sandbox bit, not the configured
// proxy/allowlist policy. Keep that config so active profiles can round-trip
// without broadening network behavior.
network_proxy_config_from_profile_network(profile.network.as_ref())
} else {
NetworkProxyConfig::default()
@@ -4,7 +4,7 @@ use codex_analytics::GuardianReviewedAction;
use codex_protocol::approvals::GuardianAssessmentAction;
use codex_protocol::approvals::GuardianCommandSource;
use codex_protocol::approvals::NetworkApprovalProtocol;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::request_permissions::RequestPermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde::Serialize;
@@ -20,7 +20,7 @@ pub(crate) enum GuardianApprovalRequest {
command: Vec<String>,
cwd: AbsolutePathBuf,
sandbox_permissions: crate::sandboxing::SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
justification: Option<String>,
},
ExecCommand {
@@ -28,7 +28,7 @@ pub(crate) enum GuardianApprovalRequest {
command: Vec<String>,
cwd: AbsolutePathBuf,
sandbox_permissions: crate::sandboxing::SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
justification: Option<String>,
tty: bool,
},
@@ -39,7 +39,7 @@ pub(crate) enum GuardianApprovalRequest {
program: String,
argv: Vec<String>,
cwd: AbsolutePathBuf,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
},
ApplyPatch {
id: String,
@@ -85,7 +85,7 @@ pub(crate) struct GuardianNetworkAccessTrigger {
pub(crate) cwd: AbsolutePathBuf,
pub(crate) sandbox_permissions: crate::sandboxing::SandboxPermissions,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) additional_permissions: Option<PermissionProfile>,
pub(crate) additional_permissions: Option<AdditionalPermissionProfile>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) justification: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -109,7 +109,7 @@ struct CommandApprovalAction<'a> {
cwd: &'a Path,
sandbox_permissions: crate::sandboxing::SandboxPermissions,
#[serde(skip_serializing_if = "Option::is_none")]
additional_permissions: Option<&'a PermissionProfile>,
additional_permissions: Option<&'a AdditionalPermissionProfile>,
#[serde(skip_serializing_if = "Option::is_none")]
justification: Option<&'a String>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -124,7 +124,7 @@ struct ExecveApprovalAction<'a> {
argv: &'a [String],
cwd: &'a Path,
#[serde(skip_serializing_if = "Option::is_none")]
additional_permissions: Option<&'a PermissionProfile>,
additional_permissions: Option<&'a AdditionalPermissionProfile>,
}
#[derive(Serialize)]
@@ -178,7 +178,7 @@ fn serialize_command_guardian_action(
command: &[String],
cwd: &Path,
sandbox_permissions: crate::sandboxing::SandboxPermissions,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
justification: Option<&String>,
tty: Option<bool>,
) -> serde_json::Result<Value> {
+7 -5
View File
@@ -91,11 +91,12 @@ use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_protocol::items::TurnItem;
use codex_protocol::items::UserMessageItem;
use codex_protocol::mcp::CallToolResult;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::BaseInstructions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::models::format_allow_prefixes;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::FileChange;
@@ -1800,7 +1801,7 @@ impl Session {
reason: Option<String>,
network_approval_context: Option<NetworkApprovalContext>,
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
available_decisions: Option<Vec<ReviewDecision>>,
) -> ReviewDecision {
// command-level approvals use `call_id`.
@@ -2222,7 +2223,8 @@ impl Session {
PermissionGrantScope::Turn => {
if let Some(turn_state) = originating_turn_state {
let mut ts = turn_state.lock().await;
let permissions: PermissionProfile = response.permissions.clone().into();
let permissions: AdditionalPermissionProfile =
response.permissions.clone().into();
ts.record_granted_permissions(permissions);
if response.strict_auto_review {
ts.enable_strict_auto_review();
@@ -2240,7 +2242,7 @@ impl Session {
clippy::await_holding_invalid_type,
reason = "active turn reads must stay consistent with the matching turn state"
)]
pub(crate) async fn granted_turn_permissions(&self) -> Option<PermissionProfile> {
pub(crate) async fn granted_turn_permissions(&self) -> Option<AdditionalPermissionProfile> {
let active = self.active_turn.lock().await;
let active = active.as_ref()?;
let ts = active.turn_state.lock().await;
@@ -2260,7 +2262,7 @@ impl Session {
ts.strict_auto_review_enabled()
}
pub(crate) async fn granted_session_permissions(&self) -> Option<PermissionProfile> {
pub(crate) async fn granted_session_permissions(&self) -> Option<AdditionalPermissionProfile> {
let state = self.state.lock().await;
state.granted_permissions()
}
+3 -10
View File
@@ -92,7 +92,8 @@ impl SessionConfiguration {
}
pub(super) fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(self.sandbox_policy.get()),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
@@ -814,14 +815,6 @@ impl Session {
// Dispatch the SessionConfiguredEvent first and then report any errors.
// If resuming, include converted initial messages in the payload so UIs can render them immediately.
let initial_messages = initial_history.get_event_msgs();
let permission_profile = if matches!(
session_configuration.file_system_sandbox_policy.kind,
FileSystemSandboxKind::ExternalSandbox
) {
None
} else {
Some(session_configuration.permission_profile())
};
let events = std::iter::once(Event {
id: INITIAL_SUBMIT_ID.to_owned(),
msg: EventMsg::SessionConfigured(SessionConfiguredEvent {
@@ -834,7 +827,7 @@ impl Session {
approval_policy: session_configuration.approval_policy.value(),
approvals_reviewer: session_configuration.approvals_reviewer,
sandbox_policy: session_configuration.sandbox_policy.get().clone(),
permission_profile,
permission_profile: Some(session_configuration.permission_profile()),
cwd: session_configuration.cwd.clone(),
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
history_log_id,
+10 -4
View File
@@ -1471,7 +1471,8 @@ async fn record_initial_history_reconstructs_forked_transcript() {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn session_configured_omits_permission_profile_for_external_sandbox() -> anyhow::Result<()> {
async fn session_configured_reports_permission_profile_for_external_sandbox() -> anyhow::Result<()>
{
let server = start_mock_server().await;
let sandbox_policy = SandboxPolicy::ExternalSandbox {
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
@@ -1489,10 +1490,15 @@ async fn session_configured_omits_permission_profile_for_external_sandbox() -> a
test.session_configured.sandbox_policy,
expected_sandbox_policy
);
let expected_permission_profile =
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&expected_sandbox_policy,
test.session_configured.cwd.as_path(),
);
assert_eq!(
test.session_configured.permission_profile, None,
"ExternalSandbox is enforced outside the PermissionProfile model, so SessionConfigured must \
not expose a lossy root-write profile"
test.session_configured.permission_profile,
Some(expected_permission_profile),
"ExternalSandbox is represented explicitly instead of as a lossy root-write profile"
);
Ok(())
}
@@ -19,9 +19,9 @@ use codex_execpolicy::RuleMatch;
use codex_features::Feature;
use codex_model_provider::create_model_provider;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::ContentItem;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseItem;
use codex_protocol::models::function_call_output_content_items_to_text;
use codex_protocol::permissions::FileSystemSandboxPolicy;
+20 -7
View File
@@ -1,8 +1,11 @@
use super::*;
use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
@@ -89,7 +92,8 @@ pub(crate) struct TurnContext {
}
impl TurnContext {
pub(crate) fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
@@ -236,12 +240,21 @@ impl TurnContext {
pub(crate) fn file_system_sandbox_context(
&self,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
) -> FileSystemSandboxContext {
let base_permissions = self.permission_profile();
let permissions =
merge_permission_profiles(Some(&base_permissions), additional_permissions.as_ref())
.unwrap_or(base_permissions);
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
&self.file_system_sandbox_policy,
additional_permissions.as_ref(),
);
let network_sandbox_policy = effective_network_sandbox_policy(
self.network_sandbox_policy,
additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
FileSystemSandboxContext {
permissions,
cwd: Some(self.cwd.clone()),
+4 -4
View File
@@ -1,6 +1,6 @@
//! Session-wide mutable state.
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::ResponseItem;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use std::collections::HashMap;
@@ -32,7 +32,7 @@ pub(crate) struct SessionState {
pub(crate) startup_prewarm: Option<SessionStartupPrewarmHandle>,
pub(crate) active_connector_selection: HashSet<String>,
pub(crate) pending_session_start_source: Option<codex_hooks::SessionStartSource>,
granted_permissions: Option<PermissionProfile>,
granted_permissions: Option<AdditionalPermissionProfile>,
next_turn_is_first: bool,
}
@@ -218,12 +218,12 @@ impl SessionState {
self.pending_session_start_source.take()
}
pub(crate) fn record_granted_permissions(&mut self, permissions: PermissionProfile) {
pub(crate) fn record_granted_permissions(&mut self, permissions: AdditionalPermissionProfile) {
self.granted_permissions =
merge_permission_profiles(self.granted_permissions.as_ref(), Some(&permissions));
}
pub(crate) fn granted_permissions(&self) -> Option<PermissionProfile> {
pub(crate) fn granted_permissions(&self) -> Option<AdditionalPermissionProfile> {
self.granted_permissions.clone()
}
}
+4 -4
View File
@@ -21,7 +21,7 @@ use tokio::sync::oneshot;
use crate::session::turn_context::TurnContext;
use crate::tasks::AnySessionTask;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::TokenUsage;
@@ -105,7 +105,7 @@ pub(crate) struct TurnState {
pending_dynamic_tools: HashMap<String, oneshot::Sender<DynamicToolResponse>>,
pending_input: Vec<ResponseInputItem>,
mailbox_delivery_phase: MailboxDeliveryPhase,
granted_permissions: Option<PermissionProfile>,
granted_permissions: Option<AdditionalPermissionProfile>,
strict_auto_review_enabled: bool,
pub(crate) tool_calls: u64,
pub(crate) has_memory_citation: bool,
@@ -247,12 +247,12 @@ impl TurnState {
self.mailbox_delivery_phase = phase;
}
pub(crate) fn record_granted_permissions(&mut self, permissions: PermissionProfile) {
pub(crate) fn record_granted_permissions(&mut self, permissions: AdditionalPermissionProfile) {
self.granted_permissions =
merge_permission_profiles(self.granted_permissions.as_ref(), Some(&permissions));
}
pub(crate) fn granted_permissions(&self) -> Option<PermissionProfile> {
pub(crate) fn granted_permissions(&self) -> Option<AdditionalPermissionProfile> {
self.granted_permissions.clone()
}
@@ -39,8 +39,8 @@ use codex_apply_patch::Hunk;
use codex_apply_patch::parse_patch_streaming;
use codex_exec_server::ExecutorFileSystem;
use codex_features::Feature;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::FileChange;
use codex_protocol::protocol::PatchApplyUpdatedEvent;
@@ -215,7 +215,7 @@ fn write_permissions_for_paths(
file_paths: &[AbsolutePathBuf],
file_system_sandbox_policy: &codex_protocol::permissions::FileSystemSandboxPolicy,
cwd: &AbsolutePathBuf,
) -> Option<PermissionProfile> {
) -> Option<AdditionalPermissionProfile> {
let write_paths = file_paths
.iter()
.map(|path| {
@@ -232,7 +232,7 @@ fn write_permissions_for_paths(
.collect::<Result<Vec<_>, _>>()
.ok()?;
let permissions = (!write_paths.is_empty()).then_some(PermissionProfile {
let permissions = (!write_paths.is_empty()).then_some(AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions::from_read_write_roots(
Some(vec![]),
Some(write_paths),
+15 -15
View File
@@ -34,7 +34,7 @@ use crate::session::session::Session;
pub(crate) use crate::tools::code_mode::CodeModeExecuteHandler;
pub(crate) use crate::tools::code_mode::CodeModeWaitHandler;
pub use apply_patch::ApplyPatchHandler;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::AskForApproval;
pub use dynamic::DynamicToolHandler;
pub use js_repl::JsReplHandler;
@@ -93,10 +93,10 @@ pub(crate) fn normalize_and_validate_additional_permissions(
additional_permissions_allowed: bool,
approval_policy: AskForApproval,
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
permissions_preapproved: bool,
_cwd: &Path,
) -> Result<Option<PermissionProfile>, String> {
) -> Result<Option<AdditionalPermissionProfile>, String> {
let uses_additional_permissions = matches!(
sandbox_permissions,
SandboxPermissions::WithAdditionalPermissions
@@ -146,15 +146,15 @@ pub(crate) fn normalize_and_validate_additional_permissions(
pub(super) struct EffectiveAdditionalPermissions {
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
pub permissions_preapproved: bool,
}
pub(super) fn implicit_granted_permissions(
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
effective_additional_permissions: &EffectiveAdditionalPermissions,
) -> Option<PermissionProfile> {
) -> Option<AdditionalPermissionProfile> {
if !sandbox_permissions.uses_additional_permissions()
&& !matches!(sandbox_permissions, SandboxPermissions::RequireEscalated)
&& additional_permissions.is_none()
@@ -171,7 +171,7 @@ pub(super) async fn apply_granted_turn_permissions(
session: &Session,
cwd: &std::path::Path,
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
) -> EffectiveAdditionalPermissions {
if matches!(sandbox_permissions, SandboxPermissions::RequireEscalated) {
return EffectiveAdditionalPermissions {
@@ -213,8 +213,8 @@ pub(super) async fn apply_granted_turn_permissions(
}
fn permissions_are_preapproved(
effective_permissions: &PermissionProfile,
granted_permissions: PermissionProfile,
effective_permissions: &AdditionalPermissionProfile,
granted_permissions: AdditionalPermissionProfile,
cwd: &Path,
) -> bool {
let materialized_effective_permissions = intersect_permission_profiles(
@@ -233,9 +233,9 @@ mod tests {
use super::normalize_and_validate_additional_permissions;
use super::permissions_are_preapproved;
use crate::sandboxing::SandboxPermissions;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -248,8 +248,8 @@ mod tests {
use pretty_assertions::assert_eq;
use tempfile::tempdir;
fn network_permissions() -> PermissionProfile {
PermissionProfile {
fn network_permissions() -> AdditionalPermissionProfile {
AdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
@@ -257,8 +257,8 @@ mod tests {
}
}
fn file_system_permissions(path: &std::path::Path) -> PermissionProfile {
PermissionProfile {
fn file_system_permissions(path: &std::path::Path) -> AdditionalPermissionProfile {
AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions::from_read_write_roots(
/*read*/ None,
Some(vec![
@@ -350,7 +350,7 @@ mod tests {
#[test]
fn relative_deny_glob_grants_remain_preapproved_after_materialization() {
let cwd = tempdir().expect("tempdir");
let requested_permissions = PermissionProfile {
let requested_permissions = AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![
FileSystemSandboxEntry {
+2 -2
View File
@@ -36,7 +36,7 @@ use crate::tools::runtimes::shell::ShellRuntime;
use crate::tools::runtimes::shell::ShellRuntimeBackend;
use crate::tools::sandboxing::ToolCtx;
use codex_features::Feature;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::ExecCommandSource;
use codex_shell_command::is_safe_command::is_known_safe_command;
use codex_tools::ShellCommandBackendConfig;
@@ -79,7 +79,7 @@ struct RunExecLikeArgs {
tool_name: String,
exec_params: ExecParams,
hook_command: String,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
prefix_rule: Option<Vec<String>>,
session: Arc<crate::session::session::Session>,
turn: Arc<TurnContext>,
@@ -28,7 +28,7 @@ use crate::unified_exec::generate_chunk_id;
use codex_features::Feature;
use codex_otel::SessionTelemetry;
use codex_otel::TOOL_CALL_UNIFIED_EXEC_METRIC;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::TerminalInteractionEvent;
use codex_shell_command::is_safe_command::is_known_safe_command;
@@ -58,7 +58,7 @@ pub(crate) struct ExecCommandArgs {
#[serde(default)]
sandbox_permissions: SandboxPermissions,
#[serde(default)]
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
#[serde(default)]
justification: Option<String>,
#[serde(default)]
@@ -2,8 +2,8 @@ use super::*;
use crate::shell::default_user_shell;
use crate::tools::handlers::parse_arguments_with_base_path;
use crate::tools::handlers::resolve_workdir_base_path;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_tools::UnifiedExecShellMode;
use codex_tools::ZshForkConfig;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -23,7 +23,9 @@ use codex_protocol::error::CodexErr;
use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::exec_output::StreamOutput;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
@@ -33,7 +35,8 @@ use codex_protocol::protocol::FileChange;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxType;
use codex_sandboxing::SandboxablePreference;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
use codex_utils_absolute_path::AbsolutePathBuf;
use futures::future::BoxFuture;
use std::path::PathBuf;
@@ -45,7 +48,7 @@ pub struct ApplyPatchRequest {
pub file_paths: Vec<AbsolutePathBuf>,
pub changes: std::collections::HashMap<PathBuf, FileChange>,
pub exec_approval_requirement: ExecApprovalRequirement,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
pub permissions_preapproved: bool,
}
@@ -77,13 +80,19 @@ impl ApplyPatchRuntime {
return None;
}
let base_permissions = PermissionProfile::from_runtime_permissions(
let file_system_policy = effective_file_system_sandbox_policy(
attempt.file_system_policy,
attempt.network_policy,
req.additional_permissions.as_ref(),
);
let network_policy = effective_network_sandbox_policy(
attempt.network_policy,
req.additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(attempt.policy),
&file_system_policy,
network_policy,
);
let permissions =
merge_permission_profiles(Some(&base_permissions), req.additional_permissions.as_ref())
.unwrap_or(base_permissions);
Some(FileSystemSandboxContext {
permissions,
cwd: Some(attempt.sandbox_cwd.clone()),
@@ -1,6 +1,7 @@
use super::*;
use crate::tools::sandboxing::SandboxAttempt;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxPolicy;
@@ -9,7 +10,8 @@ use codex_protocol::protocol::GranularApprovalConfig;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxType;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
use core_test_support::PathBufExt;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
@@ -116,7 +118,7 @@ fn file_system_sandbox_context_uses_active_attempt() {
let path = std::env::temp_dir()
.join("apply-patch-runtime-attempt.txt")
.abs();
let additional_permissions = PermissionProfile {
let additional_permissions = AdditionalPermissionProfile {
network: None,
file_system: Some(FileSystemPermissions::from_read_write_roots(
Some(vec![path.clone()]),
@@ -154,15 +156,14 @@ fn file_system_sandbox_context_uses_active_attempt() {
let sandbox = ApplyPatchRuntime::file_system_sandbox_context_for_attempt(&req, &attempt)
.expect("sandbox context");
let base_permissions = PermissionProfile::from_runtime_permissions(
&file_system_policy,
let file_system_policy =
effective_file_system_sandbox_policy(&file_system_policy, Some(&additional_permissions));
let network_policy = effective_network_sandbox_policy(
NetworkSandboxPolicy::Restricted,
Some(&additional_permissions),
);
let Some(expected_permissions) =
merge_permission_profiles(Some(&base_permissions), Some(&additional_permissions))
else {
panic!("merged permissions should not be empty");
};
let expected_permissions =
PermissionProfile::from_runtime_permissions(&file_system_policy, network_policy);
assert_eq!(sandbox.permissions, expected_permissions);
assert_eq!(sandbox.cwd, Some(path.clone()));
assert_eq!(
+2 -2
View File
@@ -14,7 +14,7 @@ use codex_network_proxy::PROXY_ACTIVE_ENV_KEY;
use codex_network_proxy::PROXY_ENV_KEYS;
#[cfg(target_os = "macos")]
use codex_network_proxy::PROXY_GIT_SSH_COMMAND_ENV_KEY;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_sandboxing::SandboxCommand;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
@@ -29,7 +29,7 @@ pub(crate) fn build_sandbox_command(
command: &[String],
cwd: &AbsolutePathBuf,
env: &HashMap<String, String>,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
) -> Result<SandboxCommand, ToolError> {
let (program, args) = command
.split_first()
+3 -3
View File
@@ -35,7 +35,7 @@ use crate::tools::sandboxing::sandbox_override_for_first_attempt;
use crate::tools::sandboxing::with_cached_approval;
use codex_network_proxy::NetworkProxy;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxablePreference;
use codex_shell_command::powershell::prefix_powershell_script_with_utf8;
@@ -53,7 +53,7 @@ pub struct ShellRequest {
pub explicit_env_overrides: HashMap<String, String>,
pub network: Option<NetworkProxy>,
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
#[cfg(unix)]
pub additional_permissions_preapproved: bool,
pub justification: Option<String>,
@@ -97,7 +97,7 @@ pub(crate) struct ApprovalKey {
command: Vec<String>,
cwd: AbsolutePathBuf,
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
}
impl ShellRuntime {
@@ -30,7 +30,9 @@ use codex_protocol::error::CodexErr;
use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::exec_output::StreamOutput;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
@@ -311,7 +313,7 @@ struct CoreShellActionProvider {
network_sandbox_policy: NetworkSandboxPolicy,
sandbox_permissions: SandboxPermissions,
approval_sandbox_permissions: SandboxPermissions,
prompt_permissions: Option<PermissionProfile>,
prompt_permissions: Option<AdditionalPermissionProfile>,
stopwatch: Stopwatch,
}
@@ -361,7 +363,7 @@ impl CoreShellActionProvider {
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> EscalationExecution {
match sandbox_permissions {
SandboxPermissions::UseDefault => EscalationExecution::TurnDefault,
@@ -373,10 +375,14 @@ impl CoreShellActionProvider {
EscalationExecution::Permissions(
EscalationPermissions::ResolvedPermissionProfile(
ResolvedPermissionProfile {
permission_profile: PermissionProfile::from_runtime_permissions(
file_system_sandbox_policy,
network_sandbox_policy,
),
permission_profile:
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(
sandbox_policy,
),
file_system_sandbox_policy,
network_sandbox_policy,
),
sandbox_policy: sandbox_policy.clone(),
},
),
@@ -392,7 +398,7 @@ impl CoreShellActionProvider {
argv: &[String],
workdir: &AbsolutePathBuf,
stopwatch: &Stopwatch,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
) -> anyhow::Result<PromptDecision> {
let command = join_program_and_argv(program, argv);
let workdir = workdir.clone();
@@ -491,7 +497,7 @@ impl CoreShellActionProvider {
program: &AbsolutePathBuf,
argv: &[String],
workdir: &AbsolutePathBuf,
prompt_permissions: Option<PermissionProfile>,
prompt_permissions: Option<AdditionalPermissionProfile>,
escalation_execution: EscalationExecution,
decision_source: DecisionSource,
) -> anyhow::Result<EscalationDecision> {
@@ -757,7 +763,7 @@ struct PrepareSandboxedExecParams<'a> {
sandbox_policy: &'a SandboxPolicy,
file_system_sandbox_policy: &'a FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
}
#[async_trait::async_trait]
@@ -16,6 +16,7 @@ use codex_execpolicy::PolicyParser;
use codex_execpolicy::RuleMatch;
use codex_hooks::Hooks;
use codex_hooks::HooksConfig;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
@@ -257,7 +258,7 @@ fn map_exec_result_preserves_stdout_and_stderr() {
#[test]
fn shell_request_escalation_execution_is_explicit() {
let requested_permissions = PermissionProfile {
let requested_permissions = AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions::from_read_write_roots(
/*read*/ None,
Some(vec![
@@ -38,7 +38,7 @@ use crate::unified_exec::UnifiedExecProcessManager;
use codex_network_proxy::NetworkProxy;
use codex_protocol::error::CodexErr;
use codex_protocol::error::SandboxErr;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxablePreference;
use codex_shell_command::powershell::prefix_powershell_script_with_utf8;
@@ -61,7 +61,7 @@ pub struct UnifiedExecRequest {
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
#[cfg(unix)]
pub additional_permissions_preapproved: bool,
pub justification: Option<String>,
@@ -76,7 +76,7 @@ pub struct UnifiedExecApprovalKey {
pub cwd: AbsolutePathBuf,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
}
/// Runtime adapter that keeps policy and sandbox orchestration on the
+2 -2
View File
@@ -28,7 +28,7 @@ use std::sync::Arc;
use std::sync::Weak;
use codex_network_proxy::NetworkProxy;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use rand::Rng;
use rand::rng;
@@ -96,7 +96,7 @@ pub(crate) struct ExecCommandRequest {
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
pub additional_permissions_preapproved: bool,
pub justification: Option<String>,
pub prefix_rule: Option<Vec<String>>,
@@ -5,8 +5,8 @@ use codex_core::config::Constrained;
use codex_core::sandboxing::SandboxPermissions;
use codex_features::Feature;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::ExecApprovalRequestEvent;
+2 -5
View File
@@ -5,7 +5,6 @@ use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::path::Path;
@@ -58,10 +57,8 @@ pub struct FileSystemSandboxContext {
impl FileSystemSandboxContext {
pub fn from_legacy_sandbox_policy(sandbox_policy: SandboxPolicy, cwd: AbsolutePathBuf) -> Self {
let permissions = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::from_legacy_sandbox_policy(&sandbox_policy, cwd.as_path()),
NetworkSandboxPolicy::from(&sandbox_policy),
);
let permissions =
PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy, cwd.as_path());
Self::from_permission_profile_with_cwd(permissions, cwd)
}
+3 -5
View File
@@ -536,11 +536,9 @@ mod tests {
},
access: FileSystemAccessMode::Write,
}]);
let sandbox_context =
crate::FileSystemSandboxContext::from_permission_profile(PermissionProfile {
network: None,
file_system: Some((&policy).into()),
});
let sandbox_context = crate::FileSystemSandboxContext::from_permission_profile(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
);
let err = sandbox_cwd(&sandbox_context).expect_err("missing cwd should be rejected");
+23 -18
View File
@@ -21,13 +21,16 @@ use codex_exec_server::FileSystemSandboxContext;
use codex_exec_server::LocalFileSystem;
use codex_exec_server::ReadDirectoryEntry;
use codex_exec_server::RemoveOptions;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
@@ -102,15 +105,10 @@ fn workspace_write_sandbox(writable_root: std::path::PathBuf) -> FileSystemSandb
}
fn sandbox_context(entries: Vec<FileSystemSandboxEntry>) -> FileSystemSandboxContext {
FileSystemSandboxContext::from_permission_profile(PermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(false),
}),
file_system: Some(FileSystemPermissions {
entries,
glob_scan_max_depth: None,
}),
})
FileSystemSandboxContext::from_permission_profile(PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::restricted(entries),
NetworkSandboxPolicy::Restricted,
))
}
#[test]
@@ -604,19 +602,26 @@ async fn file_system_sandboxed_write_allows_additional_write_root(use_remote: bo
std::fs::create_dir_all(&writable_dir)?;
let mut sandbox = read_only_sandbox(readable_dir);
let additional_permissions = PermissionProfile {
let additional_permissions = AdditionalPermissionProfile {
network: None,
file_system: Some(FileSystemPermissions::from_read_write_roots(
/*read*/ None,
Some(vec![absolute_path(writable_dir)]),
)),
};
let Some(permissions) =
merge_permission_profiles(Some(&sandbox.permissions), Some(&additional_permissions))
else {
panic!("merged permissions should not be empty");
};
sandbox.permissions = permissions;
let file_system_policy = effective_file_system_sandbox_policy(
&sandbox.permissions.file_system_sandbox_policy(),
Some(&additional_permissions),
);
let network_policy = effective_network_sandbox_policy(
sandbox.permissions.network_sandbox_policy(),
Some(&additional_permissions),
);
sandbox.permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
sandbox.permissions.enforcement(),
&file_system_policy,
network_policy,
);
file_system
.write_file(
+4 -3
View File
@@ -1,4 +1,5 @@
use crate::mcp::RequestId;
use crate::models::AdditionalPermissionProfile;
use crate::models::PermissionProfile;
use crate::parse_command::ParsedCommand;
use crate::protocol::FileChange;
@@ -28,7 +29,7 @@ pub struct ResolvedPermissionProfile {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EscalationPermissions {
/// Permissions to merge with the active turn permissions.
AdditionalPermissionProfile(PermissionProfile),
AdditionalPermissionProfile(AdditionalPermissionProfile),
/// Fully resolved permissions that should replace the active turn permissions.
ResolvedPermissionProfile(ResolvedPermissionProfile),
}
@@ -249,7 +250,7 @@ pub struct ExecApprovalRequestEvent {
/// Optional additional filesystem permissions requested for this command.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
/// Ordered list of decisions the client may present for this prompt.
///
/// When absent, clients should derive the legacy default set from the
@@ -285,7 +286,7 @@ impl ExecApprovalRequestEvent {
network_approval_context: Option<&NetworkApprovalContext>,
proposed_execpolicy_amendment: Option<&ExecPolicyAmendment>,
proposed_network_policy_amendments: Option<&[NetworkPolicyAmendment]>,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> Vec<ReviewDecision> {
if network_approval_context.is_some() {
let mut decisions = vec![ReviewDecision::Approved, ReviewDecision::ApprovedForSession];
+287 -27
View File
@@ -266,57 +266,217 @@ impl NetworkPermissions {
}
}
/// Partial permission overlay used for per-command requests and approved
/// session/turn grants.
#[derive(Debug, Clone, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
pub struct PermissionProfile {
pub struct AdditionalPermissionProfile {
pub network: Option<NetworkPermissions>,
pub file_system: Option<FileSystemPermissions>,
}
impl PermissionProfile {
impl AdditionalPermissionProfile {
pub fn is_empty(&self) -> bool {
self.network.is_none() && self.file_system.is_none()
}
}
#[derive(
Debug, Clone, Copy, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS,
)]
#[serde(rename_all = "snake_case")]
pub enum SandboxEnforcement {
/// Codex owns sandbox construction for this profile.
#[default]
Managed,
/// No outer filesystem sandbox should be applied.
Disabled,
/// Filesystem isolation is enforced by an external caller.
External,
}
impl SandboxEnforcement {
pub fn from_legacy_sandbox_policy(sandbox_policy: &SandboxPolicy) -> Self {
match sandbox_policy {
SandboxPolicy::DangerFullAccess => Self::Disabled,
SandboxPolicy::ExternalSandbox { .. } => Self::External,
SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. } => Self::Managed,
}
}
}
/// Filesystem permissions for profiles where Codex owns sandbox construction.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "snake_case")]
#[ts(tag = "type")]
pub enum ManagedFileSystemPermissions {
/// Apply a managed filesystem sandbox from the listed entries.
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
Restricted {
entries: Vec<FileSystemSandboxEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
glob_scan_max_depth: Option<NonZeroUsize>,
},
/// Apply a managed sandbox that allows all filesystem access.
Unrestricted,
}
impl ManagedFileSystemPermissions {
fn from_sandbox_policy(file_system_sandbox_policy: &FileSystemSandboxPolicy) -> Self {
match file_system_sandbox_policy.kind {
FileSystemSandboxKind::Restricted => Self::Restricted {
entries: file_system_sandbox_policy.entries.clone(),
glob_scan_max_depth: file_system_sandbox_policy
.glob_scan_max_depth
.and_then(NonZeroUsize::new),
},
FileSystemSandboxKind::Unrestricted => Self::Unrestricted,
FileSystemSandboxKind::ExternalSandbox => unreachable!(
"external filesystem policies are represented by PermissionProfile::External"
),
}
}
pub fn to_sandbox_policy(&self) -> FileSystemSandboxPolicy {
match self {
Self::Restricted {
entries,
glob_scan_max_depth,
} => FileSystemSandboxPolicy {
kind: FileSystemSandboxKind::Restricted,
glob_scan_max_depth: glob_scan_max_depth.map(usize::from),
entries: entries.clone(),
},
Self::Unrestricted => FileSystemSandboxPolicy::unrestricted(),
}
}
}
/// Canonical active runtime permissions for a conversation, turn, or command.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "snake_case")]
#[ts(tag = "type")]
pub enum PermissionProfile {
/// Codex owns sandbox construction for this profile.
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
Managed {
file_system: ManagedFileSystemPermissions,
network: NetworkSandboxPolicy,
},
/// Do not apply an outer sandbox.
Disabled,
/// Filesystem isolation is enforced by an external caller.
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
External { network: NetworkSandboxPolicy },
}
impl Default for PermissionProfile {
fn default() -> Self {
Self::Managed {
file_system: ManagedFileSystemPermissions::Restricted {
entries: Vec::new(),
glob_scan_max_depth: None,
},
network: NetworkSandboxPolicy::Restricted,
}
}
}
impl PermissionProfile {
pub fn from_runtime_permissions(
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
) -> Self {
Self {
network: Some(network_sandbox_policy.into()),
file_system: Some(file_system_sandbox_policy.into()),
let enforcement = match file_system_sandbox_policy.kind {
FileSystemSandboxKind::Restricted | FileSystemSandboxKind::Unrestricted => {
SandboxEnforcement::Managed
}
FileSystemSandboxKind::ExternalSandbox => SandboxEnforcement::External,
};
Self::from_runtime_permissions_with_enforcement(
enforcement,
file_system_sandbox_policy,
network_sandbox_policy,
)
}
pub fn from_runtime_permissions_with_enforcement(
enforcement: SandboxEnforcement,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
) -> Self {
match file_system_sandbox_policy.kind {
FileSystemSandboxKind::ExternalSandbox => Self::External {
network: network_sandbox_policy,
},
FileSystemSandboxKind::Unrestricted
if enforcement == SandboxEnforcement::Disabled
&& network_sandbox_policy.is_enabled() =>
{
Self::Disabled
}
FileSystemSandboxKind::Restricted | FileSystemSandboxKind::Unrestricted => {
Self::Managed {
file_system: ManagedFileSystemPermissions::from_sandbox_policy(
file_system_sandbox_policy,
),
network: network_sandbox_policy,
}
}
}
}
pub fn from_legacy_sandbox_policy(sandbox_policy: &SandboxPolicy, cwd: &Path) -> Self {
Self::from_runtime_permissions(
Self::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(sandbox_policy),
&FileSystemSandboxPolicy::from_legacy_sandbox_policy(sandbox_policy, cwd),
NetworkSandboxPolicy::from(sandbox_policy),
)
}
pub fn enforcement(&self) -> SandboxEnforcement {
match self {
Self::Managed { .. } => SandboxEnforcement::Managed,
Self::Disabled => SandboxEnforcement::Disabled,
Self::External { .. } => SandboxEnforcement::External,
}
}
pub fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
self.file_system.as_ref().map_or_else(
|| FileSystemSandboxPolicy::restricted(Vec::new()),
FileSystemSandboxPolicy::from,
)
match self {
Self::Managed { file_system, .. } => file_system.to_sandbox_policy(),
Self::Disabled => FileSystemSandboxPolicy::unrestricted(),
Self::External { .. } => FileSystemSandboxPolicy::external_sandbox(),
}
}
pub fn network_sandbox_policy(&self) -> NetworkSandboxPolicy {
if self
.network
.as_ref()
.and_then(|network| network.enabled)
.unwrap_or(false)
{
NetworkSandboxPolicy::Enabled
} else {
NetworkSandboxPolicy::Restricted
match self {
Self::Managed { network, .. } | Self::External { network } => *network,
Self::Disabled => NetworkSandboxPolicy::Enabled,
}
}
pub fn to_legacy_sandbox_policy(&self, cwd: &Path) -> io::Result<SandboxPolicy> {
self.file_system_sandbox_policy()
.to_legacy_sandbox_policy(self.network_sandbox_policy(), cwd)
match self {
Self::Managed {
file_system,
network,
} => file_system
.to_sandbox_policy()
.to_legacy_sandbox_policy(*network, cwd),
Self::Disabled => Ok(SandboxPolicy::DangerFullAccess),
Self::External { network } => Ok(SandboxPolicy::ExternalSandbox {
network_access: if network.is_enabled() {
crate::protocol::NetworkAccess::Enabled
} else {
crate::protocol::NetworkAccess::Restricted
},
}),
}
}
pub fn to_runtime_permissions(&self) -> (FileSystemSandboxPolicy, NetworkSandboxPolicy) {
@@ -977,7 +1137,7 @@ pub struct ShellToolCallParams {
pub prefix_rule: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
#[serde(skip_serializing_if = "Option::is_none")]
pub justification: Option<String>,
}
@@ -1003,7 +1163,7 @@ pub struct ShellCommandToolCallParams {
pub prefix_rule: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
#[serde(skip_serializing_if = "Option::is_none")]
pub justification: Option<String>,
}
@@ -1448,13 +1608,13 @@ mod tests {
}
#[test]
fn permission_profile_is_empty_when_all_fields_are_none() {
assert_eq!(PermissionProfile::default().is_empty(), true);
fn additional_permission_profile_is_empty_when_all_fields_are_none() {
assert_eq!(AdditionalPermissionProfile::default().is_empty(), true);
}
#[test]
fn permission_profile_is_not_empty_when_field_is_present_but_nested_empty() {
let permission_profile = PermissionProfile {
fn additional_permission_profile_is_not_empty_when_field_is_present_but_nested_empty() {
let permission_profile = AdditionalPermissionProfile {
network: Some(NetworkPermissions { enabled: None }),
file_system: None,
};
@@ -1483,6 +1643,106 @@ mod tests {
);
}
#[test]
fn permission_profile_round_trip_preserves_disabled_sandbox() -> Result<()> {
let cwd = tempdir()?;
let permission_profile = PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::DangerFullAccess,
cwd.path(),
);
assert_eq!(permission_profile, PermissionProfile::Disabled);
assert_eq!(
permission_profile.to_legacy_sandbox_policy(cwd.path())?,
SandboxPolicy::DangerFullAccess
);
assert_eq!(
permission_profile.to_runtime_permissions(),
(
FileSystemSandboxPolicy::unrestricted(),
NetworkSandboxPolicy::Enabled
)
);
Ok(())
}
#[test]
fn permission_profile_from_runtime_permissions_preserves_external_sandbox() {
let permission_profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::external_sandbox(),
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
permission_profile,
PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
}
);
assert_eq!(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::Managed,
&FileSystemSandboxPolicy::external_sandbox(),
NetworkSandboxPolicy::Restricted,
),
permission_profile,
);
}
#[test]
fn permission_profile_from_runtime_permissions_preserves_unrestricted_managed_network() {
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::External,
&FileSystemSandboxPolicy::unrestricted(),
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
permission_profile,
PermissionProfile::Managed {
file_system: ManagedFileSystemPermissions::Unrestricted,
network: NetworkSandboxPolicy::Restricted,
},
"the legacy ExternalSandbox projection must not hide a split unrestricted filesystem policy"
);
assert_eq!(
permission_profile.to_runtime_permissions(),
(
FileSystemSandboxPolicy::unrestricted(),
NetworkSandboxPolicy::Restricted,
)
);
}
#[test]
fn permission_profile_round_trip_preserves_external_sandbox() -> Result<()> {
let cwd = tempdir()?;
let sandbox_policy = SandboxPolicy::ExternalSandbox {
network_access: crate::protocol::NetworkAccess::Restricted,
};
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy, cwd.path());
assert_eq!(
permission_profile,
PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
}
);
assert_eq!(
permission_profile.to_legacy_sandbox_policy(cwd.path())?,
sandbox_policy
);
assert_eq!(
permission_profile.to_runtime_permissions(),
(
FileSystemSandboxPolicy::external_sandbox(),
NetworkSandboxPolicy::Restricted
)
);
Ok(())
}
#[test]
fn file_system_permissions_with_glob_scan_depth_uses_canonical_json() -> Result<()> {
let path = AbsolutePathBuf::try_from(PathBuf::from(if cfg!(windows) {
+3 -1
View File
@@ -41,6 +41,7 @@ use crate::models::MessagePhase;
use crate::models::PermissionProfile;
use crate::models::ResponseInputItem;
use crate::models::ResponseItem;
use crate::models::SandboxEnforcement;
use crate::models::WebSearchAction;
use crate::num_format::format_with_separators;
use crate::openai_models::ReasoningEffort as ReasoningEffortConfig;
@@ -3062,7 +3063,8 @@ impl TurnContextItem {
&self.cwd,
)
});
PermissionProfile::from_runtime_permissions(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy),
&file_system_sandbox_policy,
NetworkSandboxPolicy::from(&self.sandbox_policy),
)
+4 -4
View File
@@ -1,6 +1,6 @@
use crate::models::AdditionalPermissionProfile;
use crate::models::FileSystemPermissions;
use crate::models::NetworkPermissions;
use crate::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use schemars::JsonSchema;
use serde::Deserialize;
@@ -28,7 +28,7 @@ impl RequestPermissionProfile {
}
}
impl From<RequestPermissionProfile> for PermissionProfile {
impl From<RequestPermissionProfile> for AdditionalPermissionProfile {
fn from(value: RequestPermissionProfile) -> Self {
Self {
network: value.network,
@@ -37,8 +37,8 @@ impl From<RequestPermissionProfile> for PermissionProfile {
}
}
impl From<PermissionProfile> for RequestPermissionProfile {
fn from(value: PermissionProfile) -> Self {
impl From<AdditionalPermissionProfile> for RequestPermissionProfile {
fn from(value: AdditionalPermissionProfile) -> Self {
Self {
network: value.network,
file_system: value.file_system,
+2 -2
View File
@@ -7,7 +7,7 @@
use std::fmt::Display;
use std::sync::Arc;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::SandboxPermissions;
use codex_protocol::models::SearchToolCallParams;
@@ -90,7 +90,7 @@ pub enum ToolDispatchPayload {
timeout_ms: Option<u64>,
sandbox_permissions: Option<SandboxPermissions>,
prefix_rule: Option<Vec<String>>,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
justification: Option<String>,
},
Mcp {
+2 -2
View File
@@ -11,7 +11,7 @@ use crate::policy_transforms::effective_network_sandbox_policy;
use crate::policy_transforms::should_require_platform_sandbox;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
@@ -68,7 +68,7 @@ pub struct SandboxCommand {
pub args: Vec<String>,
pub cwd: AbsolutePathBuf,
pub env: HashMap<String, String>,
pub additional_permissions: Option<PermissionProfile>,
pub additional_permissions: Option<AdditionalPermissionProfile>,
}
#[derive(Debug)]
+1 -1
View File
@@ -5,9 +5,9 @@ use super::SandboxType;
use super::SandboxablePreference;
use super::get_platform_sandbox;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
+19 -19
View File
@@ -1,6 +1,6 @@
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -27,7 +27,7 @@ pub struct EffectiveSandboxPermissions {
impl EffectiveSandboxPermissions {
pub fn new(
sandbox_policy: &SandboxPolicy,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> Self {
let Some(additional_permissions) = additional_permissions else {
return Self {
@@ -42,8 +42,8 @@ impl EffectiveSandboxPermissions {
}
pub fn normalize_additional_permissions(
additional_permissions: PermissionProfile,
) -> Result<PermissionProfile, String> {
additional_permissions: AdditionalPermissionProfile,
) -> Result<AdditionalPermissionProfile, String> {
let network = additional_permissions
.network
.filter(|network| !network.is_empty());
@@ -87,16 +87,16 @@ pub fn normalize_additional_permissions(
}
None => None,
};
Ok(PermissionProfile {
Ok(AdditionalPermissionProfile {
network,
file_system,
})
}
pub fn merge_permission_profiles(
base: Option<&PermissionProfile>,
permissions: Option<&PermissionProfile>,
) -> Option<PermissionProfile> {
base: Option<&AdditionalPermissionProfile>,
permissions: Option<&AdditionalPermissionProfile>,
) -> Option<AdditionalPermissionProfile> {
let Some(permissions) = permissions else {
return base.cloned();
};
@@ -137,7 +137,7 @@ pub fn merge_permission_profiles(
(None, None) => None,
};
Some(PermissionProfile {
Some(AdditionalPermissionProfile {
network,
file_system,
})
@@ -148,10 +148,10 @@ pub fn merge_permission_profiles(
}
pub fn intersect_permission_profiles(
requested: PermissionProfile,
granted: PermissionProfile,
requested: AdditionalPermissionProfile,
granted: AdditionalPermissionProfile,
cwd: &Path,
) -> PermissionProfile {
) -> AdditionalPermissionProfile {
let file_system = requested
.file_system
.map(|requested_file_system| {
@@ -213,7 +213,7 @@ pub fn intersect_permission_profiles(
_ => None,
};
PermissionProfile {
AdditionalPermissionProfile {
network,
file_system,
}
@@ -458,7 +458,7 @@ fn dedup_absolute_paths(paths: Vec<AbsolutePathBuf>) -> Vec<AbsolutePathBuf> {
}
fn additional_permission_roots(
additional_permissions: &PermissionProfile,
additional_permissions: &AdditionalPermissionProfile,
) -> (Vec<AbsolutePathBuf>, Vec<AbsolutePathBuf>) {
(
dedup_absolute_paths(
@@ -516,7 +516,7 @@ fn merge_file_system_policy_with_additional_permissions(
pub fn effective_file_system_sandbox_policy(
file_system_policy: &FileSystemSandboxPolicy,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> FileSystemSandboxPolicy {
let Some(additional_permissions) = additional_permissions else {
return file_system_policy.clone();
@@ -557,7 +557,7 @@ fn merge_read_only_access_with_additional_reads(
fn merge_network_access(
base_network_access: bool,
additional_permissions: &PermissionProfile,
additional_permissions: &AdditionalPermissionProfile,
) -> bool {
base_network_access
|| additional_permissions
@@ -569,7 +569,7 @@ fn merge_network_access(
pub fn effective_network_sandbox_policy(
network_policy: NetworkSandboxPolicy,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> NetworkSandboxPolicy {
if additional_permissions
.is_some_and(|permissions| merge_network_access(network_policy.is_enabled(), permissions))
@@ -584,7 +584,7 @@ pub fn effective_network_sandbox_policy(
fn sandbox_policy_with_additional_permissions(
sandbox_policy: &SandboxPolicy,
additional_permissions: &PermissionProfile,
additional_permissions: &AdditionalPermissionProfile,
) -> SandboxPolicy {
if additional_permissions.is_empty() {
return sandbox_policy.clone();
@@ -654,7 +654,7 @@ fn sandbox_policy_with_additional_permissions(
fn effective_sandbox_policy(
sandbox_policy: &SandboxPolicy,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> SandboxPolicy {
additional_permissions.map_or_else(
|| sandbox_policy.clone(),
@@ -4,9 +4,9 @@ use super::merge_file_system_policy_with_additional_permissions;
use super::normalize_additional_permissions;
use super::sandbox_policy_with_additional_permissions;
use super::should_require_platform_sandbox;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -378,8 +378,8 @@ async fn handle_escalate_session_with_policy(
mod tests {
use super::*;
use codex_protocol::approvals::EscalationPermissions;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
+1 -1
View File
@@ -289,7 +289,7 @@ fn default_exec_approval_decisions(
proposed_network_policy_amendments: Option<
&[codex_protocol::approvals::NetworkPolicyAmendment],
>,
additional_permissions: Option<&codex_protocol::models::PermissionProfile>,
additional_permissions: Option<&codex_protocol::models::AdditionalPermissionProfile>,
) -> Vec<codex_protocol::protocol::ReviewDecision> {
ExecApprovalRequestEvent::default_available_decisions(
network_approval_context,
+2 -1
View File
@@ -61,6 +61,7 @@ use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::CollaborationModeMask;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Settings;
use codex_protocol::models::AdditionalPermissionProfile as CoreAdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
@@ -2476,7 +2477,7 @@ async fn inactive_thread_exec_approval_preserves_context() {
);
assert_eq!(
additional_permissions,
Some(PermissionProfile {
Some(CoreAdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
+6 -12
View File
@@ -3,7 +3,6 @@ use crate::app_server_session::ThreadSessionState;
use crate::read_session_model;
use codex_app_server_protocol::Thread;
use codex_protocol::ThreadId;
use codex_protocol::protocol::SandboxPolicy;
impl App {
pub(super) async fn sync_active_thread_permission_settings_to_cached_session(&mut self) {
@@ -14,17 +13,12 @@ impl App {
let approval_policy = self.config.permissions.approval_policy.value();
let approvals_reviewer = self.config.approvals_reviewer;
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
let permission_profile = if matches!(sandbox_policy, SandboxPolicy::ExternalSandbox { .. })
{
None
} else {
Some(
self.chat_widget
.config_ref()
.permissions
.permission_profile(),
)
};
let permission_profile = Some(
self.chat_widget
.config_ref()
.permissions
.permission_profile(),
);
let update_session = |session: &mut ThreadSessionState| {
session.approval_policy = approval_policy;
session.approvals_reviewer = approvals_reviewer;
+11 -18
View File
@@ -154,7 +154,7 @@ pub(crate) struct ThreadSessionState {
pub(crate) sandbox_policy: SandboxPolicy,
/// Canonical active permissions when available. Consumers should prefer
/// this over `sandbox_policy`; `None` means the session only has a legacy
/// sandbox projection or represents an external sandbox.
/// sandbox projection.
pub(crate) permission_profile: Option<PermissionProfile>,
pub(crate) cwd: AbsolutePathBuf,
pub(crate) instruction_source_paths: Vec<AbsolutePathBuf>,
@@ -1050,15 +1050,12 @@ fn turn_start_permission_overrides(
Option<codex_app_server_protocol::SandboxPolicy>,
Option<codex_app_server_protocol::PermissionProfile>,
) {
let is_external_sandbox = matches!(&sandbox_policy, SandboxPolicy::ExternalSandbox { .. });
match (mode, is_external_sandbox, permission_profile) {
(ThreadParamsMode::Embedded, false, Some(permission_profile)) => {
match (mode, permission_profile) {
(ThreadParamsMode::Embedded, Some(permission_profile)) => {
(None, Some(permission_profile.into()))
}
(ThreadParamsMode::Embedded, false, None) => (None, None),
(ThreadParamsMode::Embedded, true, _) | (ThreadParamsMode::Remote, _, _) => {
(Some(sandbox_policy.into()), None)
}
(ThreadParamsMode::Embedded, None) => (None, None),
(ThreadParamsMode::Remote, _) => (Some(sandbox_policy.into()), None),
}
}
@@ -1070,14 +1067,7 @@ fn permission_profile_override_from_config(
return None;
}
if matches!(
config.permissions.sandbox_policy.get(),
SandboxPolicy::ExternalSandbox { .. }
) {
None
} else {
Some(config.permissions.permission_profile().into())
}
Some(config.permissions.permission_profile().into())
}
fn thread_start_params_from_config(
@@ -1591,8 +1581,11 @@ mod tests {
&cwd,
)),
);
assert_eq!(sandbox, Some(external_sandbox.into()));
assert_eq!(profile, None);
assert_eq!(sandbox, None);
assert_eq!(
profile,
Some(PermissionProfile::from_legacy_sandbox_policy(&external_sandbox, &cwd).into())
);
}
#[tokio::test]
@@ -20,7 +20,7 @@ use crate::render::renderable::Renderable;
use codex_features::Features;
use codex_protocol::ThreadId;
use codex_protocol::mcp::RequestId;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -58,7 +58,7 @@ pub(crate) enum ApprovalRequest {
reason: Option<String>,
available_decisions: Vec<ReviewDecision>,
network_approval_context: Option<NetworkApprovalContext>,
additional_permissions: Option<PermissionProfile>,
additional_permissions: Option<AdditionalPermissionProfile>,
},
Permissions {
thread_id: ThreadId,
@@ -719,7 +719,7 @@ impl ApprovalOption {
fn exec_options(
available_decisions: &[ReviewDecision],
network_approval_context: Option<&NetworkApprovalContext>,
additional_permissions: Option<&PermissionProfile>,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> Vec<ApprovalOption> {
available_decisions
.iter()
@@ -808,7 +808,7 @@ fn exec_options(
}
pub(crate) fn format_additional_permissions_rule(
additional_permissions: &PermissionProfile,
additional_permissions: &AdditionalPermissionProfile,
) -> Option<String> {
let mut parts = Vec::new();
if additional_permissions
@@ -1341,7 +1341,7 @@ mod tests {
#[test]
fn additional_permissions_exec_options_hide_execpolicy_amendment() {
let additional_permissions = PermissionProfile {
let additional_permissions = AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions::from_read_write_roots(
Some(vec![absolute_path("/tmp/readme.txt")]),
Some(vec![absolute_path("/tmp/out.txt")]),
@@ -1383,7 +1383,7 @@ mod tests {
#[test]
fn additional_permissions_rule_shows_non_path_file_system_entries() {
let additional_permissions = PermissionProfile {
let additional_permissions = AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![
FileSystemSandboxEntry {
@@ -1477,7 +1477,7 @@ mod tests {
reason: None,
available_decisions: vec![ReviewDecision::Approved, ReviewDecision::Abort],
network_approval_context: None,
additional_permissions: Some(PermissionProfile {
additional_permissions: Some(AdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
@@ -1527,7 +1527,7 @@ mod tests {
reason: Some("need filesystem access".into()),
available_decisions: vec![ReviewDecision::Approved, ReviewDecision::Abort],
network_approval_context: None,
additional_permissions: Some(PermissionProfile {
additional_permissions: Some(AdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
@@ -133,7 +133,7 @@ fn app_server_exec_approval_request_preserves_permissions_context() {
);
assert_eq!(
request.additional_permissions,
Some(PermissionProfile {
Some(codex_protocol::models::AdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
@@ -92,11 +92,9 @@ async fn submission_includes_configured_permission_profile() {
let conversation_id = ThreadId::new();
let rollout_file = NamedTempFile::new().unwrap();
let expected_permission_profile = PermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(false),
}),
file_system: Some(FileSystemPermissions {
let expected_permission_profile = PermissionProfile::Managed {
network: codex_protocol::permissions::NetworkSandboxPolicy::Restricted,
file_system: codex_protocol::models::ManagedFileSystemPermissions::Restricted {
entries: vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -112,7 +110,7 @@ async fn submission_includes_configured_permission_profile() {
},
],
glob_scan_max_depth: None,
}),
},
};
let configured = codex_protocol::protocol::SessionConfiguredEvent {
session_id: conversation_id,