fix: keep attested Session operation actionable in safe startup summaries

This commit is contained in:
2026-11-04 18:06:13 +09:00
parent 10e16bb77d
commit 1d8c205b2e
3 changed files with 51 additions and 4 deletions
+2 -2
View File
@@ -2347,8 +2347,8 @@ impl RuntimeHttpRestError {
RuntimeError::WorkerExecutionRejected { result, .. } => startup_transfer(result), RuntimeError::WorkerExecutionRejected { result, .. } => startup_transfer(result),
_ => None, _ => None,
}; };
let message = if startup.is_some() { let message = if let Some(startup) = &startup {
"Worker startup failed".to_owned() worker::startup_failure::internal_startup_failure_summary(&startup.primary).to_owned()
} else { } else {
error.to_string() error.to_string()
}; };
+44
View File
@@ -120,6 +120,20 @@ pub fn reproject_internal_startup_failure(
StartupFailureAttachment::from_projected(&projected) StartupFailureAttachment::from_projected(&projected)
} }
/// Fixed, identifier-free summary for an already authenticated internal caller.
/// Keep the required operation actionable without exposing the source's Display.
/// This neither projects a Browser cause nor chooses an HTTP status.
pub fn internal_startup_failure_summary(failure: &StartupFailureAttachment) -> &'static str {
if failure
.project_for::<InternalTransfer, protocol::session_failure::SessionRegistrationCause>(&())
.is_ok()
{
"Worker startup failed while registering Session (session.record)"
} else {
"Worker startup failed"
}
}
/// Dispatch by the domain attested in process, not by parsing attachment fields. /// Dispatch by the domain attested in process, not by parsing attachment fields.
pub(crate) fn persist_binding_startup_failure<S: session_store::WorkerMetadataStore>( pub(crate) fn persist_binding_startup_failure<S: session_store::WorkerMetadataStore>(
store: &S, store: &S,
@@ -249,6 +263,36 @@ mod tests {
WorkerStoreError, WorkerStoreError,
}; };
#[test]
fn internal_summary_retains_only_attested_required_operation() {
use protocol::session_failure::{SessionPrincipal, SessionRegistrationCause};
let session = protocol::session_failure::session_registration_attachment(
SessionRegistrationCause::denied(SessionPrincipal::Subject),
)
.unwrap();
assert_eq!(
internal_startup_failure_summary(&session),
"Worker startup failed while registering Session (session.record)"
);
let runtime = runtime_startup_attachment(RuntimeStartupCause {
stage: StartupStage::Feature,
reason: RuntimeStartupReason::FeatureFailed,
})
.unwrap();
assert_eq!(
internal_startup_failure_summary(&runtime),
"Worker startup failed"
);
let corrupt = serde_json::from_value(serde_json::json!(
"session.record denied TEST_ONLY_CREDENTIAL /private/path raw_http_body"
))
.unwrap();
assert_eq!(
internal_startup_failure_summary(&corrupt),
"Worker startup failed"
);
}
struct LostSaveResponse(FsWorkerStore); struct LostSaveResponse(FsWorkerStore);
impl WorkerMetadataStore for LostSaveResponse { impl WorkerMetadataStore for LostSaveResponse {
fn write(&self, metadata: &WorkerMetadata) -> Result<(), WorkerStoreError> { fn write(&self, metadata: &WorkerMetadata) -> Result<(), WorkerStoreError> {
+5 -2
View File
@@ -6772,8 +6772,11 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti
), ),
}; };
if let EmbeddedRuntimeError::WorkerExecutionRejected { result, .. } = error { if let EmbeddedRuntimeError::WorkerExecutionRejected { result, .. } = error {
if result.startup_failure.is_some() { if let Some(primary) = &result.startup_failure {
projection.message = "Embedded Runtime Worker startup failed".into(); projection.message = format!(
"Embedded Runtime: {}",
worker::startup_failure::internal_startup_failure_summary(primary)
);
} }
projection.startup = result.startup_failure.as_ref().and_then(|primary| { projection.startup = result.startup_failure.as_ref().and_then(|primary| {
internal_startup_transfer(runtime_api::WorkerStartupFailure { internal_startup_transfer(runtime_api::WorkerStartupFailure {