use adk_code::{
BackendCapabilities, CodeExecutor, EnvironmentPolicy, ExecutionError, ExecutionIsolation,
ExecutionLanguage, FilesystemPolicy, NetworkPolicy, RustSandboxExecutor, SandboxPolicy,
validate_policy,
};
use proptest::prelude::*;
use std::path::PathBuf;
use std::time::Duration;
fn rust_sandbox() -> RustSandboxExecutor {
RustSandboxExecutor::default()
}
fn rust_sandbox_caps() -> BackendCapabilities {
rust_sandbox().capabilities()
}
fn arb_policy_with_unenforced_control() -> impl Strategy<Value = SandboxPolicy> {
let network = prop_oneof![
Just(NetworkPolicy::Disabled),
Just(NetworkPolicy::Enabled),
];
let filesystem = prop_oneof![
Just(FilesystemPolicy::None),
"[a-z/]{1,20}".prop_map(|p| FilesystemPolicy::WorkspaceReadOnly { root: PathBuf::from(p) }),
"[a-z/]{1,20}"
.prop_map(|p| FilesystemPolicy::WorkspaceReadWrite { root: PathBuf::from(p) }),
];
let environment = prop_oneof![
Just(EnvironmentPolicy::None),
proptest::collection::vec("[A-Z_]{1,10}", 1..5).prop_map(EnvironmentPolicy::AllowList),
];
(network, filesystem, environment)
.prop_filter("at least one unenforced control must be requested", |(net, fs, env)| {
let caps = RustSandboxExecutor::default().capabilities();
let has_unenforced_network =
matches!(net, NetworkPolicy::Disabled) && !caps.enforce_network_policy;
let has_unenforced_fs =
!matches!(fs, FilesystemPolicy::None) && !caps.enforce_filesystem_policy;
let has_unenforced_env =
!matches!(env, EnvironmentPolicy::None) && !caps.enforce_environment_policy;
has_unenforced_network || has_unenforced_fs || has_unenforced_env
})
.prop_map(|(network, filesystem, environment)| SandboxPolicy {
network,
filesystem,
environment,
timeout: Duration::from_secs(30),
max_stdout_bytes: 1_048_576,
max_stderr_bytes: 1_048_576,
working_directory: None,
})
}
fn arb_policy_within_backend_capabilities() -> impl Strategy<Value = SandboxPolicy> {
(1u64..120, 1024usize..2_097_152, 1024usize..2_097_152).prop_map(
|(timeout_secs, max_stdout, max_stderr)| SandboxPolicy {
network: NetworkPolicy::Enabled,
filesystem: FilesystemPolicy::None,
environment: EnvironmentPolicy::None,
timeout: Duration::from_secs(timeout_secs),
max_stdout_bytes: max_stdout,
max_stderr_bytes: max_stderr,
working_directory: None,
},
)
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_unenforced_controls_always_rejected(
policy in arb_policy_with_unenforced_control()
) {
let caps = rust_sandbox_caps();
let result = validate_policy(&caps, &policy);
prop_assert!(
result.is_err(),
"policy requesting unenforced controls must be rejected, but was accepted"
);
let err = result.unwrap_err();
prop_assert!(
matches!(err, ExecutionError::UnsupportedPolicy(_)),
"expected UnsupportedPolicy, got: {err:?}"
);
}
#[test]
fn prop_enforced_controls_always_accepted(
policy in arb_policy_within_backend_capabilities()
) {
let caps = rust_sandbox_caps();
let result = validate_policy(&caps, &policy);
prop_assert!(
result.is_ok(),
"policy requesting only enforced controls must be accepted, but was rejected: {:?}",
result.unwrap_err()
);
}
}
#[test]
fn isolation_class_is_host_local() {
let caps = rust_sandbox_caps();
assert_eq!(
caps.isolation,
ExecutionIsolation::HostLocal,
"phase 1 Rust sandbox must report HostLocal isolation"
);
assert_ne!(caps.isolation, ExecutionIsolation::ContainerEphemeral);
assert_ne!(caps.isolation, ExecutionIsolation::InProcess);
assert_ne!(caps.isolation, ExecutionIsolation::ContainerPersistent);
assert_ne!(caps.isolation, ExecutionIsolation::ProviderHosted);
}
#[test]
fn host_local_does_not_claim_os_level_controls() {
let caps = rust_sandbox_caps();
assert_eq!(caps.isolation, ExecutionIsolation::HostLocal);
assert!(!caps.enforce_network_policy, "host-local backend must not claim network enforcement");
assert!(
!caps.enforce_filesystem_policy,
"host-local backend must not claim filesystem enforcement"
);
assert!(
!caps.enforce_environment_policy,
"host-local backend must not claim environment enforcement"
);
}
#[test]
fn host_local_claims_timeout_enforcement() {
let caps = rust_sandbox_caps();
assert!(caps.enforce_timeout, "Rust sandbox must claim timeout enforcement");
}
#[test]
fn host_local_claims_structured_output() {
let caps = rust_sandbox_caps();
assert!(caps.supports_structured_output, "Rust sandbox must claim structured output support");
}
#[test]
fn host_local_does_not_claim_advanced_features() {
let caps = rust_sandbox_caps();
assert!(
!caps.supports_process_execution,
"Rust sandbox must not claim process execution support"
);
assert!(
!caps.supports_persistent_workspace,
"Rust sandbox must not claim persistent workspace support"
);
assert!(
!caps.supports_interactive_sessions,
"Rust sandbox must not claim interactive session support"
);
}
#[test]
fn backend_name_is_descriptive() {
let executor = rust_sandbox();
let name = executor.name();
assert!(!name.is_empty(), "backend name must not be empty");
assert_eq!(name, "rust-sandbox");
}
#[test]
fn backend_supports_only_rust() {
let executor = rust_sandbox();
assert!(executor.supports_language(&ExecutionLanguage::Rust));
assert!(!executor.supports_language(&ExecutionLanguage::JavaScript));
assert!(!executor.supports_language(&ExecutionLanguage::Python));
assert!(!executor.supports_language(&ExecutionLanguage::Wasm));
assert!(!executor.supports_language(&ExecutionLanguage::Command));
}
#[test]
fn isolation_classes_are_distinct() {
let all_classes = [
ExecutionIsolation::InProcess,
ExecutionIsolation::HostLocal,
ExecutionIsolation::ContainerEphemeral,
ExecutionIsolation::ContainerPersistent,
ExecutionIsolation::ProviderHosted,
];
for (i, a) in all_classes.iter().enumerate() {
for (j, b) in all_classes.iter().enumerate() {
if i != j {
assert_ne!(a, b, "isolation classes must be distinct: {a:?} vs {b:?}");
}
}
}
}