diff --git a/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs b/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs
new file mode 100644
index 00000000..039e158d
--- /dev/null
+++ b/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs
@@ -0,0 +1,96 @@
+/*
+ * 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/.
+ */
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SafeExamBrowser.Browser.Filters;
+using SafeExamBrowser.Settings.Browser;
+
+namespace SafeExamBrowser.Browser.UnitTests.Filters
+{
+ [TestClass]
+ public class RequestFilterTests
+ {
+ private RequestFilter sut;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ sut = new RequestFilter();
+ }
+
+ [TestMethod]
+ public void MustProcessBlockRulesFirst()
+ {
+ var allow = new FilterRuleSettings { Expression = "*", Type = FilterType.Simplified, Result = FilterResult.Allow };
+ var block = new FilterRuleSettings { Expression = "*", Type = FilterType.Simplified, Result = FilterResult.Block };
+
+ sut.Load(allow);
+ sut.Load(block);
+
+ var result = sut.Process("safeexambrowser.org");
+
+ Assert.AreEqual(FilterResult.Block, result);
+ }
+
+ [TestMethod]
+ public void MustProcessAllowRulesSecond()
+ {
+ var allow = new FilterRuleSettings { Expression = "*", Type = FilterType.Simplified, Result = FilterResult.Allow };
+ var block = new FilterRuleSettings { Expression = "xyz", Type = FilterType.Simplified, Result = FilterResult.Block };
+
+ sut.Load(allow);
+ sut.Load(block);
+
+ var result = sut.Process("safeexambrowser.org");
+
+ Assert.AreEqual(FilterResult.Allow, result);
+ }
+
+ [TestMethod]
+ public void MustReturnDefault()
+ {
+ var allow = new FilterRuleSettings { Expression = "xyz", Type = FilterType.Simplified, Result = FilterResult.Allow };
+ var block = new FilterRuleSettings { Expression = "xyz", Type = FilterType.Simplified, Result = FilterResult.Block };
+
+ sut.Default = (FilterResult) (-1);
+ sut.Load(allow);
+ sut.Load(block);
+
+ var result = sut.Process("safeexambrowser.org");
+
+ Assert.AreEqual((FilterResult) (-1), result);
+ }
+
+ [TestMethod]
+ public void MustReturnDefaultWithoutRules()
+ {
+ sut.Default = FilterResult.Allow;
+ var result = sut.Process("safeexambrowser.org");
+ Assert.AreEqual(FilterResult.Allow, result);
+
+ sut.Default = FilterResult.Block;
+ result = sut.Process("safeexambrowser.org");
+ Assert.AreEqual(FilterResult.Block, result);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void MustNotAllowUnsupportedResult()
+ {
+ sut.Load(new FilterRuleSettings { Result = (FilterResult) (-1) });
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void MustNotAllowUnsupportedFilterType()
+ {
+ sut.Load(new FilterRuleSettings { Type = (FilterType) (-1) });
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser.UnitTests/Properties/AssemblyInfo.cs b/SafeExamBrowser.Browser.UnitTests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7c1bb291
--- /dev/null
+++ b/SafeExamBrowser.Browser.UnitTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,17 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("SafeExamBrowser.Browser.UnitTests")]
+[assembly: AssemblyDescription("Safe Exam Browser")]
+[assembly: AssemblyCompany("ETH Zürich")]
+[assembly: AssemblyProduct("SafeExamBrowser.Browser.UnitTests")]
+[assembly: AssemblyCopyright("Copyright © 2019 ETH Zürich, Educational Development and Technology (LET)")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("f54c4c0e-4c72-4f88-a389-7f6de3ccb745")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyInformationalVersion("1.0.0.0")]
diff --git a/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj
new file mode 100644
index 00000000..ff793437
--- /dev/null
+++ b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj
@@ -0,0 +1,100 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}
+ Library
+ Properties
+ SafeExamBrowser.Browser.UnitTests
+ SafeExamBrowser.Browser.UnitTests
+ v4.7.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x86\Release\
+ TRACE
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+
+ ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {5fb5273d-277c-41dd-8593-a25ce1aff2e9}
+ SafeExamBrowser.Browser.Contracts
+
+
+ {04e653f1-98e6-4e34-9dd7-7f2bc1a8b767}
+ SafeExamBrowser.Browser
+
+
+ {30b2d907-5861-4f39-abad-c4abf1b3470e}
+ SafeExamBrowser.Settings
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Browser.UnitTests/packages.config b/SafeExamBrowser.Browser.UnitTests/packages.config
new file mode 100644
index 00000000..2f7c5a18
--- /dev/null
+++ b/SafeExamBrowser.Browser.UnitTests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
index 61e81cf1..259f2dda 100644
--- a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
+++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
@@ -14,9 +14,9 @@ using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Configuration.Contracts;
-using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
+using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
@@ -102,7 +102,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, logger);
+ var requestHandler = new RequestHandler(appConfig, settings.Filter, logger, text);
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
@@ -117,6 +117,8 @@ namespace SafeExamBrowser.Browser
control.AddressChanged += Control_AddressChanged;
control.LoadingStateChanged += Control_LoadingStateChanged;
control.TitleChanged += Control_TitleChanged;
+
+ requestHandler.Initiailize();
control.Initialize();
logger.Debug("Initialized browser control.");
diff --git a/SafeExamBrowser.Browser/Filters/BlockedContent.html b/SafeExamBrowser.Browser/Filters/BlockedContent.html
new file mode 100644
index 00000000..f73bea4e
--- /dev/null
+++ b/SafeExamBrowser.Browser/Filters/BlockedContent.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Browser/Filters/BlockedPage.html b/SafeExamBrowser.Browser/Filters/BlockedPage.html
new file mode 100644
index 00000000..f9dad271
--- /dev/null
+++ b/SafeExamBrowser.Browser/Filters/BlockedPage.html
@@ -0,0 +1,13 @@
+
+
+
+
+ %%TITLE%%
+
+
+
+
%%MESSAGE%%
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Browser/Filters/RequestFilter.cs b/SafeExamBrowser.Browser/Filters/RequestFilter.cs
index cb84e344..6cec7142 100644
--- a/SafeExamBrowser.Browser/Filters/RequestFilter.cs
+++ b/SafeExamBrowser.Browser/Filters/RequestFilter.cs
@@ -7,20 +7,74 @@
*/
using System;
+using System.Collections.Generic;
+using SafeExamBrowser.Browser.Filters.Rules;
using SafeExamBrowser.Settings.Browser;
namespace SafeExamBrowser.Browser.Filters
{
internal class RequestFilter
{
- internal void Load(FilterRule rule)
- {
+ private IList allowRules;
+ private IList blockRules;
+ internal FilterResult Default { get; set; }
+
+ internal RequestFilter()
+ {
+ allowRules = new List();
+ blockRules = new List();
+ Default = FilterResult.Block;
}
- internal FilterResult Process(Uri request)
+ internal void Load(FilterRuleSettings settings)
{
- return FilterResult.Allow;
+ var rule = default(Rule);
+
+ switch (settings.Type)
+ {
+ case FilterType.Regex:
+ rule = new RegexRule(settings.Expression);
+ break;
+ case FilterType.Simplified:
+ rule = new SimpleRule(settings.Expression);
+ break;
+ default:
+ throw new NotImplementedException($"Filter rule of type '{settings.Type}' is not yet implemented!");
+ }
+
+ switch (settings.Result)
+ {
+ case FilterResult.Allow:
+ allowRules.Add(rule);
+ break;
+ case FilterResult.Block:
+ blockRules.Add(rule);
+ break;
+ default:
+ throw new NotImplementedException($"Filter result '{settings.Result}' is not yet implemented!");
+ }
+ }
+
+ internal FilterResult Process(string url)
+ {
+ foreach (var rule in blockRules)
+ {
+ if (rule.IsMatch(url))
+ {
+ return FilterResult.Block;
+ }
+ }
+
+ foreach (var rule in allowRules)
+ {
+ if (rule.IsMatch(url))
+ {
+ return FilterResult.Allow;
+ }
+ }
+
+ return Default;
}
}
}
diff --git a/SafeExamBrowser.Browser/Filters/Rules/RegexRule.cs b/SafeExamBrowser.Browser/Filters/Rules/RegexRule.cs
new file mode 100644
index 00000000..d8bf6989
--- /dev/null
+++ b/SafeExamBrowser.Browser/Filters/Rules/RegexRule.cs
@@ -0,0 +1,31 @@
+/*
+ * 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/.
+ */
+
+using System.Text.RegularExpressions;
+
+namespace SafeExamBrowser.Browser.Filters.Rules
+{
+ internal class RegexRule : Rule
+ {
+ private string expression;
+
+ public RegexRule(string expression) : base(expression)
+ {
+ }
+
+ protected override void Initialize(string expression)
+ {
+ this.expression = expression;
+ }
+
+ internal override bool IsMatch(string url)
+ {
+ return Regex.IsMatch(url, expression, RegexOptions.IgnoreCase);
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser/Filters/Rules/Rule.cs b/SafeExamBrowser.Browser/Filters/Rules/Rule.cs
new file mode 100644
index 00000000..7c0a85c3
--- /dev/null
+++ b/SafeExamBrowser.Browser/Filters/Rules/Rule.cs
@@ -0,0 +1,21 @@
+/*
+ * 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.Filters.Rules
+{
+ internal abstract class Rule
+ {
+ internal Rule(string expression)
+ {
+ Initialize(expression);
+ }
+
+ internal abstract bool IsMatch(string url);
+ protected abstract void Initialize(string expression);
+ }
+}
diff --git a/SafeExamBrowser.Browser/Filters/Rules/SimpleRule.cs b/SafeExamBrowser.Browser/Filters/Rules/SimpleRule.cs
new file mode 100644
index 00000000..ccf793a1
--- /dev/null
+++ b/SafeExamBrowser.Browser/Filters/Rules/SimpleRule.cs
@@ -0,0 +1,31 @@
+/*
+ * 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/.
+ */
+
+using System.Text.RegularExpressions;
+
+namespace SafeExamBrowser.Browser.Filters.Rules
+{
+ internal class SimpleRule : Rule
+ {
+ private string expression;
+
+ public SimpleRule(string expression) : base(expression)
+ {
+ }
+
+ protected override void Initialize(string expression)
+ {
+ this.expression = expression.Replace("*", @".*");
+ }
+
+ internal override bool IsMatch(string url)
+ {
+ return Regex.IsMatch(url, expression, RegexOptions.IgnoreCase);
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs
index 78dc4ffe..60369c30 100644
--- a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs
+++ b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs
@@ -7,46 +7,30 @@
*/
using CefSharp;
-using SafeExamBrowser.Browser.Filters;
using SafeExamBrowser.Configuration.Contracts;
+using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
-using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
+using SafeExamBrowser.Settings.Browser;
namespace SafeExamBrowser.Browser.Handlers
{
internal class RequestHandler : CefSharp.Handler.RequestHandler
{
- private AppConfig appConfig;
- private BrowserSettings settings;
- private RequestFilter filter;
- private ILogger logger;
- private ResourceRequestHandler resourceRequestHandler;
+ private ResourceHandler resourceHandler;
- internal RequestHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
+ internal RequestHandler(AppConfig appConfig, BrowserFilterSettings settings, ILogger logger, IText text)
{
- this.appConfig = appConfig;
- this.settings = settings;
- this.filter = new RequestFilter();
- this.logger = logger;
- this.resourceRequestHandler = new ResourceRequestHandler(appConfig, settings, logger);
+ this.resourceHandler = new ResourceHandler(appConfig, settings, logger, text);
}
internal void Initiailize()
{
- if (settings.FilterMainRequests || settings.FilterContentRequests)
- {
- foreach (var rule in settings.FilterRules)
- {
- filter.Load(rule);
- }
-
- logger.Debug("Initialized request filter.");
- }
+ resourceHandler.Initialize();
}
- protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
+ protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
{
- return resourceRequestHandler;
+ return resourceHandler;
}
}
}
diff --git a/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs b/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs
new file mode 100644
index 00000000..ac86d6bc
--- /dev/null
+++ b/SafeExamBrowser.Browser/Handlers/ResourceHandler.cs
@@ -0,0 +1,174 @@
+/*
+ * 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/.
+ */
+
+using System;
+using System.Collections.Specialized;
+using System.IO;
+using System.Reflection;
+using CefSharp;
+using SafeExamBrowser.Browser.Filters;
+using SafeExamBrowser.Configuration.Contracts;
+using SafeExamBrowser.I18n.Contracts;
+using SafeExamBrowser.Logging.Contracts;
+using SafeExamBrowser.Settings.Browser;
+
+namespace SafeExamBrowser.Browser.Handlers
+{
+ internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler
+ {
+ private AppConfig appConfig;
+ private BrowserFilterSettings settings;
+ private ILogger logger;
+ private RequestFilter filter;
+ private IResourceHandler contentBlockedHandler;
+ private IResourceHandler pageBlockedHandler;
+ private IText text;
+
+ internal ResourceHandler(AppConfig appConfig, BrowserFilterSettings settings, ILogger logger, IText text)
+ {
+ this.appConfig = appConfig;
+ this.filter = new RequestFilter();
+ this.logger = logger;
+ this.settings = settings;
+ this.text = text;
+ }
+
+ internal void Initialize()
+ {
+ if (settings.FilterMainRequests || settings.FilterContentRequests)
+ {
+ InitializeFilter();
+ }
+ }
+
+ protected override IResourceHandler GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request)
+ {
+ if (BlockMainRequest(request))
+ {
+ return pageBlockedHandler;
+ }
+
+ if (BlockContentRequest(request))
+ {
+ return contentBlockedHandler;
+ }
+
+ return base.GetResourceHandler(chromiumWebBrowser, browser, frame, request);
+ }
+
+ protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
+ {
+ // TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser
+ // startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below
+ // reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development.
+ // AppendCustomUserAgent(request);
+
+ if (IsMailtoUrl(request.Url))
+ {
+ return CefReturnValue.Cancel;
+ }
+
+ ReplaceCustomScheme(request);
+
+ return base.OnBeforeResourceLoad(webBrowser, browser, frame, request, callback);
+ }
+
+ private void AppendCustomUserAgent(IRequest request)
+ {
+ var headers = new NameValueCollection(request.Headers);
+ var userAgent = request.Headers["User-Agent"];
+
+ headers["User-Agent"] = $"{userAgent} SEB/{appConfig.ProgramInformationalVersion}";
+ request.Headers = headers;
+ }
+
+ private bool BlockContentRequest(IRequest request)
+ {
+ if (settings.FilterContentRequests && request.ResourceType != ResourceType.MainFrame)
+ {
+ var result = filter.Process(request.Url);
+ var block = result == FilterResult.Block;
+
+ if (block)
+ {
+ logger.Info($"Blocked content request for '{request.Url}'.");
+ }
+
+ return block;
+ }
+
+ 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()
+ {
+ var assembly = Assembly.GetAssembly(typeof(RequestFilter));
+ var contentMessage = text.Get(TextKey.Browser_BlockedContentMessage);
+ var contentStream = assembly.GetManifestResourceStream($"{typeof(RequestFilter).Namespace}.BlockedContent.html");
+ var pageButton = text.Get(TextKey.Browser_BlockedPageButton);
+ var pageMessage = text.Get(TextKey.Browser_BlockedPageMessage);
+ var pageTitle = text.Get(TextKey.Browser_BlockedPageTitle);
+ var pageStream = assembly.GetManifestResourceStream($"{typeof(RequestFilter).Namespace}.BlockedPage.html");
+ var contentHtml = new StreamReader(contentStream).ReadToEnd();
+ var pageHtml = new StreamReader(pageStream).ReadToEnd();
+
+ contentHtml = contentHtml.Replace("%%MESSAGE%%", contentMessage);
+ pageHtml = pageHtml.Replace("%%MESSAGE%%", pageMessage).Replace("%%TITLE%%", pageTitle).Replace("%%BACK_BUTTON%%", pageButton);
+
+ 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.");
+ }
+
+ private bool IsMailtoUrl(string url)
+ {
+ return url.StartsWith(Uri.UriSchemeMailto);
+ }
+
+ private void ReplaceCustomScheme(IRequest request)
+ {
+ if (Uri.IsWellFormedUriString(request.Url, UriKind.RelativeOrAbsolute))
+ {
+ var uri = new Uri(request.Url);
+
+ if (uri.Scheme == appConfig.SebUriScheme)
+ {
+ request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri.AbsoluteUri;
+ }
+ else if (uri.Scheme == appConfig.SebUriSchemeSecure)
+ {
+ request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.Uri.AbsoluteUri;
+ }
+ }
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser/Handlers/ResourceRequestHandler.cs b/SafeExamBrowser.Browser/Handlers/ResourceRequestHandler.cs
deleted file mode 100644
index 35897e24..00000000
--- a/SafeExamBrowser.Browser/Handlers/ResourceRequestHandler.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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/.
- */
-
-using System;
-using System.Collections.Specialized;
-using CefSharp;
-using SafeExamBrowser.Configuration.Contracts;
-using SafeExamBrowser.Logging.Contracts;
-using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
-
-namespace SafeExamBrowser.Browser.Handlers
-{
- internal class ResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
- {
- private AppConfig appConfig;
- private BrowserSettings settings;
- private ILogger logger;
-
- internal ResourceRequestHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
- {
- this.appConfig = appConfig;
- this.settings = settings;
- this.logger = logger;
- }
-
- protected override IResourceHandler GetResourceHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request)
- {
- if (FilterMainRequest(request) || FilterContentRequest(request))
- {
- return ResourceHandler.FromString("Blocked!");
- }
-
- return base.GetResourceHandler(webBrowser, browser, frame, request);
- }
-
- protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
- {
- // TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser
- // startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below
- // reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development.
- // AppendCustomUserAgent(request);
-
- if (IsMailtoUrl(request.Url))
- {
- return CefReturnValue.Cancel;
- }
-
- ReplaceCustomScheme(request);
-
- return base.OnBeforeResourceLoad(webBrowser, browser, frame, request, callback);
- }
-
- private void AppendCustomUserAgent(IRequest request)
- {
- var headers = new NameValueCollection(request.Headers);
- var userAgent = request.Headers["User-Agent"];
-
- headers["User-Agent"] = $"{userAgent} SEB/{appConfig.ProgramInformationalVersion}";
- request.Headers = headers;
- }
-
- private bool FilterContentRequest(IRequest request)
- {
- return settings.FilterContentRequests && request.ResourceType != ResourceType.MainFrame;
- }
-
- private bool FilterMainRequest(IRequest request)
- {
- return settings.FilterMainRequests && request.ResourceType == ResourceType.MainFrame;
- }
-
- private bool IsMailtoUrl(string url)
- {
- return url.StartsWith(Uri.UriSchemeMailto);
- }
-
- private void ReplaceCustomScheme(IRequest request)
- {
- if (Uri.IsWellFormedUriString(request.Url, UriKind.RelativeOrAbsolute))
- {
- var uri = new Uri(request.Url);
-
- if (uri.Scheme == appConfig.SebUriScheme)
- {
- request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri.AbsoluteUri;
- }
- else if (uri.Scheme == appConfig.SebUriSchemeSecure)
- {
- request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.Uri.AbsoluteUri;
- }
- }
- }
- }
-}
diff --git a/SafeExamBrowser.Browser/Properties/AssemblyInfo.cs b/SafeExamBrowser.Browser/Properties/AssemblyInfo.cs
index e827a509..357b5ac6 100644
--- a/SafeExamBrowser.Browser/Properties/AssemblyInfo.cs
+++ b/SafeExamBrowser.Browser/Properties/AssemblyInfo.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -14,6 +15,7 @@ using System.Runtime.InteropServices;
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
+[assembly: InternalsVisibleTo("SafeExamBrowser.Browser.UnitTests")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("04e653f1-98e6-4e34-9dd7-7f2bc1a8b767")]
diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
index 253bda8c..93c88d9a 100644
--- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
+++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
@@ -59,6 +59,7 @@
+
@@ -72,6 +73,9 @@
+
+
+
Component
@@ -82,7 +86,7 @@
-
+
@@ -124,6 +128,10 @@
Designer
+
+
+
+
diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs
index 9579c2ac..4cabd897 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs
@@ -110,7 +110,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
if (value is bool filter)
{
- settings.Browser.FilterContentRequests = filter;
+ settings.Browser.Filter.FilterContentRequests = filter;
}
}
@@ -118,7 +118,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
if (value is bool filter)
{
- settings.Browser.FilterMainRequests = filter;
+ settings.Browser.Filter.FilterMainRequests = filter;
}
}
@@ -126,30 +126,33 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
const int ALLOW = 1;
- if (value is IEnumerable> ruleDataList)
+ if (value is IList