Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/openshell-core/src/isolation_registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ pub enum SandboxTransport {
impl SandboxTransport {
pub fn validate(&self) -> Result<(), &'static str> {
let valid = match self {
Self::Unix { socket_path } => socket_path.is_absolute(),
// Unix transport only runs on Unix hosts, but validation may not.
Self::Unix { socket_path } => crate::paths::is_posix_absolute(socket_path),
Self::Tcp {
authority,
addresses,
Expand Down
14 changes: 14 additions & 0 deletions crates/openshell-core/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ pub fn is_file_permissions_too_open(path: &Path) -> bool {
std::fs::metadata(path).is_ok_and(|m| m.permissions().mode() & 0o077 != 0)
}

/// Whether `path` is absolute under POSIX rules, whatever the host platform.
pub fn is_posix_absolute(path: &Path) -> bool {
path.to_str().is_some_and(|path| path.starts_with('/'))
}

/// Compatibility re-export; authored policy path normalization is owned by
/// `openshell-policy-schema`.
pub use openshell_policy_schema::normalize_path;
Expand All @@ -175,6 +180,15 @@ pub use openshell_policy_schema::normalize_path;
mod tests {
use super::*;

#[test]
fn is_posix_absolute_ignores_host_path_syntax() {
assert!(is_posix_absolute(Path::new("/run/backend.sock")));
assert!(is_posix_absolute(Path::new("/")));
for path in ["", "relative", "./run", r"\run", r"C:\run", "C:/run"] {
assert!(!is_posix_absolute(Path::new(path)), "{path}");
}
}

#[test]
fn xdg_config_dir_respects_env() {
// This test checks the logic — actual env var mutation is unsafe so
Expand Down
2 changes: 1 addition & 1 deletion crates/openshell-sandbox-backend/src/boundary_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ impl SandboxTransport {
pub fn validate(&self) -> Result<(), &'static str> {
let valid = match self {
Self::Unix { socket_path } | Self::RuntimeInitiatedUnix { socket_path } => {
socket_path.is_absolute()
openshell_core::paths::is_posix_absolute(socket_path)
}
Self::Tcp {
authority,
Expand Down
6 changes: 5 additions & 1 deletion crates/openshell-server/src/compute/mutation_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,11 @@ mod tests {
if op.is_distributed() {
fleet.enter(op);
}
tokio::time::sleep(hold).await;
// Windows timers round a 1 ms sleep up to ~16 ms, so yield instead.
let until = std::time::Instant::now() + hold;
while std::time::Instant::now() < until {
tokio::task::yield_now().await;
}
if op.is_distributed() {
fleet.leave(op);
}
Expand Down
3 changes: 2 additions & 1 deletion crates/openshell-server/src/persistence/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2379,7 +2379,8 @@ async fn sqlite_concurrent_writes_return_their_own_resource_version() {
use std::sync::Arc;

const WRITERS: u64 = 8;
const WRITES: u64 = 25;
// Slow Windows fsync can starve a writer past the SQLite busy timeout.
const WRITES: u64 = if cfg!(windows) { 5 } else { 25 };

let tmp = tempfile::tempdir().expect("tempdir");
let url = on_disk_store_url(&tmp.path().join("openshell.db"));
Expand Down
32 changes: 20 additions & 12 deletions crates/openshell-supervisor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5620,6 +5620,8 @@ network_policies:
.await;
}

// Baseline enrichment adds host paths that only exist on Unix.
#[cfg(unix)]
#[tokio::test(start_paused = true)]
async fn startup_policy_write_refusal_baseline_write_back_waits_for_repair() {
// The gateway policy has network rules but lacks the proxy baseline
Expand Down Expand Up @@ -5749,6 +5751,8 @@ network_policies:
.await;
}

// Baseline enrichment adds host paths that only exist on Unix.
#[cfg(unix)]
#[tokio::test(start_paused = true)]
async fn startup_policy_write_refusal_baseline_repair_before_report_refetches() {
let mut repaired = proto_tcp_policy_fixture();
Expand Down Expand Up @@ -5892,6 +5896,8 @@ network_policies:
.await;
}

// Baseline enrichment adds host paths that only exist on Unix.
#[cfg(unix)]
#[tokio::test(start_paused = true)]
async fn startup_policy_write_refusal_write_back_outlasts_the_attempt_limit() {
// The same limit applies to a refused baseline-path write-back.
Expand Down Expand Up @@ -5979,6 +5985,8 @@ network_policies:
);
}

// Baseline enrichment adds host paths that only exist on Unix.
#[cfg(unix)]
#[tokio::test(start_paused = true)]
async fn startup_policy_write_refusal_global_policy_skips_baseline_write_back() {
use openshell_core::proto::{ConfigurationAdmissionState, PolicySource};
Expand Down Expand Up @@ -6067,18 +6075,18 @@ network_policies:
// Both writes are checked: the image policy upload to a gateway with
// no policy, and the write-back of a stored policy that lacks the
// proxy baseline paths.
let writes = [
(
"image policy upload",
unset_policy_snapshot(),
ImagePolicyDiscovery::Policy(Box::new(proto_tcp_policy_fixture())),
),
(
"baseline write-back",
settings_poll_result(Some(proto_tcp_policy_fixture()), 1, PolicySource::Sandbox),
ImagePolicyDiscovery::Missing,
),
];
let image_upload = (
"image policy upload",
unset_policy_snapshot(),
ImagePolicyDiscovery::Policy(Box::new(proto_tcp_policy_fixture())),
);
let baseline_write_back = (
"baseline write-back",
settings_poll_result(Some(proto_tcp_policy_fixture()), 1, PolicySource::Sandbox),
ImagePolicyDiscovery::Missing,
);
// Baseline enrichment adds host paths that only exist on Unix.
let writes = std::iter::once(image_upload).chain(cfg!(unix).then_some(baseline_write_back));
for (write, initial, discovery) in writes {
for code in [
tonic::Code::PermissionDenied,
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-supervisor/src/sandbox_instance_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,8 @@ pub(crate) fn prepare_private_dir(_path: &Path) -> Result<()> {
))
}

#[cfg(test)]
// Hosted sandbox instances require a Unix host.
#[cfg(all(test, unix))]
mod tests {
use super::*;
use http_body_util::BodyExt as _;
Expand Down Expand Up @@ -1050,7 +1051,6 @@ mod tests {

// Files that cannot be deleted keep the key reserved until a later
// removal deletes them. Root ignores the permission this relies on.
#[cfg(unix)]
if !nix::unistd::geteuid().is_root() {
use std::os::unix::fs::PermissionsExt as _;

Expand Down
Loading