diff --git a/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs new file mode 100644 index 0000000000..0cf6fec6f0 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Graphics.Containers.Markdown; + +namespace osu.Game.Tests.Visual.Online +{ + public partial class TestSceneImageProxying : OsuTestScene + { + [Resolved] + private GameHost host { get; set; } = null!; + + [Test] + public void TestExternalImageLink() + { + AddStep("load image", () => Child = new OsuMarkdownContainer + { + RelativeSizeAxes = Axes.Both, + Text = "![](https://github.com/ppy/osu-wiki/blob/master/wiki/Announcement_messages/img/notification.png?raw=true)", + }); + } + + [Test] + public void TestLocalImageLink() + { + AddStep("load image", () => Child = new OsuMarkdownContainer + { + RelativeSizeAxes = Axes.Both, + Text = "![](https://osu.ppy.sh/help/wiki/shared/news/banners/monthly-beatmapping-contest.png)", + }); + } + + [Test] + public void TestInvalidImageLink() + { + AddStep("load image", () => Child = new OsuMarkdownContainer + { + RelativeSizeAxes = Axes.Both, + Text = "![](https://this-site-does-not-exist.com/img.png)", + }); + } + } +} diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 1d710e6395..81564cc2e8 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -6,9 +6,10 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Online; +using osu.Game.Online.API; namespace osu.Game.Audio { @@ -28,9 +29,9 @@ namespace osu.Game.Audio } [BackgroundDependencyLoader] - private void load(AudioManager audioManager) + private void load(AudioManager audioManager, IAPIProvider api) { - trackStore = audioManager.GetTrackStore(new OnlineStore()); + trackStore = audioManager.GetTrackStore(new OsuOnlineStore(api.APIEndpointUrl)); } /// diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs index 10207dd389..ff7df18f00 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs @@ -17,5 +17,8 @@ namespace osu.Game.Graphics.Containers.Markdown { TooltipText = linkInline.Title; } + + protected override ImageContainer CreateImageContainer(string url) + => base.CreateImageContainer($@"https://osu.ppy.sh/media-url?url={url}"); } } diff --git a/osu.Game/Online/OsuOnlineStore.cs b/osu.Game/Online/OsuOnlineStore.cs new file mode 100644 index 0000000000..c3e81c503f --- /dev/null +++ b/osu.Game/Online/OsuOnlineStore.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.IO.Stores; +using osu.Framework.Logging; + +namespace osu.Game.Online +{ + public class OsuOnlineStore : OnlineStore + { + private readonly string apiEndpointUrl; + + public OsuOnlineStore(string apiEndpointUrl) + { + this.apiEndpointUrl = apiEndpointUrl; + } + + protected override string GetLookupUrl(string url) + { + // add leading dot to avoid matching hosts named "ppy.sh" + if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) || !uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase)) + { + Logger.Log($@"Blocking resource lookup from external website: {url}", LoggingTarget.Network, LogLevel.Important); + return string.Empty; + } + + return url; + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 08960e3ebb..943e9b6c4e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -29,6 +29,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.IO.Stores; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Platform; @@ -824,6 +825,8 @@ namespace osu.Game protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything); + protected override OnlineStore CreateOnlineStore() => new OsuOnlineStore(CreateEndpoints().APIEndpointUrl); + #region Beatmap progression private void beatmapChanged(ValueChangedEvent beatmap) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b028280774..df3334eae8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -277,7 +277,7 @@ namespace osu.Game dependencies.CacheAs(Storage); var largeStore = new LargeTextureStore(Host.Renderer, Host.CreateTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); - largeStore.AddTextureSource(Host.CreateTextureLoaderStore(new OnlineStore())); + largeStore.AddTextureSource(Host.CreateTextureLoaderStore(CreateOnlineStore())); dependencies.Cache(largeStore); dependencies.CacheAs(LocalConfig);