11use super :: ShellRequest ;
2- use crate :: SkillMetadata ;
32use crate :: error:: CodexErr ;
43use crate :: error:: SandboxErr ;
54use crate :: exec:: ExecCapturePolicy ;
@@ -13,8 +12,6 @@ use crate::sandboxing::ExecOptions;
1312use crate :: sandboxing:: ExecRequest ;
1413use crate :: sandboxing:: SandboxPermissions ;
1514use crate :: shell:: ShellType ;
16- use crate :: skills_load_input_from_config;
17- use crate :: tools:: runtimes:: ExecveSessionApproval ;
1815use crate :: tools:: runtimes:: build_sandbox_command;
1916use crate :: tools:: sandboxing:: SandboxAttempt ;
2017use crate :: tools:: sandboxing:: ToolCtx ;
@@ -31,7 +28,6 @@ use codex_protocol::models::PermissionProfile;
3128use codex_protocol:: permissions:: FileSystemSandboxPolicy ;
3229use codex_protocol:: permissions:: NetworkSandboxPolicy ;
3330use codex_protocol:: protocol:: AskForApproval ;
34- use codex_protocol:: protocol:: ExecApprovalRequestSkillMetadata ;
3531use codex_protocol:: protocol:: NetworkPolicyRuleAction ;
3632use codex_protocol:: protocol:: ReviewDecision ;
3733use codex_protocol:: protocol:: SandboxPolicy ;
@@ -74,9 +70,6 @@ const REJECT_SANDBOX_APPROVAL_REASON: &str =
7470 "approval required by policy, but AskForApproval::Granular.sandbox_approval is false" ;
7571const REJECT_RULES_APPROVAL_REASON : & str =
7672 "approval required by policy rule, but AskForApproval::Granular.rules is false" ;
77- const REJECT_SKILL_APPROVAL_REASON : & str =
78- "approval required by skill, but AskForApproval::Granular.skill_approval is false" ;
79-
8073fn approval_sandbox_permissions (
8174 sandbox_permissions : SandboxPermissions ,
8275 additional_permissions_preapproved : bool ,
@@ -327,9 +320,6 @@ struct CoreShellActionProvider {
327320
328321#[ allow( clippy:: large_enum_variant) ]
329322enum DecisionSource {
330- SkillScript {
331- skill : SkillMetadata ,
332- } ,
333323 PrefixRule ,
334324 /// Often, this is `is_safe_command()`.
335325 UnmatchedCommandFallback ,
@@ -341,11 +331,6 @@ fn execve_prompt_is_rejected_by_policy(
341331) -> Option < & ' static str > {
342332 match ( approval_policy, decision_source) {
343333 ( AskForApproval :: Never , _) => Some ( PROMPT_CONFLICT_REASON ) ,
344- ( AskForApproval :: Granular ( granular_config) , DecisionSource :: SkillScript { .. } )
345- if !granular_config. allows_skill_approval ( ) =>
346- {
347- Some ( REJECT_SKILL_APPROVAL_REASON )
348- }
349334 ( AskForApproval :: Granular ( granular_config) , DecisionSource :: PrefixRule )
350335 if !granular_config. allows_rules_approval ( ) =>
351336 {
@@ -397,25 +382,13 @@ impl CoreShellActionProvider {
397382 }
398383 }
399384
400- fn skill_escalation_execution ( skill : & SkillMetadata ) -> EscalationExecution {
401- let permission_profile = skill. permission_profile . clone ( ) . unwrap_or_default ( ) ;
402- if permission_profile. is_empty ( ) {
403- EscalationExecution :: TurnDefault
404- } else {
405- EscalationExecution :: Permissions ( EscalationPermissions :: PermissionProfile (
406- permission_profile,
407- ) )
408- }
409- }
410-
411385 async fn prompt (
412386 & self ,
413387 program : & AbsolutePathBuf ,
414388 argv : & [ String ] ,
415389 workdir : & AbsolutePathBuf ,
416390 stopwatch : & Stopwatch ,
417391 additional_permissions : Option < PermissionProfile > ,
418- decision_source : & DecisionSource ,
419392 ) -> anyhow:: Result < ReviewDecision > {
420393 let command = join_program_and_argv ( program, argv) ;
421394 let workdir = workdir. to_path_buf ( ) ;
@@ -442,28 +415,6 @@ impl CoreShellActionProvider {
442415 )
443416 . await ;
444417 }
445- let available_decisions = vec ! [
446- Some ( ReviewDecision :: Approved ) ,
447- // Currently, ApprovedForSession is only honored for skills,
448- // so only offer it for skill script approvals.
449- if matches!( decision_source, DecisionSource :: SkillScript { .. } ) {
450- Some ( ReviewDecision :: ApprovedForSession )
451- } else {
452- None
453- } ,
454- Some ( ReviewDecision :: Abort ) ,
455- ]
456- . into_iter ( )
457- . flatten ( )
458- . collect ( ) ;
459- let skill_metadata = match decision_source {
460- DecisionSource :: SkillScript { skill } => {
461- Some ( ExecApprovalRequestSkillMetadata {
462- path_to_skills_md : skill. path_to_skills_md . clone ( ) ,
463- } )
464- }
465- DecisionSource :: PrefixRule | DecisionSource :: UnmatchedCommandFallback => None ,
466- } ;
467418 session
468419 . request_command_approval (
469420 & turn,
@@ -475,48 +426,14 @@ impl CoreShellActionProvider {
475426 /*network_approval_context*/ None ,
476427 /*proposed_execpolicy_amendment*/ None ,
477428 additional_permissions,
478- skill_metadata,
479- Some ( available_decisions ) ,
429+ /* skill_metadata*/ None ,
430+ Some ( vec ! [ ReviewDecision :: Approved , ReviewDecision :: Abort ] ) ,
480431 )
481432 . await
482433 } )
483434 . await )
484435 }
485436
486- /// Because we should be intercepting execve(2) calls, `program` should be
487- /// an absolute path. The idea is that we check to see whether it matches
488- /// any skills.
489- async fn find_skill ( & self , program : & AbsolutePathBuf ) -> Option < SkillMetadata > {
490- let force_reload = false ;
491- let turn_config = self . turn . config . as_ref ( ) ;
492- let plugin_outcome = self
493- . session
494- . services
495- . plugins_manager
496- . plugins_for_config ( turn_config) ;
497- let effective_skill_roots = plugin_outcome. effective_skill_roots ( ) ;
498- let skills_input = skills_load_input_from_config ( turn_config, effective_skill_roots) ;
499- let skills_outcome = self
500- . session
501- . services
502- . skills_manager
503- . skills_for_cwd ( & skills_input, force_reload)
504- . await ;
505-
506- let program_path = program. as_path ( ) ;
507- for skill in skills_outcome. skills {
508- // We intentionally ignore "enabled" status here for now.
509- let Some ( skill_root) = skill. path_to_skills_md . parent ( ) else {
510- continue ;
511- } ;
512- if program_path. starts_with ( skill_root. join ( "scripts" ) ) {
513- return Some ( skill) ;
514- }
515- }
516-
517- None
518- }
519-
520437 #[ allow( clippy:: too_many_arguments) ]
521438 async fn process_decision (
522439 & self ,
@@ -540,51 +457,18 @@ impl CoreShellActionProvider {
540457 EscalationDecision :: deny ( Some ( "Execution forbidden by policy" . to_string ( ) ) )
541458 } else {
542459 match self
543- . prompt (
544- program,
545- argv,
546- workdir,
547- & self . stopwatch ,
548- prompt_permissions,
549- & decision_source,
550- )
460+ . prompt ( program, argv, workdir, & self . stopwatch , prompt_permissions)
551461 . await ?
552462 {
553463 ReviewDecision :: Approved
464+ | ReviewDecision :: ApprovedForSession
554465 | ReviewDecision :: ApprovedExecpolicyAmendment { .. } => {
555466 if needs_escalation {
556467 EscalationDecision :: escalate ( escalation_execution. clone ( ) )
557468 } else {
558469 EscalationDecision :: run ( )
559470 }
560471 }
561- ReviewDecision :: ApprovedForSession => {
562- // Currently, we only add session approvals for
563- // skill scripts because we are storing only the
564- // `program` whereas prefix rules may be restricted by a longer prefix.
565- if let DecisionSource :: SkillScript { skill } = decision_source {
566- tracing:: debug!(
567- "Adding session approval for {program:?} due to user approval of skill script {skill:?}"
568- ) ;
569- self . session
570- . services
571- . execve_session_approvals
572- . write ( )
573- . await
574- . insert (
575- program. clone ( ) ,
576- ExecveSessionApproval {
577- skill : Some ( skill. clone ( ) ) ,
578- } ,
579- ) ;
580- }
581-
582- if needs_escalation {
583- EscalationDecision :: escalate ( escalation_execution. clone ( ) )
584- } else {
585- EscalationDecision :: run ( )
586- }
587- }
588472 ReviewDecision :: NetworkPolicyAmendment {
589473 network_policy_amendment,
590474 } => match network_policy_amendment. action {
@@ -641,69 +525,6 @@ impl EscalationPolicy for CoreShellActionProvider {
641525 "Determining escalation action for command {program:?} with args {argv:?} in {workdir:?}"
642526 ) ;
643527
644- // Check to see whether `program` has an existing entry in
645- // `execve_session_approvals`. If so, we can skip policy checks and user
646- // prompts and go straight to allowing execution.
647- let approval = {
648- self . session
649- . services
650- . execve_session_approvals
651- . read ( )
652- . await
653- . get ( program)
654- . cloned ( )
655- } ;
656- if let Some ( approval) = approval {
657- tracing:: debug!(
658- "Found session approval for {program:?}, allowing execution without further checks"
659- ) ;
660- let execution = approval
661- . skill
662- . as_ref ( )
663- . map ( Self :: skill_escalation_execution)
664- . unwrap_or ( EscalationExecution :: TurnDefault ) ;
665-
666- return Ok ( EscalationDecision :: escalate ( execution) ) ;
667- }
668-
669- // In the usual case, the execve wrapper reports the command being
670- // executed in `program`, so a direct skill lookup is sufficient.
671- if let Some ( skill) = self . find_skill ( program) . await {
672- // For now, scripts that look like they belong to skills bypass
673- // general exec policy evaluation. Permissionless skills inherit the
674- // turn sandbox directly; skills with declared permissions still
675- // prompt here before applying their permission profile.
676- let prompt_permissions = skill. permission_profile . clone ( ) ;
677- if prompt_permissions
678- . as_ref ( )
679- . is_none_or ( PermissionProfile :: is_empty)
680- {
681- tracing:: debug!(
682- "Matched {program:?} to permissionless skill {skill:?}, inheriting turn sandbox"
683- ) ;
684- return Ok ( EscalationDecision :: escalate (
685- EscalationExecution :: TurnDefault ,
686- ) ) ;
687- }
688- tracing:: debug!( "Matched {program:?} to skill {skill:?}, prompting for approval" ) ;
689- let needs_escalation = true ;
690- let decision_source = DecisionSource :: SkillScript {
691- skill : skill. clone ( ) ,
692- } ;
693- return self
694- . process_decision (
695- Decision :: Prompt ,
696- needs_escalation,
697- program,
698- argv,
699- workdir,
700- prompt_permissions,
701- Self :: skill_escalation_execution ( & skill) ,
702- decision_source,
703- )
704- . await ;
705- }
706-
707528 let evaluation = {
708529 let policy = self . policy . read ( ) . await ;
709530 evaluate_intercepted_exec_policy (
@@ -746,7 +567,6 @@ impl EscalationPolicy for CoreShellActionProvider {
746567 . macos_seatbelt_profile_extensions
747568 . as_ref ( ) ,
748569 ) ,
749- DecisionSource :: SkillScript { .. } => unreachable ! ( "handled above" ) ,
750570 } ;
751571 self . process_decision (
752572 evaluation. decision ,
0 commit comments