160 lines
5.2 KiB
Rust
160 lines
5.2 KiB
Rust
//! 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() {
|
|
crate::common::cwd_init();
|
|
}
|
|
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!(
|
|
crate::common::build_brush()?.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:8090",
|
|
)?);
|
|
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(())
|
|
}
|