Pass cached conversation ancestry to bash execution
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
//! A faux model runs real bash and curl with inherited conversation grants through the host hook.
|
||||
use anyhow::{Result, ensure};
|
||||
use sausage::{
|
||||
api::Api,
|
||||
cred::{Caller, Cred, grants::Grant, types::Subject},
|
||||
world::World,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
fn components() -> Result<PathBuf> {
|
||||
let userland = Path::new(env!("CARGO_MANIFEST_DIR")).join("../userland");
|
||||
let target = Path::new(env!("CARGO_TARGET_TMPDIR")).parent().unwrap();
|
||||
if !userland.join("cwd-init/init.o").is_file() {
|
||||
ensure!(
|
||||
std::process::Command::new("nu")
|
||||
.args(["--no-config-file", "cwd-init/build.nu"])
|
||||
.current_dir(&userland)
|
||||
.status()?
|
||||
.success(),
|
||||
"cwd fixture build failed"
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
std::process::Command::new("cargo")
|
||||
.args([
|
||||
"build",
|
||||
"--release",
|
||||
"--locked",
|
||||
"--target",
|
||||
"wasm32-wasip2",
|
||||
"-j",
|
||||
"2",
|
||||
"-p",
|
||||
"curl",
|
||||
"--target-dir"
|
||||
])
|
||||
.arg(target)
|
||||
.current_dir(&userland)
|
||||
.status()?
|
||||
.success(),
|
||||
"curl fixture build failed"
|
||||
);
|
||||
let release = target.join("wasm32-wasip2/release");
|
||||
if !release.join("brush.wasm").is_file() {
|
||||
ensure!(
|
||||
std::process::Command::new("nu")
|
||||
.arg("brush/build.nu")
|
||||
.current_dir(&userland)
|
||||
.status()?
|
||||
.success(),
|
||||
"shell fixture build failed"
|
||||
);
|
||||
}
|
||||
Ok(release)
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn ancestry_reaches_the_real_hook() -> Result<()> {
|
||||
let components = components()?;
|
||||
let temp = tempfile::tempdir()?;
|
||||
let world = World::open(temp.path())?;
|
||||
let disk = temp.path().join("world");
|
||||
std::fs::create_dir_all(disk.join("bin"))?;
|
||||
for (source, name) in [("curl.wasm", "curl.wasm"), ("brush.wasm", "sh.wasm")] {
|
||||
std::fs::copy(components.join(source), disk.join("bin").join(name))?;
|
||||
}
|
||||
let server = Arc::new(
|
||||
tiny_http::Server::http("127.0.0.1:0")
|
||||
.map_err(|_| anyhow::anyhow!("fixture bind failed"))?,
|
||||
);
|
||||
let port = server.server_addr().to_ip().unwrap().port();
|
||||
let cred = Arc::new(Cred::open(&world.host_dir)?);
|
||||
let caller = Caller::default();
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
let api = Arc::new(Api::open(
|
||||
cred.clone(),
|
||||
&world.host_dir,
|
||||
"localhost",
|
||||
"http://localhost:8081",
|
||||
)?);
|
||||
let runtime = Arc::new(sausage::process::Runtime::new_with_hooks(
|
||||
World {
|
||||
root: world.root.clone(),
|
||||
host_dir: world.host_dir.clone(),
|
||||
processes: world.processes.clone(),
|
||||
},
|
||||
cred.clone(),
|
||||
sausage::process::default_script(),
|
||||
Some(api),
|
||||
)?);
|
||||
sausage::harness::run_module_with_credentials(
|
||||
World {
|
||||
root: world.root.clone(),
|
||||
host_dir: world.host_dir.clone(),
|
||||
processes: world.processes.clone(),
|
||||
},
|
||||
runtime.clone(),
|
||||
cred.clone(),
|
||||
include_str!("../js/chain-test-bundle.js"),
|
||||
)
|
||||
.await?;
|
||||
let records: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(disk.join("chain-ids"))?)?;
|
||||
for (path, subject) in [
|
||||
("/token", records["parent"]["id"].to_string()),
|
||||
("/unrelated", records["unrelated"]["id"].to_string()),
|
||||
] {
|
||||
let item = cred.create_item(serde_json::from_value(json!({"name":path,"rules":[{"scheme":"http","hosts":["127.0.0.1"],"port":port,"path_prefixes":[path]}],"transform_js":sausage::cred::summary::script("bearer")?,"fields":[{"name":"token","field_type":"text"}]}))?, &caller, cred.unlocked()?)?;
|
||||
cred.set_field(
|
||||
&item.id,
|
||||
&item.fields[0].field_id,
|
||||
&token,
|
||||
&caller,
|
||||
cred.unlocked()?,
|
||||
)?;
|
||||
cred.grant_create(
|
||||
Grant {
|
||||
id: String::new(),
|
||||
items: vec![item.id],
|
||||
subject: Subject::Conversation(subject),
|
||||
exp: None,
|
||||
max_uses: None,
|
||||
uses: 0,
|
||||
revoked: false,
|
||||
validator: None,
|
||||
note: None,
|
||||
},
|
||||
&caller,
|
||||
cred.unlocked()?,
|
||||
)?;
|
||||
}
|
||||
let serving = server.clone();
|
||||
let expected = format!("Bearer {token}");
|
||||
let thread = std::thread::spawn(move || {
|
||||
let mut accepted = 0;
|
||||
while let Ok(request) = serving.recv() {
|
||||
let valid = request.headers().iter().any(|header| {
|
||||
header.field.equiv("Authorization") && header.value.as_str() == expected
|
||||
});
|
||||
if valid {
|
||||
accepted += 1;
|
||||
}
|
||||
let _ = request.respond(tiny_http::Response::from_string(if valid {
|
||||
"accepted"
|
||||
} else {
|
||||
"missing"
|
||||
}));
|
||||
}
|
||||
accepted
|
||||
});
|
||||
let source = format!(
|
||||
"globalThis.chainReady = true; globalThis.chainPort = {port};\n{}",
|
||||
include_str!("../js/chain-test-bundle.js")
|
||||
);
|
||||
let result = tokio::time::timeout(
|
||||
Duration::from_secs(90),
|
||||
sausage::harness::run_module_with_credentials(world, runtime, cred, &source),
|
||||
)
|
||||
.await;
|
||||
server.unblock();
|
||||
let accepted = thread.join().unwrap();
|
||||
result??;
|
||||
ensure!(accepted == 3, "unexpected number of injected requests");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user