use core::fmt;
pub use fastmcp_core::crypto::{CryptoInputTooLongError, Sha256Digest, sha256_bounded};
pub const CANONICAL_DIGEST_DOMAIN: &[u8] = b"fastmcp-rel-quar-00-a-v1\0";
pub const CANONICAL_INPUT_LIMIT_BYTES: usize = 65_536;
pub const WORKFLOW_PATH: &str = ".github/workflows/release.yml";
pub const DIAGNOSTIC_SLUG: &str = "ambient-authority-inventory";
pub const UNRESOLVED_PROVIDER_OBSERVATIONS: [&str; 3] = [
"historical release.yml provider workflow-ID disablement unverified",
"ambient crates.io registry token removal/rotation unverified",
"pre-quarantine queued/in-progress run inventory and disposition unverified",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkflowRole {
HistoricalPublisher,
QuarantineVerification,
}
impl WorkflowRole {
#[must_use]
pub const fn tag(self) -> &'static str {
match self {
Self::HistoricalPublisher => "historical-publisher",
Self::QuarantineVerification => "quarantine-verification",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProviderObservation {
UnresolvedPendingSeparateAuthority,
VerifiedBySeparateAuthority,
}
impl ProviderObservation {
#[must_use]
pub const fn tag(self) -> &'static str {
match self {
Self::UnresolvedPendingSeparateAuthority => "unresolved-pending-separate-authority",
Self::VerifiedBySeparateAuthority => "verified-by-separate-authority",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuarantineContext {
TagPush,
BranchPush,
PullRequest,
ManualDispatch,
WorkflowRerun,
ReusableWorkflowInvocation,
EnvironmentApproval,
ForkContext,
TokenPresent,
TokenAbsent,
AdversarialRefOrInput,
HistoricalQueuedRun,
}
impl QuarantineContext {
#[must_use]
pub const fn tag(self) -> &'static str {
match self {
Self::TagPush => "tag-push",
Self::BranchPush => "branch-push",
Self::PullRequest => "pull-request",
Self::ManualDispatch => "manual-dispatch",
Self::WorkflowRerun => "workflow-rerun",
Self::ReusableWorkflowInvocation => "reusable-workflow-invocation",
Self::EnvironmentApproval => "environment-approval",
Self::ForkContext => "fork-context",
Self::TokenPresent => "token-present",
Self::TokenAbsent => "token-absent",
Self::AdversarialRefOrInput => "adversarial-ref-or-input",
Self::HistoricalQueuedRun => "historical-queued-run",
}
}
}
pub const ORDERED_CONTEXTS: [QuarantineContext; 12] = [
QuarantineContext::TagPush,
QuarantineContext::BranchPush,
QuarantineContext::PullRequest,
QuarantineContext::ManualDispatch,
QuarantineContext::WorkflowRerun,
QuarantineContext::ReusableWorkflowInvocation,
QuarantineContext::EnvironmentApproval,
QuarantineContext::ForkContext,
QuarantineContext::TokenPresent,
QuarantineContext::TokenAbsent,
QuarantineContext::AdversarialRefOrInput,
QuarantineContext::HistoricalQueuedRun,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MutationSink {
WritePermission,
SecretAccess,
ReleaseCreation,
RegistryUpload,
TagMutation,
PublicAssetUpload,
}
impl MutationSink {
#[must_use]
pub const fn tag(self) -> &'static str {
match self {
Self::WritePermission => "write-permission",
Self::SecretAccess => "secret-access",
Self::ReleaseCreation => "release-creation",
Self::RegistryUpload => "registry-upload",
Self::TagMutation => "tag-mutation",
Self::PublicAssetUpload => "public-asset-upload",
}
}
}
pub const ORDERED_SINKS: [MutationSink; 6] = [
MutationSink::WritePermission,
MutationSink::SecretAccess,
MutationSink::ReleaseCreation,
MutationSink::RegistryUpload,
MutationSink::TagMutation,
MutationSink::PublicAssetUpload,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SinkReachability {
ExternallyInert,
MutationReachable,
}
impl SinkReachability {
#[must_use]
pub const fn tag(self) -> &'static str {
match self {
Self::ExternallyInert => "externally-inert",
Self::MutationReachable => "mutation-reachable",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReachabilityCell {
pub context: QuarantineContext,
pub sink: MutationSink,
pub result: SinkReachability,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ActionIdentity {
pub name: &'static str,
pub commit_sha: &'static str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkflowIdentity {
pub role: WorkflowRole,
pub workflow_name: &'static str,
pub path: &'static str,
pub revision: &'static str,
pub definition_sha256_hex: &'static str,
pub events: &'static [&'static str],
pub jobs: &'static [&'static str],
pub declared_permissions: &'static [&'static str],
pub secret_references: &'static [&'static str],
pub process_invocations: &'static [&'static str],
pub actions: &'static [ActionIdentity],
pub provider_disablement: ProviderObservation,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuarantineWorkflowInventory {
pub historical: WorkflowIdentity,
pub quarantine: WorkflowIdentity,
pub ordered_contexts: Vec<QuarantineContext>,
pub ordered_sinks: Vec<MutationSink>,
pub reachability_cells: Vec<ReachabilityCell>,
pub unresolved_provider_observations: Vec<&'static str>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuarantineDiagnostic {
pub code: &'static str,
pub field: String,
}
impl fmt::Display for QuarantineDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"RELQUAR00A|Error|{}|{}|{}",
self.code, DIAGNOSTIC_SLUG, self.field
)
}
}
impl core::error::Error for QuarantineDiagnostic {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuarantineReceipt {
pub reachability_cells: usize,
pub ambient_publish_triggers: usize,
pub mutation_capable_permissions: usize,
pub secret_references: usize,
pub publication_capable_processes: usize,
pub unresolved_provider_observations: usize,
pub canonical_digest: Sha256Digest,
}
#[must_use]
pub fn event_is_ambient_publish_trigger(event: &str) -> bool {
!event.starts_with("workflow_dispatch")
}
#[must_use]
pub fn permission_is_mutation_capable(permission: &str) -> bool {
!permission.ends_with(": read")
}
#[must_use]
pub fn process_is_publication_capable(process: &str) -> bool {
const DENYLIST: [&str; 6] = [
"cargo publish",
"gh release",
"action-gh-release",
"git tag",
"git push",
"crates.io upload",
];
DENYLIST.iter().any(|needle| process.contains(needle))
}
fn historical_identity() -> WorkflowIdentity {
const ACTIONS: [ActionIdentity; 6] = [
ActionIdentity {
name: "actions/checkout",
commit_sha: "3d3c42e5aac5ba805825da76410c181273ba90b1",
},
ActionIdentity {
name: "dtolnay/rust-toolchain",
commit_sha: "2c7215f132e9ebf062739d9130488b56d53c060c",
},
ActionIdentity {
name: "Swatinem/rust-cache",
commit_sha: "e18b497796c12c097a38f9edb9d0641fb99eee32",
},
ActionIdentity {
name: "actions/upload-artifact",
commit_sha: "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a",
},
ActionIdentity {
name: "actions/download-artifact",
commit_sha: "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c",
},
ActionIdentity {
name: "softprops/action-gh-release",
commit_sha: "3bb12739c298aeb8a4eeaf626c5b8d85266b0e65",
},
];
WorkflowIdentity {
role: WorkflowRole::HistoricalPublisher,
workflow_name: "Release",
path: WORKFLOW_PATH,
revision: "7c02d8e0e2b09d3bbe2d4f40ad89efa20c619b8d",
definition_sha256_hex: "dad6d55939a1b49e221169e6c42f66fe0bc1721a6fcb2eeea10ad80159a15bf5",
events: &["push.tags: v*", "workflow_dispatch: tag input"],
jobs: &["build", "release", "publish-crates"],
declared_permissions: &[
"workflow.contents: write",
"job.release.contents: write",
"job.publish-crates.contents: read",
],
secret_references: &["secrets.CARGO_REGISTRY_TOKEN"],
process_invocations: &[
"cargo publish -p <crate> --locked (twelve-attempt retry loop)",
"softprops/action-gh-release public release creation and asset upload",
"cargo build --release (binary matrix)",
"tar/shasum release packaging",
],
actions: &ACTIONS,
provider_disablement: ProviderObservation::UnresolvedPendingSeparateAuthority,
}
}
fn quarantine_identity() -> WorkflowIdentity {
const ACTIONS: [ActionIdentity; 5] = [
ActionIdentity {
name: "actions/checkout",
commit_sha: "3d3c42e5aac5ba805825da76410c181273ba90b1",
},
ActionIdentity {
name: "dtolnay/rust-toolchain",
commit_sha: "2c7215f132e9ebf062739d9130488b56d53c060c",
},
ActionIdentity {
name: "Swatinem/rust-cache",
commit_sha: "e18b497796c12c097a38f9edb9d0641fb99eee32",
},
ActionIdentity {
name: "taiki-e/install-action",
commit_sha: "1beb33eee6d086258184383af9a538940be190ed",
},
ActionIdentity {
name: "actions/upload-artifact",
commit_sha: "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a",
},
];
WorkflowIdentity {
role: WorkflowRole::QuarantineVerification,
workflow_name: "Release Quarantine Verification",
path: WORKFLOW_PATH,
revision: "3f40ed16542c06cfc20fd54fc62d7d2a96c632f2",
definition_sha256_hex: "2eecef2ba169eeeb44f9cb63d3bb28282c0559ec12269bad7fd417ae9e38364a",
events: &["workflow_dispatch"],
jobs: &["preflight", "build"],
declared_permissions: &[
"workflow.contents: read",
"job.preflight.contents: read",
"job.build.contents: read",
],
secret_references: &[],
process_invocations: &[
"cargo metadata/fmt/check/clippy/test verification",
"cargo audit --deny warnings",
"cargo package --locked --no-verify (runner-local diagnostic)",
"cargo build --release --locked -p fastmcp-cli (diagnostic)",
"tar/shasum/Compress-Archive diagnostic packaging",
"actions/upload-artifact expiring private diagnostic (3-day retention)",
],
actions: &ACTIONS,
provider_disablement: ProviderObservation::UnresolvedPendingSeparateAuthority,
}
}
#[must_use]
pub fn quarantine_workflow_inventory() -> QuarantineWorkflowInventory {
let mut reachability_cells = Vec::with_capacity(ORDERED_CONTEXTS.len() * ORDERED_SINKS.len());
for context in ORDERED_CONTEXTS {
for sink in ORDERED_SINKS {
reachability_cells.push(ReachabilityCell {
context,
sink,
result: SinkReachability::ExternallyInert,
});
}
}
QuarantineWorkflowInventory {
historical: historical_identity(),
quarantine: quarantine_identity(),
ordered_contexts: ORDERED_CONTEXTS.to_vec(),
ordered_sinks: ORDERED_SINKS.to_vec(),
reachability_cells,
unresolved_provider_observations: UNRESOLVED_PROVIDER_OBSERVATIONS.to_vec(),
}
}
fn push_length_prefixed(buffer: &mut Vec<u8>, bytes: &[u8]) {
buffer.extend_from_slice(&(bytes.len() as u64).to_be_bytes());
buffer.extend_from_slice(bytes);
}
fn push_str(buffer: &mut Vec<u8>, value: &str) {
push_length_prefixed(buffer, value.as_bytes());
}
fn push_u64(buffer: &mut Vec<u8>, value: u64) {
push_length_prefixed(buffer, &value.to_be_bytes());
}
fn push_str_list(buffer: &mut Vec<u8>, values: &[&str]) {
push_u64(buffer, values.len() as u64);
for value in values {
push_str(buffer, value);
}
}
fn encode_identity(buffer: &mut Vec<u8>, identity: &WorkflowIdentity) {
push_str(buffer, identity.role.tag());
push_str(buffer, identity.workflow_name);
push_str(buffer, identity.path);
push_str(buffer, identity.revision);
push_str(buffer, identity.definition_sha256_hex);
push_str_list(buffer, identity.events);
push_str_list(buffer, identity.jobs);
push_str_list(buffer, identity.declared_permissions);
push_str_list(buffer, identity.secret_references);
push_str_list(buffer, identity.process_invocations);
push_u64(buffer, identity.actions.len() as u64);
for action in identity.actions {
push_str(buffer, action.name);
push_str(buffer, action.commit_sha);
}
push_str(buffer, identity.provider_disablement.tag());
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct AmbientAuthorityCounts {
ambient_publish_triggers: usize,
mutation_capable_permissions: usize,
secret_references: usize,
publication_capable_processes: usize,
}
fn ambient_authority_counts(identity: &WorkflowIdentity) -> AmbientAuthorityCounts {
AmbientAuthorityCounts {
ambient_publish_triggers: identity
.events
.iter()
.filter(|event| event_is_ambient_publish_trigger(event))
.count(),
mutation_capable_permissions: identity
.declared_permissions
.iter()
.filter(|permission| permission_is_mutation_capable(permission))
.count(),
secret_references: identity.secret_references.len(),
publication_capable_processes: identity
.process_invocations
.iter()
.filter(|process| process_is_publication_capable(process))
.count(),
}
}
#[must_use]
pub fn canonical_inventory_bytes(inventory: &QuarantineWorkflowInventory) -> Vec<u8> {
let mut buffer = Vec::with_capacity(8_192);
buffer.extend_from_slice(CANONICAL_DIGEST_DOMAIN);
encode_identity(&mut buffer, &inventory.historical);
encode_identity(&mut buffer, &inventory.quarantine);
push_u64(&mut buffer, inventory.ordered_contexts.len() as u64);
for context in &inventory.ordered_contexts {
push_str(&mut buffer, context.tag());
}
push_u64(&mut buffer, inventory.ordered_sinks.len() as u64);
for sink in &inventory.ordered_sinks {
push_str(&mut buffer, sink.tag());
}
push_u64(&mut buffer, inventory.reachability_cells.len() as u64);
for cell in &inventory.reachability_cells {
push_str(&mut buffer, cell.context.tag());
push_str(&mut buffer, cell.sink.tag());
push_str(&mut buffer, cell.result.tag());
}
let counts = ambient_authority_counts(&inventory.quarantine);
push_u64(&mut buffer, counts.ambient_publish_triggers as u64);
push_u64(&mut buffer, counts.mutation_capable_permissions as u64);
push_u64(&mut buffer, counts.secret_references as u64);
push_u64(&mut buffer, counts.publication_capable_processes as u64);
push_str(&mut buffer, inventory.historical.provider_disablement.tag());
push_str(&mut buffer, inventory.quarantine.provider_disablement.tag());
push_u64(
&mut buffer,
inventory.unresolved_provider_observations.len() as u64,
);
for observation in &inventory.unresolved_provider_observations {
push_str(&mut buffer, observation);
}
push_str(&mut buffer, inventory.quarantine.definition_sha256_hex);
buffer
}
fn is_lower_hex(value: &str, expected_len: usize) -> bool {
value.len() == expected_len
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}
fn reject(code: &'static str, field: impl Into<String>) -> QuarantineDiagnostic {
QuarantineDiagnostic {
code,
field: field.into(),
}
}
fn check_identity_shape(identity: &WorkflowIdentity) -> Result<(), QuarantineDiagnostic> {
let role = identity.role.tag();
if identity.path != WORKFLOW_PATH {
return Err(reject("E_IDENTITY_SET", format!("identity[{role}].path")));
}
if identity.workflow_name.is_empty() || identity.events.is_empty() {
return Err(reject("E_IDENTITY_SET", format!("identity[{role}].shape")));
}
if !is_lower_hex(identity.revision, 40) {
return Err(reject(
"E_IDENTITY_SET",
format!("identity[{role}].revision"),
));
}
if !is_lower_hex(identity.definition_sha256_hex, 64) {
return Err(reject(
"E_IDENTITY_SET",
format!("identity[{role}].definition_sha256_hex"),
));
}
for action in identity.actions {
if action.name.is_empty() || !is_lower_hex(action.commit_sha, 40) {
return Err(reject(
"E_ACTION_IDENTITY",
format!("identity[{role}].action[{}]", action.name),
));
}
}
if identity.provider_disablement != ProviderObservation::UnresolvedPendingSeparateAuthority {
return Err(reject(
"E_PROVIDER_INFERENCE",
format!("identity[{role}].provider_disablement"),
));
}
Ok(())
}
fn check_identity_binding(
actual: &WorkflowIdentity,
frozen: &WorkflowIdentity,
) -> Result<(), QuarantineDiagnostic> {
let role = frozen.role.tag();
let mismatch: Option<&'static str> = if actual.role != frozen.role {
Some("role")
} else if actual.workflow_name != frozen.workflow_name {
Some("workflow_name")
} else if actual.path != frozen.path {
Some("path")
} else if actual.revision != frozen.revision {
Some("revision")
} else if actual.definition_sha256_hex != frozen.definition_sha256_hex {
Some("definition_sha256_hex")
} else if actual.events != frozen.events {
Some("events")
} else if actual.jobs != frozen.jobs {
Some("jobs")
} else if actual.declared_permissions != frozen.declared_permissions {
Some("declared_permissions")
} else if actual.secret_references != frozen.secret_references {
Some("secret_references")
} else if actual.process_invocations != frozen.process_invocations {
Some("process_invocations")
} else if actual.actions != frozen.actions {
Some("actions")
} else {
None
};
if let Some(field) = mismatch {
return Err(reject(
"E_IDENTITY_BINDING",
format!("identity[{role}].{field}"),
));
}
if actual.provider_disablement != frozen.provider_disablement {
return Err(reject(
"E_PROVIDER_INFERENCE",
format!("identity[{role}].provider_disablement"),
));
}
Ok(())
}
fn check_quarantine_predicate(counts: AmbientAuthorityCounts) -> Result<(), QuarantineDiagnostic> {
if counts.ambient_publish_triggers != 0 {
return Err(reject("E_AMBIENT_TRIGGER", "quarantine.events"));
}
if counts.mutation_capable_permissions != 0 {
return Err(reject(
"E_MUTATION_PERMISSION",
"quarantine.declared_permissions",
));
}
if counts.secret_references != 0 {
return Err(reject("E_SECRET_REFERENCE", "quarantine.secret_references"));
}
if counts.publication_capable_processes != 0 {
return Err(reject(
"E_PUBLICATION_PROCESS",
"quarantine.process_invocations",
));
}
Ok(())
}
fn check_historical_discovery(identity: &WorkflowIdentity) -> Result<(), QuarantineDiagnostic> {
let counts = ambient_authority_counts(identity);
let discovery_complete = counts.ambient_publish_triggers > 0
&& counts.mutation_capable_permissions > 0
&& counts.secret_references > 0
&& counts.publication_capable_processes > 0;
if discovery_complete {
Ok(())
} else {
Err(reject("E_HISTORICAL_DISCOVERY", "historical"))
}
}
fn check_reachability_cells(
inventory: &QuarantineWorkflowInventory,
) -> Result<usize, QuarantineDiagnostic> {
if inventory.ordered_contexts != ORDERED_CONTEXTS {
return Err(reject("E_CONTEXT_SET", "ordered_contexts"));
}
if inventory.ordered_sinks != ORDERED_SINKS {
return Err(reject("E_SINK_SET", "ordered_sinks"));
}
let expected_cells = ORDERED_CONTEXTS.len() * ORDERED_SINKS.len();
if inventory.reachability_cells.len() != expected_cells {
return Err(reject("E_CONTEXT_SET", "reachability_cells.len"));
}
for (index, cell) in inventory.reachability_cells.iter().enumerate() {
let context = ORDERED_CONTEXTS[index / ORDERED_SINKS.len()];
let sink = ORDERED_SINKS[index % ORDERED_SINKS.len()];
if cell.context != context || cell.sink != sink {
return Err(reject(
"E_CONTEXT_SET",
format!("cell[context={},sink={}].order", context.tag(), sink.tag()),
));
}
if cell.result != SinkReachability::ExternallyInert {
return Err(reject(
"E_MUTATION_REACHABLE",
format!(
"cell[context={},sink={}]",
cell.context.tag(),
cell.sink.tag()
),
));
}
}
Ok(expected_cells)
}
pub fn rel_quar_00_a_ambient_authority_inventory(
inventory: &QuarantineWorkflowInventory,
) -> Result<QuarantineReceipt, QuarantineDiagnostic> {
let frozen_historical = historical_identity();
let frozen_quarantine = quarantine_identity();
check_identity_shape(&frozen_historical)?;
check_identity_shape(&frozen_quarantine)?;
check_identity_binding(&inventory.historical, &frozen_historical)?;
check_identity_binding(&inventory.quarantine, &frozen_quarantine)?;
let counts = ambient_authority_counts(&inventory.quarantine);
check_quarantine_predicate(counts)?;
check_historical_discovery(&inventory.historical)?;
if inventory.unresolved_provider_observations != UNRESOLVED_PROVIDER_OBSERVATIONS {
return Err(reject(
"E_PROVIDER_INFERENCE",
"unresolved_provider_observations",
));
}
let reachability_cells = check_reachability_cells(inventory)?;
let canonical_bytes = canonical_inventory_bytes(inventory);
let canonical_digest = sha256_bounded(&canonical_bytes, CANONICAL_INPUT_LIMIT_BYTES)
.map_err(|_| reject("E_CANONICAL_INPUT", "canonical_inventory_bytes"))?;
Ok(QuarantineReceipt {
reachability_cells,
ambient_publish_triggers: counts.ambient_publish_triggers,
mutation_capable_permissions: counts.mutation_capable_permissions,
secret_references: counts.secret_references,
publication_capable_processes: counts.publication_capable_processes,
unresolved_provider_observations: inventory.unresolved_provider_observations.len(),
canonical_digest,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ambient_trigger_classifier_admits_only_manual_dispatch() {
assert!(!event_is_ambient_publish_trigger("workflow_dispatch"));
assert!(event_is_ambient_publish_trigger("push.tags: v*"));
assert!(event_is_ambient_publish_trigger("pull_request"));
}
#[test]
fn permission_classifier_is_fail_closed() {
assert!(!permission_is_mutation_capable("workflow.contents: read"));
assert!(permission_is_mutation_capable("workflow.contents: write"));
assert!(permission_is_mutation_capable("job.x.id-token: none"));
}
#[test]
fn process_classifier_flags_publication_surfaces() {
assert!(process_is_publication_capable(
"cargo publish -p fastmcp-core --locked"
));
assert!(process_is_publication_capable(
"softprops/action-gh-release public release creation and asset upload"
));
assert!(!process_is_publication_capable(
"cargo package --locked --no-verify (runner-local diagnostic)"
));
}
#[test]
fn canonical_encoding_is_deterministic() {
let inventory = quarantine_workflow_inventory();
assert_eq!(
canonical_inventory_bytes(&inventory),
canonical_inventory_bytes(&inventory)
);
}
}