Public Access
- A remote name went straight into Path.Combine. A POSIX name may hold backslashes, a colon or a drive letter, and an S3 key may hold "../", so a name chosen by whoever could write to the host decided where on this machine a download landed. LocalDirectory.ChildPath cleans the name and requires the resolved parent to be the target folder; every download path and EDIT use it, and a name that cannot be a file there is left on the host and reported. - EDIT handed the copy to the shell's default action, which for .bat, .hta, .js and friends is to run it. LocalEditor opens such a file with its own "edit" verb or Notepad on Windows and `open -t` on macOS, and refuses a .desktop launcher on Linux. - A failed bucket upload leaves its part object, and a retry asked for an offset no bucket accepts, so the row could never finish. IRemoteFileStore.CanWriteFromAnOffset is false for buckets and the queue starts those over. - The SFTP host picker re-found its selection from the empty moment of every vault reload and landed on the first host once a minute; the chosen host is now remembered, and a typed password is only sent to the host it was typed for.
71 lines
2.6 KiB
C#
71 lines
2.6 KiB
C#
using DodoSSH.Client.App.Platform;
|
|
|
|
namespace DodoSSH.Client.App.Tests;
|
|
|
|
/// <summary>
|
|
/// <c>LocalEditor</c>: a remote file opened for editing is never run.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The file's name was chosen by whoever can write to the host, so the question for every name here is
|
|
/// whether the desktop's default action would execute it. Only the Windows rule is exercised directly — it
|
|
/// is a pure function of the name and <c>PATHEXT</c> — while what each platform starts is checked on the
|
|
/// platform the suite is running on.
|
|
/// </remarks>
|
|
public sealed class LocalEditorTests
|
|
{
|
|
[Theory]
|
|
[InlineData("deploy.bat")]
|
|
[InlineData("DEPLOY.CMD")]
|
|
[InlineData("notes.hta")]
|
|
[InlineData("setup.js")]
|
|
[InlineData("link.lnk")]
|
|
[InlineData("run.ps1")]
|
|
[InlineData("settings.reg")]
|
|
[InlineData("deploy.bat.")]
|
|
[InlineData("deploy.bat ")]
|
|
public void AFileWindowsWouldRun_IsRecognised(string name) =>
|
|
LocalEditor.WouldRunOnWindows(name, pathExt: string.Empty).ShouldBeTrue();
|
|
|
|
[Theory]
|
|
[InlineData("nginx.conf")]
|
|
[InlineData("authorized_keys")]
|
|
[InlineData(".bashrc")]
|
|
[InlineData("app.json")]
|
|
[InlineData("bat")]
|
|
public void AnOrdinaryConfigFile_IsLeftToItsAssociation(string name) =>
|
|
LocalEditor.WouldRunOnWindows(name, pathExt: string.Empty).ShouldBeFalse();
|
|
|
|
[Fact]
|
|
public void WhateverPathExtAdds_IsTreatedAsRunnableToo() =>
|
|
LocalEditor.WouldRunOnWindows("tool.foo", pathExt: ".COM;.EXE;.FOO").ShouldBeTrue();
|
|
|
|
[Fact]
|
|
public void ARunnableFile_IsNeverHandedToTheDefaultOpenAction()
|
|
{
|
|
var path = Path.Combine(Path.GetTempPath(), "deploy.bat");
|
|
|
|
if (OperatingSystem.IsLinux())
|
|
{
|
|
// No portable editor to name, and a .bat is not something a Linux desktop runs.
|
|
LocalEditor.StartInfoFor(path).UseShellExecute.ShouldBeTrue();
|
|
Should.Throw<InvalidOperationException>(() => LocalEditor.StartInfoFor("/tmp/app.desktop"));
|
|
return;
|
|
}
|
|
|
|
var start = LocalEditor.StartInfoFor(OperatingSystem.IsMacOS() ? "/tmp/run.command" : path);
|
|
|
|
// Either the type's own "edit" verb, or a named editor with the file as an argument — never the
|
|
// file itself under its default verb.
|
|
(start.Verb == "edit" || !start.UseShellExecute).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void AnOrdinaryFile_StillOpensWithItsAssociation()
|
|
{
|
|
var start = LocalEditor.StartInfoFor(Path.Combine(Path.GetTempPath(), "nginx.conf"));
|
|
|
|
start.UseShellExecute.ShouldBeTrue();
|
|
start.Verb.ShouldBeEmpty();
|
|
}
|
|
}
|