mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
permissions: make profiles represent enforcement (#19231)
## Why
`PermissionProfile` is becoming the canonical permissions abstraction,
but the old shape only carried optional filesystem and network fields.
It could describe allowed access, but not who is responsible for
enforcing it. That made `DangerFullAccess` and `ExternalSandbox` lossy
when profiles were exported, cached, or round-tripped through app-server
APIs.
The important model change is that active permissions are now a disjoint
union over the enforcement mode. Conceptually:
```rust
pub enum PermissionProfile {
Managed {
file_system: FileSystemSandboxPolicy,
network: NetworkSandboxPolicy,
},
Disabled,
External {
network: NetworkSandboxPolicy,
},
}
```
This distinction matters because `Disabled` means Codex should apply no
outer sandbox at all, while `External` means filesystem isolation is
owned by an outside caller. Those are not equivalent to a broad managed
sandbox. For example, macOS cannot nest Seatbelt inside Seatbelt, so an
inner sandbox may require the outer Codex layer to use no sandbox rather
than a permissive one.
## How Existing Modeling Maps
Legacy `SandboxPolicy` remains a boundary projection, but it now maps
into the higher-fidelity profile model:
- `ReadOnly` and `WorkspaceWrite` map to `PermissionProfile::Managed`
with restricted filesystem entries plus the corresponding network
policy.
- `DangerFullAccess` maps to `PermissionProfile::Disabled`, preserving
the “no outer sandbox” intent instead of treating it as a lax managed
sandbox.
- `ExternalSandbox { network_access }` maps to
`PermissionProfile::External { network }`, preserving external
filesystem enforcement while still carrying the active network policy.
- Split runtime policies that legacy `SandboxPolicy` cannot faithfully
express, such as managed unrestricted filesystem plus restricted
network, stay `Managed` instead of being collapsed into
`ExternalSandbox`.
- Per-command/session/turn grants remain partial overlays via
`AdditionalPermissionProfile`; full `PermissionProfile` is reserved for
complete active runtime permissions.
## What Changed
- Change active `PermissionProfile` into a tagged union: `managed`,
`disabled`, and `external`.
- Keep partial permission grants separate with
`AdditionalPermissionProfile` for command/session/turn overlays.
- Represent managed filesystem permissions as either `restricted`
entries or `unrestricted`; `glob_scan_max_depth` is non-zero when
present.
- Preserve old rollout compatibility by accepting the pre-tagged `{
network, file_system }` profile shape during deserialization.
- Preserve fidelity for important edge cases: `DangerFullAccess`
round-trips as `disabled`, `ExternalSandbox` round-trips as `external`,
and managed unrestricted filesystem + restricted network stays managed
instead of being mistaken for external enforcement.
- Preserve configured deny-read entries and bounded glob scan depth when
full profiles are projected back into runtime policies, including
unrestricted replacements that now become `:root = write` plus deny
entries.
- Regenerate the experimental app-server v2 JSON/TypeScript schema and
update the `command/exec` README example for the tagged
`permissionProfile` shape.
## Compatibility
Legacy `SandboxPolicy` remains available at config/API boundaries as the
compatibility projection. Existing rollout lines with the old
`PermissionProfile` shape continue to load. The app-server
`permissionProfile` field is experimental, so its v2 wire shape is
intentionally updated to match the higher-fidelity model.
## Verification
- `just write-app-server-schema`
- `cargo check --tests`
- `cargo test -p codex-protocol permission_profile`
- `cargo test -p codex-protocol
preserving_deny_entries_keeps_unrestricted_policy_enforceable`
- `cargo test -p codex-app-server-protocol
permission_profile_file_system_permissions`
- `cargo test -p codex-app-server-protocol serialize_client_response`
- `cargo test -p codex-core
session_configured_reports_permission_profile_for_external_sandbox`
- `just fix`
- `just fix -p codex-protocol`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-core`
- `just fix -p codex-app-server`
This commit is contained in:
committed by
GitHub
Unverified
parent
33cc135cc3
commit
4816b89204
@@ -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
@@ -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": {
|
||||
|
||||
+2
-1
@@ -78,7 +78,8 @@
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
],
|
||||
"description": "Partial overlay used for per-command permission requests."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
+2
-1
@@ -78,7 +78,8 @@
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
],
|
||||
"description": "Partial overlay used for per-command permission requests."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
+114
-42
@@ -25,7 +25,8 @@
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
],
|
||||
"description": "Partial overlay used for per-command permission requests."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -11285,61 +11286,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": {
|
||||
@@ -14560,7 +14632,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": [
|
||||
@@ -16003,7 +16075,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": [
|
||||
@@ -16330,7 +16402,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": [
|
||||
|
||||
+112
-41
@@ -8000,61 +8000,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": {
|
||||
@@ -12447,7 +12518,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": [
|
||||
@@ -13890,7 +13961,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": [
|
||||
@@ -14217,7 +14288,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
@@ -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": {
|
||||
|
||||
+109
-38
@@ -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": {
|
||||
|
||||
+110
-39
@@ -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
@@ -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": {
|
||||
|
||||
+110
-39
@@ -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
@@ -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": {
|
||||
|
||||
+110
-39
@@ -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
@@ -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": {
|
||||
|
||||
+5
-1
@@ -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, };
|
||||
|
||||
Generated
+1
-1
@@ -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" };
|
||||
|
||||
+1
-1
@@ -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, };
|
||||
|
||||
@@ -1526,22 +1526,7 @@ mod tests {
|
||||
"type": "dangerFullAccess"
|
||||
},
|
||||
"permissionProfile": {
|
||||
"network": {
|
||||
"enabled": true,
|
||||
},
|
||||
"fileSystem": {
|
||||
"entries": [
|
||||
{
|
||||
"path": {
|
||||
"type": "special",
|
||||
"value": {
|
||||
"kind": "root",
|
||||
},
|
||||
},
|
||||
"access": "write",
|
||||
},
|
||||
],
|
||||
},
|
||||
"type": "disabled"
|
||||
},
|
||||
"reasoningEffort": null
|
||||
}
|
||||
|
||||
@@ -38,7 +38,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;
|
||||
@@ -52,6 +54,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;
|
||||
@@ -1356,7 +1359,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 {
|
||||
@@ -1375,18 +1378,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1534,65 +1539,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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1601,12 +1652,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),
|
||||
@@ -1614,7 +1666,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),
|
||||
@@ -1635,7 +1687,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),
|
||||
@@ -3379,9 +3431,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>,
|
||||
@@ -3485,9 +3535,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>,
|
||||
@@ -3582,9 +3630,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>,
|
||||
@@ -7896,7 +7942,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(),
|
||||
@@ -7910,7 +7956,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
permissions,
|
||||
PermissionProfileFileSystemPermissions {
|
||||
PermissionProfileFileSystemPermissions::Restricted {
|
||||
entries: vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::GlobPattern {
|
||||
pattern: "**/*.env".to_string(),
|
||||
@@ -7921,7 +7967,7 @@ mod tests {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
CoreFileSystemPermissions::from(permissions),
|
||||
CoreManagedFileSystemPermissions::from(permissions),
|
||||
core_permissions
|
||||
);
|
||||
}
|
||||
@@ -7929,6 +7975,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,
|
||||
}))
|
||||
@@ -7982,8 +8029,8 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
CorePermissionProfile::from(response.permissions),
|
||||
CorePermissionProfile {
|
||||
CoreAdditionalPermissionProfile::from(response.permissions),
|
||||
CoreAdditionalPermissionProfile {
|
||||
network: Some(CoreNetworkPermissions {
|
||||
enabled: Some(true),
|
||||
}),
|
||||
|
||||
@@ -837,7 +837,8 @@ Run a standalone command (argv vector) in the server’s sandbox without creatin
|
||||
"env": { "FOO": "override" }, // optional; merges into the server env and overrides matching names
|
||||
"size": { "rows": 40, "cols": 120 }, // optional; PTY size in character cells, only valid with tty=true
|
||||
"permissionProfile": { // optional; defaults to user config
|
||||
"fileSystem": { "entries": [
|
||||
"type": "managed",
|
||||
"fileSystem": { "type": "restricted", "entries": [
|
||||
{ "path": { "type": "special", "value": { "kind": "root" } }, "access": "read" },
|
||||
{ "path": { "type": "special", "value": { "kind": "current_working_directory" } }, "access": "write" }
|
||||
] },
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -317,7 +317,6 @@ use codex_protocol::error::CodexErr;
|
||||
use codex_protocol::error::Result as CodexResult;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::protocol::AgentStatus;
|
||||
use codex_protocol::protocol::ConversationAudioParams;
|
||||
@@ -2346,24 +2345,8 @@ impl CodexMessageProcessor {
|
||||
file_system_sandbox_policy: &mut FileSystemSandboxPolicy,
|
||||
configured_file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
) {
|
||||
if file_system_sandbox_policy.glob_scan_max_depth.is_none() {
|
||||
file_system_sandbox_policy.glob_scan_max_depth =
|
||||
configured_file_system_sandbox_policy.glob_scan_max_depth;
|
||||
}
|
||||
|
||||
for deny_entry in configured_file_system_sandbox_policy
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|entry| entry.access == FileSystemAccessMode::None)
|
||||
{
|
||||
if !file_system_sandbox_policy
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry == deny_entry)
|
||||
{
|
||||
file_system_sandbox_policy.entries.push(deny_entry.clone());
|
||||
}
|
||||
}
|
||||
file_system_sandbox_policy
|
||||
.preserve_deny_read_restrictions_from(configured_file_system_sandbox_policy);
|
||||
}
|
||||
|
||||
async fn command_exec_write(
|
||||
@@ -2812,10 +2795,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(),
|
||||
@@ -4631,7 +4612,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,
|
||||
);
|
||||
|
||||
@@ -5302,7 +5282,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,
|
||||
);
|
||||
|
||||
@@ -8827,8 +8806,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,
|
||||
@@ -9958,17 +9936,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 {
|
||||
@@ -10285,6 +10255,7 @@ mod tests {
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
@@ -10490,25 +10461,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,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,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;
|
||||
@@ -781,9 +782,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,
|
||||
@@ -791,7 +792,7 @@ async fn turn_start_rejects_invalid_permission_profile_before_starting_turn() ->
|
||||
access: FileSystemAccessMode::Write,
|
||||
}],
|
||||
glob_scan_max_depth: None,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -90,11 +90,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;
|
||||
@@ -1843,7 +1844,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`.
|
||||
@@ -2265,7 +2266,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();
|
||||
@@ -2283,7 +2285,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;
|
||||
@@ -2303,7 +2305,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()
|
||||
}
|
||||
|
||||
@@ -94,7 +94,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,
|
||||
)
|
||||
@@ -182,26 +183,8 @@ impl SessionConfiguration {
|
||||
next_configuration.sandbox_policy.set(sandbox_policy)?;
|
||||
let (mut file_system_sandbox_policy, network_sandbox_policy) =
|
||||
permission_profile.to_runtime_permissions();
|
||||
if file_system_sandbox_policy.glob_scan_max_depth.is_none() {
|
||||
file_system_sandbox_policy.glob_scan_max_depth =
|
||||
self.file_system_sandbox_policy.glob_scan_max_depth;
|
||||
}
|
||||
for deny_entry in self
|
||||
.file_system_sandbox_policy
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|entry| {
|
||||
entry.access == codex_protocol::permissions::FileSystemAccessMode::None
|
||||
})
|
||||
{
|
||||
if !file_system_sandbox_policy
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry == deny_entry)
|
||||
{
|
||||
file_system_sandbox_policy.entries.push(deny_entry.clone());
|
||||
}
|
||||
}
|
||||
file_system_sandbox_policy
|
||||
.preserve_deny_read_restrictions_from(&self.file_system_sandbox_policy);
|
||||
next_configuration.file_system_sandbox_policy = file_system_sandbox_policy;
|
||||
next_configuration.network_sandbox_policy = network_sandbox_policy;
|
||||
} else if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
|
||||
@@ -825,14 +808,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 {
|
||||
@@ -845,7 +820,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,
|
||||
|
||||
@@ -1474,7 +1474,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,
|
||||
@@ -1492,10 +1493,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(())
|
||||
}
|
||||
|
||||
@@ -20,9 +20,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -94,7 +97,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,
|
||||
)
|
||||
@@ -243,12 +247,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()),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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];
|
||||
|
||||
+406
-27
@@ -266,43 +266,274 @@ 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, 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
|
||||
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> {
|
||||
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) {
|
||||
(
|
||||
self.file_system_sandbox_policy(),
|
||||
self.network_sandbox_policy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
enum TaggedPermissionProfile {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
Managed {
|
||||
file_system: ManagedFileSystemPermissions,
|
||||
network: NetworkSandboxPolicy,
|
||||
},
|
||||
Disabled,
|
||||
#[serde(rename_all = "snake_case")]
|
||||
External {
|
||||
network: NetworkSandboxPolicy,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<TaggedPermissionProfile> for PermissionProfile {
|
||||
fn from(value: TaggedPermissionProfile) -> Self {
|
||||
match value {
|
||||
TaggedPermissionProfile::Managed {
|
||||
file_system,
|
||||
network,
|
||||
} => Self::Managed {
|
||||
file_system,
|
||||
network,
|
||||
},
|
||||
TaggedPermissionProfile::Disabled => Self::Disabled,
|
||||
TaggedPermissionProfile::External { network } => Self::External { network },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-tagged shape written to rollout files before `PermissionProfile`
|
||||
/// represented enforcement explicitly.
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct LegacyPermissionProfile {
|
||||
network: Option<NetworkPermissions>,
|
||||
file_system: Option<FileSystemPermissions>,
|
||||
}
|
||||
|
||||
impl From<LegacyPermissionProfile> for PermissionProfile {
|
||||
fn from(value: LegacyPermissionProfile) -> Self {
|
||||
let file_system_sandbox_policy = value.file_system.as_ref().map_or_else(
|
||||
|| FileSystemSandboxPolicy::restricted(Vec::new()),
|
||||
FileSystemSandboxPolicy::from,
|
||||
);
|
||||
let network_sandbox_policy = if value
|
||||
.network
|
||||
.as_ref()
|
||||
.and_then(|network| network.enabled)
|
||||
@@ -311,19 +542,27 @@ impl PermissionProfile {
|
||||
NetworkSandboxPolicy::Enabled
|
||||
} else {
|
||||
NetworkSandboxPolicy::Restricted
|
||||
}
|
||||
};
|
||||
Self::from_runtime_permissions(&file_system_sandbox_policy, network_sandbox_policy)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum PermissionProfileDe {
|
||||
Tagged(TaggedPermissionProfile),
|
||||
Legacy(LegacyPermissionProfile),
|
||||
}
|
||||
|
||||
pub fn to_runtime_permissions(&self) -> (FileSystemSandboxPolicy, NetworkSandboxPolicy) {
|
||||
(
|
||||
self.file_system_sandbox_policy(),
|
||||
self.network_sandbox_policy(),
|
||||
)
|
||||
impl<'de> Deserialize<'de> for PermissionProfile {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(match PermissionProfileDe::deserialize(deserializer)? {
|
||||
PermissionProfileDe::Tagged(tagged) => tagged.into(),
|
||||
PermissionProfileDe::Legacy(legacy) => legacy.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,7 +1216,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 +1242,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 +1687,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 +1722,146 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn permission_profile_deserializes_legacy_rollout_shape() -> Result<()> {
|
||||
let legacy = serde_json::json!({
|
||||
"network": {
|
||||
"enabled": true,
|
||||
},
|
||||
"file_system": {
|
||||
"entries": [{
|
||||
"path": {
|
||||
"type": "special",
|
||||
"value": {
|
||||
"kind": "root",
|
||||
},
|
||||
},
|
||||
"access": "write",
|
||||
}],
|
||||
"glob_scan_max_depth": 2,
|
||||
},
|
||||
});
|
||||
|
||||
let permission_profile: PermissionProfile = serde_json::from_value(legacy)?;
|
||||
|
||||
assert_eq!(
|
||||
permission_profile,
|
||||
PermissionProfile::Managed {
|
||||
file_system: ManagedFileSystemPermissions::Restricted {
|
||||
entries: vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}],
|
||||
glob_scan_max_depth: NonZeroUsize::new(2),
|
||||
},
|
||||
network: NetworkSandboxPolicy::Enabled,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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) {
|
||||
|
||||
@@ -340,6 +340,41 @@ impl FileSystemSandboxPolicy {
|
||||
rebuilt
|
||||
}
|
||||
|
||||
/// Preserve explicit read-deny rules from `existing` when a caller
|
||||
/// replaces the allow side of a policy.
|
||||
pub fn preserve_deny_read_restrictions_from(&mut self, existing: &Self) {
|
||||
let has_deny_read_entries = existing
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry.access == FileSystemAccessMode::None);
|
||||
if matches!(self.kind, FileSystemSandboxKind::Unrestricted) && has_deny_read_entries {
|
||||
*self = Self::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
}
|
||||
|
||||
if !matches!(self.kind, FileSystemSandboxKind::Restricted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.glob_scan_max_depth.is_none() {
|
||||
self.glob_scan_max_depth = existing.glob_scan_max_depth;
|
||||
}
|
||||
|
||||
for deny_entry in existing
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|entry| entry.access == FileSystemAccessMode::None)
|
||||
{
|
||||
if !self.entries.iter().any(|entry| entry == deny_entry) {
|
||||
self.entries.push(deny_entry.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true when a restricted policy contains any entry that really
|
||||
/// reduces a broader `:root = write` grant.
|
||||
///
|
||||
@@ -2297,6 +2332,28 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserving_deny_entries_keeps_unrestricted_policy_enforceable() {
|
||||
let deny_entry = unreadable_glob_entry("/tmp/project/**/*.env".to_string());
|
||||
let mut existing = FileSystemSandboxPolicy::restricted(vec![deny_entry.clone()]);
|
||||
existing.glob_scan_max_depth = Some(2);
|
||||
let mut replacement = FileSystemSandboxPolicy::unrestricted();
|
||||
|
||||
replacement.preserve_deny_read_restrictions_from(&existing);
|
||||
|
||||
let mut expected = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
deny_entry,
|
||||
]);
|
||||
expected.glob_scan_max_depth = Some(2);
|
||||
assert_eq!(replacement, expected);
|
||||
}
|
||||
|
||||
fn deny_policy(path: &Path) -> FileSystemSandboxPolicy {
|
||||
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
}),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
@@ -1053,15 +1053,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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,14 +1070,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(
|
||||
@@ -1594,8 +1584,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,
|
||||
|
||||
Reference in New Issue
Block a user