mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Add conservative restriction helpers for tool policies (#38183)
## What changed - Add `restrict_to` helpers that combine parent and requested `WebSearchMode` and `AppToolApproval` values without granting access beyond either policy. - Treat `Auto` and `Writes` approval modes as incomparable, falling back to `Prompt` when they are combined. ## Testing - Cover every pair of web search and app tool approval modes. GitOrigin-RevId: 41b3da5ace2fbdaab667c4b834bc2051a8e5e9cf
This commit is contained in:
@@ -30,6 +30,22 @@ pub enum AppToolApproval {
|
||||
Approve,
|
||||
}
|
||||
|
||||
impl AppToolApproval {
|
||||
/// Requires approval whenever either policy could require it.
|
||||
///
|
||||
/// `Auto` and `Writes` are incomparable: each can require approval for a
|
||||
/// tool the other would approve. Their conservative intersection is `Prompt`.
|
||||
pub fn restrict_to(self, requested: Self) -> Self {
|
||||
match (self, requested) {
|
||||
(Self::Prompt, _) | (_, Self::Prompt) => Self::Prompt,
|
||||
(Self::Approve, mode) | (mode, Self::Approve) => mode,
|
||||
(Self::Auto, Self::Auto) => Self::Auto,
|
||||
(Self::Writes, Self::Writes) => Self::Writes,
|
||||
(Self::Auto, Self::Writes) | (Self::Writes, Self::Auto) => Self::Prompt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Human-readable reason a configured MCP server was disabled after requirements
|
||||
/// were applied.
|
||||
///
|
||||
|
||||
@@ -4,6 +4,32 @@ use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn app_tool_approval_restrictions_never_weaken_either_policy() {
|
||||
use AppToolApproval::Approve;
|
||||
use AppToolApproval::Auto;
|
||||
use AppToolApproval::Prompt;
|
||||
use AppToolApproval::Writes;
|
||||
|
||||
let modes = [Approve, Auto, Writes, Prompt];
|
||||
let expected = [
|
||||
[Approve, Auto, Writes, Prompt],
|
||||
[Auto, Auto, Prompt, Prompt],
|
||||
[Writes, Prompt, Writes, Prompt],
|
||||
[Prompt, Prompt, Prompt, Prompt],
|
||||
];
|
||||
|
||||
for (parent_index, parent) in modes.into_iter().enumerate() {
|
||||
for (requested_index, requested) in modes.into_iter().enumerate() {
|
||||
assert_eq!(
|
||||
parent.restrict_to(requested),
|
||||
expected[parent_index][requested_index],
|
||||
"parent: {parent:?}, requested: {requested:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_stdio_command_server_config() {
|
||||
let cfg: McpServerConfig = toml::from_str(
|
||||
|
||||
@@ -362,6 +362,18 @@ pub enum WebSearchMode {
|
||||
Live,
|
||||
}
|
||||
|
||||
impl WebSearchMode {
|
||||
/// Restricts search to the access permitted by both modes.
|
||||
pub fn restrict_to(self, requested: Self) -> Self {
|
||||
match (self, requested) {
|
||||
(Self::Disabled, _) | (_, Self::Disabled) => Self::Disabled,
|
||||
(Self::Cached, _) | (_, Self::Cached) => Self::Cached,
|
||||
(Self::Indexed, _) | (_, Self::Indexed) => Self::Indexed,
|
||||
(Self::Live, Self::Live) => Self::Live,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A model-facing surface on which a tool can be exposed.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -768,6 +780,32 @@ mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_restrictions_never_expand_either_mode() {
|
||||
use WebSearchMode::Cached;
|
||||
use WebSearchMode::Disabled;
|
||||
use WebSearchMode::Indexed;
|
||||
use WebSearchMode::Live;
|
||||
|
||||
let modes = [Disabled, Cached, Indexed, Live];
|
||||
let expected = [
|
||||
[Disabled, Disabled, Disabled, Disabled],
|
||||
[Disabled, Cached, Cached, Cached],
|
||||
[Disabled, Cached, Indexed, Indexed],
|
||||
[Disabled, Cached, Indexed, Live],
|
||||
];
|
||||
|
||||
for (parent_index, parent) in modes.into_iter().enumerate() {
|
||||
for (requested_index, requested) in modes.into_iter().enumerate() {
|
||||
assert_eq!(
|
||||
parent.restrict_to(requested),
|
||||
expected[parent_index][requested_index],
|
||||
"parent: {parent:?}, requested: {requested:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_mask_can_clear_optional_fields() {
|
||||
let mode = CollaborationMode {
|
||||
|
||||
Reference in New Issue
Block a user