Gently rework icon and background image validation (#19044)

Right now, image validation accepts web-sourced icons (boo) and rejects
images whose paths begin with `\\?\`. In addition, it will warn the user
for things out of their control like images set by fragments.

This pull request adds a filesystem path validator (which accepts images
with fully-qualified paths and UNC paths), makes the URI validator
reject any web-origin URIs (only `file` and `ms-*` are allowable), and
suppresses warnings for any images that were not _directly_ set by the
user.

Since we want to avoid using fragment images that fail validation, we no
longer `Clear` each image property but rather set it to the blank or
fallback value.

This does **not** actually add support for images at absolute paths
beginning with `\\?\`. Such images are still rejected by `Image` and the
other XAML fixtures we use for images. It's better than a warning,
though.

Closes #18703
Closes #14143
Refs #18710
Refs #5204
Related to #18922 (http-origin icons will be blank everywhere and not
just the jump list ;))
This commit is contained in:
Dustin L. Howett 2025-06-16 18:01:35 -05:00 committed by GitHub
parent 098da6ce1c
commit f28bb42979
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -488,6 +488,39 @@ void CascadiaSettings::_validateAllSchemesExist()
}
}
static bool _validateSingleMediaResource(std::wstring_view resource)
{
// URI
try
{
winrt::Windows::Foundation::Uri resourceUri{ resource };
if (!resourceUri)
{
return false;
}
const auto scheme{ resourceUri.SchemeName() };
// Only file: URIs and ms-* URIs are permissible. http, https, ftp, gopher, etc. are not.
return til::equals_insensitive_ascii(scheme, L"file") || til::starts_with_insensitive_ascii(scheme, L"ms-");
}
catch (...)
{
// fall through
}
// Not a URI? Try a path.
try
{
std::filesystem::path resourcePath{ resource };
return std::filesystem::exists(resourcePath);
}
catch (...)
{
// fall through
}
return false;
}
// Method Description:
// - Ensures that all specified images resources (icons and background images) are valid URIs.
// This does not verify that the icon or background image files are encoded as an image.
@ -501,24 +534,26 @@ void CascadiaSettings::_validateAllSchemesExist()
// we find any invalid icon images.
void CascadiaSettings::_validateMediaResources()
{
auto invalidBackground{ false };
auto invalidIcon{ false };
auto warnInvalidBackground{ false };
auto warnInvalidIcon{ false };
for (auto profile : _allProfiles)
{
if (const auto path = profile.DefaultAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
if (!_validateSingleMediaResource(path))
{
winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
profile.DefaultAppearance().ClearBackgroundImagePath();
invalidBackground = true;
if (profile.DefaultAppearance().HasBackgroundImagePath())
{
// Only warn and delete if the user set this at the top level (do not warn for fragments, just clear it)
warnInvalidBackground = true;
profile.DefaultAppearance().ClearBackgroundImagePath();
}
else
{
// reset background image path (set it to blank as an override for any fragment value)
profile.DefaultAppearance().BackgroundImagePath({});
}
}
}
@ -526,17 +561,18 @@ void CascadiaSettings::_validateMediaResources()
{
if (const auto path = profile.UnfocusedAppearance().ExpandedBackgroundImagePath(); !path.empty())
{
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
// This covers file paths on the machine, app data, URLs, and other resource paths.
try
if (!_validateSingleMediaResource(path))
{
winrt::Windows::Foundation::Uri imagePath{ path };
}
catch (...)
{
// reset background image path
profile.UnfocusedAppearance().ClearBackgroundImagePath();
invalidBackground = true;
if (profile.UnfocusedAppearance().HasBackgroundImagePath())
{
warnInvalidBackground = true;
profile.UnfocusedAppearance().ClearBackgroundImagePath();
}
else
{
// reset background image path (set it to blank as an override for any fragment value)
profile.UnfocusedAppearance().BackgroundImagePath({});
}
}
}
}
@ -552,24 +588,27 @@ void CascadiaSettings::_validateMediaResources()
if (const auto icon = profile.Icon(); icon.size() > 2 && icon != HideIconValue)
{
const auto iconPath{ wil::ExpandEnvironmentStringsW<std::wstring>(icon.c_str()) };
try
if (!_validateSingleMediaResource(iconPath))
{
winrt::Windows::Foundation::Uri imagePath{ iconPath };
}
catch (...)
{
profile.ClearIcon();
invalidIcon = true;
if (profile.HasIcon())
{
warnInvalidIcon = true;
profile.ClearIcon();
}
else
{
profile.Icon({});
}
}
}
}
if (invalidBackground)
if (warnInvalidBackground)
{
_warnings.Append(SettingsLoadWarnings::InvalidBackgroundImage);
}
if (invalidIcon)
if (warnInvalidIcon)
{
_warnings.Append(SettingsLoadWarnings::InvalidIcon);
}