20260529 修复CIR输入与拒绝审计语义
This commit is contained in:
parent
57c23f19aa
commit
9f7981e117
@ -193,18 +193,11 @@ pub fn build_cir_from_run_multi(
|
|||||||
reject_list_sha256: Vec::new(),
|
reject_list_sha256: Vec::new(),
|
||||||
rejected_objects: Vec::new(),
|
rejected_objects: Vec::new(),
|
||||||
};
|
};
|
||||||
let object_uri_set = cir
|
|
||||||
.objects
|
|
||||||
.iter()
|
|
||||||
.map(|item| item.rsync_uri.as_str())
|
|
||||||
.collect::<BTreeSet<_>>();
|
|
||||||
let mut rejected_objects = publication_points
|
let mut rejected_objects = publication_points
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|pp| pp.objects.iter())
|
.flat_map(|pp| pp.objects.iter())
|
||||||
.filter(|item| {
|
.filter(|item| item.result == AuditObjectResult::Error)
|
||||||
item.result == AuditObjectResult::Error
|
.filter(|item| item.rsync_uri.starts_with("rsync://"))
|
||||||
&& object_uri_set.contains(item.rsync_uri.as_str())
|
|
||||||
})
|
|
||||||
.map(|item| CirRejectedObject {
|
.map(|item| CirRejectedObject {
|
||||||
object_uri: item.rsync_uri.clone(),
|
object_uri: item.rsync_uri.clone(),
|
||||||
reason: item.detail.clone(),
|
reason: item.detail.clone(),
|
||||||
@ -754,7 +747,7 @@ mod tests {
|
|||||||
sha256_hex: "33".repeat(32),
|
sha256_hex: "33".repeat(32),
|
||||||
kind: crate::audit::AuditObjectKind::Roa,
|
kind: crate::audit::AuditObjectKind::Roa,
|
||||||
result: crate::audit::AuditObjectResult::Error,
|
result: crate::audit::AuditObjectResult::Error,
|
||||||
detail: Some("not in cir object set".to_string()),
|
detail: Some("second rejected roa".to_string()),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
..PublicationPointAudit::default()
|
..PublicationPointAudit::default()
|
||||||
@ -785,6 +778,18 @@ mod tests {
|
|||||||
cir.rejected_objects[1].object_uri,
|
cir.rejected_objects[1].object_uri,
|
||||||
"rsync://example.test/repo/c.roa"
|
"rsync://example.test/repo/c.roa"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
cir.objects
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.rsync_uri == "rsync://example.test/repo/a.roa"),
|
||||||
|
"rejected audit objects were still consumed as validation input",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
cir.objects
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.rsync_uri == "rsync://example.test/repo/c.roa"),
|
||||||
|
"rejected audit objects were still consumed as validation input",
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!cir.objects
|
!cir.objects
|
||||||
.iter()
|
.iter()
|
||||||
@ -793,6 +798,119 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_cir_from_run_multi_records_crl_expired_manifest_in_objects_and_rejects() {
|
||||||
|
let td = tempfile::tempdir().unwrap();
|
||||||
|
let store = RocksStore::open(td.path()).unwrap();
|
||||||
|
let ta = sample_trust_anchor();
|
||||||
|
let manifest_uri = "rsync://example.test/repo/expired-crl.mft";
|
||||||
|
let reject_reason = "manifest embedded EE certificate path validation failed: CRL not valid at validation_time (RFC 5280 §6.3.3(g); RFC 5280 §5.1.2.4-§5.1.2.5; RFC 6487 §5)";
|
||||||
|
let publication_points = vec![PublicationPointAudit {
|
||||||
|
objects: vec![crate::audit::ObjectAuditEntry {
|
||||||
|
rsync_uri: manifest_uri.to_string(),
|
||||||
|
sha256_hex: "44".repeat(32),
|
||||||
|
kind: crate::audit::AuditObjectKind::Manifest,
|
||||||
|
result: crate::audit::AuditObjectResult::Error,
|
||||||
|
detail: Some(reject_reason.to_string()),
|
||||||
|
}],
|
||||||
|
..PublicationPointAudit::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let cir = build_cir_from_run_multi(
|
||||||
|
&store,
|
||||||
|
&[CirTrustAnchorBinding {
|
||||||
|
trust_anchor: &ta,
|
||||||
|
tal_uri: "https://example.test/root.tal",
|
||||||
|
}],
|
||||||
|
sample_time(),
|
||||||
|
&publication_points,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.expect("build cir");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
cir.objects
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.rsync_uri == manifest_uri),
|
||||||
|
"manifest rejected because issuer CRL is expired was still read as validation input",
|
||||||
|
);
|
||||||
|
assert_eq!(cir.rejected_objects.len(), 1);
|
||||||
|
assert_eq!(cir.rejected_objects[0].object_uri, manifest_uri);
|
||||||
|
assert_eq!(
|
||||||
|
cir.rejected_objects[0].reason.as_deref(),
|
||||||
|
Some(reject_reason)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cir.reject_list_sha256,
|
||||||
|
compute_reject_list_sha256([manifest_uri].into_iter())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_cir_from_run_multi_excludes_manifest_locked_files_when_manifest_is_rejected() {
|
||||||
|
let td = tempfile::tempdir().unwrap();
|
||||||
|
let store = RocksStore::open(td.path()).unwrap();
|
||||||
|
let ta = sample_trust_anchor();
|
||||||
|
let manifest_uri = "rsync://example.test/repo/rejected.mft";
|
||||||
|
let roa_uri = "rsync://example.test/repo/listed.roa";
|
||||||
|
let crl_uri = "rsync://example.test/repo/listed.crl";
|
||||||
|
let publication_points = vec![PublicationPointAudit {
|
||||||
|
objects: vec![
|
||||||
|
crate::audit::ObjectAuditEntry {
|
||||||
|
rsync_uri: manifest_uri.to_string(),
|
||||||
|
sha256_hex: "44".repeat(32),
|
||||||
|
kind: crate::audit::AuditObjectKind::Manifest,
|
||||||
|
result: crate::audit::AuditObjectResult::Error,
|
||||||
|
detail: Some("manifest EE cert path rejected".to_string()),
|
||||||
|
},
|
||||||
|
crate::audit::ObjectAuditEntry {
|
||||||
|
rsync_uri: roa_uri.to_string(),
|
||||||
|
sha256_hex: "55".repeat(32),
|
||||||
|
kind: crate::audit::AuditObjectKind::Roa,
|
||||||
|
result: crate::audit::AuditObjectResult::Skipped,
|
||||||
|
detail: Some("manifest rejected before locked object validation".to_string()),
|
||||||
|
},
|
||||||
|
crate::audit::ObjectAuditEntry {
|
||||||
|
rsync_uri: crl_uri.to_string(),
|
||||||
|
sha256_hex: "66".repeat(32),
|
||||||
|
kind: crate::audit::AuditObjectKind::Crl,
|
||||||
|
result: crate::audit::AuditObjectResult::Skipped,
|
||||||
|
detail: Some("manifest rejected before locked object validation".to_string()),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..PublicationPointAudit::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let cir = build_cir_from_run_multi(
|
||||||
|
&store,
|
||||||
|
&[CirTrustAnchorBinding {
|
||||||
|
trust_anchor: &ta,
|
||||||
|
tal_uri: "https://example.test/root.tal",
|
||||||
|
}],
|
||||||
|
sample_time(),
|
||||||
|
&publication_points,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.expect("build cir");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
cir.objects
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.rsync_uri == manifest_uri),
|
||||||
|
"rejected manifest is still a current-run validation input",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!cir.objects.iter().any(|item| item.rsync_uri == roa_uri),
|
||||||
|
"ROA listed by a rejected manifest must not enter CIR objects",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!cir.objects.iter().any(|item| item.rsync_uri == crl_uri),
|
||||||
|
"CRL listed by a rejected manifest must not enter CIR objects",
|
||||||
|
);
|
||||||
|
assert_eq!(cir.rejected_objects.len(), 1);
|
||||||
|
assert_eq!(cir.rejected_objects[0].object_uri, manifest_uri);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_cir_from_run_multi_reject_digest_ignores_reason_text() {
|
fn build_cir_from_run_multi_reject_digest_ignores_reason_text() {
|
||||||
let td = tempfile::tempdir().unwrap();
|
let td = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
@ -1602,6 +1602,51 @@ mod tests {
|
|||||||
assert!(matches!(err, ManifestFreshError::EeCrlNotFound(_)), "{err}");
|
assert!(matches!(err, ManifestFreshError::EeCrlNotFound(_)), "{err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_manifest_embedded_ee_cert_path_rejects_expired_crl() {
|
||||||
|
let (manifest, _, _, publication_point_rsync_uri, _) = load_manifest_fixture();
|
||||||
|
let files = locked_files_for_manifest(&manifest, &publication_point_rsync_uri);
|
||||||
|
let ee = &manifest.signed_object.signed_data.certificates[0];
|
||||||
|
let crldp_uri = ee
|
||||||
|
.resource_cert
|
||||||
|
.tbs
|
||||||
|
.extensions
|
||||||
|
.crl_distribution_points_uris
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|uris| uris.first())
|
||||||
|
.expect("fixture manifest EE CRLDP")
|
||||||
|
.as_str()
|
||||||
|
.to_string();
|
||||||
|
let crl_file = files
|
||||||
|
.iter()
|
||||||
|
.find(|file| file.rsync_uri == crldp_uri)
|
||||||
|
.expect("fixture CRL referenced by manifest EE");
|
||||||
|
let crl = crate::data_model::crl::RpkixCrl::decode_der(
|
||||||
|
crl_file.bytes().expect("read fixture crl bytes"),
|
||||||
|
)
|
||||||
|
.expect("decode fixture crl");
|
||||||
|
let validation_time = crl.next_update.utc;
|
||||||
|
|
||||||
|
let err = validate_manifest_embedded_ee_cert_path(
|
||||||
|
&manifest,
|
||||||
|
&files,
|
||||||
|
&issuer_ca_fixture_der(),
|
||||||
|
Some(issuer_ca_rsync_uri()),
|
||||||
|
validation_time,
|
||||||
|
)
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
err,
|
||||||
|
ManifestFreshError::EeCertPath(
|
||||||
|
crate::validation::cert_path::CertPathError::CrlNotValidAtTime
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"{err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_current_instance_vcir_publication_point_returns_manifest_and_locked_files() {
|
fn load_current_instance_vcir_publication_point_returns_manifest_and_locked_files() {
|
||||||
let temp = tempfile::tempdir().expect("tempdir");
|
let temp = tempfile::tempdir().expect("tempdir");
|
||||||
|
|||||||
@ -183,6 +183,36 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_manifest_hash_hex_for_audit(&self, ca: &CaInstanceHandle) -> Option<String> {
|
||||||
|
if let Some(index_handle) = self.current_repo_index.as_ref()
|
||||||
|
&& let Ok(index) = index_handle.lock()
|
||||||
|
&& let Some(entry) = index.get_by_uri(&ca.manifest_rsync_uri)
|
||||||
|
{
|
||||||
|
return Some(entry.current_hash_hex.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.store
|
||||||
|
.load_current_object_with_hash_by_uri(&ca.manifest_rsync_uri)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|current| current.current_hash_hex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rejected_manifest_audit_entry_for_failed_fetch(
|
||||||
|
&self,
|
||||||
|
ca: &CaInstanceHandle,
|
||||||
|
fresh_err: &ManifestFreshError,
|
||||||
|
) -> Option<ObjectAuditEntry> {
|
||||||
|
let sha256_hex = self.current_manifest_hash_hex_for_audit(ca)?;
|
||||||
|
Some(ObjectAuditEntry {
|
||||||
|
rsync_uri: ca.manifest_rsync_uri.clone(),
|
||||||
|
sha256_hex,
|
||||||
|
kind: AuditObjectKind::Manifest,
|
||||||
|
result: AuditObjectResult::Error,
|
||||||
|
detail: Some(fresh_err.to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn stage_fresh_publication_point_after_repo_ready(
|
pub(crate) fn stage_fresh_publication_point_after_repo_ready(
|
||||||
&self,
|
&self,
|
||||||
ca: &CaInstanceHandle,
|
ca: &CaInstanceHandle,
|
||||||
@ -811,13 +841,21 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
|||||||
}
|
}
|
||||||
crate::policy::CaFailedFetchPolicy::ReuseCurrentInstanceVcir => {
|
crate::policy::CaFailedFetchPolicy::ReuseCurrentInstanceVcir => {
|
||||||
let projection_started = std::time::Instant::now();
|
let projection_started = std::time::Instant::now();
|
||||||
let projection = project_current_instance_vcir_on_failed_fetch(
|
let mut projection = project_current_instance_vcir_on_failed_fetch(
|
||||||
self.store,
|
self.store,
|
||||||
ca,
|
ca,
|
||||||
&fresh_err,
|
&fresh_err,
|
||||||
self.validation_time,
|
self.validation_time,
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("failed fetch VCIR projection failed: {e}"))?;
|
.map_err(|e| format!("failed fetch VCIR projection failed: {e}"))?;
|
||||||
|
if matches!(
|
||||||
|
projection.source,
|
||||||
|
PublicationPointSource::FailedFetchNoCache
|
||||||
|
) && let Some(rejected_manifest) =
|
||||||
|
self.rejected_manifest_audit_entry_for_failed_fetch(ca, &fresh_err)
|
||||||
|
{
|
||||||
|
projection.objects.audit.push(rejected_manifest);
|
||||||
|
}
|
||||||
self.append_ccr_manifest_projection_from_reuse(&projection)?;
|
self.append_ccr_manifest_projection_from_reuse(&projection)?;
|
||||||
let projection_ms = projection_started.elapsed().as_millis() as u64;
|
let projection_ms = projection_started.elapsed().as_millis() as u64;
|
||||||
warnings.extend(projection.warnings.clone());
|
warnings.extend(projection.warnings.clone());
|
||||||
@ -1849,6 +1887,40 @@ fn build_publication_point_audit_from_vcir(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if source == PublicationPointSource::FailedFetchNoCache {
|
||||||
|
let mut objects_out = Vec::with_capacity(objects.audit.len() + child_audits.len());
|
||||||
|
objects_out.extend(child_audits.iter().cloned());
|
||||||
|
objects_out.extend(objects.audit.iter().cloned());
|
||||||
|
return PublicationPointAudit {
|
||||||
|
node_id: None,
|
||||||
|
parent_node_id: None,
|
||||||
|
discovered_from: None,
|
||||||
|
rsync_base_uri: ca.rsync_base_uri.clone(),
|
||||||
|
manifest_rsync_uri: ca.manifest_rsync_uri.clone(),
|
||||||
|
publication_point_rsync_uri: ca.publication_point_rsync_uri.clone(),
|
||||||
|
rrdp_notification_uri: ca.rrdp_notification_uri.clone(),
|
||||||
|
source: source_label(source),
|
||||||
|
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||||
|
repo_sync_phase: repo_sync_phase.map(ToString::to_string),
|
||||||
|
repo_sync_duration_ms,
|
||||||
|
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||||
|
repo_terminal_state: terminal_state_label(source).to_string(),
|
||||||
|
this_update_rfc3339_utc: vcir
|
||||||
|
.validated_manifest_meta
|
||||||
|
.validated_manifest_this_update
|
||||||
|
.rfc3339_utc
|
||||||
|
.clone(),
|
||||||
|
next_update_rfc3339_utc: vcir
|
||||||
|
.validated_manifest_meta
|
||||||
|
.validated_manifest_next_update
|
||||||
|
.rfc3339_utc
|
||||||
|
.clone(),
|
||||||
|
verified_at_rfc3339_utc: vcir.last_successful_validation_time.rfc3339_utc.clone(),
|
||||||
|
warnings,
|
||||||
|
objects: objects_out,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let mut audit_by_uri: HashMap<String, ObjectAuditEntry> = HashMap::new();
|
let mut audit_by_uri: HashMap<String, ObjectAuditEntry> = HashMap::new();
|
||||||
for artifact in &vcir.related_artifacts {
|
for artifact in &vcir.related_artifacts {
|
||||||
let Some(uri) = artifact.uri.as_ref() else {
|
let Some(uri) = artifact.uri.as_ref() else {
|
||||||
@ -6428,6 +6500,140 @@ authorityKeyIdentifier = keyid:always
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_publication_point_audit_from_vcir_failed_no_cache_keeps_current_reject_only() {
|
||||||
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
let child_cert_hash = sha256_hex(b"child-cert");
|
||||||
|
let vcir = sample_vcir_for_projection(now, &child_cert_hash);
|
||||||
|
|
||||||
|
let ca = CaInstanceHandle {
|
||||||
|
depth: 0,
|
||||||
|
tal_id: "test-tal".to_string(),
|
||||||
|
parent_manifest_rsync_uri: None,
|
||||||
|
ca_certificate_der: Vec::new(),
|
||||||
|
ca_certificate_rsync_uri: None,
|
||||||
|
effective_ip_resources: None,
|
||||||
|
effective_as_resources: None,
|
||||||
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||||
|
manifest_rsync_uri: vcir.manifest_rsync_uri.clone(),
|
||||||
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||||
|
rrdp_notification_uri: Some("https://example.test/notify.xml".to_string()),
|
||||||
|
};
|
||||||
|
let objects = crate::validation::objects::ObjectsOutput {
|
||||||
|
vrps: Vec::new(),
|
||||||
|
aspas: Vec::new(),
|
||||||
|
router_keys: Vec::new(),
|
||||||
|
local_outputs_cache: Vec::new(),
|
||||||
|
warnings: Vec::new(),
|
||||||
|
stats: crate::validation::objects::ObjectsStats::default(),
|
||||||
|
audit: vec![ObjectAuditEntry {
|
||||||
|
rsync_uri: vcir.current_manifest_rsync_uri.clone(),
|
||||||
|
sha256_hex: sha256_hex(b"current-manifest"),
|
||||||
|
kind: AuditObjectKind::Manifest,
|
||||||
|
result: AuditObjectResult::Error,
|
||||||
|
detail: Some("manifest is not valid at validation_time".to_string()),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let audit = build_publication_point_audit_from_vcir(
|
||||||
|
&ca,
|
||||||
|
PublicationPointSource::FailedFetchNoCache,
|
||||||
|
Some("rsync"),
|
||||||
|
Some("rsync_only_ok"),
|
||||||
|
Some(123),
|
||||||
|
None,
|
||||||
|
Some(&vcir),
|
||||||
|
None,
|
||||||
|
&[Warning::new("latest VCIR instance_gate expired")],
|
||||||
|
&objects,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(audit.source, "failed_fetch_no_cache");
|
||||||
|
assert_eq!(audit.repo_terminal_state, "failed_no_cache");
|
||||||
|
assert_eq!(
|
||||||
|
audit.this_update_rfc3339_utc,
|
||||||
|
vcir.validated_manifest_meta
|
||||||
|
.validated_manifest_this_update
|
||||||
|
.rfc3339_utc
|
||||||
|
);
|
||||||
|
assert_eq!(audit.objects.len(), 1);
|
||||||
|
assert_eq!(audit.objects[0].rsync_uri, vcir.current_manifest_rsync_uri);
|
||||||
|
assert!(matches!(audit.objects[0].result, AuditObjectResult::Error));
|
||||||
|
assert!(
|
||||||
|
!audit
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.any(|entry| entry.rsync_uri == "rsync://example.test/repo/issuer/a.roa"),
|
||||||
|
"failed-no-cache must not expand old VCIR related artifacts into current-run audit",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!audit
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.any(|entry| entry.rsync_uri == "rsync://example.test/repo/issuer/issuer.crl"),
|
||||||
|
"failed-no-cache must not expose old CRL as current-run CIR input",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejected_manifest_audit_entry_for_failed_fetch_uses_current_repo_hash() {
|
||||||
|
let store_dir = tempfile::tempdir().expect("store dir");
|
||||||
|
let store = RocksStore::open(store_dir.path()).expect("open rocksdb");
|
||||||
|
let policy = Policy::default();
|
||||||
|
let runner = sample_runner_with_ccr_accumulator(&store, &policy);
|
||||||
|
let manifest_uri = "rsync://example.test/repo/issuer/issuer.mft";
|
||||||
|
let manifest_hash = sha256_hex(b"manifest-bytes");
|
||||||
|
store
|
||||||
|
.put_blob_bytes_batch(&[(manifest_hash.clone(), b"manifest-bytes".to_vec())])
|
||||||
|
.expect("put manifest bytes");
|
||||||
|
store
|
||||||
|
.put_repository_view_entry(&crate::storage::RepositoryViewEntry {
|
||||||
|
rsync_uri: manifest_uri.to_string(),
|
||||||
|
current_hash: Some(manifest_hash.clone()),
|
||||||
|
repository_source: Some("rsync://example.test/repo/issuer/".to_string()),
|
||||||
|
object_type: Some("mft".to_string()),
|
||||||
|
state: crate::storage::RepositoryViewState::Present,
|
||||||
|
})
|
||||||
|
.expect("put repository view");
|
||||||
|
let ca = CaInstanceHandle {
|
||||||
|
depth: 0,
|
||||||
|
tal_id: "test-tal".to_string(),
|
||||||
|
parent_manifest_rsync_uri: None,
|
||||||
|
ca_certificate_der: Vec::new(),
|
||||||
|
ca_certificate_rsync_uri: None,
|
||||||
|
effective_ip_resources: None,
|
||||||
|
effective_as_resources: None,
|
||||||
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||||
|
manifest_rsync_uri: manifest_uri.to_string(),
|
||||||
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||||
|
rrdp_notification_uri: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = runner
|
||||||
|
.rejected_manifest_audit_entry_for_failed_fetch(
|
||||||
|
&ca,
|
||||||
|
&ManifestFreshError::StaleOrEarly {
|
||||||
|
this_update_rfc3339_utc: "2026-05-27T08:37:07Z".to_string(),
|
||||||
|
next_update_rfc3339_utc: "2026-05-28T10:01:07Z".to_string(),
|
||||||
|
validation_time_rfc3339_utc: "2026-05-28T10:11:00Z".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("rejected manifest audit entry");
|
||||||
|
|
||||||
|
assert_eq!(entry.rsync_uri, manifest_uri);
|
||||||
|
assert_eq!(entry.sha256_hex, manifest_hash);
|
||||||
|
assert_eq!(entry.kind, AuditObjectKind::Manifest);
|
||||||
|
assert_eq!(entry.result, AuditObjectResult::Error);
|
||||||
|
assert!(
|
||||||
|
entry
|
||||||
|
.detail
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("")
|
||||||
|
.contains("manifest is not valid at validation_time")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_publication_point_audit_from_vcir_without_cached_inputs_returns_empty_listing() {
|
fn build_publication_point_audit_from_vcir_without_cached_inputs_returns_empty_listing() {
|
||||||
let ca = CaInstanceHandle {
|
let ca = CaInstanceHandle {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user