The emit paths parsed an encrypted envelope's payload as the integer format, so a ciphertext WIV1 could not be written. Under SPEC §6 the payload is ciphertext and a sink decrypts it before dispatch (§6.4). Wai::check_int_payload_framing and WaiMulti::check_int_payload_framing (and so to_emit_bytes and the C packers), and wai-web's pack and packMulti, skip the check when the manifest's security.encryption is neither absent nor "none" (container::manifest_v2_plaintext reads a WAI2's security member); the decode checks the plaintext. wai-rs has no decrypting path, so no check runs after decryption here. `wai inspect` reports such a payload as encrypted rather than as parsed. The extension and the CHANGELOG say so. Tests: container::tests::an_encrypted_integer_payload_is_emitted (WAI1 and WAI2; the same bytes in plaintext are refused), the wai-web writers' test (extended), cli_wrap_intent (inspect, extended). Co-Authored-By: Claude Opus 5.5 <[email protected]>
236 lines
13 KiB
Rust
236 lines
13 KiB
Rust
//! `wai wrap` writes an envelope that pins no prior, so SPEC §7 limits the intent
|
|
//! it may claim: `replicate` only over the standard classes, where a sink accepts
|
|
//! it unpinned. It used to write `intent = replicate` over every capability,
|
|
//! `wai.image.jpegai` included. These tests run the binary itself; the rule it
|
|
//! applies is `codecs::unpinned_envelope_intent`, which the library tests check
|
|
//! for every registered capability.
|
|
//!
|
|
//! `wrap` needs no codec feature, so these run in the no-system-dependency CI
|
|
//! matrix (`--test cli_wrap_intent`).
|
|
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Output};
|
|
|
|
use wai::container::{Wai, WaiMulti};
|
|
|
|
/// The committed JPEG AI reference codestream; `wrap` treats any payload as opaque.
|
|
fn payload() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/vectors/jpegai/test256.bin")
|
|
}
|
|
|
|
fn scratch(name: &str) -> PathBuf {
|
|
let dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("cli_wrap_intent");
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let p = dir.join(name);
|
|
let _ = std::fs::remove_file(&p);
|
|
p
|
|
}
|
|
|
|
fn wrap(cap: &str, out: &Path, extra: &[&str]) -> Output {
|
|
Command::new(env!("CARGO_BIN_EXE_wai"))
|
|
.arg("wrap").arg(cap).arg(payload()).arg("-o").arg(out).args(extra)
|
|
.output()
|
|
.expect("run wai")
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_refuses_replicate_over_jpegai_and_writes_nothing() {
|
|
for extra in [&[][..], &["--intent", "replicate"][..]] {
|
|
let out = scratch("jpegai.wai");
|
|
let run = wrap("wai.image.jpegai", &out, extra);
|
|
let stderr = String::from_utf8_lossy(&run.stderr);
|
|
assert!(!run.status.success(), "{extra:?}: wrap must refuse; stderr: {stderr}");
|
|
assert!(stderr.contains("not writing intent=replicate over wai.image.jpegai"), "{stderr}");
|
|
assert!(stderr.contains("entropy-consistency"), "the refusal names the tier: {stderr}");
|
|
assert!(stderr.contains("--intent"), "the refusal says how to proceed: {stderr}");
|
|
assert!(!out.exists(), "{extra:?}: a refused wrap must not write an envelope");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_writes_another_intent_over_jpegai_when_asked() {
|
|
let out = scratch("jpegai-improve.wai");
|
|
let run = wrap("wai.image.jpegai", &out, &["--intent", "improve"]);
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = Wai::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "improve");
|
|
assert_eq!(env.replicate_refusal(), None);
|
|
assert_eq!(env.payload, std::fs::read(payload()).unwrap(), "the payload is carried byte-identically");
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_refuses_replicate_when_the_fallback_is_jpegai() {
|
|
// The primary backs an unpinned replicate, but a sink without it decodes
|
|
// through the fallback (SPEC §4 step 5), where replicate is refused.
|
|
for extra in [&["--fallback", "wai.image.jpegai"][..],
|
|
&["--fallback", "wai.image.jpegai", "--intent", "replicate"][..]] {
|
|
let out = scratch("jp2-fb-jpegai.wai");
|
|
let run = wrap("wai.image.jp2", &out, extra);
|
|
let stderr = String::from_utf8_lossy(&run.stderr);
|
|
assert!(!run.status.success(), "{extra:?}: wrap must refuse; stderr: {stderr}");
|
|
assert!(stderr.contains("with fallback wai.image.jpegai"), "{stderr}");
|
|
assert!(stderr.contains("entropy-consistency"), "the refusal names the tier: {stderr}");
|
|
assert!(!out.exists(), "{extra:?}: a refused wrap must not write an envelope");
|
|
}
|
|
// Another intent is carried, fallback and all.
|
|
let out = scratch("jp2-fb-jpegai-improve.wai");
|
|
let run = wrap("wai.image.jp2", &out, &["--fallback", "wai.image.jpegai", "--intent", "improve"]);
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = Wai::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "improve");
|
|
assert_eq!(env.fallback(), Some("wai.image.jpegai"));
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_still_writes_replicate_with_a_standard_fallback() {
|
|
let out = scratch("jp2-fb-png.wai");
|
|
let run = wrap("wai.image.jp2", &out, &["--fallback", "wai.image.png"]);
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = Wai::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "replicate");
|
|
assert_eq!(env.fallback(), Some("wai.image.png"));
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_refuses_an_unpinned_replicate_over_an_integer_exact_neural_codec() {
|
|
// Integer-exact, but `replicate` over it needs a pinned integer-exact prior,
|
|
// which `wrap` does not write.
|
|
let out = scratch("int_hyper.wai");
|
|
let run = wrap("wai.neural.int_hyper", &out, &[]);
|
|
assert!(!run.status.success());
|
|
assert!(String::from_utf8_lossy(&run.stderr).contains("no pinned prior"));
|
|
assert!(!out.exists());
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_still_writes_replicate_over_a_standard_codec() {
|
|
// `wai.image.jp2` is StandardTolerance: replicate is honoured, promising the
|
|
// standard's decoder conformance, and needs no prior.
|
|
let out = scratch("jp2.wai");
|
|
let run = wrap("wai.image.jp2", &out, &[]);
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = Wai::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "replicate");
|
|
assert_eq!(env.replicate_refusal(), None, "a sink accepts what wrap wrote");
|
|
assert_eq!(env.payload, std::fs::read(payload()).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn wrap_holds_a_companion_envelope_to_the_same_intent_rule() {
|
|
// With `--companion` the output is a WAI2 envelope; its intent is checked over
|
|
// the base payload's capability as the WAI1 form's is (and over each
|
|
// companion's). HDR dynamic metadata may bind to a neural video payload
|
|
// (SPEC §7.1), whose class is NeuralFloat.
|
|
let meta = payload();
|
|
let meta = meta.to_str().unwrap();
|
|
let hdr = ["--companion", "wai.meta.hdr10plus", meta];
|
|
for extra in [hdr.to_vec(), [&["--intent", "replicate"][..], &hdr[..]].concat()] {
|
|
let out = scratch("dcvc-hdr.wai");
|
|
let run = wrap("wai.neural.dcvc_rt", &out, &extra);
|
|
let stderr = String::from_utf8_lossy(&run.stderr);
|
|
assert!(!run.status.success(), "{extra:?}: wrap must refuse; stderr: {stderr}");
|
|
assert!(stderr.contains("not writing intent=replicate over wai.neural.dcvc_rt"), "{stderr}");
|
|
assert!(!out.exists(), "{extra:?}: a refused wrap must not write an envelope");
|
|
}
|
|
// Another intent is carried, companion and all.
|
|
let out = scratch("dcvc-hdr-improve.wai");
|
|
let run = wrap("wai.neural.dcvc_rt", &out, &[&["--intent", "improve"][..], &hdr[..]].concat());
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = WaiMulti::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "improve");
|
|
assert_eq!(env.check_component_roles(), Ok(()));
|
|
// A standard base still gets `replicate`.
|
|
let out = scratch("hevc-hdr.wai");
|
|
let run = wrap("wai.video.hevc", &out, &hdr);
|
|
assert!(run.status.success(), "{}", String::from_utf8_lossy(&run.stderr));
|
|
let env = WaiMulti::read(&out).unwrap();
|
|
assert_eq!(env.intent(), "replicate");
|
|
assert_eq!(env.check_component_roles(), Ok(()));
|
|
// The WAI2 form takes no fallback; that is the refusal, whatever the
|
|
// fallback's class, rather than the WAI1 fallback-intent check.
|
|
let out = scratch("hevc-hdr-fb.wai");
|
|
let run = wrap("wai.video.hevc", &out,
|
|
&[&["--fallback", "wai.image.jpegai"][..], &hdr[..]].concat());
|
|
let stderr = String::from_utf8_lossy(&run.stderr);
|
|
assert!(!run.status.success(), "wrap must refuse; stderr: {stderr}");
|
|
assert!(stderr.contains("--fallback names a WAI1 alternative"), "{stderr}");
|
|
assert!(!out.exists(), "a refused wrap must not write an envelope");
|
|
}
|
|
|
|
/// SPEC §5 pins the payload of each WAI-native integer codec to a binary format
|
|
/// (`extensions/integer-payloads.md`). `wai wrap` parses the payload as a sink does
|
|
/// and writes nothing when it is not its capability's format: a tool's JSON output
|
|
/// (`bad_magic`, converted first, never wrapped), another format, a `WIV1` cut inside
|
|
/// its header (`stream_length`), and a keyframe-0 `WIV1` under `wai.video.int_hyper`
|
|
/// (`keyframe_capability_mismatch`), whose magic and version are right. The binary
|
|
/// payloads wrap; `wai inspect` reports what a parse finds in an envelope another
|
|
/// writer made.
|
|
#[test]
|
|
fn wrap_refuses_a_payload_that_is_not_the_pinned_integer_format() {
|
|
let v = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/vectors");
|
|
let wrap_file = |cap: &str, input: &Path, out: &Path, extra: &[&str]| -> Output {
|
|
Command::new(env!("CARGO_BIN_EXE_wai"))
|
|
.arg("wrap").arg(cap).arg(input).arg("-o").arg(out).args(extra)
|
|
.output()
|
|
.expect("run wai")
|
|
};
|
|
for (cap, json, bin, code) in [
|
|
("wai.video.int_motion", "int_video/int_video_bitstream.json", "int_video/int_video_bitstream.wiv1", "bad_magic"),
|
|
("wai.neural.int_hyper", "int_hyper/tiny_bitstream.json", "int_hyper/tiny_bitstream.wih1", "bad_magic"),
|
|
("wai.audio.int_codec", "int_video/video_nk_bitstream.wiv1", "int_audio/audio_bitstream.wia1", "format_capability_mismatch"),
|
|
("wai.splat.int_codec", "int_splat/splat.json", "int_splat/splat.wis1", "bad_magic"),
|
|
("wai.video.int_hyper", "int_video/int_video_bitstream.wiv1", "int_video/video_nk_bitstream.wiv1", "keyframe_capability_mismatch"),
|
|
] {
|
|
let out = scratch("int-payload.wai");
|
|
let run = wrap_file(cap, &v.join(json), &out, &["--intent", "improve"]);
|
|
let stderr = String::from_utf8_lossy(&run.stderr);
|
|
assert!(!run.status.success(), "{cap} {json}: wrap must refuse; stderr: {stderr}");
|
|
assert!(stderr.contains(code) && stderr.contains("integer-payloads"), "{cap}: {stderr}");
|
|
assert!(!out.exists(), "{cap}: a refused wrap must not write an envelope");
|
|
let run = wrap_file(cap, &v.join(bin), &out, &["--intent", "improve"]);
|
|
assert!(run.status.success(), "{cap} {bin}: {}", String::from_utf8_lossy(&run.stderr));
|
|
assert_eq!(Wai::read(&out).unwrap().payload, std::fs::read(v.join(bin)).unwrap());
|
|
}
|
|
// A WIV1 cut inside its fixed header.
|
|
let cut = scratch("int-payload-cut.wiv1");
|
|
std::fs::write(&cut, &std::fs::read(v.join("int_video/int_video_bitstream.wiv1")).unwrap()[..30]).unwrap();
|
|
let out = scratch("int-payload-cut.wai");
|
|
let run = wrap_file("wai.video.int_motion", &cut, &out, &["--intent", "improve"]);
|
|
assert!(!run.status.success() && String::from_utf8_lossy(&run.stderr).contains("stream_length"), "{}", String::from_utf8_lossy(&run.stderr));
|
|
assert!(!out.exists());
|
|
// The same rule for a WAI2 base.
|
|
let out = scratch("int-payload-hdr.wai");
|
|
let run = wrap_file("wai.video.int_motion", &v.join("int_video/int_video_bitstream.json"), &out,
|
|
&["--companion", "wai.meta.hdr10plus", payload().to_str().unwrap()]);
|
|
assert!(!run.status.success() && String::from_utf8_lossy(&run.stderr).contains("bad_magic"));
|
|
assert!(!out.exists());
|
|
// inspect reports an envelope that carries the JSON form.
|
|
let out = scratch("int-payload-json.wai");
|
|
wai::container::Wai::new(
|
|
wai::container::Manifest::new("video", "improve", wai::container::ModelRequirement::new("wai.video.int_motion"),
|
|
wai::container::Conditioning::new("bytes")),
|
|
std::fs::read(v.join("int_video/int_video_bitstream.json")).unwrap(),
|
|
).write(&out).unwrap();
|
|
let run = Command::new(env!("CARGO_BIN_EXE_wai")).arg("inspect").arg(&out).output().expect("run wai");
|
|
let stdout = String::from_utf8_lossy(&run.stdout);
|
|
assert!(stdout.contains("integer payload:") && stdout.contains("bad_magic"), "{stdout}");
|
|
// ...and one whose keyframe-0 WIV1 sits under wai.video.int_hyper.
|
|
let out = scratch("int-payload-k0.wai");
|
|
wai::container::Wai::new(
|
|
wai::container::Manifest::new("video", "improve", wai::container::ModelRequirement::new("wai.video.int_hyper"),
|
|
wai::container::Conditioning::new("bytes")),
|
|
std::fs::read(v.join("int_video/int_video_bitstream.wiv1")).unwrap(),
|
|
).write(&out).unwrap();
|
|
let run = Command::new(env!("CARGO_BIN_EXE_wai")).arg("inspect").arg(&out).output().expect("run wai");
|
|
let stdout = String::from_utf8_lossy(&run.stdout);
|
|
assert!(stdout.contains("integer payload:") && stdout.contains("keyframe_capability_mismatch"), "{stdout}");
|
|
// ...and an encrypted one, whose ciphertext is checked only after decryption.
|
|
let out = scratch("int-payload-encrypted.wai");
|
|
let mut m = wai::container::Manifest::new("video", "improve", wai::container::ModelRequirement::new("wai.video.int_motion"),
|
|
wai::container::Conditioning::new("bytes"));
|
|
m.security = Some(wai::container::Security::new("aes-256-gcm", 7));
|
|
wai::container::Wai::new(m, vec![0x5a; 64]).write(&out).unwrap();
|
|
let run = Command::new(env!("CARGO_BIN_EXE_wai")).arg("inspect").arg(&out).output().expect("run wai");
|
|
let stdout = String::from_utf8_lossy(&run.stdout);
|
|
assert!(stdout.contains("integer payload: encrypted"), "{stdout}");
|
|
}
|