Make core serialized enum shapes exhaustive

This commit is contained in:
ms2k 2026-07-19 05:48:19 +09:00
commit 77ba5319ce
2 changed files with 319 additions and 0 deletions

View file

@ -699,6 +699,8 @@ mod credentials;
mod documents;
#[path = "schema_records/encoding.rs"]
mod encoding;
#[path = "schema_records/enum_shapes.rs"]
mod enum_shapes;
#[path = "schema_records/participant_transitions.rs"]
mod participant_transitions;
#[path = "schema_records/process_evidence.rs"]

View file

@ -0,0 +1,317 @@
use super::*;
macro_rules! enum_corpus {
($digest:expr, $type:ty, { $($pattern:pat => $value:expr),+ $(,)? }) => {{
let values: Vec<$type> = vec![$($value),+];
let mut tags = HashSet::new();
for value in values {
let tag = match &value {
$($pattern => stringify!($pattern)),+
};
assert!(tags.insert(tag));
let encoded = postcard::to_stdvec(&value).unwrap();
$digest.update(stringify!($type).len().to_be_bytes());
$digest.update(stringify!($type).as_bytes());
$digest.update(tag.len().to_be_bytes());
$digest.update(tag.as_bytes());
$digest.update(encoded.len().to_be_bytes());
$digest.update(encoded);
}
}};
}
fn lifecycle_corpus(digest: &mut Sha256) {
enum_corpus!(digest, DurableDecision, {
DurableDecision::None => DurableDecision::None,
DurableDecision::Preparing => DurableDecision::Preparing,
DurableDecision::Published => DurableDecision::Published,
DurableDecision::Clean => DurableDecision::Clean,
});
enum_corpus!(digest, ParticipantRole, {
ParticipantRole::PreparedLower => ParticipantRole::PreparedLower,
ParticipantRole::StateAnchor => ParticipantRole::StateAnchor,
ParticipantRole::GitRouting => ParticipantRole::GitRouting,
ParticipantRole::Network => ParticipantRole::Network,
ParticipantRole::Helper => ParticipantRole::Helper,
ParticipantRole::Sandbox => ParticipantRole::Sandbox,
ParticipantRole::Publication => ParticipantRole::Publication,
});
enum_corpus!(digest, ParticipantState, {
ParticipantState::Intent => ParticipantState::Intent,
ParticipantState::LeaseHeld => ParticipantState::LeaseHeld,
ParticipantState::Prepared => ParticipantState::Prepared,
ParticipantState::Committed => ParticipantState::Committed,
ParticipantState::ReleaseIntent => ParticipantState::ReleaseIntent,
ParticipantState::Clean => ParticipantState::Clean,
});
enum_corpus!(digest, PersistedHostProcessOwnership, {
PersistedHostProcessOwnership::Unassigned => PersistedHostProcessOwnership::unassigned(),
PersistedHostProcessOwnership::Staged(_) => PersistedHostProcessOwnership::staged(process(1)),
PersistedHostProcessOwnership::Committed(_) => PersistedHostProcessOwnership::committed(process(2)),
});
enum_corpus!(digest, ProcessOwnershipMessageVersion, {
ProcessOwnershipMessageVersion::V1 => ProcessOwnershipMessageVersion::V1,
});
enum_corpus!(digest, SessionPhase, {
SessionPhase::New => SessionPhase::New,
SessionPhase::Waiting => SessionPhase::Waiting,
SessionPhase::Registering => SessionPhase::Registering,
SessionPhase::Attached => SessionPhase::Attached,
SessionPhase::Prompting => SessionPhase::Prompting,
SessionPhase::HostExecuting => SessionPhase::HostExecuting,
SessionPhase::Closed => SessionPhase::Closed,
SessionPhase::Failed => SessionPhase::Failed,
});
enum_corpus!(digest, SessionTerminalState, {
SessionTerminalState::Intent => SessionTerminalState::Intent,
SessionTerminalState::Running => SessionTerminalState::Running,
SessionTerminalState::ReleaseIntent => SessionTerminalState::ReleaseIntent,
SessionTerminalState::Clean => SessionTerminalState::Clean,
});
}
fn ownership_slot_corpus(digest: &mut Sha256) {
enum_corpus!(digest, HostProcessOwnershipSlot, {
HostProcessOwnershipSlot::SandboxHostCoordinator => HostProcessOwnershipSlot::SandboxHostCoordinator,
HostProcessOwnershipSlot::SandboxRuntimeSupervisor => HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
HostProcessOwnershipSlot::SandboxAttachedRunsc => HostProcessOwnershipSlot::SandboxAttachedRunsc,
HostProcessOwnershipSlot::StateAnchorDaemon => HostProcessOwnershipSlot::StateAnchorDaemon,
HostProcessOwnershipSlot::GitRoutingDaemon => HostProcessOwnershipSlot::GitRoutingDaemon,
HostProcessOwnershipSlot::NetworkRuntimeSupervisor => HostProcessOwnershipSlot::NetworkRuntimeSupervisor,
HostProcessOwnershipSlot::HelperConnectivityHelper => HostProcessOwnershipSlot::HelperConnectivityHelper,
HostProcessOwnershipSlot::HelperRuntimeSupervisor => HostProcessOwnershipSlot::HelperRuntimeSupervisor,
HostProcessOwnershipSlot::SessionTerminalHostCoordinator(_) => HostProcessOwnershipSlot::SessionTerminalHostCoordinator(session_id()),
HostProcessOwnershipSlot::SessionTerminalExecClient(_) => HostProcessOwnershipSlot::SessionTerminalExecClient(session_id()),
HostProcessOwnershipSlot::EscalationHostProcess(_) => HostProcessOwnershipSlot::EscalationHostProcess(RequestId::new(opaque(3))),
});
enum_corpus!(digest, ParticipantOwnership, {
ParticipantOwnership::PreparedLower(_) => ParticipantOwnership::empty(ParticipantRole::PreparedLower),
ParticipantOwnership::StateAnchor(_) => ParticipantOwnership::empty(ParticipantRole::StateAnchor),
ParticipantOwnership::GitRouting(_) => ParticipantOwnership::empty(ParticipantRole::GitRouting),
ParticipantOwnership::Network(_) => ParticipantOwnership::empty(ParticipantRole::Network),
ParticipantOwnership::Helper(_) => ParticipantOwnership::empty(ParticipantRole::Helper),
ParticipantOwnership::Sandbox(_) => ParticipantOwnership::empty(ParticipantRole::Sandbox),
ParticipantOwnership::Publication(_) => ParticipantOwnership::empty(ParticipantRole::Publication),
});
}
fn terminal_value(seed: u8) -> TerminalOutput {
TerminalOutput {
session_id: session_id(),
bytes: vec![seed],
}
}
fn terminal_corpus(digest: &mut Sha256) {
let resize = TerminalResize {
session_id: session_id(),
rows: NonZeroU16::new(24).unwrap(),
columns: NonZeroU16::new(80).unwrap(),
pixel_width: 640,
pixel_height: 480,
};
enum_corpus!(digest, TerminalSignalKind, {
TerminalSignalKind::Interrupt => TerminalSignalKind::Interrupt,
TerminalSignalKind::Quit => TerminalSignalKind::Quit,
TerminalSignalKind::Suspend => TerminalSignalKind::Suspend,
});
enum_corpus!(digest, SessionTerminalEvent, {
SessionTerminalEvent::Input(_) => SessionTerminalEvent::Input(TerminalInput { session_id: session_id(), bytes: vec![1] }),
SessionTerminalEvent::Resize(_) => SessionTerminalEvent::Resize(resize),
SessionTerminalEvent::Signal(_) => SessionTerminalEvent::Signal(TerminalSignal { session_id: session_id(), signal: TerminalSignalKind::Interrupt }),
SessionTerminalEvent::Close(_) => SessionTerminalEvent::Close(SessionClose { session_id: session_id() }),
});
enum_corpus!(digest, SessionExitOutcome, {
SessionExitOutcome::Exited => SessionExitOutcome::Exited,
SessionExitOutcome::Terminated => SessionExitOutcome::Terminated,
SessionExitOutcome::RuntimeLost => SessionExitOutcome::RuntimeLost,
});
enum_corpus!(digest, SessionTerminalMessage, {
SessionTerminalMessage::Output(_) => SessionTerminalMessage::Output(terminal_value(2)),
SessionTerminalMessage::Exit(_) => SessionTerminalMessage::Exit(SessionExit::exited(session_id(), 3)),
});
enum_corpus!(digest, InteractiveSessionEvent, {
InteractiveSessionEvent::Terminal(_) => InteractiveSessionEvent::Terminal(SessionTerminalEvent::Resize(resize)),
InteractiveSessionEvent::PromptDecision { .. } => InteractiveSessionEvent::PromptDecision { session_id: session_id(), signed_decision: vec![4] },
InteractiveSessionEvent::Poll { .. } => InteractiveSessionEvent::Poll { session_id: session_id() },
});
}
fn session_message_corpus(digest: &mut Sha256) {
let prompt = TerminalPrompt {
session_id: session_id(),
request_id: RequestId::new(opaque(5)),
request_sha256: RequestDigest::new(opaque(6)),
agent_profile: "trusted".into(),
command_argv: vec![b"tool".to_vec()],
justification: "needed".into(),
};
enum_corpus!(digest, InteractiveSessionMessage, {
InteractiveSessionMessage::Terminal(_) => InteractiveSessionMessage::Terminal(SessionTerminalMessage::Output(terminal_value(7))),
InteractiveSessionMessage::Prompt(_) => InteractiveSessionMessage::Prompt(prompt),
});
enum_corpus!(digest, NonInteractiveSessionMessage, {
NonInteractiveSessionMessage::Stdout(_) => NonInteractiveSessionMessage::Stdout(NonInteractiveOutput { session_id: session_id(), bytes: vec![8] }),
NonInteractiveSessionMessage::Stderr(_) => NonInteractiveSessionMessage::Stderr(NonInteractiveOutput { session_id: session_id(), bytes: vec![9] }),
NonInteractiveSessionMessage::Exit(_) => NonInteractiveSessionMessage::Exit(SessionExit::runtime_lost(session_id())),
});
}
fn enrollment_request() -> ClientEnrollmentRequest {
ClientEnrollmentRequest {
protocol_version: ProtocolVersion::V1,
generation_identity: generation_identity(),
cli_id: ClientId::new(opaque(10)),
certificate_signing_request_der: vec![11],
}
}
fn enrollment_response() -> ClientEnrollmentResponse {
ClientEnrollmentResponse {
protocol_version: ProtocolVersion::V1,
credential: TlsCredential::client_cli(
generation_identity(),
ClientId::new(opaque(10)),
vec![12],
CertificateDigest::new(opaque(13)),
),
}
}
fn registration_request() -> SessionRegistrationRequest {
SessionRegistrationRequest {
protocol_version: ProtocolVersion::V1,
session_binding: session_binding(),
}
}
fn registration_response() -> SessionRegistrationResponse {
SessionRegistrationResponse {
protocol_version: ProtocolVersion::V1,
session_capability: session_capability(),
}
}
fn control_corpus(digest: &mut Sha256) {
let resize = TerminalResize {
session_id: session_id(),
rows: NonZeroU16::new(24).unwrap(),
columns: NonZeroU16::new(80).unwrap(),
pixel_width: 0,
pixel_height: 0,
};
enum_corpus!(digest, ControlRequest, {
ControlRequest::Enroll(_) => ControlRequest::Enroll(enrollment_request()),
ControlRequest::Register(_) => ControlRequest::Register(registration_request()),
ControlRequest::Execute(_) => ControlRequest::Execute(NonInteractiveSessionRequest { protocol_version: ProtocolVersion::V1, session_id: session_id(), argv: vec![b"tool".to_vec()] }),
ControlRequest::ExecuteInteractive(_) => ControlRequest::ExecuteInteractive(InteractiveSessionRequest { protocol_version: ProtocolVersion::V1, session_id: session_id(), argv: vec![b"tool".to_vec()], initial_size: resize }),
});
enum_corpus!(digest, ControlResponse, {
ControlResponse::Enrolled(_) => ControlResponse::Enrolled(enrollment_response()),
ControlResponse::Registered(_) => ControlResponse::Registered(registration_response()),
});
}
fn credential_corpus(digest: &mut Sha256) {
enum_corpus!(digest, ProtocolVersion, {
ProtocolVersion::V1 => ProtocolVersion::V1,
});
enum_corpus!(digest, TlsRole, {
TlsRole::SessionCoordinator => TlsRole::SessionCoordinator,
TlsRole::ClientCli => TlsRole::ClientCli,
});
enum_corpus!(digest, TlsSubjectId, {
TlsSubjectId::SessionCoordinator(_) => TlsSubjectId::SessionCoordinator(coordinator_id()),
TlsSubjectId::ClientCli(_) => TlsSubjectId::ClientCli(ClientId::new(opaque(14))),
});
enum_corpus!(digest, ProtocolCredentialRole, {
ProtocolCredentialRole::SessionCoordinator => ProtocolCredentialRole::SessionCoordinator,
ProtocolCredentialRole::AgentSandbox => ProtocolCredentialRole::AgentSandbox,
});
enum_corpus!(digest, ProtocolSubjectId, {
ProtocolSubjectId::SessionCoordinator(_) => ProtocolSubjectId::SessionCoordinator(coordinator_id()),
ProtocolSubjectId::AgentSandbox(_) => ProtocolSubjectId::AgentSandbox(sandbox_id()),
});
enum_corpus!(digest, DeveloperDecisionKind, {
DeveloperDecisionKind::Approve => DeveloperDecisionKind::Approve,
DeveloperDecisionKind::Deny => DeveloperDecisionKind::Deny,
});
enum_corpus!(digest, EscalationOutcome, {
EscalationOutcome::Completed => EscalationOutcome::Completed,
EscalationOutcome::Denied => EscalationOutcome::Denied,
EscalationOutcome::TimedOut => EscalationOutcome::TimedOut,
EscalationOutcome::Cancelled => EscalationOutcome::Cancelled,
EscalationOutcome::Failed => EscalationOutcome::Failed,
});
enum_corpus!(digest, ResultStream, {
ResultStream::Stdout => ResultStream::Stdout,
ResultStream::Stderr => ResultStream::Stderr,
});
}
fn network_corpus(digest: &mut Sha256) {
enum_corpus!(digest, AddressFamily, {
AddressFamily::Ipv4 => AddressFamily::Ipv4,
AddressFamily::Ipv6 => AddressFamily::Ipv6,
});
enum_corpus!(digest, AddressFamilyDispositionValue, {
AddressFamilyDispositionValue::Enabled => AddressFamilyDispositionValue::Enabled,
AddressFamilyDispositionValue::Disabled => AddressFamilyDispositionValue::Disabled,
});
enum_corpus!(digest, IpAddress, {
IpAddress::V4(_) => IpAddress::V4([1, 2, 3, 4]),
IpAddress::V6(_) => IpAddress::V6([5; 16]),
});
enum_corpus!(digest, PolicyAction, {
PolicyAction::Allow => PolicyAction::Allow,
PolicyAction::Deny => PolicyAction::Deny,
});
enum_corpus!(digest, PolicyDelivery, {
PolicyDelivery::SealedMemfd => PolicyDelivery::SealedMemfd,
});
}
fn storage_configuration_waiter_corpus(digest: &mut Sha256) {
enum_corpus!(digest, ArtifactState, {
ArtifactState::Available => ArtifactState::Available,
ArtifactState::Reclaiming => ArtifactState::Reclaiming,
});
enum_corpus!(digest, PrepareStageState, {
PrepareStageState::Intent => PrepareStageState::Intent,
PrepareStageState::Owned => PrepareStageState::Owned,
});
enum_corpus!(digest, VolumeKind, {
VolumeKind::File => VolumeKind::File,
VolumeKind::Directory => VolumeKind::Directory,
});
enum_corpus!(digest, VolumeAccess, {
VolumeAccess::ReadOnly => VolumeAccess::ReadOnly,
VolumeAccess::ReadWrite => VolumeAccess::ReadWrite,
});
enum_corpus!(digest, BootstrapWaitOutcome, {
BootstrapWaitOutcome::Waiting => BootstrapWaitOutcome::Waiting,
BootstrapWaitOutcome::Published => BootstrapWaitOutcome::Published,
BootstrapWaitOutcome::Failed => BootstrapWaitOutcome::Failed,
BootstrapWaitOutcome::Refused => BootstrapWaitOutcome::Refused,
});
}
#[test]
fn every_serialized_enum_variant_has_fixed_shape_evidence() {
let mut digest = Sha256::new();
lifecycle_corpus(&mut digest);
ownership_slot_corpus(&mut digest);
terminal_corpus(&mut digest);
session_message_corpus(&mut digest);
control_corpus(&mut digest);
credential_corpus(&mut digest);
network_corpus(&mut digest);
storage_configuration_waiter_corpus(&mut digest);
assert_eq!(
digest.finalize().as_slice(),
[
91, 180, 216, 8, 22, 13, 86, 141, 24, 220, 203, 225, 25, 175, 149, 91, 254, 88, 90,
142, 176, 169, 45, 94, 24, 209, 70, 185, 116, 120, 58, 128,
]
);
}