open-standards/wai/wai-pack/src/capcheck.rs
Transaction Science e864482332 wai-pack: a policy is read from its bytes: UTF-8, no byte-order mark, nesting capped at 32
Fix round, item 2. Policy::parse_bytes reads a policy as bytes; the CLI and
the corpus use it (no lossy decode). Before the value kinds, and before
anything else, these are policy-toml in both implementations:

- bytes that are not UTF-8;
- a byte-order mark (the house rule: UTF-8 with no byte-order mark);
- not TOML 1.0, an integer outside TOML's 64-bit range among it
  (pack_verify.py's reader takes any size, so it now checks the range);
- arrays and tables nested past 32, the document's table at depth 1: a
  pass of its own before the value-kind walk, so a nesting fault outranks
  a float found earlier in key order, as the Rust parser's own nesting
  limit already made it. pack_verify.py also reads a RecursionError from
  its TOML reader as policy-toml.

packaging §3.1 and §3.2 state it. Corpus: a byte-order mark, bytes that are
not UTF-8, an integer at 2^63, nesting to depth 33 and 80. policy_diff.py
(from the review's byte-level cases and seeded policies) compares every
verdict and digest of wai-pack policy-verdict with pack_verify.py's; CI
runs it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 09:09:57 -04:00

113 lines
5 KiB
Rust

//! What a packager may emit ([packaging] §3.6): only capabilities SPEC §5 registers as
//! payloads with a pinned format, each in a role it can play, and, under
//! `royalty_free_only`, only those the registry classifies royalty-free. Read from
//! the registry of the `wai` crate (`codecs`), never from the policy.
//!
//! For each rung in ladder order, the first that holds is its refusal:
//!
//! 1. `capability-unknown:<cap>`: SPEC §5 does not register the string.
//! 2. `capability-not-payload:<cap>`: it is registered as a record or a derivation
//! operation, which names no payload format.
//! 3. `capability-candidate:<cap>`: a candidate, whose payload format no revision has
//! pinned (SPEC §5, *Candidate capabilities*); an encoder MUST NOT emit it.
//! 4. `capability-role:<cap>`: a base or a floor whose capability is not standalone
//! media, or a refinement whose capability is not refinement-only.
//! 5. Under `royalty_free_only`: `license-unclassified:<cap>` where the registry gives
//! no licence class, and `license-refused:<cap>:<label>` for any class other than
//! royalty-free, with the class's SPEC §5 label.
//!
//! [packaging]: ../../../extensions/packaging.md
use crate::policy::{Policy, PolicyError, Role};
use wai::codecs::{self, Kind, LicenseClass};
/// One rung's refusal: the rung's id and the reason, which names the capability.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CapRefusal {
pub rung: String,
pub code: String,
}
/// The refusal of capability `cap` in role `role`, or `None` when a packager may emit it.
pub fn rung_refusal(cap: &str, role: Role, royalty_free_only: bool) -> Option<String> {
match codecs::capability_kind(cap) {
None => return Some(format!("capability-unknown:{cap}")),
Some(Kind::Record | Kind::Derivation) => return Some(format!("capability-not-payload:{cap}")),
Some(Kind::Payload | Kind::Companion | Kind::Refinement) => {}
}
if codecs::is_candidate(cap) {
return Some(format!("capability-candidate:{cap}"));
}
let want = match role {
Role::Base | Role::Floor => "standalone",
Role::Refinement => "refinement",
};
if codecs::capability_role(cap) != Some(want) {
return Some(format!("capability-role:{cap}"));
}
if royalty_free_only {
match codecs::capability_license(cap) {
None => return Some(format!("license-unclassified:{cap}")),
Some(LicenseClass::RoyaltyFree) => {}
Some(c) => return Some(format!("license-refused:{cap}:{}", c.label())),
}
}
None
}
/// Every rung's refusal, in ladder order; `Ok` when there is none.
pub fn check(p: &Policy) -> Result<(), Vec<CapRefusal>> {
let r: Vec<CapRefusal> = p
.ladder
.iter()
.filter_map(|x| rung_refusal(&x.capability, x.role, p.royalty_free_only).map(|code| CapRefusal { rung: x.id.clone(), code }))
.collect();
if r.is_empty() { Ok(()) } else { Err(r) }
}
/// Why a packager refuses a policy: the policy itself (§3), or what it asks to emit
/// (§3.6).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Refused {
Policy(PolicyError),
Capability(Vec<CapRefusal>),
}
impl Refused {
/// The reason a corpus states: the policy's, or the first rung's.
pub fn code(&self) -> String {
match self {
Refused::Policy(e) => e.code().to_string(),
Refused::Capability(v) => v[0].code.clone(),
}
}
}
/// A policy a packager accepts, from its bytes: parsed, checked, with the overrides
/// applied, and every rung emittable.
pub fn admit(bytes: &[u8], o: &crate::policy::Overrides) -> Result<Policy, Refused> {
let p = Policy::parse_bytes(bytes).map_err(Refused::Policy)?.effective(o);
check(&p).map_err(Refused::Capability)?;
Ok(p)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_refusal_in_its_order() {
let r = |cap, role, rf| rung_refusal(cap, role, rf);
assert_eq!(r("wai.video.int_motion", Role::Base, true), None);
assert_eq!(r("wai.video.int_refine", Role::Refinement, true), None);
assert_eq!(r("wai.video.av1", Role::Floor, true), None);
assert_eq!(r("wai.video.nope", Role::Base, false).unwrap(), "capability-unknown:wai.video.nope");
assert_eq!(r("wai.video.package", Role::Base, false).unwrap(), "capability-not-payload:wai.video.package");
assert_eq!(r("wai.splat.sog", Role::Base, false).unwrap(), "capability-candidate:wai.splat.sog");
assert_eq!(r("wai.video.int_refine", Role::Base, false).unwrap(), "capability-role:wai.video.int_refine");
assert_eq!(r("wai.video.int_motion", Role::Refinement, false).unwrap(), "capability-role:wai.video.int_motion");
assert_eq!(r("wai.meta.hdr10plus", Role::Base, false).unwrap(), "capability-role:wai.meta.hdr10plus");
assert_eq!(r("wai.video.avc", Role::Base, true).unwrap(), "license-refused:wai.video.avc:patent-pool");
assert_eq!(r("wai.video.avc", Role::Base, false), None);
}
}