kcode-server-object-envelopes 0.1.1

Typed application payload envelopes stored by KennedyServer in Kweb objects
Documentation
use chrono::{DateTime, Utc};
use kcode_kweb_db::ObjectId;
use kcode_server_object_envelopes::{
    StoredArtifact, StoredProvenance, decode_file, decode_provenance, encode_file,
    encode_provenance, sanitize_file_name,
};

fn object_id(value: u8) -> ObjectId {
    ObjectId::from_bytes([128, 1, 2, 3, 4, value]).unwrap()
}

#[test]
fn file_envelope_preserves_safe_metadata_and_exact_bytes() {
    let encoded = encode_file(
        "pending:9",
        Some("../voice note.ogg"),
        "audio/ogg",
        Some("voice"),
        b"OggS payload".to_vec(),
    )
    .unwrap();
    let decoded = decode_file(object_id(5), encoded).unwrap();
    assert_eq!(decoded.object_id, object_id(5));
    assert_eq!(decoded.file_name, "voice note.ogg");
    assert_eq!(decoded.media_type, "audio/ogg");
    assert_eq!(decoded.transport_kind.as_deref(), Some("voice"));
    assert_eq!(decoded.bytes, b"OggS payload");
    assert!(decoded.enveloped);
}

#[test]
fn file_envelope_uses_bounded_fallbacks_and_rejects_trailing_bytes() {
    let encoded = encode_file(
        "pending:12",
        Some("../\n"),
        "not a media type",
        Some("voice! note"),
        vec![1, 2, 3],
    )
    .unwrap();
    let decoded = decode_file(object_id(6), encoded.clone()).unwrap();
    assert_eq!(decoded.file_name, "object-12.bin");
    assert_eq!(decoded.media_type, "application/octet-stream");
    assert_eq!(decoded.transport_kind.as_deref(), Some("voicenote"));

    let mut with_trailing_byte = encoded;
    with_trailing_byte.push(0);
    assert!(decode_file(object_id(6), with_trailing_byte).is_err());
    assert_eq!(
        sanitize_file_name("folder/résumé.pdf", "object.bin"),
        "résumé.pdf"
    );
}

#[test]
fn unrestricted_logical_ids_produce_safe_decodable_fallbacks() {
    let encoded = encode_file(
        "../x",
        None,
        "application/octet-stream",
        None,
        vec![1, 2, 3],
    )
    .unwrap();
    let decoded = decode_file(object_id(12), encoded).unwrap();

    assert_eq!(decoded.file_name, "object-.._x.bin");
    assert_eq!(decoded.bytes, [1, 2, 3]);
    assert!(decoded.enveloped);
}

#[test]
fn every_encoded_file_decodes_with_hostile_empty_and_long_names() {
    let hostile_id = format!("pending:{}", "/\\\"\r\n".repeat(500));
    let very_long_id = format!("pending:{}", "x".repeat(10_000));
    let very_long_name = format!("directory\\{}", "é".repeat(500));
    let cases = [
        ("", None),
        (hostile_id.as_str(), Some("../\\\n")),
        (very_long_id.as_str(), None),
        ("pending:7", Some(very_long_name.as_str())),
    ];

    for (logical_id, file_name) in cases {
        let encoded = encode_file(
            logical_id,
            file_name,
            "application/octet-stream",
            Some("file"),
            vec![0, 1, 2, 3],
        )
        .unwrap();
        let decoded = decode_file(object_id(9), encoded).unwrap();

        assert!(decoded.enveloped);
        assert_eq!(decoded.bytes, [0, 1, 2, 3]);
        assert!(!decoded.file_name.trim().is_empty());
        assert!(decoded.file_name.len() <= 255);
        assert!(
            !decoded.file_name.chars().any(|character| {
                character.is_control() || matches!(character, '/' | '\\' | '"')
            })
        );
    }

    assert_eq!(sanitize_file_name("", ""), "object.bin");
    let sanitized_fallback = sanitize_file_name("", &format!("fallback\\{}", "é".repeat(500)));
    assert!(!sanitized_fallback.is_empty());
    assert!(sanitized_fallback.len() <= 255);
}

#[test]
fn filename_basenames_and_encoded_bytes_are_platform_independent() {
    assert_eq!(
        sanitize_file_name("/srv/files/report.pdf", "object.bin"),
        "report.pdf"
    );
    assert_eq!(
        sanitize_file_name(r"C:\files\report.pdf", "object.bin"),
        "report.pdf"
    );

    let slash_encoded = encode_file(
        "pending:10",
        Some("/srv/files/report.pdf"),
        "application/pdf",
        None,
        b"%PDF-content".to_vec(),
    )
    .unwrap();
    let backslash_encoded = encode_file(
        "pending:10",
        Some(r"C:\files\report.pdf"),
        "application/pdf",
        None,
        b"%PDF-content".to_vec(),
    )
    .unwrap();
    assert_eq!(slash_encoded, backslash_encoded);
}

#[test]
fn dot_segment_filenames_are_normalized_and_rejected() {
    assert_eq!(sanitize_file_name(".", "fallback.bin"), "fallback.bin");
    assert_eq!(sanitize_file_name("..", "."), "object.bin");

    for file_name in [".", ".."] {
        let encoded = encode_file(
            "pending:13",
            Some(file_name),
            "application/octet-stream",
            None,
            Vec::new(),
        )
        .unwrap();
        let decoded = decode_file(object_id(13), encoded).unwrap();
        assert_eq!(decoded.file_name, "object-13.bin");

        let mut noncanonical = Vec::new();
        noncanonical.extend_from_slice(b"KFILE001");
        noncanonical.extend_from_slice(&u32::try_from(file_name.len()).unwrap().to_be_bytes());
        noncanonical.extend_from_slice(&24u32.to_be_bytes());
        noncanonical.extend_from_slice(&0u32.to_be_bytes());
        noncanonical.extend_from_slice(&0u64.to_be_bytes());
        noncanonical.extend_from_slice(file_name.as_bytes());
        noncanonical.extend_from_slice(b"application/octet-stream");
        assert!(decode_file(object_id(13), noncanonical).is_err());
    }
}

#[test]
fn reserved_kfile_markers_are_never_treated_as_historical_raw_files() {
    for payload in [
        &b"KFILE"[..],
        &b"KFILE0"[..],
        &b"KFILE00"[..],
        &b"KFILE001"[..],
        &b"KFILE002payload"[..],
    ] {
        assert!(decode_file(object_id(10), payload.to_vec()).is_err());
    }
}

#[test]
fn historical_raw_files_remain_readable_without_becoming_enveloped() {
    let decoded = decode_file(object_id(7), b"%PDF-original".to_vec()).unwrap();
    assert_eq!(decoded.file_name, format!("{}.pdf", object_id(7)));
    assert_eq!(decoded.media_type, "application/pdf");
    assert_eq!(decoded.bytes, b"%PDF-original");
    assert!(!decoded.enveloped);
}

#[test]
fn decoders_reject_impossible_declared_sizes() {
    let mut oversized_filename = Vec::new();
    oversized_filename.extend_from_slice(b"KFILE001");
    oversized_filename.extend_from_slice(&u32::MAX.to_be_bytes());
    oversized_filename.extend_from_slice(&0u32.to_be_bytes());
    oversized_filename.extend_from_slice(&0u32.to_be_bytes());
    oversized_filename.extend_from_slice(&0u64.to_be_bytes());
    assert!(decode_file(object_id(11), oversized_filename).is_err());

    let mut oversized_content = Vec::new();
    oversized_content.extend_from_slice(b"KFILE001");
    oversized_content.extend_from_slice(&1u32.to_be_bytes());
    oversized_content.extend_from_slice(&3u32.to_be_bytes());
    oversized_content.extend_from_slice(&0u32.to_be_bytes());
    oversized_content.extend_from_slice(&u64::MAX.to_be_bytes());
    oversized_content.extend_from_slice(b"a/b");
    assert!(decode_file(object_id(11), oversized_content).is_err());

    for (file_name_length, media_type_length, transport_kind_length, expected) in [
        (256u32, 1u32, 0u32, "file object filename length"),
        (1u32, 256u32, 0u32, "file object media type length"),
        (1u32, 1u32, 65u32, "file object transport kind length"),
    ] {
        let mut oversized_metadata = Vec::new();
        oversized_metadata.extend_from_slice(b"KFILE001");
        oversized_metadata.extend_from_slice(&file_name_length.to_be_bytes());
        oversized_metadata.extend_from_slice(&media_type_length.to_be_bytes());
        oversized_metadata.extend_from_slice(&transport_kind_length.to_be_bytes());
        oversized_metadata.extend_from_slice(&0u64.to_be_bytes());
        let error = decode_file(object_id(11), oversized_metadata).unwrap_err();
        assert!(error.to_string().starts_with(expected));
    }

    let mut oversized_provenance_source = Vec::new();
    oversized_provenance_source.extend_from_slice(b"KPROV\0\x01\0");
    oversized_provenance_source.extend_from_slice(&0i64.to_be_bytes());
    oversized_provenance_source.extend_from_slice(&0u32.to_be_bytes());
    oversized_provenance_source.extend_from_slice(&u32::MAX.to_be_bytes());
    assert!(decode_provenance(&oversized_provenance_source).is_err());

    let mut oversized_artifact_count = Vec::new();
    oversized_artifact_count.extend_from_slice(b"KPROV\0\x01\0");
    oversized_artifact_count.extend_from_slice(&0i64.to_be_bytes());
    oversized_artifact_count.extend_from_slice(&0u32.to_be_bytes());
    oversized_artifact_count.extend_from_slice(&0u32.to_be_bytes());
    oversized_artifact_count.extend_from_slice(&0u32.to_be_bytes());
    oversized_artifact_count.extend_from_slice(&u64::MAX.to_be_bytes());
    assert!(decode_provenance(&oversized_artifact_count).is_err());
}

#[test]
fn provenance_envelope_has_one_canonical_binary_round_trip() {
    let value = StoredProvenance {
        data: "complete source".into(),
        source: "test".into(),
        source_created_at: DateTime::parse_from_rfc3339("2026-07-23T00:00:00Z")
            .unwrap()
            .with_timezone(&Utc),
        artifacts: vec![StoredArtifact {
            object_id: object_id(8),
            original_filename: "source.bin".into(),
            media_type: "application/octet-stream".into(),
            role: "media".into(),
            byte_length: 3,
            sha256: [42; 32],
        }],
    };
    let first = encode_provenance(&value).unwrap();
    assert!(first.starts_with(b"KPROV\0\x01\0"));
    assert!(!first.starts_with(b"{"));
    let decoded = decode_provenance(&first).unwrap();
    let second = encode_provenance(&decoded).unwrap();
    assert_eq!(first, second);
    assert_eq!(decoded, value);
}

#[test]
fn provenance_decoder_rejects_unknown_truncated_and_extended_payloads() {
    let value = StoredProvenance {
        data: "data".into(),
        source: "source".into(),
        source_created_at: DateTime::parse_from_rfc3339("2026-07-23T00:00:00Z")
            .unwrap()
            .with_timezone(&Utc),
        artifacts: Vec::new(),
    };
    let encoded = encode_provenance(&value).unwrap();
    assert!(decode_provenance(b"unknown").is_err());
    assert!(decode_provenance(&encoded[..encoded.len() - 1]).is_err());
    let mut extended = encoded;
    extended.push(0);
    assert!(decode_provenance(&extended).is_err());
}