Display Unicode URIs side-by-side with their Punycode encoding (#15488)
06174a9
didn't properly fix the issue of us showing homoglyphs in our URI tooltip. This commit introduces a different approach where we display both, the Punycode and Unicode encoding, whenever we encounter an IDN. This isn't perfect but simple to implement. Closes #15432 ## Validation Steps Performed * `https://www.xn--fcbook-3nf5b.com/` (which contains confusing glyphs) is shown both in its Punycode and Unicode form simultaneously. ✅ --------- Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com> (cherry picked from commitf1aa6993f1
) Service-Card-Id: 90012448 Service-Version: 1.17
This commit is contained in:
parent
4247381bd2
commit
54950185f2
|
@ -77,6 +77,7 @@ IConnection
|
|||
ICustom
|
||||
IDialog
|
||||
IDirect
|
||||
Idn
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
|
|
|
@ -221,6 +221,7 @@ cmt
|
|||
cmw
|
||||
cmyk
|
||||
CNL
|
||||
cnn
|
||||
cnt
|
||||
CNTRL
|
||||
Codeflow
|
||||
|
@ -260,7 +261,6 @@ condrv
|
|||
conechokey
|
||||
conemu
|
||||
configurability
|
||||
confusables
|
||||
conhost
|
||||
conime
|
||||
conimeinfo
|
||||
|
@ -802,7 +802,6 @@ HIBYTE
|
|||
hicon
|
||||
HIDEWINDOW
|
||||
hinst
|
||||
Hirots
|
||||
HISTORYBUFS
|
||||
HISTORYNODUP
|
||||
HISTORYSIZE
|
||||
|
@ -817,6 +816,8 @@ hlsl
|
|||
hmod
|
||||
hmodule
|
||||
hmon
|
||||
homeglyphs
|
||||
homoglyph
|
||||
HORZ
|
||||
hostable
|
||||
hostlib
|
||||
|
@ -1273,7 +1274,6 @@ nullability
|
|||
nullness
|
||||
nullonfailure
|
||||
nullopts
|
||||
NULs
|
||||
numlock
|
||||
numpad
|
||||
NUMSCROLL
|
||||
|
@ -1771,7 +1771,6 @@ somefile
|
|||
SOURCEBRANCH
|
||||
sourced
|
||||
spammy
|
||||
spand
|
||||
SRCCODEPAGE
|
||||
SRCCOPY
|
||||
SRCINVERT
|
||||
|
@ -2295,7 +2294,6 @@ xunit
|
|||
xutr
|
||||
XVIRTUALSCREEN
|
||||
XWalk
|
||||
xwwyzz
|
||||
xxyyzz
|
||||
yact
|
||||
YCast
|
||||
|
|
|
@ -178,6 +178,10 @@
|
|||
<data name="HowToOpenRun.Text" xml:space="preserve">
|
||||
<value>Ctrl+Click to follow link</value>
|
||||
</data>
|
||||
<data name="InvalidUri" xml:space="preserve">
|
||||
<value>Invalid URI</value>
|
||||
<comment>Whenever we encounter an invalid URI or URL we show this string as a warning.</comment>
|
||||
</data>
|
||||
<data name="NoticeFontNotFound" xml:space="preserve">
|
||||
<value>Unable to find the selected font "{0}".
|
||||
|
||||
|
|
|
@ -2893,56 +2893,85 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_core.ClearHoveredCell();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable /*sender*/,
|
||||
IInspectable /*args*/)
|
||||
// Attackers abuse Unicode characters that happen to look similar to ASCII characters. Cyrillic for instance has
|
||||
// its own glyphs for а, с, е, о, р, х, and у that look practically identical to their ASCII counterparts.
|
||||
// This is called an "IDN homoglyph attack".
|
||||
//
|
||||
// But outright showing Punycode URIs only is similarly flawed as they can end up looking similar to valid ASCII URIs.
|
||||
// xn--cnn.com for instance looks confusingly similar to cnn.com, but actually represents U+407E.
|
||||
//
|
||||
// An optimal solution would detect any URI that contains homoglyphs and show them in their Punycode form.
|
||||
// Such a detector however is not quite trivial and requires constant maintenance, which this project's
|
||||
// maintainers aren't currently well equipped to handle. As such we do the next best thing and show the
|
||||
// Punycode encoding side-by-side with the Unicode string for any IDN.
|
||||
static winrt::hstring sanitizeURI(winrt::hstring uri)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
if (auto self{ weakThis.get() })
|
||||
if (uri.empty())
|
||||
{
|
||||
auto lastHoveredCell = _core.HoveredCell();
|
||||
if (lastHoveredCell)
|
||||
{
|
||||
winrt::hstring uriText = _core.HoveredUriText();
|
||||
if (uriText.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// DisplayUri will filter out non-printable characters and confusables.
|
||||
Windows::Foundation::Uri parsedUri{ uriText };
|
||||
if (!parsedUri)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
uriText = parsedUri.DisplayUri();
|
||||
|
||||
const auto panel = SwapChainPanel();
|
||||
const auto scale = panel.CompositionScaleX();
|
||||
const auto offset = panel.ActualOffset();
|
||||
|
||||
// Update the tooltip with the URI
|
||||
HoveredUri().Text(uriText);
|
||||
|
||||
// Set the border thickness so it covers the entire cell
|
||||
const auto charSizeInPixels = CharacterDimensions();
|
||||
const auto htInDips = charSizeInPixels.Height / scale;
|
||||
const auto wtInDips = charSizeInPixels.Width / scale;
|
||||
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::point locationInDIPs{ _toPosInDips(lastHoveredCell.Value()) };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
|
||||
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), locationInDIPs.y - offset.y);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
wchar_t punycodeBuffer[256];
|
||||
wchar_t unicodeBuffer[256];
|
||||
|
||||
// These functions return int, but are documented to only return positive numbers.
|
||||
// Better make sure though. It allows us to pass punycodeLength right into IdnToUnicode.
|
||||
const auto punycodeLength = std::max(0, IdnToAscii(0, uri.data(), gsl::narrow<int>(uri.size()), &punycodeBuffer[0], 256));
|
||||
const auto unicodeLength = std::max(0, IdnToUnicode(0, &punycodeBuffer[0], punycodeLength, &unicodeBuffer[0], 256));
|
||||
|
||||
if (punycodeLength <= 0 || unicodeLength <= 0)
|
||||
{
|
||||
return RS_(L"InvalidUri");
|
||||
}
|
||||
|
||||
const std::wstring_view punycode{ &punycodeBuffer[0], gsl::narrow_cast<size_t>(punycodeLength) };
|
||||
const std::wstring_view unicode{ &unicodeBuffer[0], gsl::narrow_cast<size_t>(unicodeLength) };
|
||||
|
||||
// IdnToAscii/IdnToUnicode return the input string as is if it's all
|
||||
// plain ASCII. But we don't know if the input URI is Punycode or not.
|
||||
// --> It's non-Punycode and ASCII if it round-trips.
|
||||
if (uri == punycode && uri == unicode)
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
|
||||
return winrt::hstring{ fmt::format(FMT_COMPILE(L"{}\n({})"), punycode, unicode) };
|
||||
}
|
||||
|
||||
void TermControl::_hoveredHyperlinkChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/)
|
||||
{
|
||||
const auto lastHoveredCell = _core.HoveredCell();
|
||||
if (!lastHoveredCell)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto uriText = sanitizeURI(_core.HoveredUriText());
|
||||
if (uriText.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto panel = SwapChainPanel();
|
||||
const auto scale = panel.CompositionScaleX();
|
||||
const auto offset = panel.ActualOffset();
|
||||
|
||||
// Update the tooltip with the URI
|
||||
HoveredUri().Text(uriText);
|
||||
|
||||
// Set the border thickness so it covers the entire cell
|
||||
const auto charSizeInPixels = CharacterDimensions();
|
||||
const auto htInDips = charSizeInPixels.Height / scale;
|
||||
const auto wtInDips = charSizeInPixels.Width / scale;
|
||||
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::point locationInDIPs{ _toPosInDips(lastHoveredCell.Value()) };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
|
||||
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), locationInDIPs.y - offset.y);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_updateSelectionMarkers(IInspectable /*sender*/, Control::UpdateSelectionMarkersEventArgs args)
|
||||
|
|
|
@ -304,7 +304,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs);
|
||||
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
|
||||
|
||||
winrt::fire_and_forget _hoveredHyperlinkChanged(IInspectable sender, IInspectable args);
|
||||
void _hoveredHyperlinkChanged(const IInspectable& sender, const IInspectable& args);
|
||||
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
|
||||
|
||||
void _coreFontSizeChanged(const int fontWidth,
|
||||
|
|
Loading…
Reference in New Issue