Harden first-release validation and dependency hygiene
Some checks failed
ci / rust (push) Has been cancelled
ci / docker (push) Has been cancelled
ci / audit (push) Has been cancelled

This commit is contained in:
2026-09-10 09:53:45 +08:00
parent 277cbca878
commit 591bed90a8
61 changed files with 1641 additions and 622 deletions

View File

@@ -13,7 +13,11 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
}
policy.ta_constraints = args.ta_constraints.clone();
for warning in policy.ta_constraints.configuration_warnings() {
crate::logging::emit(crate::logging::Level::Warn, "constraint_warning", || serde_json::json!({"reason": warning}));
crate::logging::emit(
crate::logging::Level::Warn,
"constraint_warning",
|| serde_json::json!({"reason": warning}),
);
}
let validation_time = args
.validation_time
@@ -45,8 +49,7 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
let config = TreeRunConfig {
max_depth: Some(args.max_ca_depth),
max_instances: args.max_instances,
compact_audit: args.skip_report_build
&& args.report_json_path.is_none(),
compact_audit: args.skip_report_build && args.report_json_path.is_none(),
build_ccr_accumulator: args.ccr_out_path.is_some(),
};
@@ -66,7 +69,7 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
let fmt = time::format_description::parse_borrowed::<2>(
"[year][month][day]T[hour][minute][second]Z",
)
.map_err(|e| format!("format description parse failed: {e}"))?;
.map_err(|e| format!("format description parse failed: {e}"))?;
time::OffsetDateTime::now_utc()
.format(&fmt)
.map_err(|e| format!("format timestamp failed: {e}"))?
@@ -157,7 +160,11 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
};
let validation_ms = validation_started.elapsed().as_millis() as u64;
crate::logging::emit(crate::logging::Level::Info, "validation_phase_completed", || serde_json::json!({"elapsed_ms": validation_ms}));
crate::logging::emit(
crate::logging::Level::Info,
"validation_phase_completed",
|| serde_json::json!({"elapsed_ms": validation_ms}),
);
let shared = PostValidationShared::from_run_output(out);
record_memory_checkpoint(
&mut memory_checkpoints,
@@ -222,56 +229,50 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
.compare_view_trust_anchor
.as_deref()
.unwrap_or("unknown");
let (report_result, ccr_result, compare_view_result) =
std::thread::scope(|scope| {
// Reborrow `shared` as a plain reference so the scoped output tasks
// capture the reference instead of moving fields out of the owned value.
let shared = &shared;
let report_handle = if args.skip_report_build {
None
} else {
Some(scope.spawn(|| {
run_report_task(
&policy,
validation_time,
shared,
args.report_json_path.as_deref(),
report_json_format,
)
}))
};
let ccr_handle = scope.spawn(|| {
run_ccr_task(
let (report_result, ccr_result, compare_view_result) = std::thread::scope(|scope| {
// Reborrow `shared` as a plain reference so the scoped output tasks
// capture the reference instead of moving fields out of the owned value.
let shared = &shared;
let report_handle = if args.skip_report_build {
None
} else {
Some(scope.spawn(|| {
run_report_task(
&policy,
validation_time,
shared,
args.ccr_out_path.as_deref(),
ccr_produced_at,
args.report_json_path.as_deref(),
report_json_format,
)
});
let compare_view_handle = scope.spawn(|| {
run_compare_view_task(
shared,
args.vrps_csv_out_path.as_deref(),
args.vaps_csv_out_path.as_deref(),
compare_view_trust_anchor,
)
});
let report_result = match report_handle {
Some(handle) => handle
.join()
.map_err(|_| "report task panicked".to_string())
.and_then(|result| result),
None => Ok(ReportTaskOutput::skipped()),
};
let ccr_result = ccr_handle
.join()
.map_err(|_| "ccr task panicked".to_string())
.and_then(|result| result);
let compare_view_result = compare_view_handle
.join()
.map_err(|_| "compare view task panicked".to_string())
.and_then(|result| result);
(report_result, ccr_result, compare_view_result)
}))
};
let ccr_handle =
scope.spawn(|| run_ccr_task(shared, args.ccr_out_path.as_deref(), ccr_produced_at));
let compare_view_handle = scope.spawn(|| {
run_compare_view_task(
shared,
args.vrps_csv_out_path.as_deref(),
args.vaps_csv_out_path.as_deref(),
compare_view_trust_anchor,
)
});
let report_result = match report_handle {
Some(handle) => handle
.join()
.map_err(|_| "report task panicked".to_string())
.and_then(|result| result),
None => Ok(ReportTaskOutput::skipped()),
};
let ccr_result = ccr_handle
.join()
.map_err(|_| "ccr task panicked".to_string())
.and_then(|result| result);
let compare_view_result = compare_view_handle
.join()
.map_err(|_| "compare view task panicked".to_string())
.and_then(|result| result);
(report_result, ccr_result, compare_view_result)
});
let report_output = report_result?;
let ccr_output = ccr_result?;
let compare_view_output = compare_view_result?;
@@ -374,9 +375,7 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
// the transport/validation stages rather than by the compact publication
// point audit, so the final summary can use those authoritative counts.
if let Some(path) = args.summary_out_path.as_deref() {
let analysis_counts = timing
.as_ref()
.map(|(_, handle)| handle.counts_snapshot());
let analysis_counts = timing.as_ref().map(|(_, handle)| handle.counts_snapshot());
write_summary(
path,
&shared,
@@ -458,9 +457,7 @@ fn write_summary(
summary.synchronized_objects += point.objects.len();
match point.repo_sync_phase.as_deref() {
Some(phase) if phase.contains("fallback") => summary.rrdp_snapshot_fallbacks += 1,
Some(phase) if phase.contains("snapshot") => {
summary.rrdp_snapshot_repositories += 1
}
Some(phase) if phase.contains("snapshot") => summary.rrdp_snapshot_repositories += 1,
Some(phase) if phase.contains("delta") => summary.rrdp_delta_repositories += 1,
Some(phase) if phase.contains("noop") => summary.rrdp_noop_repositories += 1,
_ => {}
@@ -469,13 +466,25 @@ fn write_summary(
let accepted = object.result == AuditObjectResult::Ok;
match &object.kind {
AuditObjectKind::Manifest => {
if accepted { summary.validated_manifests += 1; } else { summary.rejected_manifests += 1; }
if accepted {
summary.validated_manifests += 1;
} else {
summary.rejected_manifests += 1;
}
}
AuditObjectKind::Crl => {
if accepted { summary.validated_crls += 1; } else { summary.rejected_crls += 1; }
if accepted {
summary.validated_crls += 1;
} else {
summary.rejected_crls += 1;
}
}
AuditObjectKind::Roa => {
if accepted { summary.validated_roas += 1; } else { summary.rejected_roas += 1; }
if accepted {
summary.validated_roas += 1;
} else {
summary.rejected_roas += 1;
}
}
_ => {}
}
@@ -522,15 +531,17 @@ fn write_summary(
.and_then(|counts| {
let snapshot = counts.get("rrdp_snapshot_objects_applied_total").copied();
let delta = counts.get("rrdp_delta_ops_applied_total").copied();
snapshot.zip(delta).map(|(snapshot, delta)| snapshot + delta)
snapshot
.zip(delta)
.map(|(snapshot, delta)| snapshot + delta)
})
.unwrap_or_else(|| {
shared
.download_stats
.by_kind
.values()
.filter_map(|stats| stats.objects_count_total)
.sum()
.download_stats
.by_kind
.values()
.filter_map(|stats| stats.objects_count_total)
.sum()
});
if downloaded_objects > 0 {
summary.synchronized_objects = downloaded_objects as usize;
@@ -538,8 +549,12 @@ fn write_summary(
summary.synchronized_repositories = notification_uris.len();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.map_err(|error| format!("create Panda RPKI summary parent {}: {error}", parent.display()))?;
std::fs::create_dir_all(parent).map_err(|error| {
format!(
"create Panda RPKI summary parent {}: {error}",
parent.display()
)
})?;
}
let bytes = serde_json::to_vec_pretty(&summary)
.map_err(|error| format!("serialize Panda RPKI summary: {error}"))?;