From 4549b93d2a34f6abf8d738dbe6d63db42ef99b6e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 19 Jul 2026 10:00:00 -0400 Subject: [PATCH] =?UTF-8?q?fix(secrets-cli):=20doctor=20flow=20=E2=80=94?= =?UTF-8?q?=20catch=20scoped=20exactly=20to=20the=20rewrap=20call?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interactive/Flows/KekDoctorFlow.cs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/Flows/KekDoctorFlow.cs b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/Flows/KekDoctorFlow.cs index ea4d5b8..0fd8ed8 100644 --- a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/Flows/KekDoctorFlow.cs +++ b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets.Cli/Interactive/Flows/KekDoctorFlow.cs @@ -108,35 +108,41 @@ public sealed class KekDoctorFlow : IInteractiveFlow { IMasterKeyProvider oldKek = PromptForOldKek(console); + // Prompt and confirm OUTSIDE the guard: only the rewrap call itself is caught, so a stray + // ArgumentException from setup/prompt code is never mis-reported as a "rewrap failed" message. + string oldKekId = oldKek.KekId; + int moving = report.Rows.Count(r => string.Equals(r.RowKekId, oldKekId, StringComparison.Ordinal)); + + string confirmText = + $"Re-wrap {moving} row(s) from KEK '{Markup.Escape(oldKekId)}' → session KEK " + + $"'{Markup.Escape(report.SessionKekId)}'? (rewraps every row on the old KEK)"; + if (!console.Prompt(new ConfirmationPrompt(confirmText) { DefaultValue = false })) + { + console.MarkupLine("[grey]Rewrap cancelled — no rows changed.[/]"); + return; + } + + RewrapReport result; try { - string oldKekId = oldKek.KekId; - int moving = report.Rows.Count(r => string.Equals(r.RowKekId, oldKekId, StringComparison.Ordinal)); - - string confirmText = - $"Re-wrap {moving} row(s) from KEK '{Markup.Escape(oldKekId)}' → session KEK " + - $"'{Markup.Escape(report.SessionKekId)}'? (rewraps every row on the old KEK)"; - if (!console.Prompt(new ConfirmationPrompt(confirmText) { DefaultValue = false })) - { - console.MarkupLine("[grey]Rewrap cancelled — no rows changed.[/]"); - return; - } - - RewrapReport result = await doctor.RewrapAllAsync(session, oldKek, ct).ConfigureAwait(false); - console.MarkupLineInterpolated( - $"[green]Re-wrapped {result.Rewrapped} row(s)[/] onto the session KEK ({result.AlreadyCurrent} already current) of {result.Total} total."); + result = await doctor.RewrapAllAsync(session, oldKek, ct).ConfigureAwait(false); } catch (ArgumentException ex) { // Old and session KEK are identical — nothing to rotate. console.MarkupLineInterpolated($"[red]Rewrap failed:[/] {ex.Message}"); + return; } catch (SecretDecryptionException ex) { // Wrong old key, or a row wrapped by neither the old nor the session KEK (the pass aborted; // any rows re-wrapped before the anomaly stay persisted). console.MarkupLineInterpolated($"[red]Rewrap failed:[/] {ex.Message}"); + return; } + + console.MarkupLineInterpolated( + $"[green]Re-wrapped {result.Rewrapped} row(s)[/] onto the session KEK ({result.AlreadyCurrent} already current) of {result.Total} total."); } // Prompts for the old-KEK source and builds the matching provider. Paste feeds a masked base64 entry to