Pin the size gate for a second message_id length

F2: the boundary test always used message_id "m-0", so a gate that measures
payload.len() + 231 -- exactly that id's fixed overhead -- passed unnoticed.
message_id rides the encoded envelope and echoes the client's own value, so a
longer id changes the overhead the gate must measure honestly.

Parametrize synthetic/sized_exactly by message_id and add a second boundary
test at a 40-byte id: 1,228,800 bytes still passes and 1,228,801 still
refuses. Add mutation entry D20L6, the "+231" formula, which dies against the
new test and survived the old one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-22 03:22:09 +02:00
commit 7f60310039
2 changed files with 73 additions and 11 deletions

View file

@ -659,12 +659,15 @@ mod tests {
encode_cultnet_message_to_vec(message, CultNetWireContract::CultNetSchemaV0).unwrap().len() as u64
}
/// A reply carrying `payload_len` bytes of payload: the envelope a view
/// answer travels in, with the document replaced by filler, so a size can
/// be chosen instead of found.
fn synthetic(payload_len: usize) -> CultNetMessage {
/// A reply carrying `payload_len` bytes of payload under a chosen
/// `message_id`: the envelope a view answer travels in, with the document
/// replaced by filler, so a size can be chosen instead of found. The
/// `message_id` is part of the encoded envelope too, so pinning the gate
/// against one length alone (every other fixture uses `m-0`) hides a gate
/// that hard-codes that length's contribution instead of measuring it.
fn synthetic_with_id(message_id: &str, payload_len: usize) -> CultNetMessage {
CultNetMessage::OperationResponse {
message_id: "m-0".into(),
message_id: message_id.into(),
service_id: MIND_SERVICE_ID.into(),
operation: "view".into(),
status: "accepted".into(),
@ -676,17 +679,26 @@ mod tests {
}
}
/// One whose encoded envelope is exactly `target` bytes. The envelope's
/// own overhead is measured rather than assumed: the length prefix is the
/// same width on either side of a megabyte, so one probe fixes it.
fn sized_exactly(target: u64) -> CultNetMessage {
fn synthetic(payload_len: usize) -> CultNetMessage {
synthetic_with_id("m-0", payload_len)
}
/// One whose encoded envelope is exactly `target` bytes, under a chosen
/// `message_id`. The envelope's own overhead is measured rather than
/// assumed: the length prefix is the same width on either side of a
/// megabyte, so one probe fixes it.
fn sized_exactly_with_id(message_id: &str, target: u64) -> CultNetMessage {
let probe = 1_200_000_usize;
let at_probe = encoded_len(&synthetic(probe));
let message = synthetic((probe as i64 + (target as i64 - at_probe as i64)) as usize);
let at_probe = encoded_len(&synthetic_with_id(message_id, probe));
let message = synthetic_with_id(message_id, (probe as i64 + (target as i64 - at_probe as i64)) as usize);
assert_eq!(encoded_len(&message), target);
message
}
fn sized_exactly(target: u64) -> CultNetMessage {
sized_exactly_with_id("m-0", target)
}
/// A connected session whose accept has been acknowledged, so its window
/// holds `MAX_PENDING_RELIABLE_PACKETS` and not one less. See the module
/// documentation: a test that measures the window exactly and does not
@ -775,6 +787,39 @@ mod tests {
.expect_err("the accepted answer filled the window, so nothing follows it");
}
/// The boundary above is pinned for one envelope shape: every reply in it
/// carries `message_id` `m-0`, three bytes, so a gate that measures
/// `payload.len() + 231` — exactly the encoded envelope's overhead for
/// that one id — passes it unnoticed. The reply echoes the client's own
/// `message_id`, so that overhead is under the client's control: at a
/// longer id, such a gate is wrong, and wrong on the side that lets an
/// oversize answer through the size check to a send that then fails
/// silently. Forty bytes is used because it is far from `m-0`'s length in
/// either direction, not chosen to sit near some other boundary.
#[test]
fn the_gates_boundary_holds_for_a_second_message_id_length() {
let long_id = format!("m-{}", "0".repeat(38));
assert_eq!(long_id.len(), 40, "a message_id far from m-0's own length");
let at = sized_exactly_with_id(&long_id, MAX_RESPONSE_BYTES);
let over = sized_exactly_with_id(&long_id, MAX_RESPONSE_BYTES + 1);
assert_eq!(
within_window(at.clone(), &long_id, "view", "huginn-yggdrasil"),
at,
"exactly the limit passes at this id's length too"
);
let refused = within_window(over.clone(), &long_id, "view", "huginn-yggdrasil");
let (correlation, answered) = decode_response(&refused).unwrap();
assert_eq!(correlation, long_id);
let HuginnMindResponse::Refused(MindRefusal::ResponseTooLarge { bytes, limit }) =
answered.expect("a refusal is an answer, not an envelope failure")
else {
panic!("one byte over the limit was not refused: {refused:?}");
};
assert_eq!((bytes, limit), (MAX_RESPONSE_BYTES + 1, MAX_RESPONSE_BYTES));
}
/// One cut spec's own reference, by its cut label.
fn spec_ref(daemon: &mut Daemon<OwnedRedbMessagePackBackingStore, NoIndex>, cut: &str) -> PipelineRef {
let query = PipelineQuery {

View file

@ -358,6 +358,23 @@ pub fn require_grammatical_instance(declared: &Slug) -> Result<(), MindRefusal>
CultNetMessage::OperationResponse { payload, .. } => payload.len() as u64,
_ => bytes.len() as u64,
},
'@
}
@{
Id = 'D20L6'
Rule = 'What is measured holds for more than one envelope shape: a longer `message_id` changes the encoded envelope, so a formula that hard-codes one length''s overhead is wrong at another.'
Test = 'serve::tests::the_gates_boundary_holds_for_a_second_message_id_length'
File = 'crates/huginn-daemon/src/serve.rs'
Old = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => bytes.len() as u64,
'@
New = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => match &reply {
CultNetMessage::OperationResponse { payload, .. } => payload.len() as u64 + 231,
_ => bytes.len() as u64,
},
'@
}
@{