Handle pre-approved permissions in zsh fork (#14431)
This commit is contained in:
parent
e99e8e4a6b
commit
19d0949aab
6 changed files with 93 additions and 1 deletions
|
|
@ -422,6 +422,9 @@ impl ShellHandler {
|
|||
network: exec_params.network.clone(),
|
||||
sandbox_permissions: effective_additional_permissions.sandbox_permissions,
|
||||
additional_permissions: normalized_additional_permissions,
|
||||
#[cfg(unix)]
|
||||
additional_permissions_preapproved: effective_additional_permissions
|
||||
.permissions_preapproved,
|
||||
justification: exec_params.justification.clone(),
|
||||
exec_approval_requirement,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ pub struct ShellRequest {
|
|||
pub network: Option<NetworkProxy>,
|
||||
pub sandbox_permissions: SandboxPermissions,
|
||||
pub additional_permissions: Option<PermissionProfile>,
|
||||
#[cfg(unix)]
|
||||
pub additional_permissions_preapproved: bool,
|
||||
pub justification: Option<String>,
|
||||
pub exec_approval_requirement: ExecApprovalRequirement,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,22 @@ const REJECT_RULES_APPROVAL_REASON: &str =
|
|||
const REJECT_SKILL_APPROVAL_REASON: &str =
|
||||
"approval required by skill, but AskForApproval::Reject.skill_approval is set";
|
||||
|
||||
fn approval_sandbox_permissions(
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
additional_permissions_preapproved: bool,
|
||||
) -> SandboxPermissions {
|
||||
if additional_permissions_preapproved
|
||||
&& matches!(
|
||||
sandbox_permissions,
|
||||
SandboxPermissions::WithAdditionalPermissions
|
||||
)
|
||||
{
|
||||
SandboxPermissions::UseDefault
|
||||
} else {
|
||||
sandbox_permissions
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn try_run_zsh_fork(
|
||||
req: &ShellRequest,
|
||||
attempt: &SandboxAttempt<'_>,
|
||||
|
|
@ -170,6 +186,10 @@ pub(super) async fn try_run_zsh_fork(
|
|||
// escalation server.
|
||||
let stopwatch = Stopwatch::new(effective_timeout);
|
||||
let cancel_token = stopwatch.cancellation_token();
|
||||
let approval_sandbox_permissions = approval_sandbox_permissions(
|
||||
req.sandbox_permissions,
|
||||
req.additional_permissions_preapproved,
|
||||
);
|
||||
let escalation_policy = CoreShellActionProvider {
|
||||
policy: Arc::clone(&exec_policy),
|
||||
session: Arc::clone(&ctx.session),
|
||||
|
|
@ -181,6 +201,7 @@ pub(super) async fn try_run_zsh_fork(
|
|||
file_system_sandbox_policy: command_executor.file_system_sandbox_policy.clone(),
|
||||
network_sandbox_policy: command_executor.network_sandbox_policy,
|
||||
sandbox_permissions: req.sandbox_permissions,
|
||||
approval_sandbox_permissions,
|
||||
prompt_permissions: req.additional_permissions.clone(),
|
||||
stopwatch: stopwatch.clone(),
|
||||
};
|
||||
|
|
@ -281,6 +302,10 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
|
|||
file_system_sandbox_policy: exec_request.file_system_sandbox_policy.clone(),
|
||||
network_sandbox_policy: exec_request.network_sandbox_policy,
|
||||
sandbox_permissions: req.sandbox_permissions,
|
||||
approval_sandbox_permissions: approval_sandbox_permissions(
|
||||
req.sandbox_permissions,
|
||||
req.additional_permissions_preapproved,
|
||||
),
|
||||
prompt_permissions: req.additional_permissions.clone(),
|
||||
stopwatch: Stopwatch::unlimited(),
|
||||
};
|
||||
|
|
@ -312,6 +337,7 @@ struct CoreShellActionProvider {
|
|||
file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
approval_sandbox_permissions: SandboxPermissions,
|
||||
prompt_permissions: Option<PermissionProfile>,
|
||||
stopwatch: Stopwatch,
|
||||
}
|
||||
|
|
@ -696,7 +722,7 @@ impl EscalationPolicy for CoreShellActionProvider {
|
|||
approval_policy: self.approval_policy,
|
||||
sandbox_policy: &self.sandbox_policy,
|
||||
file_system_sandbox_policy: &self.file_system_sandbox_policy,
|
||||
sandbox_permissions: self.sandbox_permissions,
|
||||
sandbox_permissions: self.approval_sandbox_permissions,
|
||||
enable_shell_wrapper_parsing:
|
||||
ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -165,6 +165,22 @@ fn execve_prompt_rejection_keeps_unmatched_commands_on_sandbox_flag() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn approval_sandbox_permissions_only_downgrades_preapproved_additional_permissions() {
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::WithAdditionalPermissions, true),
|
||||
SandboxPermissions::UseDefault,
|
||||
);
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::WithAdditionalPermissions, false),
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
);
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::RequireEscalated, true),
|
||||
SandboxPermissions::RequireEscalated,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_shell_script_preserves_login_flag() {
|
||||
assert_eq!(
|
||||
|
|
@ -548,6 +564,47 @@ host_executable(name = "git", paths = ["{git_path_literal}"])
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intercepted_exec_policy_treats_preapproved_additional_permissions_as_default() {
|
||||
let policy = PolicyParser::new().build();
|
||||
let program = AbsolutePathBuf::try_from(host_absolute_path(&["usr", "bin", "printf"])).unwrap();
|
||||
let argv = ["printf".to_string(), "hello".to_string()];
|
||||
let approval_policy = AskForApproval::OnRequest;
|
||||
let sandbox_policy = SandboxPolicy::new_workspace_write_policy();
|
||||
let file_system_sandbox_policy = read_only_file_system_sandbox_policy();
|
||||
|
||||
let preapproved = evaluate_intercepted_exec_policy(
|
||||
&policy,
|
||||
&program,
|
||||
&argv,
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy,
|
||||
sandbox_policy: &sandbox_policy,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_permissions: super::approval_sandbox_permissions(
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
true,
|
||||
),
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
);
|
||||
let fresh_request = evaluate_intercepted_exec_policy(
|
||||
&policy,
|
||||
&program,
|
||||
&argv,
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy,
|
||||
sandbox_policy: &sandbox_policy,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_permissions: SandboxPermissions::WithAdditionalPermissions,
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(preapproved.decision, Decision::Allow);
|
||||
assert_eq!(fresh_request.decision, Decision::Prompt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intercepted_exec_policy_rejects_disallowed_host_executable_mapping() {
|
||||
let allowed_git = host_absolute_path(&["usr", "bin", "git"]);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ pub struct UnifiedExecRequest {
|
|||
pub tty: bool,
|
||||
pub sandbox_permissions: SandboxPermissions,
|
||||
pub additional_permissions: Option<PermissionProfile>,
|
||||
#[cfg(unix)]
|
||||
pub additional_permissions_preapproved: bool,
|
||||
pub justification: Option<String>,
|
||||
pub exec_approval_requirement: ExecApprovalRequirement,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -603,6 +603,8 @@ impl UnifiedExecProcessManager {
|
|||
tty: request.tty,
|
||||
sandbox_permissions: request.sandbox_permissions,
|
||||
additional_permissions: request.additional_permissions.clone(),
|
||||
#[cfg(unix)]
|
||||
additional_permissions_preapproved: request.additional_permissions_preapproved,
|
||||
justification: request.justification.clone(),
|
||||
exec_approval_requirement,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue