From d51422e188baaf8530a3181b3eb018ee8eeb3e69 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Thu, 12 Sep 2019 12:31:17 +0200 Subject: [PATCH] SEBWIN-314: Main requests are now entirely prevented (before any loading happens) and only content requests are intercepted with custom resource handling. --- .../SafeExamBrowser.Browser.Contracts.csproj | 1 - .../BrowserApplicationInstance.cs | 15 +++- .../Events/ProgressChangedEventHandler.cs | 7 +- .../Events/RequestBlockedEventHandler.cs | 12 +++ .../Handlers/DisplayHandler.cs | 1 - .../Handlers/RequestHandler.cs | 57 +++++++++++++- .../Handlers/ResourceHandler.cs | 74 ++++++++----------- .../SafeExamBrowser.Browser.csproj | 2 + SafeExamBrowser.I18n.Contracts/TextKey.cs | 2 + SafeExamBrowser.I18n/Text.xml | 6 ++ 10 files changed, 123 insertions(+), 54 deletions(-) rename {SafeExamBrowser.Browser.Contracts => SafeExamBrowser.Browser}/Events/ProgressChangedEventHandler.cs (52%) create mode 100644 SafeExamBrowser.Browser/Events/RequestBlockedEventHandler.cs diff --git a/SafeExamBrowser.Browser.Contracts/SafeExamBrowser.Browser.Contracts.csproj b/SafeExamBrowser.Browser.Contracts/SafeExamBrowser.Browser.Contracts.csproj index 0027d68e..fee2d426 100644 --- a/SafeExamBrowser.Browser.Contracts/SafeExamBrowser.Browser.Contracts.csproj +++ b/SafeExamBrowser.Browser.Contracts/SafeExamBrowser.Browser.Contracts.csproj @@ -57,7 +57,6 @@ - diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs index 4a6fbdbf..2f6f127c 100644 --- a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs +++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs @@ -8,6 +8,7 @@ using System; using System.Net.Http; +using System.Threading.Tasks; using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts.Events; using SafeExamBrowser.Browser.Contracts.Events; @@ -102,7 +103,7 @@ namespace SafeExamBrowser.Browser var keyboardHandler = new KeyboardHandler(); var lifeSpanHandler = new LifeSpanHandler(); var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} {Id}"); - var requestHandler = new RequestHandler(appConfig, settings.Filter, logger, text); + var requestHandler = new RequestHandler(appConfig, settings.Filter, requestLogger, text); displayHandler.FaviconChanged += DisplayHandler_FaviconChanged; displayHandler.ProgressChanged += DisplayHandler_ProgressChanged; @@ -112,6 +113,7 @@ namespace SafeExamBrowser.Browser keyboardHandler.ZoomOutRequested += ZoomOutRequested; keyboardHandler.ZoomResetRequested += ZoomResetRequested; lifeSpanHandler.PopupRequested += LifeSpanHandler_PopupRequested; + requestHandler.RequestBlocked += RequestHandler_RequestBlocked; control = new BrowserControl(contextMenuHandler, displayHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, url); control.AddressChanged += Control_AddressChanged; @@ -224,6 +226,17 @@ namespace SafeExamBrowser.Browser } } + private void RequestHandler_RequestBlocked(string url) + { + Task.Run(() => + { + var message = text.Get(TextKey.MessageBox_BrowserNavigationBlocked).Replace("%%URL%%", url); + var title = text.Get(TextKey.MessageBox_BrowserNavigationBlockedTitle); + + messageBox.Show(message, title, parent: window); + }); + } + private void ReloadRequested() { if (WindowSettings.AllowReloading && WindowSettings.ShowReloadWarning) diff --git a/SafeExamBrowser.Browser.Contracts/Events/ProgressChangedEventHandler.cs b/SafeExamBrowser.Browser/Events/ProgressChangedEventHandler.cs similarity index 52% rename from SafeExamBrowser.Browser.Contracts/Events/ProgressChangedEventHandler.cs rename to SafeExamBrowser.Browser/Events/ProgressChangedEventHandler.cs index b15d5b51..678a32a7 100644 --- a/SafeExamBrowser.Browser.Contracts/Events/ProgressChangedEventHandler.cs +++ b/SafeExamBrowser.Browser/Events/ProgressChangedEventHandler.cs @@ -6,10 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Browser.Contracts.Events +namespace SafeExamBrowser.Browser.Events { - /// - /// Event handler used to indicate the current progress value of the page load process (from 0.0 to 1.0). - /// - public delegate void ProgressChangedEventHandler(double value); + internal delegate void ProgressChangedEventHandler(double value); } diff --git a/SafeExamBrowser.Browser/Events/RequestBlockedEventHandler.cs b/SafeExamBrowser.Browser/Events/RequestBlockedEventHandler.cs new file mode 100644 index 00000000..0c28057a --- /dev/null +++ b/SafeExamBrowser.Browser/Events/RequestBlockedEventHandler.cs @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +namespace SafeExamBrowser.Browser.Events +{ + internal delegate void RequestBlockedEventHandler(string url); +} diff --git a/SafeExamBrowser.Browser/Handlers/DisplayHandler.cs b/SafeExamBrowser.Browser/Handlers/DisplayHandler.cs index 285e7316..32cad179 100644 --- a/SafeExamBrowser.Browser/Handlers/DisplayHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/DisplayHandler.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using CefSharp; using CefSharp.Structs; -using SafeExamBrowser.Browser.Contracts.Events; using SafeExamBrowser.Browser.Events; namespace SafeExamBrowser.Browser.Handlers diff --git a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs index 60369c30..0fcb0170 100644 --- a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs @@ -7,6 +7,8 @@ */ using CefSharp; +using SafeExamBrowser.Browser.Events; +using SafeExamBrowser.Browser.Filters; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; @@ -16,15 +18,28 @@ namespace SafeExamBrowser.Browser.Handlers { internal class RequestHandler : CefSharp.Handler.RequestHandler { + private RequestFilter filter; + private ILogger logger; private ResourceHandler resourceHandler; + private BrowserFilterSettings settings; + + internal event RequestBlockedEventHandler RequestBlocked; internal RequestHandler(AppConfig appConfig, BrowserFilterSettings settings, ILogger logger, IText text) { - this.resourceHandler = new ResourceHandler(appConfig, settings, logger, text); + this.filter = new RequestFilter(); + this.logger = logger; + this.resourceHandler = new ResourceHandler(appConfig, settings, filter, logger, text); + this.settings = settings; } internal void Initiailize() { + if (settings.FilterMainRequests || settings.FilterContentRequests) + { + InitializeFilter(); + } + resourceHandler.Initialize(); } @@ -32,5 +47,45 @@ namespace SafeExamBrowser.Browser.Handlers { return resourceHandler; } + + protected override bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect) + { + if (Block(request)) + { + RequestBlocked?.Invoke(request.Url); + + return true; + } + + return base.OnBeforeBrowse(chromiumWebBrowser, browser, frame, request, userGesture, isRedirect); + } + + private bool Block(IRequest request) + { + if (settings.FilterMainRequests) + { + var result = filter.Process(request.Url); + var block = result == FilterResult.Block; + + if (block) + { + logger.Info($"Blocked main request for '{request.Url}'."); + } + + return block; + } + + return false; + } + + private void InitializeFilter() + { + foreach (var rule in settings.Rules) + { + filter.Load(rule); + } + + logger.Debug($"Initialized request filter with {settings.Rules.Count} rules."); + } } } diff --git a/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs b/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs index ac86d6bc..5c725917 100644 --- a/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs @@ -25,14 +25,14 @@ namespace SafeExamBrowser.Browser.Handlers private BrowserFilterSettings settings; private ILogger logger; private RequestFilter filter; - private IResourceHandler contentBlockedHandler; - private IResourceHandler pageBlockedHandler; + private IResourceHandler contentHandler; + private IResourceHandler pageHandler; private IText text; - internal ResourceHandler(AppConfig appConfig, BrowserFilterSettings settings, ILogger logger, IText text) + internal ResourceHandler(AppConfig appConfig, BrowserFilterSettings settings, RequestFilter filter, ILogger logger, IText text) { this.appConfig = appConfig; - this.filter = new RequestFilter(); + this.filter = filter; this.logger = logger; this.settings = settings; this.text = text; @@ -40,22 +40,17 @@ namespace SafeExamBrowser.Browser.Handlers internal void Initialize() { - if (settings.FilterMainRequests || settings.FilterContentRequests) + if (settings.FilterContentRequests) { - InitializeFilter(); + InitializeResourceHandlers(); } } protected override IResourceHandler GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request) { - if (BlockMainRequest(request)) + if (Block(request)) { - return pageBlockedHandler; - } - - if (BlockContentRequest(request)) - { - return contentBlockedHandler; + return ResourceHandlerFor(request.ResourceType); } return base.GetResourceHandler(chromiumWebBrowser, browser, frame, request); @@ -73,7 +68,7 @@ namespace SafeExamBrowser.Browser.Handlers return CefReturnValue.Cancel; } - ReplaceCustomScheme(request); + ReplaceSebScheme(request); return base.OnBeforeResourceLoad(webBrowser, browser, frame, request, callback); } @@ -87,9 +82,9 @@ namespace SafeExamBrowser.Browser.Handlers request.Headers = headers; } - private bool BlockContentRequest(IRequest request) + private bool Block(IRequest request) { - if (settings.FilterContentRequests && request.ResourceType != ResourceType.MainFrame) + if (settings.FilterContentRequests) { var result = filter.Process(request.Url); var block = result == FilterResult.Block; @@ -105,25 +100,7 @@ namespace SafeExamBrowser.Browser.Handlers return false; } - private bool BlockMainRequest(IRequest request) - { - if (settings.FilterMainRequests && request.ResourceType == ResourceType.MainFrame) - { - var result = filter.Process(request.Url); - var block = result == FilterResult.Block; - - if (block) - { - logger.Info($"Blocked main request for '{request.Url}'."); - } - - return block; - } - - return false; - } - - private void InitializeFilter() + private void InitializeResourceHandlers() { var assembly = Assembly.GetAssembly(typeof(RequestFilter)); var contentMessage = text.Get(TextKey.Browser_BlockedContentMessage); @@ -136,17 +113,12 @@ namespace SafeExamBrowser.Browser.Handlers var pageHtml = new StreamReader(pageStream).ReadToEnd(); contentHtml = contentHtml.Replace("%%MESSAGE%%", contentMessage); + contentHandler = CefSharp.ResourceHandler.FromString(contentHtml); + pageHtml = pageHtml.Replace("%%MESSAGE%%", pageMessage).Replace("%%TITLE%%", pageTitle).Replace("%%BACK_BUTTON%%", pageButton); + pageHandler = CefSharp.ResourceHandler.FromString(pageHtml); - contentBlockedHandler = CefSharp.ResourceHandler.FromString(contentHtml); - pageBlockedHandler = CefSharp.ResourceHandler.FromString(pageHtml); - - foreach (var rule in settings.Rules) - { - filter.Load(rule); - } - - logger.Debug($"Initialized request filter with {settings.Rules.Count} rules."); + logger.Debug("Initialized resource handlers for blocked requests."); } private bool IsMailtoUrl(string url) @@ -154,7 +126,7 @@ namespace SafeExamBrowser.Browser.Handlers return url.StartsWith(Uri.UriSchemeMailto); } - private void ReplaceCustomScheme(IRequest request) + private void ReplaceSebScheme(IRequest request) { if (Uri.IsWellFormedUriString(request.Url, UriKind.RelativeOrAbsolute)) { @@ -170,5 +142,17 @@ namespace SafeExamBrowser.Browser.Handlers } } } + + private IResourceHandler ResourceHandlerFor(ResourceType resourceType) + { + switch (resourceType) + { + case ResourceType.MainFrame: + case ResourceType.SubFrame: + return pageHandler; + default: + return contentHandler; + } + } } } diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj index 93c88d9a..f4ad08ff 100644 --- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj +++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj @@ -72,6 +72,8 @@ + + diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 51557657..a478c32c 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -24,6 +24,8 @@ namespace SafeExamBrowser.I18n.Contracts LogWindow_Title, MessageBox_ApplicationError, MessageBox_ApplicationErrorTitle, + MessageBox_BrowserNavigationBlocked, + MessageBox_BrowserNavigationBlockedTitle, MessageBox_CancelButton, MessageBox_ClientConfigurationError, MessageBox_ClientConfigurationErrorTitle, diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml index 17a3f8a4..19896597 100644 --- a/SafeExamBrowser.I18n/Text.xml +++ b/SafeExamBrowser.I18n/Text.xml @@ -30,6 +30,12 @@ Application Error + + Access to "%%URL%%" is not allowed according to the application configuration. + + + Page Blocked + Cancel