commit cb00b55db5cb194332b8cc4e505ee2c547beb102
parent 800aa5981357bd2b71d629c83f77a26af27a342d
Author: triesap <tyson@radroots.org>
Date: Tue, 25 Aug 2026 22:02:39 +0000
service-host: keep admin socket tests path-safe
- allocate native admin fixtures beneath a short deterministic temp root
- preserve isolated random directories and automatic cleanup semantics
- exercise every client server and Unix binding vector under extbuild
- keep the helper private and test-only with no public API drift
Diffstat:
5 files changed, 45 insertions(+), 29 deletions(-)
diff --git a/crates/service_host/src/admin/client.rs b/crates/service_host/src/admin/client.rs
@@ -18,6 +18,8 @@ use hyper_util::rt::TokioIo;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::task::JoinHandle;
+#[cfg(test)]
+use super::test_support;
use super::{
ADMIN_CONTRACT_VERSION, ADMIN_ROUTE_PATH_MAX_UTF8_BYTES, AdminCorrelationId,
AdminFailureResponse, AdminHttpMethod, AdminOperationId, AdminSuccessResponse,
@@ -897,7 +899,7 @@ mod tests {
#[tokio::test]
async fn server_client_round_trip_preserves_version_and_correlation() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let server = AdminServer::new(echo_router(), AdminTransportLimits::DEFAULT, FixedEntropy)
.expect("admin server");
@@ -956,7 +958,7 @@ mod tests {
#[tokio::test]
async fn unavailable_socket_and_deadline_are_typed_and_safe() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let missing = directory.path().join("missing.sock");
let client = AdminClient::new(&missing, AdminTransportLimits::DEFAULT).expect("client");
let target = AdminClientTarget::new("/v1/status").expect("target");
@@ -992,7 +994,7 @@ mod tests {
#[tokio::test]
async fn version_malformed_duplicate_and_oversized_responses_fail_closed() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let target = AdminClientTarget::new("/v1/status").expect("target");
let cases = [
(
@@ -1092,7 +1094,7 @@ mod tests {
#[tokio::test]
async fn request_and_query_limits_fail_before_socket_access() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let missing = directory.path().join("missing.sock");
let client = AdminClient::new(
&missing,
@@ -1152,7 +1154,7 @@ mod tests {
assert!(!debug.contains("secret-id"));
assert!(!debug.contains("protected"));
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("protected-admin.sock");
let client = AdminClient::new(&socket, AdminTransportLimits::DEFAULT).expect("client");
assert!(!format!("{client:?}").contains("protected-admin.sock"));
diff --git a/crates/service_host/src/admin/mod.rs b/crates/service_host/src/admin/mod.rs
@@ -7,6 +7,8 @@ mod model;
mod peer;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod server;
+#[cfg(all(test, any(target_os = "linux", target_os = "macos")))]
+mod test_support;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod unix;
diff --git a/crates/service_host/src/admin/server.rs b/crates/service_host/src/admin/server.rs
@@ -22,6 +22,8 @@ use hyper_util::rt::{TokioIo, TokioTimer};
use tokio::sync::Semaphore;
use tokio::task::JoinSet;
+#[cfg(test)]
+use super::test_support;
use super::{
ADMIN_CONTRACT_VERSION, AdminCorrelationId, AdminError, AdminErrorCode, AdminErrorMessage,
AdminFailureResponse, AdminMutationRequest, AdminTransportLimits, UnixAdminSocketBinding,
@@ -1361,7 +1363,7 @@ mod tests {
#[tokio::test]
async fn serves_valid_json_with_exact_caller_correlation_and_no_web_headers() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let server = AdminServer::new(
echo_router(),
@@ -1401,7 +1403,7 @@ mod tests {
#[tokio::test]
async fn rejects_oversized_and_malformed_json_before_the_handler() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let calls = Arc::new(AtomicUsize::new(0));
let handler_calls = Arc::clone(&calls);
@@ -1454,7 +1456,7 @@ mod tests {
#[tokio::test]
async fn rejects_invalid_mutation_envelopes_duplicates_and_nested_null_before_dispatch() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let calls = Arc::new(AtomicUsize::new(0));
let handler_calls = Arc::clone(&calls);
@@ -1506,7 +1508,7 @@ mod tests {
#[tokio::test]
async fn parameterized_routes_percent_decode_bounded_values_without_service_authority() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let calls = Arc::new(AtomicUsize::new(0));
let handler_calls = Arc::clone(&calls);
@@ -1570,7 +1572,7 @@ mod tests {
#[tokio::test]
async fn caller_correlation_precedes_entropy_and_survives_timeout_handoff() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let entropy_calls = Arc::new(AtomicUsize::new(0));
let mut router = AdminRouter::new();
@@ -1616,7 +1618,7 @@ mod tests {
#[tokio::test]
async fn rejects_http_1_0_before_dispatch() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let calls = Arc::new(AtomicUsize::new(0));
let handler_calls = Arc::clone(&calls);
@@ -1649,7 +1651,7 @@ mod tests {
#[tokio::test]
async fn request_deadline_returns_a_safe_timeout_and_cancels_the_handler_future() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let mut router = AdminRouter::new();
router
@@ -1680,7 +1682,7 @@ mod tests {
#[tokio::test]
async fn enforces_header_query_response_and_body_correlation_boundaries() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let calls = Arc::new(AtomicUsize::new(0));
let handler_calls = Arc::clone(&calls);
@@ -1766,7 +1768,7 @@ mod tests {
#[tokio::test]
async fn connection_admission_never_exceeds_the_configured_limit() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let entered = Arc::new(Notify::new());
let release = Arc::new(Notify::new());
@@ -1827,7 +1829,7 @@ mod tests {
#[tokio::test]
async fn graceful_cancellation_stops_admission_and_drains_an_active_request() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let (socket, binding) = binding(&directory).await;
let entered = Arc::new(Notify::new());
let release = Arc::new(Notify::new());
diff --git a/crates/service_host/src/admin/test_support.rs b/crates/service_host/src/admin/test_support.rs
@@ -0,0 +1,8 @@
+use std::path::Path;
+
+pub(super) fn short_tempdir() -> tempfile::TempDir {
+ tempfile::Builder::new()
+ .prefix("rsh")
+ .tempdir_in(Path::new("/tmp"))
+ .expect("short temporary directory")
+}
diff --git a/crates/service_host/src/admin/unix.rs b/crates/service_host/src/admin/unix.rs
@@ -17,6 +17,8 @@ use rustix::fs::{
use rustix::process::geteuid;
use super::peer::{AdminPeerAuthorizationPolicy, PeerAuthorizer};
+#[cfg(test)]
+use super::test_support;
const WRITER_LOCK_FILE_NAME: &str = ".radroots-admin-writer.lock";
/// Final owner-only mode for the runtime directory.
@@ -536,7 +538,7 @@ mod tests {
#[tokio::test]
async fn binds_owner_only_socket_sets_modes_and_cleans_up() {
- let directory = tempfile::tempdir().expect("temporary directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
let authority =
UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority");
@@ -577,7 +579,7 @@ mod tests {
#[cfg(target_os = "linux")]
#[tokio::test]
async fn configured_admin_group_sets_group_access_modes_and_identity() {
- let directory = tempfile::tempdir().expect("temporary directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
let admin_gid = rustix::process::getegid().as_raw();
let policy = AdminPeerAuthorizationPolicy::with_admin_gid(admin_gid)
@@ -616,7 +618,7 @@ mod tests {
#[tokio::test]
async fn refuses_a_live_socket_owned_outside_the_writer_guard() {
- let directory = tempfile::tempdir().expect("temporary directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
let live = UnixListener::bind(&socket).expect("live listener");
let authority =
@@ -632,7 +634,7 @@ mod tests {
#[tokio::test]
async fn recovers_only_a_proven_stale_socket() {
- let directory = tempfile::tempdir().expect("temporary directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
drop(UnixListener::bind(&socket).expect("stale listener"));
assert!(socket.exists());
@@ -649,8 +651,8 @@ mod tests {
#[tokio::test]
async fn refuses_paths_outside_the_authorized_runtime_directory() {
- let directory = tempfile::tempdir().expect("runtime directory");
- let outside = tempfile::tempdir().expect("outside directory");
+ let directory = super::test_support::short_tempdir();
+ let outside = super::test_support::short_tempdir();
let authority =
UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority");
@@ -662,7 +664,7 @@ mod tests {
#[tokio::test]
async fn refuses_non_socket_entries_without_unlinking_them() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
fs::write(&socket, b"not a socket").expect("sentinel file");
let authority =
@@ -680,7 +682,7 @@ mod tests {
#[test]
fn one_writer_authority_excludes_a_second_writer() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let first = UnixAdminSocketWriterAuthority::acquire(directory.path())
.expect("first writer authority");
let error = UnixAdminSocketWriterAuthority::acquire(directory.path())
@@ -691,8 +693,8 @@ mod tests {
#[test]
fn refuses_a_symlink_runtime_directory_and_wrong_owner_identity() {
- let target = tempfile::tempdir().expect("runtime directory");
- let link_parent = tempfile::tempdir().expect("link parent");
+ let target = super::test_support::short_tempdir();
+ let link_parent = super::test_support::short_tempdir();
let link = link_parent.path().join("runtime");
symlink(target.path(), &link).expect("runtime symlink");
assert!(matches!(
@@ -716,7 +718,7 @@ mod tests {
#[tokio::test]
async fn cleanup_never_unlinks_a_replacement_socket() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let socket = directory.path().join("admin.sock");
let authority =
UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority");
@@ -734,7 +736,7 @@ mod tests {
#[test]
fn public_errors_and_authority_debug_never_reveal_runtime_paths() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let authority =
UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority");
let debug = format!("{authority:?}");
@@ -749,7 +751,7 @@ mod tests {
#[test]
fn helper_admission_checks_bind_every_identity_and_mode_dimension() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let uid = geteuid().as_raw();
assert!(validate_owner(uid, uid).is_ok());
assert_eq!(
@@ -827,7 +829,7 @@ mod tests {
#[test]
fn live_directory_revalidation_rejects_mode_identity_and_path_drift() {
- let directory = tempfile::tempdir().expect("runtime directory");
+ let directory = super::test_support::short_tempdir();
let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).unwrap();
assert!(authority.ensure_directory_identity().is_ok());
assert_eq!(