fix(launcher): Campaign LA LA3 narrow review fixes
This commit is contained in:
parent
26feba8186
commit
347a1a5d16
9 changed files with 495 additions and 145 deletions
|
|
@ -1,4 +1,3 @@
|
|||
using System.Threading;
|
||||
using AcDream.Launcher.Core.Profiles;
|
||||
|
||||
namespace AcDream.Launcher.Core.Tests.Profiles;
|
||||
|
|
@ -287,68 +286,42 @@ public sealed class LauncherProfileStoreTests : IDisposable
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveNeverLeavesTheTempFileWorldOrGroupReadableDuringTheWrite()
|
||||
public void TempCredentialCreationOptionsRequestAtomicPlatformCorrectCreation()
|
||||
{
|
||||
// Review finding F4: the temp file used to be created with the
|
||||
// process's default umask and only chmod'd AFTER the atomic
|
||||
// rename, leaving a window where the plaintext-credential temp
|
||||
// file could be world/group-readable. The fix chmods the temp
|
||||
// file immediately after creation, BEFORE any content (including
|
||||
// the password) is serialized into it. A large document makes
|
||||
// the write take long enough for a concurrent poller to have a
|
||||
// real chance at observing a regression.
|
||||
FileStreamOptions options =
|
||||
LauncherProfileStore.CreateCredentialTempFileOptions();
|
||||
Assert.Equal(FileMode.CreateNew, options.Mode);
|
||||
Assert.Equal(FileAccess.Write, options.Access);
|
||||
Assert.Equal(FileShare.None, options.Share);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Assert.Equal(
|
||||
LauncherProfileStore.OwnerOnlyFileMode,
|
||||
options.UnixCreateMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(options.UnixCreateMode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TempCredentialFileIsOwnerOnlyFromItsFirstObservableLinuxState()
|
||||
{
|
||||
// Deterministic proof of the exact production create path: inspect
|
||||
// the file while the CreateNew handle is still open, before any
|
||||
// serialization or post-create chmod can occur. This replaces the
|
||||
// old timing-only poller, which could miss the vulnerable window.
|
||||
if (!OperatingSystem.IsLinux())
|
||||
return;
|
||||
|
||||
var store = new LauncherProfileStore(_filePath);
|
||||
store.Load();
|
||||
store.AddServer("Local ACE", "127.0.0.1", 9000);
|
||||
for (int i = 0; i < 300; i++)
|
||||
{
|
||||
store.AddAccount("Local ACE", $"account{i}", new string('x', 4096));
|
||||
}
|
||||
|
||||
string tempPath = _filePath + ".tmp";
|
||||
bool observedLooseMode = false;
|
||||
bool stop = false;
|
||||
var poller = new Thread(() =>
|
||||
{
|
||||
while (!Volatile.Read(ref stop))
|
||||
{
|
||||
if (File.Exists(tempPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// The platform-compat analyzer can't see the
|
||||
// enclosing test method's `OperatingSystem.IsLinux()`
|
||||
// guard across this lambda boundary; suppressed
|
||||
// rather than restructured, since the guard is
|
||||
// real and this whole method is a no-op off Linux.
|
||||
#pragma warning disable CA1416
|
||||
UnixFileMode mode = File.GetUnixFileMode(tempPath);
|
||||
#pragma warning restore CA1416
|
||||
if ((mode & ~(UnixFileMode.UserRead | UnixFileMode.UserWrite)) != 0)
|
||||
{
|
||||
observedLooseMode = true;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Renamed/deleted between the Exists check and
|
||||
// GetUnixFileMode — not a finding, just keep
|
||||
// polling.
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
poller.Start();
|
||||
using FileStream stream = LauncherProfileStore.CreateCredentialTempFile(tempPath);
|
||||
|
||||
store.Save();
|
||||
|
||||
Volatile.Write(ref stop, true);
|
||||
poller.Join();
|
||||
|
||||
Assert.False(observedLooseMode);
|
||||
Assert.Equal(
|
||||
LauncherProfileStore.OwnerOnlyFileMode,
|
||||
File.GetUnixFileMode(tempPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue