fix(launcher): verify self-update rollback sources
This commit is contained in:
parent
1955ca8ab5
commit
09d84387a8
6 changed files with 949 additions and 103 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using AcDream.Launcher.Core.Updates;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace AcDream.Launcher.Core.Tests.Updates;
|
||||
|
||||
|
|
@ -103,8 +104,16 @@ public sealed class LauncherSelfUpdateManagerTests : IDisposable
|
|||
SelfUpdatePlan rolledBack = await harness.Manager
|
||||
.RollbackAwaitingConfirmationAsync(harness.Target);
|
||||
|
||||
Assert.Equal(SelfUpdatePlanState.Staged, rolledBack.State);
|
||||
Assert.Null(rolledBack.Apply);
|
||||
Assert.Equal(SelfUpdatePlanState.RolledBack, rolledBack.State);
|
||||
Assert.All(rolledBack.Apply!, entry =>
|
||||
{
|
||||
if (entry.HadOriginal)
|
||||
{
|
||||
Assert.Matches("^[0-9a-f]{64}$", entry.PriorSha256!);
|
||||
Assert.NotNull(entry.PriorSize);
|
||||
Assert.NotNull(entry.PriorUnixMode);
|
||||
}
|
||||
});
|
||||
Assert.Equal("old-launcher", await File.ReadAllTextAsync(harness.LauncherPath));
|
||||
Assert.Equal("old-support", await File.ReadAllTextAsync(harness.SupportPath));
|
||||
SelfUpdatePlan retried = await harness.Manager.ApplyPendingAsync(harness.Target);
|
||||
|
|
@ -150,7 +159,7 @@ public sealed class LauncherSelfUpdateManagerTests : IDisposable
|
|||
SelfUpdatePlan rolledBack = await harness.Manager
|
||||
.RollbackAwaitingConfirmationAsync(harness.Target);
|
||||
|
||||
Assert.Equal(SelfUpdatePlanState.Staged, rolledBack.State);
|
||||
Assert.Equal(SelfUpdatePlanState.RolledBack, rolledBack.State);
|
||||
Assert.Equal("launcher-v2", await File.ReadAllTextAsync(harness.LauncherPath));
|
||||
Assert.Equal("support-v2", await File.ReadAllTextAsync(harness.SupportPath));
|
||||
Assert.Equal("obsolete-v2", await File.ReadAllTextAsync(obsoletePath));
|
||||
|
|
@ -170,7 +179,7 @@ public sealed class LauncherSelfUpdateManagerTests : IDisposable
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApplyFailpointAfterCanonicalAtomicReplaceRollsBackToStagedState()
|
||||
public async Task ApplyFailpointAfterCanonicalAtomicReplaceLeavesVerifiedRollbackReceipt()
|
||||
{
|
||||
using var harness = new Harness(_root);
|
||||
_ = await harness.StageAsync();
|
||||
|
|
@ -193,7 +202,8 @@ public sealed class LauncherSelfUpdateManagerTests : IDisposable
|
|||
await harness.Manager.LoadPendingAsync());
|
||||
|
||||
Assert.Equal("failpoint", failure.Message);
|
||||
Assert.Equal(SelfUpdatePlanState.Staged, recovered.State);
|
||||
Assert.Equal(SelfUpdatePlanState.RolledBack, recovered.State);
|
||||
await harness.Manager.VerifyRestoredPriorAsync(harness.Target);
|
||||
Assert.Equal("old-launcher", await File.ReadAllTextAsync(harness.LauncherPath));
|
||||
Assert.Equal("old-support", await File.ReadAllTextAsync(harness.SupportPath));
|
||||
Assert.Equal("new-launcher", await File.ReadAllTextAsync(Path.Combine(
|
||||
|
|
@ -201,6 +211,41 @@ public sealed class LauncherSelfUpdateManagerTests : IDisposable
|
|||
harness.LauncherName)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConditionalPriorIntegrityFieldsAreStrictAndFailClosed()
|
||||
{
|
||||
using var harness = new Harness(_root);
|
||||
_ = await harness.StageAsync();
|
||||
SelfUpdatePlan applied = await harness.Manager.ApplyPendingAsync(harness.Target);
|
||||
SelfUpdateApplyEntry canonical = Assert.Single(
|
||||
applied.Apply!,
|
||||
entry => entry.Path == harness.LauncherName);
|
||||
Assert.True(canonical.HadOriginal);
|
||||
Assert.Matches("^[0-9a-f]{64}$", canonical.PriorSha256!);
|
||||
Assert.NotNull(canonical.PriorSize);
|
||||
Assert.NotNull(canonical.PriorUnixMode);
|
||||
Assert.Matches("^[0-9a-f]{64}$", canonical.ReplacementSha256!);
|
||||
|
||||
JsonObject document = Assert.IsType<JsonObject>(JsonNode.Parse(
|
||||
await File.ReadAllTextAsync(harness.Manager.PendingPlanPath)));
|
||||
JsonArray apply = Assert.IsType<JsonArray>(document["apply"]);
|
||||
JsonObject canonicalNode = Assert.IsType<JsonObject>(apply.Single(node =>
|
||||
string.Equals(
|
||||
node?["path"]?.GetValue<string>(),
|
||||
harness.LauncherName,
|
||||
StringComparison.Ordinal)));
|
||||
canonicalNode["priorSha256"] = null;
|
||||
await File.WriteAllTextAsync(
|
||||
harness.Manager.PendingPlanPath,
|
||||
document.ToJsonString());
|
||||
|
||||
await Assert.ThrowsAsync<LauncherUpdateException>(() =>
|
||||
harness.Manager.LoadPendingAsync());
|
||||
Assert.Equal("new-launcher", await File.ReadAllTextAsync(harness.LauncherPath));
|
||||
Assert.True(Directory.Exists(
|
||||
harness.Manager.GetTargetTransactionDirectory(applied)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CorruptPayloadWrongTargetAndUnknownPlanFieldFailClosed()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue