fix(launcher): Campaign LA gate-round-1 review findings F1-F6 + hardening
F1: the crash reporter comment claimed the launcher never holds a password in any field - false (ProfileEditorDialogViewModel, AccountProfile.Password, StartRequest.Password). Reworded to the true, narrower invariant (no throw site interpolates a credential VALUE into an exception message) and pinned it with CrashReportNeverContainsAStoredPassword: a real STJ failure over a profiles document containing a known password, corrupted after the credential, must yield a crash file with the stack and without the value. F2: the co-deploy Inputs covered only Bake own sources; a Content edit never refreshed the 83 MB exe. Now the full reference closure. Fixing it surfaced two more incrementality traps, both fixed and comment-documented: SkipUnchangedFiles left the output older than the triggering input (target re-ran forever - added an explicit Touch), and %(Item.Metadata) in a plain Include does not batch (the literal percent-text became a permanently out-of-date phantom input - globs are now spelled per project). Verified: Core edit retriggers, then two consecutive clean incremental builds. F3: RID publishes ran BOTH co-deploy paths (two self-contained bake publishes). Build-time target now guarded on _IsPublishing; verified a real win-x64 publish runs zero build-target co-deploys and still ships both exes. F4: comment misattributed PublishBakeTool=false to CI lanes; it is target-local recursion guarding. F5: the x:Name reflection sweep now walks the markup as XML and tolerates template-scoped names (no generated field exists for those). F6: dead using removed. Hardening: the crash reporter positional --data-dir fallback requires a fully-qualified path so a relative or flag-shaped value cannot create ./crash-reports at an arbitrary CWD. Launcher 67/67, Launcher.Core 317/317. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1f87acf1af
commit
981e168fb9
6 changed files with 178 additions and 32 deletions
|
|
@ -58,22 +58,28 @@ internal static class Program
|
|||
/// exception goes to a file under the resolved data root instead of to
|
||||
/// stderr, and the path is printed.
|
||||
///
|
||||
/// <para>Redaction contract, stated exactly. This method writes only the
|
||||
/// exception chain plus non-identifying host facts; it never serializes
|
||||
/// <paramref name="args"/>, the environment, or process state. It does NOT
|
||||
/// claim the text is value-free: an exception message may quote whatever
|
||||
/// the thrower put in it, including an offending option name or a path
|
||||
/// (observed: "Launcher option '--x' requires a value"). That is acceptable
|
||||
/// because a credential cannot reach this text by construction — the
|
||||
/// launcher never holds a password in any field, credentials go straight to
|
||||
/// a child process's stdin, and <c>LauncherProcessSpec</c> carries no
|
||||
/// credential member (guarded by its own test). If that ever changes, this
|
||||
/// sink needs the same credential scanning the status stream has.</para>
|
||||
/// <para>Redaction contract, stated exactly (corrected by the gate-round-1
|
||||
/// review, F1). This method writes only the exception chain plus
|
||||
/// non-identifying host facts; it never serializes <paramref name="args"/>,
|
||||
/// the environment, or process state. Exception TEXT may quote whatever
|
||||
/// the thrower put in it — an option name, a path (observed: "Launcher
|
||||
/// option '--x' requires a value"). The launcher DOES hold credentials:
|
||||
/// <c>ProfileEditorDialogViewModel</c>'s password field,
|
||||
/// <c>AccountProfile.Password</c> (plaintext by user decision), and
|
||||
/// <c>StartRequest.Password</c>. The invariant this sink actually rests on
|
||||
/// is narrower: NO code path interpolates a credential VALUE into an
|
||||
/// exception message (System.Text.Json failures quote the JSON path, not
|
||||
/// the value; option/path errors quote the flag, not file contents).
|
||||
/// <c>MainWindowViewTests</c> pins it with a forced-failure test asserting
|
||||
/// a stored password never appears in the crash file. Any new throw site
|
||||
/// that puts profile or request state into a message breaks that test —
|
||||
/// at which point this sink needs the status-stream's credential
|
||||
/// scanning, not a bigger comment.</para>
|
||||
///
|
||||
/// <para>Never throws: a crash reporter that can itself fail would replace
|
||||
/// the original failure with its own.</para>
|
||||
/// </summary>
|
||||
private static string? TryWriteCrashReport(string[] args, Exception failure)
|
||||
internal static string? TryWriteCrashReport(string[] args, Exception failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -124,14 +130,18 @@ internal static class Program
|
|||
/// reporter only, so an isolated run keeps its evidence inside its own
|
||||
/// roots even when option parsing is what failed. Never used for anything
|
||||
/// the launcher actually runs on — <see cref="LauncherStartupOptions"/>
|
||||
/// remains the only validated path authority.
|
||||
/// remains the only validated path authority. Fully-qualified paths only
|
||||
/// (gate-round-1 review): a relative or flag-shaped value would create
|
||||
/// <c>./<value>/crash-reports</c> wherever the CWD happens to be,
|
||||
/// which defeats the isolation this fallback exists to preserve.
|
||||
/// </summary>
|
||||
private static string? TryReadRequestedDataDirectory(string[] args)
|
||||
{
|
||||
for (int index = 0; index + 1 < args.Length; index++)
|
||||
{
|
||||
if (string.Equals(args[index], "--data-dir", StringComparison.Ordinal)
|
||||
&& !string.IsNullOrWhiteSpace(args[index + 1]))
|
||||
&& !string.IsNullOrWhiteSpace(args[index + 1])
|
||||
&& Path.IsPathFullyQualified(args[index + 1]))
|
||||
{
|
||||
return args[index + 1];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue