mirror of
https://github.com/openai/codex.git
synced 2026-04-24 22:54:54 +00:00
execpolicy struct
This commit is contained in:
@@ -25,6 +25,7 @@ use crate::util::error_or_panic;
|
||||
use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use codex_protocol::ConversationId;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_protocol::protocol::HasLegacyEvent;
|
||||
@@ -875,7 +876,7 @@ impl Session {
|
||||
/// commands can use the newly approved prefix.
|
||||
pub(crate) async fn persist_execpolicy_amendment(
|
||||
&self,
|
||||
amendment: &[String],
|
||||
amendment: &ExecPolicyAmendment,
|
||||
) -> Result<(), ExecPolicyUpdateError> {
|
||||
let features = self.features.clone();
|
||||
let (codex_home, current_policy) = {
|
||||
@@ -898,7 +899,7 @@ impl Session {
|
||||
crate::exec_policy::append_execpolicy_amendment_and_update(
|
||||
&codex_home,
|
||||
¤t_policy,
|
||||
amendment,
|
||||
&amendment.command,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -919,7 +920,7 @@ impl Session {
|
||||
cwd: PathBuf,
|
||||
reason: Option<String>,
|
||||
risk: Option<SandboxCommandAssessment>,
|
||||
proposed_execpolicy_amendment: Option<Vec<String>>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
) -> ReviewDecision {
|
||||
let sub_id = turn_context.sub_id.clone();
|
||||
// Add the tx_approve callback to the map before sending the request.
|
||||
|
||||
@@ -12,6 +12,7 @@ use codex_execpolicy::Policy;
|
||||
use codex_execpolicy::PolicyParser;
|
||||
use codex_execpolicy::RuleMatch;
|
||||
use codex_execpolicy::blocking_append_allow_prefix_rule;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use thiserror::Error;
|
||||
@@ -156,11 +157,8 @@ pub(crate) async fn append_execpolicy_amendment_and_update(
|
||||
/// on `prog1`, we return `Some(vec!["prog1", "--option1", "arg1"])`.
|
||||
/// - execpolicy: contains a `prompt for prefix ["prog2"]` rule. For the same command as above,
|
||||
/// we return `None` because an execpolicy prompt still applies even if we amend execpolicy to allow ["prog1", "--option1", "arg1"].
|
||||
fn proposed_execpolicy_amendment(
|
||||
evaluation: &Evaluation,
|
||||
features: &Features,
|
||||
) -> Option<Vec<String>> {
|
||||
if !features.enabled(Feature::ExecPolicy) || evaluation.decision != Decision::Prompt {
|
||||
fn proposed_execpolicy_amendment(evaluation: &Evaluation) -> Option<ExecPolicyAmendment> {
|
||||
if evaluation.decision != Decision::Prompt {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -179,7 +177,7 @@ fn proposed_execpolicy_amendment(
|
||||
}
|
||||
}
|
||||
|
||||
first_prompt_from_heuristics
|
||||
first_prompt_from_heuristics.map(ExecPolicyAmendment::from)
|
||||
}
|
||||
|
||||
/// Only return PROMPT_REASON when an execpolicy rule drove the prompt decision.
|
||||
@@ -230,10 +228,11 @@ pub(crate) async fn create_exec_approval_requirement_for_command(
|
||||
} else {
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: derive_prompt_reason(&evaluation),
|
||||
proposed_execpolicy_amendment: proposed_execpolicy_amendment(
|
||||
&evaluation,
|
||||
features,
|
||||
),
|
||||
proposed_execpolicy_amendment: if features.enabled(Feature::ExecPolicy) {
|
||||
proposed_execpolicy_amendment(&evaluation)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,7 +508,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
|
||||
requirement,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: Some(command)
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(command))
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -540,7 +539,9 @@ prefix_rule(pattern=["rm"], decision="forbidden")
|
||||
.await,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["orange".to_string()])
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"orange".to_string()
|
||||
]))
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -612,7 +613,7 @@ prefix_rule(pattern=["rm"], decision="forbidden")
|
||||
requirement,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: Some(command)
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(command))
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -693,7 +694,10 @@ prefix_rule(pattern=["rm"], decision="forbidden")
|
||||
requirement,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["cargo".to_string(), "build".to_string()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"cargo".to_string(),
|
||||
"build".to_string()
|
||||
])),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -725,7 +729,9 @@ prefix_rule(pattern=["rm"], decision="forbidden")
|
||||
.await,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["apple".to_string()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"apple".to_string()
|
||||
])),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::sandboxing::CommandSpec;
|
||||
use crate::sandboxing::SandboxManager;
|
||||
use crate::sandboxing::SandboxTransformError;
|
||||
use crate::state::SessionServices;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use std::collections::HashMap;
|
||||
@@ -100,14 +101,14 @@ pub(crate) enum ExecApprovalRequirement {
|
||||
reason: Option<String>,
|
||||
/// Proposed execpolicy amendment to skip future approvals for similar commands
|
||||
/// See core/src/exec_policy.rs for more details on how proposed_execpolicy_amendment is determined.
|
||||
proposed_execpolicy_amendment: Option<Vec<String>>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
},
|
||||
/// Execution forbidden for this tool call.
|
||||
Forbidden { reason: String },
|
||||
}
|
||||
|
||||
impl ExecApprovalRequirement {
|
||||
pub fn proposed_execpolicy_amendment(&self) -> Option<&Vec<String>> {
|
||||
pub fn proposed_execpolicy_amendment(&self) -> Option<&ExecPolicyAmendment> {
|
||||
match self {
|
||||
Self::NeedsApproval {
|
||||
proposed_execpolicy_amendment: Some(prefix),
|
||||
|
||||
@@ -6,6 +6,7 @@ use codex_core::protocol::ApplyPatchApprovalRequestEvent;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::ExecApprovalRequestEvent;
|
||||
use codex_core::protocol::ExecPolicyAmendment;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -1581,7 +1582,8 @@ async fn approving_execpolicy_amendment_persists_policy_and_skips_future_prompts
|
||||
.await?;
|
||||
let expected_command =
|
||||
expected_command.expect("execpolicy amendment scenario should produce a shell command");
|
||||
let expected_execpolicy_amendment = vec!["touch".to_string(), "allow-prefix.txt".to_string()];
|
||||
let expected_execpolicy_amendment =
|
||||
ExecPolicyAmendment::new(vec!["touch".to_string(), "allow-prefix.txt".to_string()]);
|
||||
|
||||
let _ = mount_sse_once(
|
||||
&server,
|
||||
|
||||
@@ -17,6 +17,34 @@ pub enum SandboxRiskLevel {
|
||||
High,
|
||||
}
|
||||
|
||||
/// Proposed execpolicy change to allow commands starting with this prefix.
|
||||
///
|
||||
/// The `command` tokens form the prefix that would be added as an execpolicy
|
||||
/// `prefix_rule(..., decision="allow")`, letting the agent bypass approval for
|
||||
/// commands that start with this token sequence.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(transparent)]
|
||||
#[ts(type = "Array<string>")]
|
||||
pub struct ExecPolicyAmendment {
|
||||
pub command: Vec<String>,
|
||||
}
|
||||
|
||||
impl ExecPolicyAmendment {
|
||||
pub fn new(command: Vec<String>) -> Self {
|
||||
Self { command }
|
||||
}
|
||||
|
||||
pub fn command(&self) -> &[String] {
|
||||
&self.command
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<String>> for ExecPolicyAmendment {
|
||||
fn from(command: Vec<String>) -> Self {
|
||||
Self { command }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct SandboxCommandAssessment {
|
||||
pub description: String,
|
||||
@@ -53,8 +81,8 @@ pub struct ExecApprovalRequestEvent {
|
||||
pub risk: Option<SandboxCommandAssessment>,
|
||||
/// Proposed execpolicy amendment that can be applied to allow future runs.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional, type = "Array<string>")]
|
||||
pub proposed_execpolicy_amendment: Option<Vec<String>>,
|
||||
#[ts(optional)]
|
||||
pub proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
pub parsed_cmd: Vec<ParsedCommand>,
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ use ts_rs::TS;
|
||||
pub use crate::approvals::ApplyPatchApprovalRequestEvent;
|
||||
pub use crate::approvals::ElicitationAction;
|
||||
pub use crate::approvals::ExecApprovalRequestEvent;
|
||||
pub use crate::approvals::ExecPolicyAmendment;
|
||||
pub use crate::approvals::SandboxCommandAssessment;
|
||||
pub use crate::approvals::SandboxRiskLevel;
|
||||
|
||||
@@ -1658,7 +1659,7 @@ pub enum ReviewDecision {
|
||||
/// User has approved this command and wants to apply the proposed execpolicy
|
||||
/// amendment so future matching commands are permitted.
|
||||
ApprovedExecpolicyAmendment {
|
||||
proposed_execpolicy_amendment: Vec<String>,
|
||||
proposed_execpolicy_amendment: ExecPolicyAmendment,
|
||||
},
|
||||
|
||||
/// User has approved this command and wants to automatically approve any
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::render::highlight::highlight_bash_to_lines;
|
||||
use crate::render::renderable::ColumnRenderable;
|
||||
use crate::render::renderable::Renderable;
|
||||
use codex_core::protocol::ElicitationAction;
|
||||
use codex_core::protocol::ExecPolicyAmendment;
|
||||
use codex_core::protocol::FileChange;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::ReviewDecision;
|
||||
@@ -43,7 +44,7 @@ pub(crate) enum ApprovalRequest {
|
||||
command: Vec<String>,
|
||||
reason: Option<String>,
|
||||
risk: Option<SandboxCommandAssessment>,
|
||||
proposed_execpolicy_amendment: Option<Vec<String>>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
},
|
||||
ApplyPatch {
|
||||
id: String,
|
||||
@@ -440,7 +441,7 @@ enum ApprovalVariant {
|
||||
Exec {
|
||||
id: String,
|
||||
command: Vec<String>,
|
||||
proposed_execpolicy_amendment: Option<Vec<String>>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
},
|
||||
ApplyPatch {
|
||||
id: String,
|
||||
@@ -473,7 +474,7 @@ impl ApprovalOption {
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_options(proposed_execpolicy_amendment: Option<Vec<String>>) -> Vec<ApprovalOption> {
|
||||
fn exec_options(proposed_execpolicy_amendment: Option<ExecPolicyAmendment>) -> Vec<ApprovalOption> {
|
||||
vec![
|
||||
ApprovalOption {
|
||||
label: "Yes, proceed".to_string(),
|
||||
@@ -602,7 +603,9 @@ mod tests {
|
||||
command: vec!["echo".to_string()],
|
||||
reason: None,
|
||||
risk: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["echo".to_string()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"echo".to_string(),
|
||||
])),
|
||||
},
|
||||
tx,
|
||||
);
|
||||
@@ -613,7 +616,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
decision,
|
||||
ReviewDecision::ApprovedExecpolicyAmendment {
|
||||
proposed_execpolicy_amendment: vec!["echo".to_string()]
|
||||
proposed_execpolicy_amendment: ExecPolicyAmendment::new(vec![
|
||||
"echo".to_string()
|
||||
])
|
||||
}
|
||||
);
|
||||
saw_op = true;
|
||||
@@ -622,7 +627,7 @@ mod tests {
|
||||
}
|
||||
assert!(
|
||||
saw_op,
|
||||
"expected approval decision to emit an op with allow prefix"
|
||||
"expected approval decision to emit an op with command prefix"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ use codex_core::protocol::ExecApprovalRequestEvent;
|
||||
use codex_core::protocol::ExecCommandBeginEvent;
|
||||
use codex_core::protocol::ExecCommandEndEvent;
|
||||
use codex_core::protocol::ExecCommandSource;
|
||||
use codex_core::protocol::ExecPolicyAmendment;
|
||||
use codex_core::protocol::ExitedReviewModeEvent;
|
||||
use codex_core::protocol::FileChange;
|
||||
use codex_core::protocol::Op;
|
||||
@@ -1993,7 +1994,11 @@ fn approval_modal_exec_snapshot() {
|
||||
"this is a test reason such as one that would be produced by the model".into(),
|
||||
),
|
||||
risk: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["echo".into(), "hello".into(), "world".into()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"echo".into(),
|
||||
"hello".into(),
|
||||
"world".into(),
|
||||
])),
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -2040,7 +2045,11 @@ fn approval_modal_exec_without_reason_snapshot() {
|
||||
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
|
||||
reason: None,
|
||||
risk: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["echo".into(), "hello".into(), "world".into()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"echo".into(),
|
||||
"hello".into(),
|
||||
"world".into(),
|
||||
])),
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -2254,7 +2263,10 @@ fn status_widget_and_approval_modal_snapshot() {
|
||||
"this is a test reason such as one that would be produced by the model".into(),
|
||||
),
|
||||
risk: None,
|
||||
proposed_execpolicy_amendment: Some(vec!["echo".into(), "hello world".into()]),
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
|
||||
"echo".into(),
|
||||
"hello world".into(),
|
||||
])),
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
chat.handle_codex_event(Event {
|
||||
|
||||
Reference in New Issue
Block a user