SEBWIN-314: Completed infrastructure for browser request filtering.
This commit is contained in:
parent
d51422e188
commit
5209103c97
25 changed files with 416 additions and 165 deletions
33
SafeExamBrowser.Browser.Contracts/Filters/IRequestFilter.cs
Normal file
33
SafeExamBrowser.Browser.Contracts/Filters/IRequestFilter.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Contracts.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the filter for browser requests.
|
||||
/// </summary>
|
||||
public interface IRequestFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// The default result to be returned by <see cref="Process(Request)"/> if no rule matches.
|
||||
/// </summary>
|
||||
FilterResult Default { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads the given filter rule to be used when processing requests.
|
||||
/// </summary>
|
||||
void Load(IRule rule);
|
||||
|
||||
/// <summary>
|
||||
/// Filters the given request according to the loaded rules.
|
||||
/// </summary>
|
||||
FilterResult Process(Request request);
|
||||
}
|
||||
}
|
33
SafeExamBrowser.Browser.Contracts/Filters/IRule.cs
Normal file
33
SafeExamBrowser.Browser.Contracts/Filters/IRule.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Contracts.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a request filter rule.
|
||||
/// </summary>
|
||||
public interface IRule
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter result to be used if the rule matches a request.
|
||||
/// </summary>
|
||||
FilterResult Result { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the rule for processing requests.
|
||||
/// </summary>
|
||||
void Initialize(FilterRuleSettings settings);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the rule applies for the given request.
|
||||
/// </summary>
|
||||
bool IsMatch(Request request);
|
||||
}
|
||||
}
|
23
SafeExamBrowser.Browser.Contracts/Filters/IRuleFactory.cs
Normal file
23
SafeExamBrowser.Browser.Contracts/Filters/IRuleFactory.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Contracts.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds request filter rules.
|
||||
/// </summary>
|
||||
public interface IRuleFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a filter rule for the given type.
|
||||
/// </summary>
|
||||
IRule CreateRule(FilterRuleType type);
|
||||
}
|
||||
}
|
|
@ -6,16 +6,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Browser.Filters.Rules
|
||||
namespace SafeExamBrowser.Browser.Contracts.Filters
|
||||
{
|
||||
internal abstract class Rule
|
||||
/// <summary>
|
||||
/// Holds data relevant for filtering requests.
|
||||
/// </summary>
|
||||
public class Request
|
||||
{
|
||||
internal Rule(string expression)
|
||||
{
|
||||
Initialize(expression);
|
||||
}
|
||||
|
||||
internal abstract bool IsMatch(string url);
|
||||
protected abstract void Initialize(string expression);
|
||||
/// <summary>
|
||||
/// The full URL of the request.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
|
@ -56,6 +56,10 @@
|
|||
<Compile Include="Events\DownloadEventArgs.cs" />
|
||||
<Compile Include="Events\DownloadFinishedCallback.cs" />
|
||||
<Compile Include="Events\DownloadRequestedEventHandler.cs" />
|
||||
<Compile Include="Filters\IRequestFilter.cs" />
|
||||
<Compile Include="Filters\IRule.cs" />
|
||||
<Compile Include="Filters\IRuleFactory.cs" />
|
||||
<Compile Include="Filters\Request.cs" />
|
||||
<Compile Include="IBrowserApplication.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -64,6 +68,10 @@
|
|||
<Project>{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}</Project>
|
||||
<Name>SafeExamBrowser.Applications.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
|
||||
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
||||
<Name>SafeExamBrowser.Settings</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
|
@ -27,13 +29,20 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
|||
[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 };
|
||||
var allow = new Mock<IRule>();
|
||||
var block = new Mock<IRule>();
|
||||
|
||||
sut.Load(allow);
|
||||
sut.Load(block);
|
||||
allow.SetupGet(r => r.Result).Returns(FilterResult.Allow);
|
||||
block.SetupGet(r => r.Result).Returns(FilterResult.Block);
|
||||
block.Setup(r => r.IsMatch(It.IsAny<Request>())).Returns(true);
|
||||
|
||||
var result = sut.Process("safeexambrowser.org");
|
||||
sut.Load(allow.Object);
|
||||
sut.Load(block.Object);
|
||||
|
||||
var result = sut.Process(new Request());
|
||||
|
||||
allow.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Never);
|
||||
block.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Once);
|
||||
|
||||
Assert.AreEqual(FilterResult.Block, result);
|
||||
}
|
||||
|
@ -41,28 +50,41 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
|||
[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 };
|
||||
var allow = new Mock<IRule>();
|
||||
var block = new Mock<IRule>();
|
||||
|
||||
sut.Load(allow);
|
||||
sut.Load(block);
|
||||
allow.SetupGet(r => r.Result).Returns(FilterResult.Allow);
|
||||
allow.Setup(r => r.IsMatch(It.IsAny<Request>())).Returns(true);
|
||||
block.SetupGet(r => r.Result).Returns(FilterResult.Block);
|
||||
|
||||
var result = sut.Process("safeexambrowser.org");
|
||||
sut.Load(allow.Object);
|
||||
sut.Load(block.Object);
|
||||
|
||||
var result = sut.Process(new Request());
|
||||
|
||||
allow.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Once);
|
||||
block.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Once);
|
||||
|
||||
Assert.AreEqual(FilterResult.Allow, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustReturnDefault()
|
||||
public void MustReturnDefaultWithoutMatch()
|
||||
{
|
||||
var allow = new FilterRuleSettings { Expression = "xyz", Type = FilterType.Simplified, Result = FilterResult.Allow };
|
||||
var block = new FilterRuleSettings { Expression = "xyz", Type = FilterType.Simplified, Result = FilterResult.Block };
|
||||
var allow = new Mock<IRule>();
|
||||
var block = new Mock<IRule>();
|
||||
|
||||
allow.SetupGet(r => r.Result).Returns(FilterResult.Allow);
|
||||
block.SetupGet(r => r.Result).Returns(FilterResult.Block);
|
||||
|
||||
sut.Default = (FilterResult) (-1);
|
||||
sut.Load(allow);
|
||||
sut.Load(block);
|
||||
sut.Load(allow.Object);
|
||||
sut.Load(block.Object);
|
||||
|
||||
var result = sut.Process("safeexambrowser.org");
|
||||
var result = sut.Process(new Request());
|
||||
|
||||
allow.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Once);
|
||||
block.Verify(r => r.IsMatch(It.IsAny<Request>()), Times.Once);
|
||||
|
||||
Assert.AreEqual((FilterResult) (-1), result);
|
||||
}
|
||||
|
@ -71,11 +93,11 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
|||
public void MustReturnDefaultWithoutRules()
|
||||
{
|
||||
sut.Default = FilterResult.Allow;
|
||||
var result = sut.Process("safeexambrowser.org");
|
||||
var result = sut.Process(new Request());
|
||||
Assert.AreEqual(FilterResult.Allow, result);
|
||||
|
||||
sut.Default = FilterResult.Block;
|
||||
result = sut.Process("safeexambrowser.org");
|
||||
result = sut.Process(new Request());
|
||||
Assert.AreEqual(FilterResult.Block, result);
|
||||
}
|
||||
|
||||
|
@ -83,14 +105,10 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
|||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedResult()
|
||||
{
|
||||
sut.Load(new FilterRuleSettings { Result = (FilterResult) (-1) });
|
||||
}
|
||||
var rule = new Mock<IRule>();
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedFilterType()
|
||||
{
|
||||
sut.Load(new FilterRuleSettings { Type = (FilterType) (-1) });
|
||||
rule.SetupGet(r => r.Result).Returns((FilterResult) (-1));
|
||||
sut.Load(rule.Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.Browser.Filters.Rules;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters
|
||||
{
|
||||
[TestClass]
|
||||
public class RuleFactoryTests
|
||||
{
|
||||
private RuleFactory sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new RuleFactory();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCreateCorrectRules()
|
||||
{
|
||||
Assert.IsInstanceOfType(sut.CreateRule(FilterRuleType.Regex), typeof(RegexRule));
|
||||
Assert.IsInstanceOfType(sut.CreateRule(FilterRuleType.Simplified), typeof(SimplifiedRule));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedFilterType()
|
||||
{
|
||||
sut.CreateRule((FilterRuleType) (-1));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
{
|
||||
[TestClass]
|
||||
public class RegexRuleTests
|
||||
{
|
||||
private RegexRule sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new RegexRule();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
{
|
||||
[TestClass]
|
||||
public class SimplifiedRuleTests
|
||||
{
|
||||
private SimplifiedRule sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new SimplifiedRule();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -57,20 +57,37 @@
|
|||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.13.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.13.0\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Filters\RequestFilterTests.cs" />
|
||||
<Compile Include="Filters\RuleFactoryTests.cs" />
|
||||
<Compile Include="Filters\Rules\RegexRuleTests.cs" />
|
||||
<Compile Include="Filters\Rules\SimplifiedRuleTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -93,8 +110,8 @@
|
|||
<PropertyGroup>
|
||||
<ErrorText>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}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" />
|
||||
</Project>
|
11
SafeExamBrowser.Browser.UnitTests/app.config
Normal file
11
SafeExamBrowser.Browser.UnitTests/app.config
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
|
@ -1,5 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net472" />
|
||||
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net472" />
|
||||
<package id="Castle.Core" version="4.4.0" targetFramework="net472" />
|
||||
<package id="Moq" version="4.13.0" targetFramework="net472" />
|
||||
<package id="MSTest.TestAdapter" version="2.0.0" targetFramework="net472" />
|
||||
<package id="MSTest.TestFramework" version="2.0.0" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net472" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.3" targetFramework="net472" />
|
||||
</packages>
|
|
@ -13,6 +13,7 @@ using SafeExamBrowser.Applications.Contracts;
|
|||
using SafeExamBrowser.Applications.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
|
@ -102,8 +103,9 @@ namespace SafeExamBrowser.Browser
|
|||
var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
|
||||
var keyboardHandler = new KeyboardHandler();
|
||||
var lifeSpanHandler = new LifeSpanHandler();
|
||||
var requestFilter = new RequestFilter();
|
||||
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} {Id}");
|
||||
var requestHandler = new RequestHandler(appConfig, settings.Filter, requestLogger, text);
|
||||
var requestHandler = new RequestHandler(appConfig, settings.Filter, requestFilter, requestLogger, text);
|
||||
|
||||
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
||||
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
||||
|
@ -115,14 +117,27 @@ namespace SafeExamBrowser.Browser
|
|||
lifeSpanHandler.PopupRequested += LifeSpanHandler_PopupRequested;
|
||||
requestHandler.RequestBlocked += RequestHandler_RequestBlocked;
|
||||
|
||||
if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests)
|
||||
{
|
||||
var factory = new RuleFactory();
|
||||
|
||||
foreach (var settings in settings.Filter.Rules)
|
||||
{
|
||||
var rule = factory.CreateRule(settings.Type);
|
||||
|
||||
rule.Initialize(settings);
|
||||
requestFilter.Load(rule);
|
||||
}
|
||||
|
||||
logger.Debug($"Initialized request filter with {settings.Filter.Rules.Count} rule(s).");
|
||||
}
|
||||
|
||||
control = new BrowserControl(contextMenuHandler, displayHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, url);
|
||||
control.AddressChanged += Control_AddressChanged;
|
||||
control.LoadingStateChanged += Control_LoadingStateChanged;
|
||||
control.TitleChanged += Control_TitleChanged;
|
||||
|
||||
requestHandler.Initiailize();
|
||||
control.Initialize();
|
||||
|
||||
logger.Debug("Initialized browser control.");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,42 +8,28 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Filters
|
||||
{
|
||||
internal class RequestFilter
|
||||
internal class RequestFilter : IRequestFilter
|
||||
{
|
||||
private IList<Rule> allowRules;
|
||||
private IList<Rule> blockRules;
|
||||
private IList<IRule> allowRules;
|
||||
private IList<IRule> blockRules;
|
||||
|
||||
internal FilterResult Default { get; set; }
|
||||
public FilterResult Default { get; set; }
|
||||
|
||||
internal RequestFilter()
|
||||
{
|
||||
allowRules = new List<Rule>();
|
||||
blockRules = new List<Rule>();
|
||||
allowRules = new List<IRule>();
|
||||
blockRules = new List<IRule>();
|
||||
Default = FilterResult.Block;
|
||||
}
|
||||
|
||||
internal void Load(FilterRuleSettings settings)
|
||||
public void Load(IRule rule)
|
||||
{
|
||||
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)
|
||||
switch (rule.Result)
|
||||
{
|
||||
case FilterResult.Allow:
|
||||
allowRules.Add(rule);
|
||||
|
@ -52,15 +38,15 @@ namespace SafeExamBrowser.Browser.Filters
|
|||
blockRules.Add(rule);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Filter result '{settings.Result}' is not yet implemented!");
|
||||
throw new NotImplementedException($"Filter processing for result '{rule.Result}' is not yet implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
internal FilterResult Process(string url)
|
||||
public FilterResult Process(Request request)
|
||||
{
|
||||
foreach (var rule in blockRules)
|
||||
{
|
||||
if (rule.IsMatch(url))
|
||||
if (rule.IsMatch(request))
|
||||
{
|
||||
return FilterResult.Block;
|
||||
}
|
||||
|
@ -68,7 +54,7 @@ namespace SafeExamBrowser.Browser.Filters
|
|||
|
||||
foreach (var rule in allowRules)
|
||||
{
|
||||
if (rule.IsMatch(url))
|
||||
if (rule.IsMatch(request))
|
||||
{
|
||||
return FilterResult.Allow;
|
||||
}
|
||||
|
|
31
SafeExamBrowser.Browser/Filters/RuleFactory.cs
Normal file
31
SafeExamBrowser.Browser/Filters/RuleFactory.cs
Normal file
|
@ -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;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Filters
|
||||
{
|
||||
internal class RuleFactory : IRuleFactory
|
||||
{
|
||||
public IRule CreateRule(FilterRuleType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case FilterRuleType.Regex:
|
||||
return new RegexRule();
|
||||
case FilterRuleType.Simplified:
|
||||
return new SimplifiedRule();
|
||||
default:
|
||||
throw new NotImplementedException($"Filter rule of type '{type}' is not yet implemented!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,25 +7,26 @@
|
|||
*/
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Filters.Rules
|
||||
{
|
||||
internal class RegexRule : Rule
|
||||
internal class RegexRule : IRule
|
||||
{
|
||||
private string expression;
|
||||
|
||||
public RegexRule(string expression) : base(expression)
|
||||
public FilterResult Result { get; private set; }
|
||||
|
||||
public void Initialize(FilterRuleSettings settings)
|
||||
{
|
||||
expression = settings.Expression;
|
||||
Result = settings.Result;
|
||||
}
|
||||
|
||||
protected override void Initialize(string expression)
|
||||
public bool IsMatch(Request request)
|
||||
{
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
internal override bool IsMatch(string url)
|
||||
{
|
||||
return Regex.IsMatch(url, expression, RegexOptions.IgnoreCase);
|
||||
return Regex.IsMatch(request.Url, expression, RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,25 +7,26 @@
|
|||
*/
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Filters.Rules
|
||||
{
|
||||
internal class SimpleRule : Rule
|
||||
internal class SimplifiedRule : IRule
|
||||
{
|
||||
private string expression;
|
||||
|
||||
public SimpleRule(string expression) : base(expression)
|
||||
public FilterResult Result { get; private set; }
|
||||
|
||||
public void Initialize(FilterRuleSettings settings)
|
||||
{
|
||||
expression = settings.Expression.Replace("*", @".*");
|
||||
Result = settings.Result;
|
||||
}
|
||||
|
||||
protected override void Initialize(string expression)
|
||||
public bool IsMatch(Request request)
|
||||
{
|
||||
this.expression = expression.Replace("*", @".*");
|
||||
}
|
||||
|
||||
internal override bool IsMatch(string url)
|
||||
{
|
||||
return Regex.IsMatch(url, expression, RegexOptions.IgnoreCase);
|
||||
return Regex.IsMatch(request.Url, expression, RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
@ -18,31 +18,21 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
{
|
||||
internal class RequestHandler : CefSharp.Handler.RequestHandler
|
||||
{
|
||||
private RequestFilter filter;
|
||||
private IRequestFilter filter;
|
||||
private ILogger logger;
|
||||
private ResourceHandler resourceHandler;
|
||||
private BrowserFilterSettings settings;
|
||||
|
||||
internal event RequestBlockedEventHandler RequestBlocked;
|
||||
|
||||
internal RequestHandler(AppConfig appConfig, BrowserFilterSettings settings, ILogger logger, IText text)
|
||||
internal RequestHandler(AppConfig appConfig, BrowserFilterSettings settings, IRequestFilter filter, ILogger logger, IText text)
|
||||
{
|
||||
this.filter = new RequestFilter();
|
||||
this.filter = filter;
|
||||
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();
|
||||
}
|
||||
|
||||
protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
|
||||
{
|
||||
return resourceHandler;
|
||||
|
@ -62,9 +52,9 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
private bool Block(IRequest request)
|
||||
{
|
||||
if (settings.FilterMainRequests)
|
||||
if (settings.ProcessMainRequests)
|
||||
{
|
||||
var result = filter.Process(request.Url);
|
||||
var result = filter.Process(new Request { Url = request.Url });
|
||||
var block = result == FilterResult.Block;
|
||||
|
||||
if (block)
|
||||
|
@ -77,15 +67,5 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InitializeFilter()
|
||||
{
|
||||
foreach (var rule in settings.Rules)
|
||||
{
|
||||
filter.Load(rule);
|
||||
}
|
||||
|
||||
logger.Debug($"Initialized request filter with {settings.Rules.Count} rules.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Collections.Specialized;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
|
@ -24,12 +25,12 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
private AppConfig appConfig;
|
||||
private BrowserFilterSettings settings;
|
||||
private ILogger logger;
|
||||
private RequestFilter filter;
|
||||
private IRequestFilter filter;
|
||||
private IResourceHandler contentHandler;
|
||||
private IResourceHandler pageHandler;
|
||||
private IText text;
|
||||
|
||||
internal ResourceHandler(AppConfig appConfig, BrowserFilterSettings settings, RequestFilter filter, ILogger logger, IText text)
|
||||
internal ResourceHandler(AppConfig appConfig, BrowserFilterSettings settings, IRequestFilter filter, ILogger logger, IText text)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.filter = filter;
|
||||
|
@ -38,14 +39,6 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
this.text = text;
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
if (settings.FilterContentRequests)
|
||||
{
|
||||
InitializeResourceHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IResourceHandler GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request)
|
||||
{
|
||||
if (Block(request))
|
||||
|
@ -84,9 +77,9 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
private bool Block(IRequest request)
|
||||
{
|
||||
if (settings.FilterContentRequests)
|
||||
if (settings.ProcessContentRequests)
|
||||
{
|
||||
var result = filter.Process(request.Url);
|
||||
var result = filter.Process(new Request { Url = request.Url });
|
||||
var block = result == FilterResult.Block;
|
||||
|
||||
if (block)
|
||||
|
@ -100,27 +93,6 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
return false;
|
||||
}
|
||||
|
||||
private void InitializeResourceHandlers()
|
||||
{
|
||||
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);
|
||||
contentHandler = CefSharp.ResourceHandler.FromString(contentHtml);
|
||||
|
||||
pageHtml = pageHtml.Replace("%%MESSAGE%%", pageMessage).Replace("%%TITLE%%", pageTitle).Replace("%%BACK_BUTTON%%", pageButton);
|
||||
pageHandler = CefSharp.ResourceHandler.FromString(pageHtml);
|
||||
|
||||
logger.Debug("Initialized resource handlers for blocked requests.");
|
||||
}
|
||||
|
||||
private bool IsMailtoUrl(string url)
|
||||
{
|
||||
return url.StartsWith(Uri.UriSchemeMailto);
|
||||
|
@ -145,6 +117,11 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
private IResourceHandler ResourceHandlerFor(ResourceType resourceType)
|
||||
{
|
||||
if (contentHandler == default(IResourceHandler) || pageHandler == default(IResourceHandler))
|
||||
{
|
||||
InitializeResourceHandlers();
|
||||
}
|
||||
|
||||
switch (resourceType)
|
||||
{
|
||||
case ResourceType.MainFrame:
|
||||
|
@ -154,5 +131,26 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
return contentHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeResourceHandlers()
|
||||
{
|
||||
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);
|
||||
contentHandler = CefSharp.ResourceHandler.FromString(contentHtml);
|
||||
|
||||
pageHtml = pageHtml.Replace("%%MESSAGE%%", pageMessage).Replace("%%TITLE%%", pageTitle).Replace("%%BACK_BUTTON%%", pageButton);
|
||||
pageHandler = CefSharp.ResourceHandler.FromString(pageHtml);
|
||||
|
||||
logger.Debug("Initialized resource handlers for blocked requests.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,9 +75,9 @@
|
|||
<Compile Include="Events\ProgressChangedEventHandler.cs" />
|
||||
<Compile Include="Events\RequestBlockedEventHandler.cs" />
|
||||
<Compile Include="Filters\RequestFilter.cs" />
|
||||
<Compile Include="Filters\Rules\Rule.cs" />
|
||||
<Compile Include="Filters\Rules\RegexRule.cs" />
|
||||
<Compile Include="Filters\Rules\SimpleRule.cs" />
|
||||
<Compile Include="Filters\RuleFactory.cs" />
|
||||
<Compile Include="Filters\Rules\SimplifiedRule.cs" />
|
||||
<Compile Include="Handlers\ContextMenuHandler.cs" />
|
||||
<Compile Include="BrowserControl.cs">
|
||||
<SubType>Component</SubType>
|
||||
|
|
|
@ -108,17 +108,17 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
|
||||
private void MapEnableContentRequestFilter(ApplicationSettings settings, object value)
|
||||
{
|
||||
if (value is bool filter)
|
||||
if (value is bool process)
|
||||
{
|
||||
settings.Browser.Filter.FilterContentRequests = filter;
|
||||
settings.Browser.Filter.ProcessContentRequests = process;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapEnableMainRequestFilter(ApplicationSettings settings, object value)
|
||||
{
|
||||
if (value is bool filter)
|
||||
if (value is bool process)
|
||||
{
|
||||
settings.Browser.Filter.FilterMainRequests = filter;
|
||||
settings.Browser.Filter.ProcessMainRequests = process;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
|
||||
if (ruleData.TryGetValue(Keys.Browser.Filter.RuleExpressionIsRegex, out v) && v is bool regex)
|
||||
{
|
||||
rule.Type = regex ? FilterType.Regex : FilterType.Simplified;
|
||||
rule.Type = regex ? FilterRuleType.Regex : FilterRuleType.Simplified;
|
||||
}
|
||||
|
||||
settings.Browser.Filter.Rules.Add(rule);
|
||||
|
|
|
@ -18,14 +18,14 @@ namespace SafeExamBrowser.Settings.Browser
|
|||
public class BrowserFilterSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines whether all content requests for a web page should be filtered according to the defined <see cref="Rules"/>.
|
||||
/// Defines whether content requests for a web page should be filtered according to the defined <see cref="Rules"/>.
|
||||
/// </summary>
|
||||
public bool FilterContentRequests { get; set; }
|
||||
public bool ProcessContentRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines whether the main request for a web page should be filtered according to the defined <see cref="Rules"/>.
|
||||
/// </summary>
|
||||
public bool FilterMainRequests { get; set; }
|
||||
public bool ProcessMainRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines all rules to be used to filter web requests.
|
||||
|
|
|
@ -29,6 +29,6 @@ namespace SafeExamBrowser.Settings.Browser
|
|||
/// <summary>
|
||||
/// The filter type which defines how the <see cref="Expression"/> is processed.
|
||||
/// </summary>
|
||||
public FilterType Type { get; set; }
|
||||
public FilterRuleType Type { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,17 @@
|
|||
namespace SafeExamBrowser.Settings.Browser
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all possible request filter types.
|
||||
/// Defines all possible filter rule types.
|
||||
/// </summary>
|
||||
public enum FilterType
|
||||
public enum FilterRuleType
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter is based on a regular expression.
|
||||
/// The filter rule is based on a regular expression.
|
||||
/// </summary>
|
||||
Regex,
|
||||
|
||||
/// <summary>
|
||||
/// The filter is based on a simplified expression with wildcards.
|
||||
/// The filter rule is based on a simplified expression with wildcards.
|
||||
/// </summary>
|
||||
Simplified
|
||||
}
|
|
@ -58,7 +58,7 @@
|
|||
<Compile Include="Browser\BrowserWindowSettings.cs" />
|
||||
<Compile Include="Browser\FilterResult.cs" />
|
||||
<Compile Include="Browser\FilterRuleSettings.cs" />
|
||||
<Compile Include="Browser\FilterType.cs" />
|
||||
<Compile Include="Browser\FilterRuleType.cs" />
|
||||
<Compile Include="ConfigurationMode.cs" />
|
||||
<Compile Include="KioskMode.cs" />
|
||||
<Compile Include="Logging\LogLevel.cs" />
|
||||
|
|
Loading…
Reference in a new issue