Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions ImageDescriber.Test/ImageScannerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,54 @@
}
}

[TestMethod]
public void ScanForImagesSkipsUnreadableSubdirectories()
{
if (OperatingSystem.IsWindows())
{
Assert.Inconclusive("Unix file modes are used to make the folder unreadable.");
return;
}

Check warning on line 41 in ImageDescriber.Test/ImageScannerTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[OSCondition]' attribute instead of 'RuntimeInformation.IsOSPlatform' calls with early return or 'Assert.Inconclusive'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_ImageDescriber&issues=AaDmthAfHTUeIeoVadNL&open=AaDmthAfHTUeIeoVadNL&pullRequest=168

string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
string lockedDir = Path.Combine(tempDir, "locked");
Directory.CreateDirectory(lockedDir);
File.WriteAllBytes(Path.Combine(tempDir, "readable.jpg"), [0xFF, 0xD8]);
File.WriteAllBytes(Path.Combine(lockedDir, "hidden.jpg"), [0xFF, 0xD8]);
File.SetUnixFileMode(lockedDir, UnixFileMode.None);

try
{
if (Directory.Exists(lockedDir) && CanList(lockedDir))
{
Assert.Inconclusive("Running as a user that can read any folder, such as root.");
}

IReadOnlyList<AbsoluteFilePath> results = ImageScanner.ScanForImages(tempDir.As<AbsoluteDirectoryPath>());

Assert.AreEqual(1, results.Count);

Check warning on line 59 in ImageDescriber.Test/ImageScannerTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.HasCount' instead of 'Assert.AreEqual'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_ImageDescriber&issues=AaDmthAfHTUeIeoVadNK&open=AaDmthAfHTUeIeoVadNK&pullRequest=168
Assert.AreEqual("readable.jpg", Path.GetFileName(results[0].WeakString));
}
finally
{
File.SetUnixFileMode(lockedDir, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
Directory.Delete(tempDir, true);
}
}

private static bool CanList(string directory)
{
try
{
_ = Directory.GetFiles(directory);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}

[TestMethod]
public void ScanForImagesIgnoresNonImageFiles()
{
Expand Down
10 changes: 9 additions & 1 deletion ImageDescriber/ImageScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,16 @@ internal static IReadOnlyList<AbsoluteFilePath> ScanForImages(AbsoluteDirectoryP
return [];
}

// The SearchOption overload stops at the first folder it can't open, which on Windows is
// any drive or profile root, so skip unreadable folders and scan the rest of the tree.
EnumerationOptions options = new()
{
RecurseSubdirectories = true,
IgnoreInaccessible = true,
};

List<AbsoluteFilePath> imageFiles = [];
foreach (string file in Directory.EnumerateFiles(path.WeakString, "*", SearchOption.AllDirectories))
foreach (string file in Directory.EnumerateFiles(path.WeakString, "*", options))
{
string ext = Path.GetExtension(file);
if (string.IsNullOrEmpty(ext))
Expand Down
Loading