#![expect(
clippy::disallowed_types,
reason = "the approved wire-body seam: the signing canary reads one attribute out of a version document the SUT answered with"
)]
use std::fmt;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::bench::BenchError;
use crate::bench::client::{BenchClient, PreferReturn, created_identifier};
use crate::bench::pack::Fixture;
macro_rules! posture_vocabulary {
(
$(#[$meta:meta])*
$name:ident, $vocabulary:literal, {
$( $(#[$variant_meta:meta])* $variant:ident => $token:literal ),+ $(,)?
}
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum $name {
$( $(#[$variant_meta])* $variant ),+
}
impl $name {
pub const ALL: &[$name] = &[ $( $name::$variant ),+ ];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
$( $name::$variant => $token ),+
}
}
pub fn parse(token: &str) -> Result<Self, BenchError> {
Self::ALL
.iter()
.copied()
.find(|value| value.as_str() == token)
.ok_or_else(|| BenchError::UnknownToken {
vocabulary: $vocabulary,
token: token.to_owned(),
accepted: Self::ALL
.iter()
.map(|value| value.as_str())
.collect::<Vec<_>>()
.join(", "),
})
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for $name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
let token = String::deserialize(deserializer)?;
$name::parse(&token).map_err(serde::de::Error::custom)
}
}
};
}
posture_vocabulary! {
PostureItem, "posture item", {
Audit => "audit",
VersionSigning => "version_signing",
CommitValidation => "commit_validation",
Authn => "authn",
Tls => "tls",
Compression => "compression",
Tenancy => "tenancy",
}
}
impl PostureItem {
#[must_use]
pub const fn is_observable(self) -> bool {
match self {
PostureItem::Audit | PostureItem::Tenancy => false,
PostureItem::VersionSigning
| PostureItem::CommitValidation
| PostureItem::Authn
| PostureItem::Tls
| PostureItem::Compression => true,
}
}
}
posture_vocabulary! {
AuditSink, "audit sink", {
Off => "off",
Internal => "internal",
ExternalSink => "external-sink",
QueryableApi => "queryable-api",
}
}
posture_vocabulary! {
SigningScheme, "signing scheme", {
None => "none",
Digest => "digest",
Pgp => "pgp",
}
}
posture_vocabulary! {
ValidationDepth, "validation depth", {
None => "none",
Syntax => "syntax",
Template => "template",
}
}
posture_vocabulary! {
AuthnMode, "authn mode", {
None => "none",
Basic => "basic",
Bearer => "bearer",
}
}
posture_vocabulary! {
TlsMode, "tls mode", {
Off => "off",
On => "on",
}
}
posture_vocabulary! {
CompressionMode, "compression mode", {
Off => "off",
Response => "response",
}
}
posture_vocabulary! {
Tenancy, "tenancy", {
Single => "single",
Multi => "multi",
}
}
posture_vocabulary! {
Bracket, "posture bracket", {
Before => "before",
After => "after",
}
}
posture_vocabulary! {
CanaryOutcome, "canary outcome", {
Confirmed => "confirmed",
NotObservable => "not-observable",
Contradicted => "contradicted",
}
}
posture_vocabulary! {
PostureDefectKind, "posture defect", {
Missing => "missing-item",
Unverified => "unverified-observable",
Unverifiable => "unverifiable-claim",
}
}
posture_vocabulary! {
Assurance, "assurance", {
Verified => "verified",
DeclaredOnly => "declared-only",
}
}
const NOT_OBSERVED: &str = "(not observable)";
const PGP_ARMOR: &str = "-----BEGIN PGP SIGNATURE-----";
const SIGNING_SAMPLES: usize = 3;
const CANARY_ACCEPT_ENCODING: (&str, &str) = ("Accept-Encoding", "gzip, br");
const CANARY_PATH: &str = "/definition/template/adl1.4";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PostureProfile {
pub name: &'static str,
pub summary: &'static str,
pub audit: AuditSink,
pub signing: SigningScheme,
pub validation: ValidationDepth,
pub compression: CompressionMode,
pub tenancy: Tenancy,
}
pub static MINIMAL: PostureProfile = PostureProfile {
name: "minimal",
summary: "The bare spec-conformant surface: no audit trail, unsigned versions, commits validated against the operational template, uncompressed responses, one tenant.",
audit: AuditSink::Off,
signing: SigningScheme::None,
validation: ValidationDepth::Template,
compression: CompressionMode::Off,
tenancy: Tenancy::Single,
};
pub static MINIMAL_SIGNED_DIGEST: PostureProfile = PostureProfile {
name: "minimal-signed-digest",
summary: "The minimal surface with digest version signing: no audit trail, digest-signed versions, commits validated against the operational template, uncompressed responses, one tenant.",
audit: AuditSink::Off,
signing: SigningScheme::Digest,
validation: ValidationDepth::Template,
compression: CompressionMode::Off,
tenancy: Tenancy::Single,
};
pub static MINIMAL_SIGNED_PGP: PostureProfile = PostureProfile {
name: "minimal-signed-pgp",
summary: "The minimal surface with openPGP version signing: no audit trail, openPGP-signed versions, commits validated against the operational template, uncompressed responses, one tenant.",
audit: AuditSink::Off,
signing: SigningScheme::Pgp,
validation: ValidationDepth::Template,
compression: CompressionMode::Off,
tenancy: Tenancy::Single,
};
pub static CLINICAL_DEFAULT: PostureProfile = PostureProfile {
name: "clinical-default",
summary: "A clinical deployment's usual configuration: an audit trail written to the deployment's own store, unsigned versions, commits validated against the operational template, uncompressed responses, one tenant.",
audit: AuditSink::Internal,
signing: SigningScheme::None,
validation: ValidationDepth::Template,
compression: CompressionMode::Off,
tenancy: Tenancy::Single,
};
impl PostureProfile {
#[must_use]
pub const fn declared(&self, item: PostureItem) -> Option<&'static str> {
match item {
PostureItem::Audit => Some(self.audit.as_str()),
PostureItem::VersionSigning => Some(self.signing.as_str()),
PostureItem::CommitValidation => Some(self.validation.as_str()),
PostureItem::Compression => Some(self.compression.as_str()),
PostureItem::Tenancy => Some(self.tenancy.as_str()),
PostureItem::Authn | PostureItem::Tls => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionSample {
pub ehr_id: String,
pub object_uid: String,
pub version_uid: String,
}
#[derive(Debug)]
pub struct CanaryTarget<'a> {
pub client: &'a BenchClient,
pub raw: &'a BenchClient,
pub anonymous: &'a BenchClient,
pub profile: &'a PostureProfile,
pub authn: AuthnMode,
pub tls: TlsMode,
pub invalid_twin: Option<Fixture>,
pub samples: &'a [VersionSample],
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CanaryReading {
pub bracket: Bracket,
pub outcome: CanaryOutcome,
pub observed: String,
pub evidence: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PostureDisagreement {
pub item: String,
pub declared: String,
pub bracket: String,
pub observed: String,
pub evidence: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PostureDisclosure {
pub item: PostureItem,
pub declared: String,
pub assurance: Assurance,
pub readings: Vec<CanaryReading>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PostureDivergence {
pub item: PostureItem,
pub profile_declares: String,
pub deployment_configures: String,
pub source: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PostureRecord {
pub profile: String,
pub summary: String,
pub items: Vec<PostureDisclosure>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comparability: Vec<PostureDivergence>,
}
impl PostureRecord {
#[must_use]
pub fn declared(&self, item: PostureItem) -> Option<&str> {
self.items
.iter()
.find(|line| line.item == item)
.map(|line| line.declared.as_str())
}
#[must_use]
pub fn signature(&self) -> String {
self.items
.iter()
.map(|line| format!("{}={}", line.item, line.declared))
.collect::<Vec<_>>()
.join(" ")
}
#[must_use]
pub fn verified_items(&self) -> Vec<PostureItem> {
self.items
.iter()
.filter(|line| line.assurance == Assurance::Verified)
.map(|line| line.item)
.collect()
}
#[must_use]
pub fn declared_only_items(&self) -> Vec<PostureItem> {
self.items
.iter()
.filter(|line| line.assurance == Assurance::DeclaredOnly)
.map(|line| line.item)
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PostureDefect {
pub item: PostureItem,
pub kind: PostureDefectKind,
pub detail: String,
}
impl fmt::Display for PostureDefect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`{}` ({}): {}", self.item, self.kind, self.detail)
}
}
#[must_use]
pub fn submission_defects(record: &PostureRecord) -> Vec<PostureDefect> {
let mut defects = Vec::new();
for item in PostureItem::ALL.iter().copied() {
let Some(line) = record.items.iter().find(|line| line.item == item) else {
defects.push(PostureDefect {
item,
kind: PostureDefectKind::Missing,
detail: "the block carries no line for this item".to_owned(),
});
continue;
};
match (item.is_observable(), line.assurance) {
(true, Assurance::DeclaredOnly) => defects.push(PostureDefect {
item,
kind: PostureDefectKind::Unverified,
detail: format!(
"a canary observes this item, and the block declares `{}` without standing behind it: {}",
line.declared,
unverified_evidence(line)
),
}),
(false, Assurance::Verified) => defects.push(PostureDefect {
item,
kind: PostureDefectKind::Unverifiable,
detail: format!(
"nothing on the wire discloses this item, and the block claims `{}` was verified",
line.declared
),
}),
(true, Assurance::Verified) | (false, Assurance::DeclaredOnly) => {}
}
}
defects
}
fn unverified_evidence(line: &PostureDisclosure) -> String {
if line.readings.is_empty() {
return "the block carries no reading at all".to_owned();
}
line.readings
.iter()
.map(|reading| {
format!(
"{} read {} ({})",
reading.bracket, reading.outcome, reading.evidence
)
})
.collect::<Vec<_>>()
.join("; ")
}
#[must_use]
pub fn bracket(target: &CanaryTarget<'_>, bracket: Bracket) -> Vec<(PostureItem, CanaryReading)> {
PostureItem::ALL
.iter()
.copied()
.map(|item| {
let reading = match item {
PostureItem::Audit => unobservable(
bracket,
"released ITS-REST defines no audit-trail read operation (specifications/operations/), so nothing on the wire discloses whether one is written or to which sink",
),
PostureItem::Tenancy => unobservable(
bracket,
"released ITS-REST defines no tenancy resource (specifications/operations/), so nothing on the wire discloses how many tenants share the deployment",
),
PostureItem::VersionSigning => probe_signing(target, bracket),
PostureItem::CommitValidation => probe_validation(target, bracket),
PostureItem::Authn => probe_authn(target, bracket),
PostureItem::Tls => probe_tls(target, bracket),
PostureItem::Compression => probe_compression(target, bracket),
};
(item, reading)
})
.collect()
}
fn unobservable(bracket: Bracket, evidence: &str) -> CanaryReading {
CanaryReading {
bracket,
outcome: CanaryOutcome::NotObservable,
observed: NOT_OBSERVED.to_owned(),
evidence: evidence.to_owned(),
}
}
fn compared(bracket: Bracket, declared: &str, observed: &str, evidence: String) -> CanaryReading {
let outcome = if declared == observed {
CanaryOutcome::Confirmed
} else {
CanaryOutcome::Contradicted
};
CanaryReading {
bracket,
outcome,
observed: observed.to_owned(),
evidence,
}
}
fn probe_signing(target: &CanaryTarget<'_>, bracket: Bracket) -> CanaryReading {
let mut signatures = Vec::new();
for sample in target.samples.iter().take(SIGNING_SAMPLES) {
let path = format!(
"/ehr/{}/versioned_composition/{}/version/{}",
sample.ehr_id, sample.object_uid, sample.version_uid
);
let Ok(reply) = target.client.send(
"posture canary: signing",
reqwest::Method::GET,
&path,
None,
PreferReturn::Unstated,
) else {
return unobservable(
bracket,
"the version read the signing canary samples never reached a response",
);
};
if !reply.status.is_success() {
return unobservable(
bracket,
&format!(
"the version read the signing canary samples answered {}, so no version could be inspected",
reply.status
),
);
}
let Ok(document) = serde_json::from_slice::<Value>(&reply.body) else {
return unobservable(
bracket,
"the sampled version did not parse as JSON, so its signature could not be inspected",
);
};
signatures.push(
document
.pointer("/signature")
.and_then(Value::as_str)
.map(str::to_owned)
.filter(|signature| !signature.is_empty()),
);
}
let sampled = signatures.len();
if sampled == 0 {
return unobservable(
bracket,
"the run committed no version the signing canary could sample",
);
}
let signed: Vec<&str> = signatures
.iter()
.filter_map(|signature| signature.as_deref())
.collect();
let observed = if signed.is_empty() {
SigningScheme::None
} else if signed.len() != sampled {
return unobservable(
bracket,
&format!(
"{} of {sampled} sampled versions carried a signature and the rest did not, so no single scheme is in force",
signed.len()
),
);
} else if signed.iter().all(|signature| signature.contains(PGP_ARMOR)) {
SigningScheme::Pgp
} else if signed.iter().any(|signature| signature.contains(PGP_ARMOR)) {
return unobservable(
bracket,
"the sampled versions mixed armored and unarmored signatures, so no single scheme is in force",
);
} else {
SigningScheme::Digest
};
compared(
bracket,
target.profile.signing.as_str(),
observed.as_str(),
format!(
"{sampled} version(s) committed by this run's own seed traffic were read back through GET /ehr/{{ehr_id}}/versioned_composition/{{uid}}/version/{{version_uid}} and their `signature` inspected (openEHR RM UML/classes/version.adoc §Attributes)"
),
)
}
const TWIN_REFUSED: &str = "refuses-the-invalid-twin";
const TWIN_ACCEPTED: &str = "accepts-the-invalid-twin";
const fn predicted_twin_fate(depth: ValidationDepth) -> &'static str {
match depth {
ValidationDepth::None | ValidationDepth::Syntax => TWIN_ACCEPTED,
ValidationDepth::Template => TWIN_REFUSED,
}
}
fn probe_validation(target: &CanaryTarget<'_>, bracket: Bracket) -> CanaryReading {
let Some(twin) = target.invalid_twin else {
return unobservable(
bracket,
"this pack embeds no invalid twin, so no commit refusal could be offered",
);
};
let Ok(created) = target.client.send(
"posture canary: validation scratch ehr",
reqwest::Method::POST,
"/ehr",
None,
PreferReturn::Identifier,
) else {
return unobservable(
bracket,
"the scratch EHR the validation canary commits into never reached a response",
);
};
let Some(ehr_id) = created_identifier(&created) else {
return unobservable(
bracket,
"the scratch EHR the validation canary commits into disclosed no identifier",
);
};
let Ok(commit) = target.client.send(
"posture canary: validation twin",
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/composition"),
Some((twin.kind.media_type(), twin.bytes.as_bytes().to_vec())),
PreferReturn::Identifier,
) else {
return unobservable(
bracket,
"the invalid twin's commit never reached a response",
);
};
let observed = if commit.status.is_success() {
TWIN_ACCEPTED
} else if commit.status.is_client_error() {
TWIN_REFUSED
} else {
return unobservable(
bracket,
&format!(
"the invalid twin's commit answered {}, which is neither an acceptance nor a client-side refusal, so it says nothing about validation",
commit.status
),
);
};
compared(
bracket,
predicted_twin_fate(target.profile.validation),
observed,
format!(
"the pinned invalid twin `{}` — this pack's own composition with the mandatory COMPOSITION.composer [1..1] removed (openEHR RM UML/classes/composition.adoc §Attributes) — was committed and answered {} (ITS-REST specifications/responses/422.yaml: a commit is refused when the template \"is not validating the supplied resource\")",
twin.key, commit.status
),
)
}
const AUTHN_ENFORCED: &str = "enforced";
const AUTHN_NOT_ENFORCED: &str = "not-enforced";
const fn predicted_authn(mode: AuthnMode) -> &'static str {
match mode {
AuthnMode::None => AUTHN_NOT_ENFORCED,
AuthnMode::Basic | AuthnMode::Bearer => AUTHN_ENFORCED,
}
}
fn probe_authn(target: &CanaryTarget<'_>, bracket: Bracket) -> CanaryReading {
let Ok(reply) = target.anonymous.send(
"posture canary: authn",
reqwest::Method::GET,
CANARY_PATH,
None,
PreferReturn::Unstated,
) else {
return unobservable(
bracket,
"the uncredentialed read the authentication canary offers never reached a response",
);
};
let unauthorized = reply.status == reqwest::StatusCode::UNAUTHORIZED
|| reply.status == reqwest::StatusCode::FORBIDDEN;
let observed = if unauthorized {
AUTHN_ENFORCED
} else if reply.status.is_success() {
AUTHN_NOT_ENFORCED
} else {
return unobservable(
bracket,
&format!(
"the uncredentialed read answered {}, which is neither a refusal nor an acceptance, so it says nothing about enforcement",
reply.status
),
);
};
compared(
bracket,
predicted_authn(target.authn),
observed,
format!(
"GET {CANARY_PATH} with no Authorization header answered {}",
reply.status
),
)
}
fn probe_tls(target: &CanaryTarget<'_>, bracket: Bracket) -> CanaryReading {
let base_url = target.client.recorded_base_url();
let observed = tls_of(&base_url);
compared(
bracket,
target.tls.as_str(),
observed.as_str(),
format!("the recorded base URL `{base_url}` names its own scheme"),
)
}
#[must_use]
pub fn tls_of(base_url: &str) -> TlsMode {
if base_url.starts_with("https://") {
TlsMode::On
} else {
TlsMode::Off
}
}
fn probe_compression(target: &CanaryTarget<'_>, bracket: Bracket) -> CanaryReading {
let Ok(reply) = target.raw.send_with_headers(
"posture canary: compression",
reqwest::Method::GET,
CANARY_PATH,
None,
PreferReturn::Unstated,
&[CANARY_ACCEPT_ENCODING],
) else {
return unobservable(
bracket,
"the read the compression canary offers never reached a response",
);
};
if !reply.status.is_success() {
return unobservable(
bracket,
&format!(
"the read the compression canary offers answered {}, so no response body was encoded either way",
reply.status
),
);
}
let encoding = reply
.content_encoding
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty() && !value.eq_ignore_ascii_case("identity"));
let observed = match encoding {
Some(_) => CompressionMode::Response,
None => CompressionMode::Off,
};
compared(
bracket,
target.profile.compression.as_str(),
observed.as_str(),
format!(
"GET {CANARY_PATH} stating `Accept-Encoding: {}` answered Content-Encoding: {}",
CANARY_ACCEPT_ENCODING.1,
encoding.unwrap_or("(absent)")
),
)
}
pub fn settle(
profile: &PostureProfile,
authn: AuthnMode,
tls: TlsMode,
before: &[(PostureItem, CanaryReading)],
after: &[(PostureItem, CanaryReading)],
) -> Result<PostureRecord, BenchError> {
let mut items = Vec::with_capacity(PostureItem::ALL.len());
for item in PostureItem::ALL.iter().copied() {
let declared = declared_value(profile, authn, tls, item);
let first = reading_of(before, item, Bracket::Before)?;
let second = reading_of(after, item, Bracket::After)?;
for reading in [first, second] {
if reading.outcome == CanaryOutcome::Contradicted {
return Err(BenchError::PostureContradiction(Box::new(
PostureDisagreement {
item: item.as_str().to_owned(),
declared,
bracket: reading.bracket.as_str().to_owned(),
observed: reading.observed.clone(),
evidence: reading.evidence.clone(),
},
)));
}
}
if first.outcome != second.outcome || first.observed != second.observed {
return Err(BenchError::PostureFlip {
item: item.as_str().to_owned(),
before: format!("{} ({})", first.observed, first.outcome),
after: format!("{} ({})", second.observed, second.outcome),
});
}
let assurance = if first.outcome == CanaryOutcome::Confirmed {
Assurance::Verified
} else {
Assurance::DeclaredOnly
};
items.push(PostureDisclosure {
item,
declared,
assurance,
readings: vec![first.clone(), second.clone()],
});
}
Ok(PostureRecord {
profile: profile.name.to_owned(),
summary: profile.summary.to_owned(),
items,
comparability: Vec::new(),
})
}
fn declared_value(
profile: &PostureProfile,
authn: AuthnMode,
tls: TlsMode,
item: PostureItem,
) -> String {
match item {
PostureItem::Audit => profile.audit.as_str().to_owned(),
PostureItem::VersionSigning => profile.signing.as_str().to_owned(),
PostureItem::CommitValidation => profile.validation.as_str().to_owned(),
PostureItem::Compression => profile.compression.as_str().to_owned(),
PostureItem::Tenancy => profile.tenancy.as_str().to_owned(),
PostureItem::Authn => authn.as_str().to_owned(),
PostureItem::Tls => tls.as_str().to_owned(),
}
}
fn reading_of(
readings: &[(PostureItem, CanaryReading)],
item: PostureItem,
bracket: Bracket,
) -> Result<&CanaryReading, BenchError> {
readings
.iter()
.find(|(key, _)| *key == item)
.map(|(_, reading)| reading)
.ok_or_else(|| BenchError::PostureBracket {
item: item.as_str().to_owned(),
bracket: bracket.as_str().to_owned(),
})
}
#[cfg(test)]
#[expect(
clippy::panic_in_result_fn,
reason = "Result-returning tests in the Book ch11 shape, each asserting; \
clippy offers no allow-in-tests knob for this lint"
)]
mod tests {
use super::*;
fn reading(bracket: Bracket, outcome: CanaryOutcome, observed: &str) -> CanaryReading {
CanaryReading {
bracket,
outcome,
observed: observed.to_owned(),
evidence: "a synthetic reading".to_owned(),
}
}
fn confirming(profile: &PostureProfile, bracket: Bracket) -> Vec<(PostureItem, CanaryReading)> {
PostureItem::ALL
.iter()
.copied()
.map(|item| {
let entry = match item {
PostureItem::Audit | PostureItem::Tenancy => {
reading(bracket, CanaryOutcome::NotObservable, NOT_OBSERVED)
}
PostureItem::VersionSigning => {
reading(bracket, CanaryOutcome::Confirmed, profile.signing.as_str())
}
PostureItem::CommitValidation => reading(
bracket,
CanaryOutcome::Confirmed,
predicted_twin_fate(profile.validation),
),
PostureItem::Authn => {
reading(bracket, CanaryOutcome::Confirmed, AUTHN_NOT_ENFORCED)
}
PostureItem::Tls => {
reading(bracket, CanaryOutcome::Confirmed, TlsMode::Off.as_str())
}
PostureItem::Compression => reading(
bracket,
CanaryOutcome::Confirmed,
profile.compression.as_str(),
),
};
(item, entry)
})
.collect()
}
#[test]
fn every_posture_token_round_trips() -> Result<(), BenchError> {
for item in PostureItem::ALL {
assert_eq!(PostureItem::parse(item.as_str())?, *item);
}
for sink in AuditSink::ALL {
assert_eq!(AuditSink::parse(sink.as_str())?, *sink);
}
for scheme in SigningScheme::ALL {
assert_eq!(SigningScheme::parse(scheme.as_str())?, *scheme);
}
for depth in ValidationDepth::ALL {
assert_eq!(ValidationDepth::parse(depth.as_str())?, *depth);
}
for mode in AuthnMode::ALL {
assert_eq!(AuthnMode::parse(mode.as_str())?, *mode);
}
for mode in TlsMode::ALL {
assert_eq!(TlsMode::parse(mode.as_str())?, *mode);
}
for mode in CompressionMode::ALL {
assert_eq!(CompressionMode::parse(mode.as_str())?, *mode);
}
for tenancy in Tenancy::ALL {
assert_eq!(Tenancy::parse(tenancy.as_str())?, *tenancy);
}
for kind in PostureDefectKind::ALL {
assert_eq!(PostureDefectKind::parse(kind.as_str())?, *kind);
}
assert!(SigningScheme::parse("PGP").is_err());
assert!(ValidationDepth::parse("strict").is_err());
assert!(AuditSink::parse("on").is_err());
Ok(())
}
#[test]
fn only_audit_and_tenancy_are_unobservable() {
let unobservable: Vec<PostureItem> = PostureItem::ALL
.iter()
.copied()
.filter(|item| !item.is_observable())
.collect();
assert_eq!(unobservable, vec![PostureItem::Audit, PostureItem::Tenancy]);
}
#[test]
fn a_settled_block_carries_the_verification_the_canaries_can_give()
-> Result<(), Box<dyn std::error::Error>> {
let record = settle(
&MINIMAL,
AuthnMode::None,
TlsMode::Off,
&confirming(&MINIMAL, Bracket::Before),
&confirming(&MINIMAL, Bracket::After),
)?;
assert_eq!(submission_defects(&record), Vec::new());
assert!(
record
.verified_items()
.iter()
.all(|item| item.is_observable())
);
assert_eq!(
record.declared_only_items(),
vec![PostureItem::Audit, PostureItem::Tenancy]
);
Ok(())
}
#[test]
fn a_disclosure_serializes_as_its_tokens() -> Result<(), Box<dyn std::error::Error>> {
let line = PostureDisclosure {
item: PostureItem::VersionSigning,
declared: SigningScheme::Digest.as_str().to_owned(),
assurance: Assurance::Verified,
readings: vec![reading(Bracket::Before, CanaryOutcome::Confirmed, "digest")],
};
let text = serde_json::to_string(&line)?;
assert!(text.contains("\"item\":\"version_signing\""), "{text}");
assert!(text.contains("\"assurance\":\"verified\""), "{text}");
assert!(text.contains("\"bracket\":\"before\""), "{text}");
let back: PostureDisclosure = serde_json::from_str(&text)?;
assert_eq!(back, line);
Ok(())
}
#[test]
fn matching_brackets_label_each_item_by_what_was_seen() -> Result<(), Box<dyn std::error::Error>>
{
let record = settle(
&MINIMAL,
AuthnMode::None,
TlsMode::Off,
&confirming(&MINIMAL, Bracket::Before),
&confirming(&MINIMAL, Bracket::After),
)?;
assert_eq!(record.profile, "minimal");
assert_eq!(record.items.len(), PostureItem::ALL.len());
assert_eq!(
record.verified_items(),
vec![
PostureItem::VersionSigning,
PostureItem::CommitValidation,
PostureItem::Authn,
PostureItem::Tls,
PostureItem::Compression,
]
);
for item in [PostureItem::Audit, PostureItem::Tenancy] {
let line = record
.items
.iter()
.find(|line| line.item == item)
.ok_or("the item is missing")?;
assert_eq!(line.assurance, Assurance::DeclaredOnly);
assert_eq!(line.readings.len(), 2);
}
assert_eq!(record.declared(PostureItem::Authn), Some("none"));
assert_eq!(record.declared(PostureItem::Tls), Some("off"));
assert!(record.signature().contains("commit_validation=template"));
Ok(())
}
#[test]
fn a_contradicting_canary_refuses_the_run() {
let mut after = confirming(&MINIMAL, Bracket::After);
for (item, entry) in &mut after {
if *item == PostureItem::CommitValidation {
*entry = reading(Bracket::After, CanaryOutcome::Contradicted, TWIN_ACCEPTED);
}
}
let error = settle(
&MINIMAL,
AuthnMode::None,
TlsMode::Off,
&confirming(&MINIMAL, Bracket::Before),
&after,
)
.unwrap_err();
assert!(
matches!(error, BenchError::PostureContradiction(_)),
"{error}"
);
assert!(error.to_string().contains("commit_validation"), "{error}");
assert!(
error.to_string().contains("accepts-the-invalid-twin"),
"{error}"
);
}
#[test]
fn a_flip_between_brackets_refuses_the_run() {
let mut after = confirming(&MINIMAL, Bracket::After);
for (item, entry) in &mut after {
if *item == PostureItem::Compression {
*entry = reading(Bracket::After, CanaryOutcome::NotObservable, NOT_OBSERVED);
}
}
let error = settle(
&MINIMAL,
AuthnMode::None,
TlsMode::Off,
&confirming(&MINIMAL, Bracket::Before),
&after,
)
.unwrap_err();
assert!(matches!(error, BenchError::PostureFlip { .. }), "{error}");
assert!(error.to_string().contains("compression"), "{error}");
}
#[test]
fn a_short_bracket_refuses_the_run() {
let mut before = confirming(&MINIMAL, Bracket::Before);
before.retain(|(item, _)| *item != PostureItem::Tls);
let error = settle(
&MINIMAL,
AuthnMode::None,
TlsMode::Off,
&before,
&confirming(&MINIMAL, Bracket::After),
)
.unwrap_err();
assert!(
matches!(error, BenchError::PostureBracket { .. }),
"{error}"
);
}
#[test]
fn the_two_profiles_differ_only_in_the_audit_item() {
assert_eq!(MINIMAL.audit, AuditSink::Off);
assert_eq!(CLINICAL_DEFAULT.audit, AuditSink::Internal);
assert_eq!(MINIMAL.signing, CLINICAL_DEFAULT.signing);
assert_eq!(MINIMAL.validation, CLINICAL_DEFAULT.validation);
assert_eq!(MINIMAL.compression, CLINICAL_DEFAULT.compression);
assert_eq!(MINIMAL.tenancy, CLINICAL_DEFAULT.tenancy);
assert_ne!(MINIMAL.name, CLINICAL_DEFAULT.name);
}
#[test]
fn a_profile_declares_only_what_an_operator_configures() {
assert_eq!(MINIMAL.declared(PostureItem::Audit), Some("off"));
assert_eq!(MINIMAL.declared(PostureItem::Authn), None);
assert_eq!(MINIMAL.declared(PostureItem::Tls), None);
}
#[test]
fn the_transport_follows_the_url_scheme() {
assert_eq!(tls_of("https://cdr.example/openehr/v1"), TlsMode::On);
assert_eq!(tls_of("http://127.0.0.1:8080/openehr/v1"), TlsMode::Off);
assert_eq!(tls_of("cdr.example"), TlsMode::Off);
}
}