mod support;
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use std::{
fs,
io::Write,
path::{Path, PathBuf},
process::{Command, Output, Stdio},
};
struct Fixture(PathBuf);
impl Drop for Fixture {
fn drop(&mut self) {
support::remove_dir_all(&self.0);
}
}
fn fixture() -> Fixture {
let root = fs::canonicalize(std::env::temp_dir())
.unwrap()
.join(format!("shepherd-transport-bind-{}", uuid::Uuid::now_v7()));
fs::create_dir(&root).unwrap();
assert!(
Command::new("git")
.args(["init", "--quiet"])
.current_dir(&root)
.status()
.unwrap()
.success()
);
Fixture(root)
}
fn invoke(root: &Path, args: &[&str], input: Option<Value>) -> Output {
let mut command = Command::new(env!("CARGO_BIN_EXE_shepherd"));
command
.args(args)
.current_dir(root)
.env("SHEPHERD_HOME", root.join("isolated-home"))
.env_remove("SHEPHERD_NATIVE_DESCRIPTOR")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = command.spawn().unwrap();
if let Some(input) = input {
child
.stdin
.take()
.unwrap()
.write_all(&serde_json::to_vec(&input).unwrap())
.unwrap();
}
drop(child.stdin.take());
child.wait_with_output().unwrap()
}
fn install(root: &Path, target: &str) -> PathBuf {
let installed = root.join(format!("installed-{target}"));
let result = invoke(
root,
&[
"compile",
"--target",
target,
"--out",
installed.to_str().unwrap(),
],
None,
);
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
installed
}
fn request(installed: &Path, harness: &str) -> Value {
json!({"schema":"shepherd.native-transport-bind/1", "harness":harness,
"installed_package_root":installed})
}
#[test]
fn ordinary_installed_carriers_receive_native_selected_protected_transport() {
for (target, harness) in [("claude", "claude-code"), ("codex", "codex"), ("pi", "pi")] {
let fixture = fixture();
let root = &fixture.0;
let installed = install(root, target);
let result = invoke(
root,
&["dispatch", "transport-bind"],
Some(request(&installed, harness)),
);
assert!(
result.status.success(),
"{harness}: {}",
String::from_utf8_lossy(&result.stderr)
);
let binding: Value = serde_json::from_slice(&result.stdout).unwrap();
assert_eq!(binding["schema"], "shepherd.native-transport-binding/1");
let path = PathBuf::from(binding["descriptor_path"].as_str().unwrap());
assert!(path.starts_with(root.join(".shepherd/tmp/native-transports")));
let descriptor: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
assert_eq!(descriptor["schema"], "shepherd.native-transport/2");
assert_eq!(descriptor["project_root"], root.to_str().unwrap());
assert_eq!(
descriptor["installed_package_root"],
installed.to_str().unwrap()
);
let binary = fs::canonicalize(env!("CARGO_BIN_EXE_shepherd")).unwrap();
assert_eq!(descriptor["binary"], binary.to_str().unwrap());
assert_eq!(
descriptor["candidate_sha256"],
Sha256::digest(fs::read(binary).unwrap())
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>()
);
assert_eq!(descriptor["env"], json!({}));
assert!(descriptor.get("auth_snapshot").is_none());
assert!(
!root.join(".shepherd/runs").exists(),
"package binding creates no run or dispatch authority"
);
#[cfg(unix)]
{
use std::os::unix::fs::{MetadataExt, PermissionsExt};
let metadata = fs::metadata(&path).unwrap();
assert_eq!(metadata.nlink(), 1);
assert_eq!(metadata.permissions().mode() & 0o777, 0o600);
assert_eq!(
fs::metadata(path.parent().unwrap())
.unwrap()
.permissions()
.mode()
& 0o777,
0o700
);
}
}
}
#[test]
fn transport_binding_rejects_caller_authority_fields_and_mismatched_carriers() {
let fixture = fixture();
let root = &fixture.0;
let installed = install(root, "pi");
for field in [
"binary",
"project_root",
"session_id",
"role",
"env",
"auth_snapshot",
"candidate_sha256",
"destination",
] {
let mut input = request(&installed, "pi");
input[field] = json!("caller-choice");
let result = invoke(root, &["dispatch", "transport-bind"], Some(input));
assert!(!result.status.success(), "accepted {field}");
assert!(!root.join(".shepherd/tmp/native-transports").exists());
}
let wrong = invoke(
root,
&["dispatch", "transport-bind"],
Some(request(&installed, "codex")),
);
assert!(!wrong.status.success());
assert!(!root.join(".shepherd/tmp/native-transports").exists());
fs::write(
installed.join("skills/start/SKILL.md"),
b"unverified startup override\n",
)
.unwrap();
let changed = invoke(
root,
&["dispatch", "transport-bind"],
Some(request(&installed, "pi")),
);
assert!(!changed.status.success());
assert!(!root.join(".shepherd/tmp/native-transports").exists());
}
#[cfg(unix)]
#[test]
fn transport_binding_refuses_installed_and_destination_symlinks() {
let fixture = fixture();
let root = &fixture.0;
let installed = install(root, "pi");
let alias = root.join("installed-alias");
std::os::unix::fs::symlink(&installed, &alias).unwrap();
let result = invoke(
root,
&["dispatch", "transport-bind"],
Some(request(&alias, "pi")),
);
assert!(!result.status.success());
let outside = root.join("outside");
fs::create_dir(&outside).unwrap();
fs::create_dir_all(root.join(".shepherd")).unwrap();
std::os::unix::fs::symlink(&outside, root.join(".shepherd/tmp")).unwrap();
let result = invoke(
root,
&["dispatch", "transport-bind"],
Some(request(&installed, "pi")),
);
assert!(!result.status.success());
assert_eq!(fs::read_dir(outside).unwrap().count(), 0);
}