fuzz/src/lib.rs
//! **monetize's fuzz entry points.** One function per target in `fuzz_targets/`, each taking
//! the bytes a stranger chose and handing them to the code monetize runs on them. Each
//! returns whether the input was ACCEPTED (it verified, parsed or was taken), which
//! `tests/seeds.rs` uses to prove the committed `ok-*` seeds still reach the deep end of
//! their parser. A panic, an overflow or a hang here is the bug a fuzzer is looking for.
//!
//! **The modes.** Most inputs are signed, and a mutated signature never verifies, so a
//! fuzzer that only mutated bytes would only ever explore the refusal. The first byte of a
//! signed target's input picks a mode: an even byte hands the rest to the code exactly as it
//! came (the refusal path: header, envelope, signature parsing); an odd byte lets the harness
//! SIGN what the rest says with the throwaway key the code under test trusts, so the
//! mutations reach the parsers that only run on a verified input. Neither mode skips any
//! check of the code under test: the harness only plays the honest signer.
//!
//! The keys and secrets below are throwaway test values derived from constants: nothing secret.
use std::collections::BTreeMap;
use std::sync::{Arc, OnceLock};
use monetize_embed::fleet::{FleetCall, OrderAck, SignedOrder};
use monetize_estate_side::pump::{
Addressee, Asked, Asker, BoxKey, Ctx, Fault, Link, Puller, PumpBox, Signed,
};
use monetize_pay::invoice::{InvoiceConfig, InvoiceProvider, IMPORT_TOKEN_HEADER};
use monetize_pay::sandbox::{SandboxConfig, SandboxProvider, SIGNATURE_HEADER};
use monetize_pay::{ChargeRequest, Market, Money, PayError, RawCallback};
use prost::Message;
/// The clock every target runs at (2027-01-15T08:00:00Z).
pub const NOW: u64 = 1_800_000_000_000;
/// The sandbox vendor's throwaway callback secret.
pub const SANDBOX_SECRET: &str = "fuzz-sandbox-secret";
/// The invoice importer's throwaway token.
pub const IMPORT_TOKEN: &str = "fuzz-import-token";
/// The reference the harness reserves at each vendor (so a callback can name an open charge).
pub const REFERENCE: &str = "fuzzprod/acme/2027";
/// The product, estate and box the Fleet targets speak as.
pub const PRODUCT: &str = "fuzzprod";
pub const INSTANCE: &str = "acme";
/// monetize's throwaway signing seed (the product key the facts, go-aheads and orders are signed with).
pub const MONETIZE_SEED: [u8; 32] = [7u8; 32];
/// A box's throwaway fleet key seed (main, twin and front: one key each).
pub fn box_seed(role: &str) -> [u8; 32] {
match role {
"main" => [11u8; 32],
"twin" => [12u8; 32],
_ => [13u8; 32],
}
}
/// monetize's throwaway signer.
pub fn monetize_signer() -> monetize::Signer {
monetize::Signer::from_bytes(&MONETIZE_SEED)
}
fn odd(mode: u8) -> bool {
mode & 1 == 1
}
// ── framing ───────────────────────────────────────────────────────────────────
/// An HTTP-ish delivery: `name: value` header lines, an empty line, then the body.
pub fn split_http(data: &[u8]) -> (Vec<(String, String)>, Vec<u8>) {
let at = data.windows(2).position(|w| w == b"\n\n");
let (head, body) = match at {
Some(i) => (&data[..i], &data[i + 2..]),
None => (&data[..0], data),
};
let headers = String::from_utf8_lossy(head)
.lines()
.filter_map(|l| {
l.split_once(':')
.map(|(k, v)| (k.trim().to_string(), v.trim().to_string()))
})
.collect();
(headers, body.to_vec())
}
/// The inverse of [`split_http`], for the seeds.
pub fn http(headers: &[(&str, &str)], body: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
for (k, v) in headers {
out.extend_from_slice(format!("{k}: {v}\n").as_bytes());
}
out.extend_from_slice(b"\n");
out.extend_from_slice(body);
out
}
fn verified<T>(r: &Result<T, PayError>) -> bool {
matches!(
r,
Ok(_) | Err(PayError::Signup(_)) | Err(PayError::Ignored(_))
)
}
fn req(reference: &str, market: Market) -> ChargeRequest {
ChargeRequest {
reference: reference.into(),
amount: Money {
minor: 12_500,
currency: market.currency(),
},
market,
description: "fuzz".into(),
payer: None,
}
}
// ── pay_callback ──────────────────────────────────────────────────────────────
/// **A payment vendor's callback** (`POST /callback/<kind>/<instance>`), on the open vendors:
/// the sandbox (its JSON body, its signature header, the signup arm, the row it names) and
/// the invoice importer (the import token, a BgMax/KTL bank file, the OCR check digits, the
/// number it minted). Mode (first byte) 0 = sandbox as sent, 1 = sandbox with the harness's
/// signature (`@H` in the body is replaced by the handle the harness reserved), 2 = invoice.
pub fn pay_callback(data: &[u8]) -> bool {
let Some((&mode, rest)) = data.split_first() else {
return false;
};
let (mut headers, mut body) = split_http(rest);
match mode % 3 {
2 => {
let p = InvoiceProvider::new(InvoiceConfig {
import_token: IMPORT_TOKEN.into(),
bankgiro: None,
});
for m in [Market::Se, Market::Fi] {
let _ = p.reserve(&req(REFERENCE, m));
}
let _ = monetize_pay::invoice::bankfile::parse_with_fallback_date(&body, NOW);
if let Ok(text) = std::str::from_utf8(&body) {
let _ = monetize_pay::time::parse_rfc3339_ms(text);
let _ = monetize_pay::invoice::ocr::bankgiro_verify(text.trim());
let _ = monetize_pay::invoice::ocr::finnish_verify(text.trim());
}
verified(&p.parse_callback(RawCallback {
headers: &headers,
body: &body,
}))
}
m => {
let p = SandboxProvider::new(SandboxConfig {
callback_secret: SANDBOX_SECRET.into(),
state_path: None,
})
.with_clock(|| NOW);
let Ok(handle) = p.reserve(&req(REFERENCE, Market::Se)) else {
return false;
};
if m == 1 {
body = replace(&body, b"@H", handle.id.as_bytes());
if let Ok(v) = serde_json::from_slice::<serde_json::Value>(&body) {
let sig = match v.get("signup").and_then(|s| s.as_str()) {
Some(session) => SandboxProvider::signup_signature(SANDBOX_SECRET, session),
None => SandboxProvider::signature(
SANDBOX_SECRET,
v.get("reference").and_then(|s| s.as_str()).unwrap_or(""),
v.get("handle").and_then(|s| s.as_str()).unwrap_or(""),
),
};
headers.push((SIGNATURE_HEADER.to_string(), sig));
}
}
verified(&p.parse_callback(RawCallback {
headers: &headers,
body: &body,
}))
}
}
}
fn replace(hay: &[u8], needle: &[u8], with: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(hay.len());
let mut i = 0;
while i < hay.len() {
if hay[i..].starts_with(needle) {
out.extend_from_slice(with);
i += needle.len();
} else {
out.push(hay[i]);
i += 1;
}
}
out
}
// ── feed_config ───────────────────────────────────────────────────────────────
/// **The operator's feed and monetize's configuration** (TOML; monetize reads no RON). Mode
/// (first byte) even: the rest is a feed (a JSON object of strings, as `PUT /feed` takes it)
/// through every intake reader the ONE box applies (plugins and their blocks and prices,
/// secrets, vendors, control principals, clouds and the other guests, the operator's
/// `monetize.toml`) and the estate side's readers of the same feed (the box pump's fleet
/// config, the front's shop door). Odd: the rest is a `monetize.toml` (the whole config with
/// its checks, a `[products.<id>]` block, a `[vendors.<kind>.<instance>]` block).
pub fn feed_config(data: &[u8]) -> bool {
let Some((&mode, rest)) = data.split_first() else {
return false;
};
if odd(mode) {
let Ok(text) = std::str::from_utf8(rest) else {
return false;
};
let _ = toml::from_str::<monetize_server::config::ProductConfig>(text);
let _ = toml::from_str::<monetize_server::config::VendorConfig>(text);
return monetize_server::config::Config::from_toml(text).is_ok();
}
let Ok(feed) = serde_json::from_slice::<BTreeMap<String, String>>(rest) else {
return false;
};
use monetize_front::intake;
let (plugins, roots, mut refused) = intake::plugins(&feed);
let (secrets, r) = intake::secrets(&feed);
refused.extend(r);
let (vendors, r) = intake::vendors(&feed);
refused.extend(r);
let (_, r) = intake::control_principals(&feed);
refused.extend(r);
let (_, r) = intake::operator_toml(&feed);
refused.extend(r);
let (clouds, r) = intake::clouds(&feed);
refused.extend(r);
for prefix in ["signup_guest.", "dns_guest."] {
let _ = intake::modules_at(&feed, prefix);
}
let _ = monetize_estate_side::pump::FleetConfig::from_feed(&feed).map(|c| c.key());
let _ = monetize_estate_side::sold_door::ShopDoor::from_feed(&feed);
let _ = (roots, secrets);
refused.is_empty() && (!plugins.is_empty() || !vendors.is_empty() || !clouds.is_empty())
}
// ── signed_facts ──────────────────────────────────────────────────────────────
/// **What a product checks before it believes monetize** (monetize-embed): a signed snapshot
/// (the entitlement cache's `refresh`), one pushed fact, a growth go-ahead, a sold estate's
/// growth, an actor ticket. Mode (first byte) even: the rest as sent, every reader; odd: the
/// harness re-signs what the rest parses as (a snapshot, else a go-ahead) with monetize's
/// throwaway key, so the mutations reach what runs after the signature.
pub fn signed_facts(data: &[u8]) -> bool {
let Some((&mode, rest)) = data.split_first() else {
return false;
};
let signer = monetize_signer();
let key = signer.verifying_key();
let Ok(cache) = monetize_embed::EntitlementCache::new(
&signer.public_key(),
"https://fuzz.invalid/billing/{tenant}",
) else {
return false;
};
if odd(mode) {
if let Ok(snap) = serde_json::from_slice::<monetize_embed::signing::Snapshot>(rest) {
let facts = snap
.facts
.into_iter()
.map(|f| signer.sign_fact_at(f, snap.issued_unix_ms))
.collect();
let snap = signer.sign_snapshot(snap.issued_unix_ms, facts);
let Ok(bytes) = serde_json::to_vec(&snap) else {
return false;
};
let n = cache.refresh(&bytes);
for f in &snap.facts {
let _ = cache.allows(&f.tenant, monetize_embed::Action::Write);
let _ = cache.notice(&f.tenant);
}
return n.is_ok();
}
if let Ok(go) = serde_json::from_slice::<monetize_embed::signing::GoAhead>(rest) {
let bytes = signer.sign_go_ahead(go.target_sectors, &go.nonce, go.issued_unix_ms);
return monetize_embed::signing::verify_go_ahead(&bytes, &key).is_ok();
}
return false;
}
let mut accepted = cache.refresh(rest).is_ok();
if let Ok(fact) = serde_json::from_slice::<monetize_product::EntitlementFact>(rest) {
accepted |= cache.push(fact).is_ok();
}
accepted |= monetize_embed::signing::verify_go_ahead(rest, &key).is_ok();
accepted |= monetize_embed::signing::verify_estate_growth(rest, &key).is_ok();
accepted |= monetize_embed::ticket::verify_ticket(
rest,
&key,
PRODUCT,
INSTANCE,
"push",
&BTreeMap::new(),
NOW as i64,
)
.is_ok();
if let Ok(text) = std::str::from_utf8(rest) {
let _ = monetize_embed::signing::base64_decode(text);
}
accepted
}
// ── fleet_door ────────────────────────────────────────────────────────────────
struct Door {
guardian: monetize_guardian::Guardian,
}
fn door() -> &'static Door {
static DOOR: OnceLock<Door> = OnceLock::new();
DOOR.get_or_init(|| {
let store =
Arc::new(monetize_guardian::Store::in_memory().expect("an in-memory guardian store"));
store
.declare(INSTANCE, PRODUCT)
.expect("declare the estate");
for role in ["main", "twin", "front"] {
let public = BoxKey::from_seed(box_seed(role)).public();
store
.register_key(
INSTANCE,
role,
&monetize_embed::signing::base64_encode(&public),
)
.expect("register a box key");
}
// An estate being laid: a new box may pin its key on its first call.
store
.open_pin(INSTANCE, "new", NOW + 3_600_000)
.expect("open a pin window");
let keys = Arc::new(monetize::Keyring::new(monetize_signer()));
let guardian = monetize_guardian::Guardian::new(
store,
keys,
monetize_guardian::GuardianConfig {
looks_in_tick: false,
..Default::default()
},
);
Door { guardian }
})
}
/// **The Fleet door** (`Fleet.Report`, `Fleet.Orders`, `Fleet.Ask`, the old sold-estate door
/// folded into `Fleet.Ask`): the rest is a protobuf `FleetSigned` as tonic hands it over;
/// the guardian authenticates it (the box key registered for the estate and role, the time
/// window) or pins a new box's key, then reads its JSON body (a report, an orders request,
/// an ask, a front's ask). Mode (first byte) odd: the harness signs the call as the box it
/// names (`main`, `twin`, `front`) at [`NOW`].
pub fn fleet_door(data: &[u8]) -> bool {
let Some((&mode, rest)) = data.split_first() else {
return false;
};
let Ok(wire) = monetize_proto::FleetSigned::decode(rest) else {
return false;
};
let mut c = FleetCall {
product: wire.product,
instance: wire.instance,
role: wire.role,
call: wire.call,
body: wire.body,
at_unix_ms: wire.at_unix_ms,
};
let mut signature = wire.signature;
if odd(mode) {
c.at_unix_ms = NOW;
signature = BoxKey::from_seed(box_seed(&c.role)).sign(&c);
}
let g = &door().guardian;
let _ = g.first_call(&c, &signature, &wire.box_key, NOW);
if let Ok(ask) = serde_json::from_str::<monetize_embed::fleet::Ask>(&c.body) {
let _ = serde_json::from_value::<monetize_embed::fleet::FrontAsk>(ask.body.clone());
let _ = serde_json::from_value::<monetize_embed::fleet::GrowSetAsk>(ask.body);
}
match c.call.as_str() {
"report" => g.report(&c, &signature, NOW).is_ok(),
"orders" => g.orders(&c, &signature, NOW).is_ok(),
"ask" => g.ask(&c, &signature, NOW).is_ok(),
_ => false,
}
}
// ── fleet_orders ──────────────────────────────────────────────────────────────
/// monetize in memory, as far as a box's pull sees it: it answers the orders it was given.
struct Wire {
orders: Vec<SignedOrder>,
}
impl Link for Wire {
fn report(&mut self, _: Signed) -> Result<u64, Fault> {
Ok(30_000)
}
fn orders(&mut self, _: Signed) -> Result<Vec<SignedOrder>, Fault> {
Ok(std::mem::take(&mut self.orders))
}
fn ask(&mut self, _: Signed) -> Result<Asked, Fault> {
Ok(Asked::default())
}
}
/// A product's box, as far as an order's body goes: it reads the bodies monetize's own
/// order kinds carry (a growth go-ahead and the go-ahead inside it, a sign-in link, a
/// refeed) and acks the order.
struct Product;
impl PumpBox for Product {
fn run(&self, ctx: &Ctx, order: &SignedOrder, ask: &mut dyn Asker) -> OrderAck {
use monetize_embed::fleet::{kind, GrowGoAhead, Refeed, SignInAgain};
let ok = match order.kind.as_str() {
kind::GROW_GO_AHEAD => match serde_json::from_str::<GrowGoAhead>(&order.body) {
Ok(g) => {
monetize_embed::signing::verify_go_ahead(g.go_ahead.as_bytes(), ctx.key).is_ok()
|| monetize_estate_side::pump::grow_set(ask, g.data_gib).is_ok()
}
Err(_) => false,
},
kind::SIGN_IN_AGAIN => serde_json::from_str::<SignInAgain>(&order.body).is_ok(),
kind::REFEED => serde_json::from_str::<Refeed>(&order.body).is_ok(),
kind::RESTATE_ENTITLEMENTS => serde_json::from_str::<serde_json::Value>(&order.body)
.ok()
.and_then(|v| {
serde_json::from_value::<monetize_embed::signing::Snapshot>(
v["snapshot"].clone(),
)
.ok()
})
.is_some_and(|snap| {
monetize_embed::signing::verify_snapshot(&snap, ctx.key).is_ok()
}),
_ => serde_json::from_str::<serde_json::Value>(&order.body).is_ok(),
};
OrderAck {
seq: order.seq,
ok,
detail: String::new(),
receipt_json: String::new(),
}
}
}
/// **The orders a box pulls** (`Fleet.Orders`' answer, as the box decodes it): the rest is a
/// protobuf `FleetOrdersAnswer`; the box's puller checks each order (monetize's signature,
/// for this box, newer than the last, in its window), runs the fresh ones and queues their
/// acks; the answers of the front's asks (the old sold-estate door's messages, prost-encoded
/// in `FleetAskAnswer.answer`) are decoded too. Mode (first byte) odd: the harness signs each
/// order with monetize's throwaway key, so the bodies are read.
pub fn fleet_orders(data: &[u8]) -> bool {
let Some((&mode, rest)) = data.split_first() else {
return false;
};
if let Ok(a) = monetize_proto::FleetAskAnswer::decode(rest) {
let _ = monetize_proto::EstateUpgradeResponse::decode(a.answer.as_slice());
let _ = monetize_proto::EstateRechargeResponse::decode(a.answer.as_slice());
let _ = monetize_proto::EstateTakeMaterialResponse::decode(a.answer.as_slice());
let _ = monetize_proto::EstateUpdateTakeResponse::decode(a.answer.as_slice());
let _ = monetize_proto::EstateUpdateReportResponse::decode(a.answer.as_slice());
}
let Ok(answer) = monetize_proto::FleetOrdersAnswer::decode(rest) else {
return false;
};
let signer = monetize_signer();
let orders: Vec<SignedOrder> = answer
.orders
.into_iter()
.map(monetize_estate_side::pump::grpc::order_of)
.map(|o| if odd(mode) { signer.sign_order(o) } else { o })
.collect();
let me = Addressee {
product: PRODUCT.into(),
instance: INSTANCE.into(),
role: "main".into(),
};
let mut puller = Puller::new(me, BoxKey::from_seed(box_seed("main")), "fuzz");
let mut wire = Wire { orders };
match puller.pull(&mut wire, &Product, Some(&signer.public_key()), 0, NOW) {
Ok(pull) => pull.acks.iter().any(|a| a.ok),
Err(_) => false,
}
}
// ── seeds ─────────────────────────────────────────────────────────────────────
/// A BgMax TK15 deposit line (the payment date) and one TK20 payment line.
pub fn bgmax_body(reference: &str, minor: u64, yyyymmdd: &str) -> String {
let tk20 = format!(
"20{:<8}{:<25}{:0>18}{:<2}{:<2}{:<12}{}",
"50501055", reference, minor, " ", " ", "", " "
);
let tk15 = format!(
"15{:<35}{}{:0>5}{:0>18}{}{:0>8}{}",
"SE1234567890", yyyymmdd, 1, minor, "SEK", 1, "K"
);
format!("{tk15}\r\n{tk20}\r\n")
}
fn with_mode(mode: u8, rest: &[u8]) -> Vec<u8> {
let mut v = vec![mode];
v.extend_from_slice(rest);
v
}
fn fact(tenant: &str) -> monetize_product::EntitlementFact {
serde_json::from_value(serde_json::json!({
"tenant": tenant,
"plan": "fuzzprod/acme/2027+10gb",
"state": "Paid",
"paid_until_unix_ms": NOW + 86_400_000,
"caps": { "pack_quota_bytes": 10_737_418_240u64 },
"source": "operator",
"signature": [],
}))
.expect("a fact")
}
fn signed_call(role: &str, call: &str, body: &str) -> Vec<u8> {
let c = FleetCall {
product: PRODUCT.into(),
instance: INSTANCE.into(),
role: role.into(),
call: call.into(),
body: body.into(),
at_unix_ms: NOW,
};
let signature = BoxKey::from_seed(box_seed(role)).sign(&c);
monetize_proto::FleetSigned {
product: c.product,
instance: c.instance,
role: c.role,
call: c.call,
body: c.body,
at_unix_ms: c.at_unix_ms,
signature,
box_key: Vec::new(),
}
.encode_to_vec()
}
fn wire_order(seq: u64, kind: &str, body: &str, sign: bool) -> monetize_proto::FleetOrder {
let mut o = SignedOrder {
seq,
product: PRODUCT.into(),
instance: INSTANCE.into(),
role: "main".into(),
kind: kind.into(),
body: body.into(),
issued_unix_ms: NOW - 1_000,
not_after_unix_ms: NOW + 600_000,
signature: Vec::new(),
};
if sign {
o = monetize_signer().sign_order(o);
}
monetize_proto::FleetOrder {
seq: o.seq,
product: o.product,
instance: o.instance,
role: o.role,
kind: o.kind,
body: o.body,
issued_unix_ms: o.issued_unix_ms,
not_after_unix_ms: o.not_after_unix_ms,
signature: o.signature,
}
}
/// **The committed seeds** (target, file name, bytes): `ok-*` ones are accepted by their
/// target, `refused-*` ones walk a refusal. `tests/seeds.rs` writes them (`write_seeds`, ignored).
pub fn seeds() -> Vec<(&'static str, String, Vec<u8>)> {
let mut out: Vec<(&'static str, String, Vec<u8>)> = Vec::new();
let mut add =
|t: &'static str, name: &str, bytes: Vec<u8>| out.push((t, name.to_string(), bytes));
// pay_callback
let body = format!(r#"{{"reference":"{REFERENCE}","handle":"@H"}}"#);
add(
"pay_callback",
"ok-sandbox-harness-signed",
with_mode(
1,
&http(&[("content-type", "application/json")], body.as_bytes()),
),
);
add(
"pay_callback",
"ok-sandbox-unknown-signup",
with_mode(1, &http(&[], br#"{"signup":"cs_fuzz_1"}"#)),
);
add(
"pay_callback",
"refused-sandbox-forged",
with_mode(0, &http(&[(SIGNATURE_HEADER, "00")], body.as_bytes())),
);
let inv = InvoiceProvider::new(InvoiceConfig {
import_token: IMPORT_TOKEN.into(),
bankgiro: None,
});
let number = inv
.reserve(&req(REFERENCE, Market::Se))
.expect("an invoice number")
.id;
add(
"pay_callback",
"ok-invoice-bgmax",
with_mode(
2,
&http(
&[(IMPORT_TOKEN_HEADER, IMPORT_TOKEN)],
bgmax_body(&number, 12_500, "20270115").as_bytes(),
),
),
);
add(
"pay_callback",
"refused-invoice-no-token",
with_mode(
2,
&http(&[], bgmax_body(&number, 12_500, "20270115").as_bytes()),
),
);
// feed_config
let feed = serde_json::json!({
"vendor.sandbox.acme.wasm": "AGFzbQEAAAA=",
"vendor.sandbox.acme.toml": "enabled = true\nhttp_hosts = [\"sandbox.invalid\"]\n",
"monetize.toml": "[policy]\n",
"fleet": "https://monetize.fuzz.invalid",
"fleet_instance": INSTANCE,
"monetize_pubkey": "00",
});
add(
"feed_config",
"ok-feed-vendor",
with_mode(0, feed.to_string().as_bytes()),
);
add(
"feed_config",
"ok-config-empty",
with_mode(1, b"data_dir = \"/var/lib/monetize\"\n"),
);
add(
"feed_config",
"refused-config-signup",
with_mode(1, b"[signup]\nx = 1\n"),
);
// signed_facts
let signer = monetize_signer();
let snap = signer.sign_snapshot(NOW, vec![signer.sign_fact_at(fact(INSTANCE), NOW)]);
add(
"signed_facts",
"ok-snapshot",
with_mode(0, &serde_json::to_vec(&snap).unwrap()),
);
add(
"signed_facts",
"ok-go-ahead",
with_mode(0, &signer.sign_go_ahead(134_217_728, "n-1", NOW as i64)),
);
add(
"signed_facts",
"ok-growth",
with_mode(
0,
&signer.sign_estate_growth(
INSTANCE,
50,
(NOW + 86_400_000) as i64,
REFERENCE,
NOW as i64,
),
),
);
let unsigned = monetize_embed::signing::Snapshot {
issued_unix_ms: NOW,
facts: vec![fact("bob")],
signature: Vec::new(),
};
add(
"signed_facts",
"ok-snapshot-harness-signed",
with_mode(1, &serde_json::to_vec(&unsigned).unwrap()),
);
// fleet_door
add(
"fleet_door",
"ok-report",
with_mode(
0,
&signed_call(
"main",
"report",
r#"{"build":"fuzz","measured_unix_ms":1800000000000}"#,
),
),
);
add(
"fleet_door",
"ok-orders",
with_mode(
0,
&signed_call("twin", "orders", r#"{"after":0,"wait_ms":0}"#),
),
);
add(
"fleet_door",
"ok-ask-harness-signed",
with_mode(
1,
&signed_call("main", "ask", r#"{"kind":"review_twin","body":{}}"#),
),
);
add(
"fleet_door",
"refused-unsigned",
with_mode(
0,
&monetize_proto::FleetSigned {
product: PRODUCT.into(),
instance: INSTANCE.into(),
role: "main".into(),
call: "report".into(),
..Default::default()
}
.encode_to_vec(),
),
);
// fleet_orders
let signed = monetize_proto::FleetOrdersAnswer {
orders: vec![wire_order(
1,
"sign_in_again",
r#"{"account":"acme","to":"a@mock.invalid","host":"acme.fuzz.invalid","ttl_secs":600}"#,
true,
)],
};
add(
"fleet_orders",
"ok-sign-in-again",
with_mode(0, &signed.encode_to_vec()),
);
let unsigned = monetize_proto::FleetOrdersAnswer {
orders: vec![
wire_order(
2,
"refeed",
r#"{"keys":{"console_public_site":"on"}}"#,
false,
),
wire_order(
3,
"grow_go_ahead",
r#"{"go_ahead":"","data_gib":50}"#,
false,
),
],
};
add(
"fleet_orders",
"ok-refeed-harness-signed",
with_mode(1, &unsigned.encode_to_vec()),
);
add(
"fleet_orders",
"refused-forged",
with_mode(0, &unsigned.encode_to_vec()),
);
out
}