Restore SEBPatch
This commit is contained in:
553
SafeExamBrowser.Browser.UnitTests/Filters/LegacyFilter.cs
Normal file
553
SafeExamBrowser.Browser.UnitTests/Filters/LegacyFilter.cs
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla internal
|
||||
* 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.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters
|
||||
{
|
||||
internal class LegacyFilter
|
||||
{
|
||||
internal Regex scheme;
|
||||
internal Regex user;
|
||||
internal Regex password;
|
||||
internal Regex host;
|
||||
internal int? port;
|
||||
internal Regex path;
|
||||
internal Regex query;
|
||||
internal Regex fragment;
|
||||
|
||||
internal LegacyFilter(string filterExpressionString)
|
||||
{
|
||||
SEBURLFilterExpression URLFromString = new SEBURLFilterExpression(filterExpressionString);
|
||||
try
|
||||
{
|
||||
this.scheme = RegexForFilterString(URLFromString.scheme);
|
||||
this.user = RegexForFilterString(URLFromString.user);
|
||||
this.password = RegexForFilterString(URLFromString.password);
|
||||
this.host = RegexForHostFilterString(URLFromString.host);
|
||||
this.port = URLFromString.port;
|
||||
this.path = RegexForPathFilterString(URLFromString.path);
|
||||
this.query = RegexForQueryFilterString(URLFromString.query);
|
||||
this.fragment = RegexForFilterString(URLFromString.fragment);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Method comparing all components of a passed URL with the filter expression
|
||||
// and returning YES (= allow or block) if it matches
|
||||
internal bool IsMatch(Uri URLToFilter)
|
||||
{
|
||||
Regex filterComponent;
|
||||
|
||||
// If a scheme is indicated in the filter expression, it has to match
|
||||
filterComponent = scheme;
|
||||
UriBuilder urlToFilterParts = new UriBuilder(URLToFilter);
|
||||
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(URLToFilter.Scheme, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
// Scheme of the URL to filter doesn't match the one from the filter expression: Exit with matching = NO
|
||||
return false;
|
||||
}
|
||||
|
||||
string userInfo = URLToFilter.UserInfo;
|
||||
filterComponent = user;
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(urlToFilterParts.UserName, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
filterComponent = password;
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(urlToFilterParts.Password, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
filterComponent = host;
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(URLToFilter.Host, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (port != null && URLToFilter.Port != port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
filterComponent = path;
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(URLToFilter.AbsolutePath.TrimEnd(new char[] { '/' }), filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string urlQuery = URLToFilter.GetComponents(UriComponents.Query, UriFormat.Unescaped);
|
||||
filterComponent = query;
|
||||
if (filterComponent != null)
|
||||
{
|
||||
// If there's a query filter component, then we need to even filter empty URL query strings
|
||||
// as the filter might either allow some specific queries or no query at all ("?." query filter)
|
||||
if (urlQuery == null)
|
||||
{
|
||||
urlQuery = "";
|
||||
}
|
||||
if (!Regex.IsMatch(urlQuery, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string urlFragment = URLToFilter.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
filterComponent = fragment;
|
||||
if (filterComponent != null &&
|
||||
!Regex.IsMatch(urlFragment, filterComponent.ToString(), RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// URL matches the filter expression
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static Regex RegexForFilterString(string filterString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filterString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
string regexString = Regex.Escape(filterString);
|
||||
regexString = regexString.Replace("\\*", ".*?");
|
||||
// Add regex command characters for matching at start and end of a line (part)
|
||||
regexString = string.Format("^{0}$", regexString);
|
||||
|
||||
try
|
||||
{
|
||||
Regex regex = new Regex(regexString, RegexOptions.IgnoreCase);
|
||||
return regex;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Regex RegexForHostFilterString(string filterString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filterString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if host string has a dot "." prefix to disable subdomain matching
|
||||
if (filterString.Length > 1 && filterString.StartsWith("."))
|
||||
{
|
||||
// Get host string without the "." prefix
|
||||
filterString = filterString.Substring(1);
|
||||
// Get regex for host <*://example.com> (without possible subdomains)
|
||||
return RegexForFilterString(filterString);
|
||||
}
|
||||
// Allow subdomain matching: Create combined regex for <example.com> and <*.example.com>
|
||||
string regexString = Regex.Escape(filterString);
|
||||
regexString = regexString.Replace("\\*", ".*?");
|
||||
// Add regex command characters for matching at start and end of a line (part)
|
||||
regexString = string.Format("^(({0})|(.*?\\.{0}))$", regexString);
|
||||
Regex regex = new Regex(regexString, RegexOptions.IgnoreCase);
|
||||
return regex;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Regex RegexForPathFilterString(string filterString)
|
||||
{
|
||||
// Trim a possible trailing slash "/", we will instead add a rule to also match paths to directories without trailing slash
|
||||
filterString = filterString.TrimEnd(new char[] { '/' });
|
||||
;
|
||||
|
||||
if (string.IsNullOrEmpty(filterString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if path string ends with a "/*" for matching contents of a directory
|
||||
if (filterString.EndsWith("/*"))
|
||||
{
|
||||
// As the path filter string matches for a directory, we need to add a string to match directories without trailing slash
|
||||
|
||||
// Get path string without the "/*" suffix
|
||||
string filterStringDirectory = filterString.Substring(0, filterString.Length - 2);
|
||||
|
||||
string regexString = Regex.Escape(filterString);
|
||||
regexString = regexString.Replace("\\*", ".*?");
|
||||
|
||||
string regexStringDir = Regex.Escape(filterString);
|
||||
regexStringDir = regexStringDir.Replace("\\*", ".*?");
|
||||
|
||||
// Add regex command characters for matching at start and end of a line (part)
|
||||
regexString = string.Format("^(({0})|({1}))$", regexString, regexStringDir);
|
||||
|
||||
Regex regex = new Regex(regexString, RegexOptions.IgnoreCase);
|
||||
return regex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RegexForFilterString(filterString);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Regex RegexForQueryFilterString(string filterString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filterString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filterString.Equals("."))
|
||||
{
|
||||
// Add regex command characters for matching at start and end of a line (part)
|
||||
// and regex for no string allowed
|
||||
string regexString = @"^$";
|
||||
try
|
||||
{
|
||||
Regex regex = new Regex(regexString, RegexOptions.IgnoreCase);
|
||||
return regex;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return RegexForFilterString(filterString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder expressionString = new StringBuilder();
|
||||
string part;
|
||||
expressionString.Append("^");
|
||||
|
||||
/// Scheme
|
||||
if (this.scheme != null)
|
||||
{
|
||||
// If there is a regex filter for scheme
|
||||
// get stripped regex pattern
|
||||
part = StringForRegexFilter(this.scheme);
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise use the regex wildcard pattern for scheme
|
||||
part = @".*?";
|
||||
}
|
||||
|
||||
expressionString.AppendFormat("{0}:\\/\\/", part);
|
||||
|
||||
/// User/Password
|
||||
if (this.user != null)
|
||||
{
|
||||
part = StringForRegexFilter(this.user);
|
||||
|
||||
expressionString.Append(part);
|
||||
|
||||
if (this.password != null)
|
||||
{
|
||||
expressionString.AppendFormat(":{0}@", StringForRegexFilter(this.password));
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.Append("@");
|
||||
}
|
||||
}
|
||||
|
||||
/// Host
|
||||
string hostPort = "";
|
||||
if (this.host != null)
|
||||
{
|
||||
hostPort = StringForRegexFilter(this.host);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostPort = ".*?";
|
||||
}
|
||||
|
||||
/// Port
|
||||
if (this.port != null && this.port > 0 && this.port <= 65535)
|
||||
{
|
||||
hostPort = string.Format("{0}:{1}", hostPort, this.port);
|
||||
}
|
||||
|
||||
// When there is a host, but no path
|
||||
if (this.host != null && this.path == null)
|
||||
{
|
||||
hostPort = string.Format("(({0})|({0}\\/.*?))", hostPort);
|
||||
}
|
||||
|
||||
expressionString.Append(hostPort);
|
||||
|
||||
/// Path
|
||||
if (this.path != null)
|
||||
{
|
||||
string path = StringForRegexFilter(this.path);
|
||||
if (path.StartsWith("\\/"))
|
||||
{
|
||||
expressionString.Append(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.AppendFormat("\\/{0}", path);
|
||||
}
|
||||
}
|
||||
|
||||
/// Query
|
||||
if (this.query != null)
|
||||
{
|
||||
// Check for special case Query = "?." which means no query string is allowed
|
||||
if (StringForRegexFilter(this.query).Equals("."))
|
||||
{
|
||||
expressionString.AppendFormat("[^\\?]");
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.AppendFormat("\\?{0}", StringForRegexFilter(this.query));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.AppendFormat("(()|(\\?.*?))");
|
||||
}
|
||||
|
||||
/// Fragment
|
||||
if (this.fragment != null)
|
||||
{
|
||||
expressionString.AppendFormat("#{0}", StringForRegexFilter(this.fragment));
|
||||
}
|
||||
|
||||
expressionString.Append("$");
|
||||
|
||||
return expressionString.ToString();
|
||||
}
|
||||
|
||||
internal string StringForRegexFilter(Regex regexFilter)
|
||||
{
|
||||
// Get pattern string from regular expression
|
||||
string regexPattern = regexFilter.ToString();
|
||||
if (regexPattern.Length <= 2)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
// Remove the regex command characters for matching at start and end of a line
|
||||
regexPattern = regexPattern.Substring(1, regexPattern.Length - 2);
|
||||
return regexPattern;
|
||||
}
|
||||
|
||||
private class SEBURLFilterExpression
|
||||
{
|
||||
internal string scheme;
|
||||
internal string user;
|
||||
internal string password;
|
||||
internal string host;
|
||||
internal int? port;
|
||||
internal string path;
|
||||
internal string query;
|
||||
internal string fragment;
|
||||
|
||||
internal SEBURLFilterExpression(string filterExpressionString)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filterExpressionString))
|
||||
{
|
||||
/// Convert Uri to a SEBURLFilterExpression
|
||||
string splitURLRegexPattern = @"(?:([^\:]*)\:\/\/)?(?:([^\:\@]*)(?:\:([^\@]*))?\@)?(?:([^\/\:]*))?(?:\:([0-9\*]*))?([^\?#]*)?(?:\?([^#]*))?(?:#(.*))?";
|
||||
Regex splitURLRegex = new Regex(splitURLRegexPattern);
|
||||
Match regexMatch = splitURLRegex.Match(filterExpressionString);
|
||||
if (regexMatch.Success == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.scheme = regexMatch.Groups[1].Value;
|
||||
this.user = regexMatch.Groups[2].Value;
|
||||
this.password = regexMatch.Groups[3].Value;
|
||||
this.host = regexMatch.Groups[4].Value;
|
||||
|
||||
// Treat a special case when a query or fragment is interpreted as part of the host address
|
||||
if (this.host.Contains("?") || this.host.Contains("#"))
|
||||
{
|
||||
string splitURLRegexPattern2 = @"([^\?#]*)?(?:\?([^#]*))?(?:#(.*))?";
|
||||
Regex splitURLRegex2 = new Regex(splitURLRegexPattern2);
|
||||
Match regexMatch2 = splitURLRegex2.Match(this.host);
|
||||
if (regexMatch.Success == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.host = regexMatch2.Groups[1].Value;
|
||||
this.port = null;
|
||||
this.path = "";
|
||||
this.query = regexMatch2.Groups[2].Value;
|
||||
this.fragment = regexMatch2.Groups[3].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
string portNumber = regexMatch.Groups[5].Value;
|
||||
|
||||
// We only want a port if the filter expression string explicitely defines one!
|
||||
if (portNumber.Length == 0 || portNumber == "*")
|
||||
{
|
||||
this.port = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.port = UInt16.Parse(portNumber);
|
||||
}
|
||||
|
||||
this.path = regexMatch.Groups[6].Value.TrimEnd(new char[] { '/' });
|
||||
this.query = regexMatch.Groups[7].Value;
|
||||
this.fragment = regexMatch.Groups[8].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static string User(string userInfo)
|
||||
{
|
||||
string user = "";
|
||||
if (!string.IsNullOrEmpty(userInfo))
|
||||
{
|
||||
int userPasswordSeparator = userInfo.IndexOf(":");
|
||||
if (userPasswordSeparator == -1)
|
||||
{
|
||||
user = userInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (userPasswordSeparator != 0)
|
||||
{
|
||||
user = userInfo.Substring(0, userPasswordSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
internal static string Password(string userInfo)
|
||||
{
|
||||
string password = "";
|
||||
if (!string.IsNullOrEmpty(userInfo))
|
||||
{
|
||||
int userPasswordSeparator = userInfo.IndexOf(":");
|
||||
if (userPasswordSeparator != -1)
|
||||
{
|
||||
if (userPasswordSeparator < userInfo.Length - 1)
|
||||
{
|
||||
password = userInfo.Substring(userPasswordSeparator + 1, userInfo.Length - 1 - userPasswordSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
internal SEBURLFilterExpression(string scheme, string user, string password, string host, int port, string path, string query, string fragment)
|
||||
{
|
||||
this.scheme = scheme;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
this.query = query;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder expressionString = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(this.scheme))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.host))
|
||||
{
|
||||
expressionString.AppendFormat("{0}://", this.scheme);
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.AppendFormat("{0}:", this.scheme);
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.user))
|
||||
{
|
||||
expressionString.Append(this.user);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.password))
|
||||
{
|
||||
expressionString.AppendFormat(":{0}@", this.password);
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.Append("@");
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.host))
|
||||
{
|
||||
expressionString.Append(this.host);
|
||||
}
|
||||
if (this.port != null && this.port > 0 && this.port <= 65535)
|
||||
{
|
||||
expressionString.AppendFormat(":{0}", this.port);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.path))
|
||||
{
|
||||
if (this.path.StartsWith("/"))
|
||||
{
|
||||
expressionString.Append(this.path);
|
||||
}
|
||||
else
|
||||
{
|
||||
expressionString.AppendFormat("/{0}", this.path);
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.query))
|
||||
{
|
||||
expressionString.AppendFormat("?{0}", this.query);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.fragment))
|
||||
{
|
||||
expressionString.AppendFormat("#{0}", this.fragment);
|
||||
}
|
||||
|
||||
return expressionString.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs
Normal file
114
SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
|
||||
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 Mock<IRule>();
|
||||
var block = new Mock<IRule>();
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustProcessAllowRulesSecond()
|
||||
{
|
||||
var allow = new Mock<IRule>();
|
||||
var block = new Mock<IRule>();
|
||||
|
||||
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);
|
||||
|
||||
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 MustReturnDefaultWithoutMatch()
|
||||
{
|
||||
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.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) (-1), result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustReturnDefaultWithoutRules()
|
||||
{
|
||||
sut.Default = FilterResult.Allow;
|
||||
var result = sut.Process(new Request());
|
||||
Assert.AreEqual(FilterResult.Allow, result);
|
||||
|
||||
sut.Default = FilterResult.Block;
|
||||
result = sut.Process(new Request());
|
||||
Assert.AreEqual(FilterResult.Block, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedResult()
|
||||
{
|
||||
var rule = new Mock<IRule>();
|
||||
|
||||
rule.SetupGet(r => r.Result).Returns((FilterResult) (-1));
|
||||
sut.Load(rule.Object);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Filter;
|
||||
|
||||
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,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Text.RegularExpressions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
{
|
||||
[TestClass]
|
||||
public class RegexRuleTests
|
||||
{
|
||||
private RegexRule sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new RegexRule();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustIgnoreCase()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = Regex.Escape("http://www.test.org/path/file.txt?param=123") });
|
||||
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "hTtP://wWw.TeSt.OrG/pAtH/fIlE.tXt?PaRaM=123" }));
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "HtTp://WwW.tEst.oRg/PaTh/FiLe.TxT?pArAm=123" }));
|
||||
|
||||
sut.Initialize(new FilterRuleSettings { Expression = Regex.Escape("HTTP://WWW.TEST.ORG/PATH/FILE.TXT?PARAM=123") });
|
||||
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "hTtP://wWw.TeSt.OrG/pAtH/fIlE.tXt?PaRaM=123" }));
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "HtTp://WwW.tEst.oRg/PaTh/FiLe.TxT?pArAm=123" }));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitializeResult()
|
||||
{
|
||||
foreach (var result in Enum.GetValues(typeof(FilterResult)))
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "", Result = (FilterResult) result });
|
||||
Assert.AreEqual(result, sut.Result);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void MustNotAllowUndefinedExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void MustValidateExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" });
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,962 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Filters.Rules;
|
||||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
{
|
||||
[TestClass]
|
||||
public class SimplifiedRuleTests
|
||||
{
|
||||
private SimplifiedRule sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new SimplifiedRule();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustIgnoreCase()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "http://www.test.org/path/file.txt?param=123" });
|
||||
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "hTtP://wWw.TeSt.OrG/pAtH/fIlE.tXt?PaRaM=123" }));
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "HtTp://WwW.tEst.oRg/PaTh/FiLe.TxT?pArAm=123" }));
|
||||
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "HTTP://WWW.TEST.ORG/PATH/FILE.TXT?PARAM=123" });
|
||||
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "hTtP://wWw.TeSt.OrG/pAtH/fIlE.tXt?PaRaM=123" }));
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = "HtTp://WwW.tEst.oRg/PaTh/FiLe.TxT?pArAm=123" }));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitializeResult()
|
||||
{
|
||||
foreach (var result in Enum.GetValues(typeof(FilterResult)))
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "*", Result = (FilterResult) result });
|
||||
Assert.AreEqual(result, sut.Result);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void MustNotAllowUndefinedExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustValidateExpression()
|
||||
{
|
||||
var invalid = new[]
|
||||
{
|
||||
".", "+", "\"", "ç", "%", "&", "/", "(", ")", "=", "?", "^", "!", "[", "]", "{", "}", "¦", "@", "#", "°", "§", "¬", "|", "¢", "´", "'", "`", "~", "<", ">", "\\"
|
||||
};
|
||||
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "*" });
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "a" });
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "A" });
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "0" });
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "abcdeFGHIJK-12345" });
|
||||
|
||||
foreach (var expression in invalid)
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = expression }));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestAlphanumericExpression()
|
||||
{
|
||||
var expression = "hostname123";
|
||||
var positive = new[]
|
||||
{
|
||||
$"scheme://{expression}"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
$"scheme://hostname",
|
||||
$"scheme://hostname1",
|
||||
$"scheme://hostname12",
|
||||
$"scheme://hostname1234",
|
||||
$"scheme://{expression}.org",
|
||||
$"scheme://www.{expression}.org",
|
||||
$"scheme://subdomain.{expression}.com",
|
||||
$"scheme://www.realhost.{expression}",
|
||||
$"scheme://subdomain-1.subdomain-2.{expression}.org",
|
||||
$"scheme://user:password@www.{expression}.org/path/file.txt?param=123#fragment",
|
||||
$"scheme://{expression}4",
|
||||
$"scheme://hostname.org",
|
||||
$"scheme://hostname12.org",
|
||||
$"scheme://{expression}4.org",
|
||||
$"scheme://{expression}.realhost.org",
|
||||
$"scheme://subdomain.{expression}.realhost.org",
|
||||
$"{expression}://www.host.org",
|
||||
$"scheme://www.host.org/{expression}/path",
|
||||
$"scheme://www.host.org/path?param={expression}",
|
||||
$"scheme://{expression}:password@www.host.org",
|
||||
$"scheme://user:{expression}@www.host.org",
|
||||
$"scheme://user:password@www.host.org/path?param=123#{expression}"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative, false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestAlphanumericExpressionWithWildcard()
|
||||
{
|
||||
var expression = "hostname*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://hostname.org",
|
||||
"scheme://hostnameabc.org",
|
||||
"scheme://hostname-12.org",
|
||||
"scheme://hostname-abc-def-123-456.org",
|
||||
"scheme://www.hostname-abc.org",
|
||||
"scheme://www.realhost.hostname",
|
||||
"scheme://subdomain.hostname-xyz.com",
|
||||
"scheme://hostname.realhost.org",
|
||||
"scheme://subdomain.hostname.realhost.org",
|
||||
"scheme://subdomain-1.subdomain-2.hostname-abc-123.org",
|
||||
"scheme://user:password@www.hostname-abc.org/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://hostnam.org",
|
||||
"hostname://www.host.org",
|
||||
"scheme://www.host.org/hostname/path",
|
||||
"scheme://www.host.org/path?param=hostname",
|
||||
"scheme://hostname:password@www.host.org",
|
||||
"scheme://user:hostname@www.host.org",
|
||||
"scheme://user:password@www.host.org/path?param=123#hostname"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative, false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithDomain()
|
||||
{
|
||||
var expression = "123-hostname.org";
|
||||
var positive = new[]
|
||||
{
|
||||
$"scheme://{expression}",
|
||||
$"scheme://www.{expression}",
|
||||
$"scheme://subdomain.{expression}",
|
||||
$"scheme://subdomain-1.subdomain-2.{expression}",
|
||||
$"scheme://user:password@www.{expression}/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
$"scheme://123.org",
|
||||
$"scheme://123-host.org",
|
||||
$"scheme://{expression}.com",
|
||||
$"scheme://{expression}s.org",
|
||||
$"scheme://{expression}.realhost.org",
|
||||
$"scheme://subdomain.{expression}.realhost.org",
|
||||
$"scheme{expression}://www.host.org",
|
||||
$"scheme://www.host.org/{expression}/path",
|
||||
$"scheme://www.host.org/path?param={expression}",
|
||||
$"scheme://{expression}:password@www.host.org",
|
||||
$"scheme://user:{expression}@www.host.org",
|
||||
$"scheme://user:password@www.host.org/path?param=123#{expression}"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithWildcard()
|
||||
{
|
||||
var expression = "test.*.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://test.host.org",
|
||||
"scheme://test.host.domain.org",
|
||||
"scheme://subdomain.test.host.org",
|
||||
"scheme://user:password@test.domain.org/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://test.org",
|
||||
"scheme://host.com/test.host.org",
|
||||
"scheme://www.host.org/test.host.org/path",
|
||||
"scheme://www.host.org/path?param=test.host.org",
|
||||
"scheme://test.host.org:password@www.host.org",
|
||||
"scheme://user:test.host.org@www.host.org",
|
||||
"scheme://user:password@www.host.org/path?param=123#test.host.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithWildcardAsSuffix()
|
||||
{
|
||||
var expression = "test.host.*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://test.host.org",
|
||||
"scheme://test.host.domain.org",
|
||||
"scheme://subdomain.test.host.org",
|
||||
"scheme://user:password@test.host.org/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.com",
|
||||
"scheme://test.host",
|
||||
"scheme://host.com/test.host.txt"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithWildcardAsPrefix()
|
||||
{
|
||||
var expression = "*.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://domain.org",
|
||||
"scheme://test.host.org",
|
||||
"scheme://test.host.domain.org",
|
||||
"scheme://user:password@www.host.org/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://org",
|
||||
"scheme://host.com",
|
||||
"scheme://test.net",
|
||||
"scheme://test.ch",
|
||||
"scheme://host.com/test.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithExactSubdomain()
|
||||
{
|
||||
var expression = ".www.host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://www.host.org",
|
||||
"scheme://user:password@www.host.org/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://test.www.host.org",
|
||||
"scheme://www.host.org.com"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithTrailingSlash()
|
||||
{
|
||||
var expression = "host.org/";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org/",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/url/",
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://host.org/url/path/",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.host.org/url/path/?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestHostWithoutTrailingSlash()
|
||||
{
|
||||
var expression = "host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org/",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/url/",
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://host.org/url/path/",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.host.org/url/path/?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPortNumber()
|
||||
{
|
||||
var expression = "host.org:2020";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org:2020",
|
||||
"scheme://www.host.org:2020",
|
||||
"scheme://user:password@www.host.org:2020/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://www.host.org",
|
||||
"scheme://www.host.org:2",
|
||||
"scheme://www.host.org:20",
|
||||
"scheme://www.host.org:202",
|
||||
"scheme://www.host.org:20202"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPortWildcard()
|
||||
{
|
||||
var expression = "host.org:*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org:0",
|
||||
"scheme://host.org:1",
|
||||
"scheme://host.org:2020",
|
||||
"scheme://host.org:65535",
|
||||
"scheme://www.host.org",
|
||||
"scheme://www.host.org:2",
|
||||
"scheme://www.host.org:20",
|
||||
"scheme://www.host.org:202",
|
||||
"scheme://www.host.org:2020",
|
||||
"scheme://www.host.org:20202",
|
||||
"scheme://user:password@www.host.org:2020/path/file.txt?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPortNumberWithHostWildcard()
|
||||
{
|
||||
var expression = "*:2020";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org:2020",
|
||||
"scheme://domain.com:2020",
|
||||
"scheme://user:password@www.server.net:2020/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new List<string>
|
||||
{
|
||||
"scheme://host.org"
|
||||
};
|
||||
|
||||
for (var port = 0; port < 65536; port++)
|
||||
{
|
||||
if (port != 2020)
|
||||
{
|
||||
negative.Add($"{negative[0]}:{port}");
|
||||
}
|
||||
}
|
||||
|
||||
Test(expression, positive, negative.ToArray());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPath()
|
||||
{
|
||||
var expression = "host.org/url/path";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org//",
|
||||
"scheme://host.org///",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/path",
|
||||
"scheme://host.org/url/path.txt",
|
||||
"scheme://host.org/another/url/path"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPathWithFile()
|
||||
{
|
||||
var expression = "host.org/url/path/to/file.txt";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org/url/path/to/file.txt",
|
||||
"scheme://subdomain.host.org/url/path/to/file.txt",
|
||||
"scheme://user:password@www.host.org/url/path/to/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/path",
|
||||
"scheme://host.org/file.txt",
|
||||
"scheme://host.org/url/path.txt",
|
||||
"scheme://host.org/url/path/to.txt",
|
||||
"scheme://host.org/url/path/to/file",
|
||||
"scheme://host.org/url/path/to/file.",
|
||||
"scheme://host.org/url/path/to/file.t",
|
||||
"scheme://host.org/url/path/to/file.tx",
|
||||
"scheme://host.org/url/path/to/file.txt/segment",
|
||||
"scheme://host.org/url/path/to/file.txtt",
|
||||
"scheme://host.org/another/url/path/to/file.txt"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPathWithWildcard()
|
||||
{
|
||||
var expression = "host.org/*/path";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org//path",
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://host.org/another/url/path",
|
||||
"scheme://user:password@www.host.org/yet/another/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/path",
|
||||
"scheme://host.org/url/path.txt",
|
||||
"scheme://host.org/url/path/2"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPathWithHostWildcard()
|
||||
{
|
||||
var expression = "*/url/path";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://local/url/path",
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://www.host.org/url/path",
|
||||
"scheme://another.server.org/url/path",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org/url",
|
||||
"scheme://host.org/path",
|
||||
"scheme://host.org/url/path.txt",
|
||||
"scheme://host.org/url/path/2"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPathWithTrailingSlash()
|
||||
{
|
||||
var expression = "host.org/url/path/";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://host.org/url/path/",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.host.org/url/path/?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPathWithoutTrailingSlash()
|
||||
{
|
||||
var expression = "host.org/url/path";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org/url/path",
|
||||
"scheme://host.org/url/path/",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.host.org/url/path/?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestScheme()
|
||||
{
|
||||
var expression = "scheme://host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://www.host.org",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"//host.org",
|
||||
"https://host.org",
|
||||
"ftp://host.org",
|
||||
"ftps://host.org",
|
||||
"schemes://host.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSchemeWithWildcard()
|
||||
{
|
||||
var expression = "*tp://host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"tp://host.org",
|
||||
"ftp://www.host.org",
|
||||
"http://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"//host.org",
|
||||
"p://host.org",
|
||||
"https://host.org",
|
||||
"ftps://host.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSchemeWithHostWildcard()
|
||||
{
|
||||
var expression = "scheme://*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host",
|
||||
"scheme://www.server.org",
|
||||
"scheme://subdomain.domain.org",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"//host.org",
|
||||
"http://host.org",
|
||||
"https://host.org",
|
||||
"ftp://host.org",
|
||||
"ftps://host.org",
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestUserInfoWithName()
|
||||
{
|
||||
var expression = "user@host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://user@host.org",
|
||||
"scheme://user@www.host.org",
|
||||
"scheme://user:password@host.org",
|
||||
"scheme://user:password-123@host.org",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://u@host.org",
|
||||
"scheme://us@host.org",
|
||||
"scheme://use@host.org",
|
||||
"scheme://usera@host.org",
|
||||
"scheme://user@server.net",
|
||||
"scheme://usertwo@www.host.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestUserInfoWithNameWildcard()
|
||||
{
|
||||
var expression = "user*@host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://user@host.org",
|
||||
"scheme://userabc@host.org",
|
||||
"scheme://user:abc@host.org",
|
||||
"scheme://user-123@www.host.org",
|
||||
"scheme://user-123:password@host.org",
|
||||
"scheme://user-123:password-123@host.org",
|
||||
"scheme://user-abc-123:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://u@host.org",
|
||||
"scheme://us@host.org",
|
||||
"scheme://use@host.org",
|
||||
"scheme://user@server.net",
|
||||
"scheme://usertwo@server.org"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestUserInfoWithPassword()
|
||||
{
|
||||
var expression = "user:password@host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://user:password@host.org",
|
||||
"scheme://user:password@www.host.org",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://u@host.org",
|
||||
"scheme://us@host.org",
|
||||
"scheme://use@host.org",
|
||||
"scheme://user@server.net",
|
||||
"scheme://usertwo@server.org",
|
||||
"scheme://user@host.org",
|
||||
"scheme://userabc@host.org",
|
||||
"scheme://user:abc@host.org",
|
||||
"scheme://user-123@www.host.org",
|
||||
"scheme://user-123:password@host.org",
|
||||
"scheme://user-123:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password-123@host.org",
|
||||
"scheme://user:password-123@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestUserInfoWithWildcard()
|
||||
{
|
||||
var expression = "*@host.org";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://user@host.org",
|
||||
"scheme://user:password@host.org",
|
||||
"scheme://www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://server.org",
|
||||
"scheme://user@server.org",
|
||||
"scheme://www.server.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.server.org/url/path?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestUserInfoWithHostWildcard()
|
||||
{
|
||||
var expression = "user:password@*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://user:password@host.org",
|
||||
"scheme://user:password@server.net",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://server.org",
|
||||
"scheme://user@host.org",
|
||||
"scheme://user@server.org",
|
||||
"scheme://password@host.org",
|
||||
"scheme://www.host.org/url/path?param=123#fragment",
|
||||
"scheme://www.server.org/url/path?param=123#fragment",
|
||||
"scheme://user@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user@www.server.org/url/path?param=123#fragment",
|
||||
"scheme://password@www.server.org/url/path?param=123#fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestQuery()
|
||||
{
|
||||
var expression = "host.org?param=123";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org?param=123",
|
||||
"scheme://www.host.org/?param=123",
|
||||
"scheme://www.host.org/path/?param=123",
|
||||
"scheme://www.host.org/some/other/random/path?param=123",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org?",
|
||||
"scheme://host.org?=",
|
||||
"scheme://host.org?=123",
|
||||
"scheme://host.org?param=",
|
||||
"scheme://host.org?param=1",
|
||||
"scheme://host.org?param=12",
|
||||
"scheme://host.org?param=1234"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestQueryWithWildcardAsPrefix()
|
||||
{
|
||||
var expression = "host.org?*param=123";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org?param=123",
|
||||
"scheme://www.host.org?param=123",
|
||||
"scheme://www.host.org/path/?param=123",
|
||||
"scheme://www.host.org/some/other/random/path?param=123",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://host.org?other_param=456¶m=123",
|
||||
"scheme://host.org?param=123&another_param=123",
|
||||
"scheme://www.host.org?other_param=456¶m=123",
|
||||
"scheme://www.host.org/path/?other_param=456¶m=123",
|
||||
"scheme://www.host.org/some/other/random/path?other_param=456¶m=123",
|
||||
"scheme://user:password@www.host.org/url/path?other_param=456¶m=123#fragment",
|
||||
"scheme://host.org?some_param=123469yvuiopwo&another_param=some%20whitespaces%26special%20characters%2B%22%2A%25%C3%A7%2F%28¶m=123"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org?",
|
||||
"scheme://host.org?=",
|
||||
"scheme://host.org?=123",
|
||||
"scheme://host.org?aram=123",
|
||||
"scheme://host.org?param=",
|
||||
"scheme://host.org?param=1",
|
||||
"scheme://host.org?param=12",
|
||||
"scheme://host.org?param=1234",
|
||||
"scheme://host.org?param=123&another_param=456"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestQueryWithWildcardAsSuffix()
|
||||
{
|
||||
var expression = "host.org?param=123*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org?param=123",
|
||||
"scheme://host.org?param=1234",
|
||||
"scheme://www.host.org?param=123",
|
||||
"scheme://www.host.org/path/?param=123",
|
||||
"scheme://host.org?param=123&another_param=456",
|
||||
"scheme://www.host.org/some/other/random/path?param=123",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://host.org?param=123&other_param=456",
|
||||
"scheme://www.host.org/path/?param=123&other_param=456",
|
||||
"scheme://www.host.org/some/other/random/path?param=123&other_param=456",
|
||||
"scheme://user:password@www.host.org/url/path?param=123&other_param=456#fragment",
|
||||
"scheme://host.org?param=123&some_param=123469yvuiopwo&another_param=some%20whitespaces%26special%20characters%2B%22%2A%25%C3%A7%2F%28"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org?",
|
||||
"scheme://host.org?=",
|
||||
"scheme://host.org?=123",
|
||||
"scheme://host.org?aram=123",
|
||||
"scheme://host.org?param=",
|
||||
"scheme://host.org?param=1",
|
||||
"scheme://host.org?param=12",
|
||||
"scheme://host.org?aparam=123",
|
||||
"scheme://www.host.org?param=456¶m=123",
|
||||
"scheme://host.org?some_param=123469yvuiopwo&another_param=some%20whitespaces%26special%20characters%2B%22%2A%25%C3%A7%2F%28¶m=123"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestQueryNotAllowed()
|
||||
{
|
||||
var expression = "host.org?.";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org?",
|
||||
"scheme://user:password@www.host.org/url/path#fragment",
|
||||
"scheme://user:password@www.host.org/url/path?#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org?a",
|
||||
"scheme://host.org?%20",
|
||||
"scheme://host.org?=",
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestQueryWithHostWildcard()
|
||||
{
|
||||
var expression = "*?param=123";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org?param=123",
|
||||
"scheme://server.net?param=123",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.server.net/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org?param=1234",
|
||||
"scheme://host.org?param=12",
|
||||
"scheme://host.org?",
|
||||
"scheme://host.org?param",
|
||||
"scheme://host.org?123",
|
||||
"scheme://host.org?="
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFragment()
|
||||
{
|
||||
var expression = "host.org#fragment";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org#fragment",
|
||||
"scheme://www.host.org#fragment",
|
||||
"scheme://user:password@www.host.org/url/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org#",
|
||||
"scheme://host.org#fragmen",
|
||||
"scheme://host.org#fragment123",
|
||||
"scheme://host.org#otherfragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFragmentWithWildcardAsPrefix()
|
||||
{
|
||||
var expression = "host.org#*fragment";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org#fragment",
|
||||
"scheme://host.org#somefragment",
|
||||
"scheme://www.host.org#another_fragment",
|
||||
"scheme://user:password@www.host.org/url/path/file.txt?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org#",
|
||||
"scheme://host.org#fragmen",
|
||||
"scheme://host.org#fragment123"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFragmentWithWildcardAsSuffix()
|
||||
{
|
||||
var expression = "host.org#fragment*";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org#fragment",
|
||||
"scheme://host.org#fragment-123",
|
||||
"scheme://user:password@www.host.org/url/path/file.txt?param=123#fragment_abc"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org#",
|
||||
"scheme://host.org#fragmen",
|
||||
"scheme://www.host.org#another_fragment"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFragmentWithHostWildcard()
|
||||
{
|
||||
var expression = "*#fragment";
|
||||
var positive = new[]
|
||||
{
|
||||
"scheme://host.org#fragment",
|
||||
"scheme://server.net#fragment",
|
||||
"scheme://user:password@www.host.org/url/path?param=123#fragment",
|
||||
"scheme://user:password@www.server.net/url/path?param=123#fragment"
|
||||
};
|
||||
var negative = new[]
|
||||
{
|
||||
"scheme://host.org",
|
||||
"scheme://host.org#",
|
||||
"scheme://host.org#fragmen",
|
||||
"scheme://host.org#fragment123"
|
||||
};
|
||||
|
||||
Test(expression, positive, negative);
|
||||
}
|
||||
|
||||
private void Test(string expression, string[] positive, string[] negative, bool testLegacy = true)
|
||||
{
|
||||
var legacy = new LegacyFilter(expression);
|
||||
|
||||
sut.Initialize(new FilterRuleSettings { Expression = expression });
|
||||
|
||||
foreach (var url in positive)
|
||||
{
|
||||
Assert.IsTrue(sut.IsMatch(new Request { Url = url }), url);
|
||||
|
||||
if (testLegacy)
|
||||
{
|
||||
Assert.IsTrue(legacy.IsMatch(new Uri(url)), url);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var url in negative)
|
||||
{
|
||||
Assert.IsFalse(sut.IsMatch(new Request { Url = url }), url);
|
||||
|
||||
if (testLegacy)
|
||||
{
|
||||
Assert.IsFalse(legacy.IsMatch(new Uri(url)), url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class ContextMenuHandlerTests
|
||||
{
|
||||
private ContextMenuHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new ContextMenuHandler();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustClearContextMenu()
|
||||
{
|
||||
var menu = new Mock<IMenuModel>();
|
||||
|
||||
sut.OnBeforeContextMenu(default(IWebBrowser), default(IBrowser), default(IFrame), default(IContextMenuParams), menu.Object);
|
||||
menu.Verify(m => m.Clear(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustBlockContextMenu()
|
||||
{
|
||||
var command = sut.OnContextMenuCommand(default(IWebBrowser), default(IBrowser), default(IFrame), default(IContextMenuParams), default(CefMenuCommand), default(CefEventFlags));
|
||||
var run = sut.RunContextMenu(default(IWebBrowser), default(IBrowser), default(IFrame), default(IContextMenuParams), default(IMenuModel), default(IRunContextMenuCallback));
|
||||
|
||||
Assert.IsFalse(command);
|
||||
Assert.IsFalse(run);
|
||||
}
|
||||
}
|
||||
}
|
106
SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs
Normal file
106
SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Collections.Generic;
|
||||
using System.Threading;
|
||||
using CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class DialogHandlerTests
|
||||
{
|
||||
private DialogHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new DialogHandler();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyCancelDialog()
|
||||
{
|
||||
RequestDialog(default, false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyRequestOpenFileDialog()
|
||||
{
|
||||
var args = RequestDialog(CefFileDialogMode.Open);
|
||||
|
||||
Assert.AreEqual(FileSystemElement.File, args.Element);
|
||||
Assert.AreEqual(FileSystemOperation.Open, args.Operation);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyRequestOpenFolderDialog()
|
||||
{
|
||||
var args = RequestDialog(CefFileDialogMode.OpenFolder);
|
||||
|
||||
Assert.AreEqual(FileSystemElement.Folder, args.Element);
|
||||
Assert.AreEqual(FileSystemOperation.Open, args.Operation);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyRequestSaveFileDialog()
|
||||
{
|
||||
var args = RequestDialog(CefFileDialogMode.Save);
|
||||
|
||||
Assert.AreEqual(FileSystemElement.File, args.Element);
|
||||
Assert.AreEqual(FileSystemOperation.Save, args.Operation);
|
||||
}
|
||||
|
||||
private DialogRequestedEventArgs RequestDialog(CefFileDialogMode mode, bool confirm = true)
|
||||
{
|
||||
var args = default(DialogRequestedEventArgs);
|
||||
var callback = new Mock<IFileDialogCallback>();
|
||||
var title = "Some random dialog title";
|
||||
var initialPath = @"C:\Some\Random\Path";
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
callback.Setup(c => c.Cancel()).Callback(() => sync.Set());
|
||||
callback.Setup(c => c.Continue(It.IsAny<List<string>>())).Callback(() => sync.Set());
|
||||
sut.DialogRequested += (a) =>
|
||||
{
|
||||
args = a;
|
||||
args.Success = confirm;
|
||||
args.FullPath = @"D:\Some\Other\File\Path.txt";
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
};
|
||||
|
||||
var status = sut.OnFileDialog(default, default, mode, title, initialPath, default, callback.Object);
|
||||
|
||||
sync.WaitOne();
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
callback.Verify(c => c.Continue(It.IsAny<List<string>>()), Times.Once);
|
||||
callback.Verify(c => c.Cancel(), Times.Never);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.Verify(c => c.Continue(It.IsAny<List<string>>()), Times.Never);
|
||||
callback.Verify(c => c.Cancel(), Times.Once);
|
||||
}
|
||||
|
||||
Assert.IsTrue(status);
|
||||
Assert.AreEqual(initialPath, args.InitialPath);
|
||||
Assert.AreEqual(title, args.Title);
|
||||
Assert.AreNotEqual(threadId, Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class DisplayHandlerTests
|
||||
{
|
||||
private DisplayHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new DisplayHandler();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustUseDefaultHandling()
|
||||
{
|
||||
var text = default(string);
|
||||
|
||||
Assert.IsFalse(sut.OnAutoResize(default, default, default));
|
||||
Assert.IsFalse(sut.OnConsoleMessage(default, default));
|
||||
Assert.IsFalse(sut.OnCursorChange(default, default, default, default, default));
|
||||
Assert.IsFalse(sut.OnTooltipChanged(default, ref text));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustHandleFaviconChange()
|
||||
{
|
||||
var newUrl = "www.someurl.org/favicon.ico";
|
||||
var url = default(string);
|
||||
var called = false;
|
||||
|
||||
sut.FaviconChanged += (u) =>
|
||||
{
|
||||
called = true;
|
||||
url = u;
|
||||
};
|
||||
sut.OnFaviconUrlChange(default, default, new List<string>());
|
||||
|
||||
Assert.AreEqual(default, url);
|
||||
Assert.IsFalse(called);
|
||||
|
||||
sut.OnFaviconUrlChange(default, default, new List<string> { newUrl });
|
||||
|
||||
Assert.AreEqual(newUrl, url);
|
||||
Assert.IsTrue(called);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustHandleProgressChange()
|
||||
{
|
||||
var expected = 0.123456;
|
||||
var actual = default(double);
|
||||
|
||||
sut.ProgressChanged += (p) => actual = p;
|
||||
sut.OnLoadingProgressChange(default, default, expected);
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.IO;
|
||||
using System.Threading;
|
||||
using CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class DownloadHandlerTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<ILogger> logger;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
private DownloadHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
|
||||
sut = new DownloadHandler(appConfig, logger.Object, settings, windowSettings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleConfigurationByFileExtension()
|
||||
{
|
||||
var item = new DownloadItem
|
||||
{
|
||||
SuggestedFileName = "File.seb",
|
||||
Url = "https://somehost.org/some-path"
|
||||
};
|
||||
|
||||
RequestConfigurationDownload(item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleConfigurationByUrlExtension()
|
||||
{
|
||||
var item = new DownloadItem
|
||||
{
|
||||
SuggestedFileName = "Abc.xyz",
|
||||
Url = "https://somehost.org/some-path-to/file.seb"
|
||||
};
|
||||
|
||||
RequestConfigurationDownload(item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleConfigurationByMimeType()
|
||||
{
|
||||
appConfig.ConfigurationFileMimeType = "some/mime-type";
|
||||
|
||||
var item = new DownloadItem
|
||||
{
|
||||
MimeType = appConfig.ConfigurationFileMimeType,
|
||||
SuggestedFileName = "Abc.xyz",
|
||||
Url = "https://somehost.org/some-path"
|
||||
};
|
||||
|
||||
RequestConfigurationDownload(item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleDeniedConfigurationFileDownload()
|
||||
{
|
||||
var args = default(DownloadEventArgs);
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var failed = false;
|
||||
var fileName = default(string);
|
||||
var item = new DownloadItem
|
||||
{
|
||||
SuggestedFileName = "File.seb",
|
||||
Url = "https://somehost.org/some-path"
|
||||
};
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
settings.AllowDownloads = false;
|
||||
settings.AllowConfigurationDownloads = true;
|
||||
sut.ConfigurationDownloadRequested += (f, a) =>
|
||||
{
|
||||
args = a;
|
||||
args.AllowDownload = false;
|
||||
fileName = f;
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
sync.Set();
|
||||
};
|
||||
sut.DownloadUpdated += (state) => failed = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
sync.WaitOne();
|
||||
|
||||
callback.VerifyNoOtherCalls();
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
Assert.AreEqual(item.SuggestedFileName, fileName);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleFileDownload()
|
||||
{
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var downloadPath = default(string);
|
||||
var failed = false;
|
||||
var item = new DownloadItem
|
||||
{
|
||||
MimeType = "application/something",
|
||||
SuggestedFileName = "File.txt",
|
||||
Url = "https://somehost.org/somefile.abc"
|
||||
};
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
callback.Setup(c => c.Continue(It.IsAny<string>(), It.IsAny<bool>())).Callback<string, bool>((f, s) =>
|
||||
{
|
||||
downloadPath = f;
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
sync.Set();
|
||||
});
|
||||
settings.AllowDownloads = true;
|
||||
settings.AllowConfigurationDownloads = false;
|
||||
sut.ConfigurationDownloadRequested += (f, a) => failed = true;
|
||||
sut.DownloadUpdated += (state) => failed = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
sync.WaitOne();
|
||||
|
||||
callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(downloadPath)), false), Times.Once);
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleFileDownloadWithCustomDirectory()
|
||||
{
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var failed = false;
|
||||
var item = new DownloadItem
|
||||
{
|
||||
MimeType = "application/something",
|
||||
SuggestedFileName = "File.txt",
|
||||
Url = "https://somehost.org/somefile.abc"
|
||||
};
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
callback.Setup(c => c.Continue(It.IsAny<string>(), It.IsAny<bool>())).Callback(() =>
|
||||
{
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
sync.Set();
|
||||
});
|
||||
settings.AllowDownloads = true;
|
||||
settings.AllowConfigurationDownloads = false;
|
||||
settings.AllowCustomDownAndUploadLocation = true;
|
||||
settings.DownAndUploadDirectory = @"%APPDATA%\Downloads";
|
||||
sut.ConfigurationDownloadRequested += (f, a) => failed = true;
|
||||
sut.DownloadUpdated += (state) => failed = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
sync.WaitOne();
|
||||
|
||||
var downloadPath = Path.Combine(Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory), item.SuggestedFileName);
|
||||
|
||||
callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(downloadPath)), true), Times.Once);
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingIfDownloadsNotAllowed()
|
||||
{
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var fail = false;
|
||||
var item = new DownloadItem
|
||||
{
|
||||
SuggestedFileName = "File.txt",
|
||||
Url = "https://somehost.org/somefile.abc"
|
||||
};
|
||||
|
||||
settings.AllowDownloads = false;
|
||||
settings.AllowConfigurationDownloads = false;
|
||||
sut.ConfigurationDownloadRequested += (file, args) => fail = true;
|
||||
sut.DownloadUpdated += (state) => fail = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
|
||||
callback.VerifyNoOtherCalls();
|
||||
Assert.IsFalse(fail);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustUpdateDownloadProgress()
|
||||
{
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var failed = false;
|
||||
var item = new DownloadItem
|
||||
{
|
||||
MimeType = "application/something",
|
||||
SuggestedFileName = "File.txt",
|
||||
Url = "https://somehost.org/somefile.abc"
|
||||
};
|
||||
var state = default(DownloadItemState);
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
callback.Setup(c => c.Continue(It.IsAny<string>(), It.IsAny<bool>())).Callback(() => sync.Set());
|
||||
settings.AllowDownloads = true;
|
||||
settings.AllowConfigurationDownloads = false;
|
||||
sut.ConfigurationDownloadRequested += (f, a) => failed = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
sync.WaitOne();
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
|
||||
sut.DownloadUpdated += (s) =>
|
||||
{
|
||||
state = s;
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
sync.Set();
|
||||
};
|
||||
|
||||
item.PercentComplete = 10;
|
||||
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback));
|
||||
sync.WaitOne();
|
||||
|
||||
Assert.IsFalse(state.IsCancelled);
|
||||
Assert.IsFalse(state.IsComplete);
|
||||
Assert.AreEqual(0.1, state.Completion);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
|
||||
item.PercentComplete = 20;
|
||||
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback));
|
||||
sync.WaitOne();
|
||||
|
||||
Assert.IsFalse(state.IsCancelled);
|
||||
Assert.IsFalse(state.IsComplete);
|
||||
Assert.AreEqual(0.2, state.Completion);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
|
||||
item.PercentComplete = 50;
|
||||
item.IsCancelled = true;
|
||||
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback));
|
||||
sync.WaitOne();
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
Assert.IsTrue(state.IsCancelled);
|
||||
Assert.IsFalse(state.IsComplete);
|
||||
Assert.AreEqual(0.5, state.Completion);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
|
||||
private void RequestConfigurationDownload(DownloadItem item)
|
||||
{
|
||||
var args = default(DownloadEventArgs);
|
||||
var callback = new Mock<IBeforeDownloadCallback>();
|
||||
var failed = false;
|
||||
var fileName = default(string);
|
||||
var sync = new AutoResetEvent(false);
|
||||
var threadId = default(int);
|
||||
|
||||
callback.Setup(c => c.Continue(It.IsAny<string>(), It.IsAny<bool>())).Callback(() => sync.Set());
|
||||
settings.AllowDownloads = false;
|
||||
settings.AllowConfigurationDownloads = true;
|
||||
sut.ConfigurationDownloadRequested += (f, a) =>
|
||||
{
|
||||
args = a;
|
||||
args.AllowDownload = true;
|
||||
args.DownloadPath = @"C:\Downloads\File.seb";
|
||||
fileName = f;
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
};
|
||||
sut.DownloadUpdated += (state) => failed = true;
|
||||
|
||||
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object);
|
||||
sync.WaitOne();
|
||||
|
||||
callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(args.DownloadPath)), false), Times.Once);
|
||||
|
||||
Assert.IsFalse(failed);
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
Assert.AreEqual(item.SuggestedFileName, fileName);
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Windows.Forms;
|
||||
using CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class KeyboardHandlerTests
|
||||
{
|
||||
private KeyboardHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new KeyboardHandler();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectFindCommand()
|
||||
{
|
||||
var findRequested = false;
|
||||
|
||||
sut.FindRequested += () => findRequested = true;
|
||||
|
||||
var handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.F, default(int), CefEventFlags.ControlDown, default(bool));
|
||||
|
||||
Assert.IsTrue(findRequested);
|
||||
Assert.IsFalse(handled);
|
||||
|
||||
findRequested = false;
|
||||
handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), default(KeyType), default(int), default(int), CefEventFlags.ControlDown, default(bool));
|
||||
|
||||
Assert.IsFalse(findRequested);
|
||||
Assert.IsFalse(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectHomeNavigationCommand()
|
||||
{
|
||||
var homeRequested = false;
|
||||
|
||||
sut.HomeNavigationRequested += () => homeRequested = true;
|
||||
|
||||
var handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.Home, default(int), default(CefEventFlags), default(bool));
|
||||
|
||||
Assert.IsTrue(homeRequested);
|
||||
Assert.IsFalse(handled);
|
||||
|
||||
homeRequested = false;
|
||||
handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), default(KeyType), default(int), default(int), default(CefEventFlags), default(bool));
|
||||
|
||||
Assert.IsFalse(homeRequested);
|
||||
Assert.IsFalse(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectReloadCommand()
|
||||
{
|
||||
var isShortcut = default(bool);
|
||||
var reloadRequested = false;
|
||||
|
||||
sut.ReloadRequested += () => reloadRequested = true;
|
||||
|
||||
var handled = sut.OnPreKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.F5, default(int), default(CefEventFlags), default(bool), ref isShortcut);
|
||||
|
||||
Assert.IsTrue(reloadRequested);
|
||||
Assert.IsTrue(handled);
|
||||
|
||||
reloadRequested = false;
|
||||
handled = sut.OnPreKeyEvent(default(IWebBrowser), default(IBrowser), default(KeyType), default(int), default(int), default(CefEventFlags), default(bool), ref isShortcut);
|
||||
|
||||
Assert.IsFalse(reloadRequested);
|
||||
Assert.IsFalse(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectZoomInCommand()
|
||||
{
|
||||
var zoomIn = false;
|
||||
var zoomOut = false;
|
||||
var zoomReset = false;
|
||||
|
||||
sut.ZoomInRequested += () => zoomIn = true;
|
||||
sut.ZoomOutRequested += () => zoomOut = true;
|
||||
sut.ZoomResetRequested += () => zoomReset = true;
|
||||
|
||||
var handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.Add, default(int), CefEventFlags.ControlDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsTrue(zoomIn);
|
||||
Assert.IsFalse(zoomOut);
|
||||
Assert.IsFalse(zoomReset);
|
||||
|
||||
zoomIn = false;
|
||||
handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.D1, default(int), CefEventFlags.ControlDown | CefEventFlags.ShiftDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsTrue(zoomIn);
|
||||
Assert.IsFalse(zoomOut);
|
||||
Assert.IsFalse(zoomReset);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectZoomOutCommand()
|
||||
{
|
||||
var zoomIn = false;
|
||||
var zoomOut = false;
|
||||
var zoomReset = false;
|
||||
|
||||
sut.ZoomInRequested += () => zoomIn = true;
|
||||
sut.ZoomOutRequested += () => zoomOut = true;
|
||||
sut.ZoomResetRequested += () => zoomReset = true;
|
||||
|
||||
var handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.Subtract, default(int), CefEventFlags.ControlDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsFalse(zoomIn);
|
||||
Assert.IsTrue(zoomOut);
|
||||
Assert.IsFalse(zoomReset);
|
||||
|
||||
zoomOut = false;
|
||||
handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.OemMinus, default(int), CefEventFlags.ControlDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsFalse(zoomIn);
|
||||
Assert.IsTrue(zoomOut);
|
||||
Assert.IsFalse(zoomReset);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectZoomResetCommand()
|
||||
{
|
||||
var zoomIn = false;
|
||||
var zoomOut = false;
|
||||
var zoomReset = false;
|
||||
|
||||
sut.ZoomInRequested += () => zoomIn = true;
|
||||
sut.ZoomOutRequested += () => zoomOut = true;
|
||||
sut.ZoomResetRequested += () => zoomReset = true;
|
||||
|
||||
var handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.D0, default(int), CefEventFlags.ControlDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsFalse(zoomIn);
|
||||
Assert.IsFalse(zoomOut);
|
||||
Assert.IsTrue(zoomReset);
|
||||
|
||||
zoomReset = false;
|
||||
handled = sut.OnKeyEvent(default(IWebBrowser), default(IBrowser), KeyType.KeyUp, (int) Keys.NumPad0, default(int), CefEventFlags.ControlDown, false);
|
||||
|
||||
Assert.IsFalse(handled);
|
||||
Assert.IsFalse(zoomIn);
|
||||
Assert.IsFalse(zoomOut);
|
||||
Assert.IsTrue(zoomReset);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
using SafeExamBrowser.Settings.Browser.Proxy;
|
||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||
using Request = SafeExamBrowser.Browser.Contracts.Filters.Request;
|
||||
using ResourceHandler = SafeExamBrowser.Browser.Handlers.ResourceHandler;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class RequestHandlerTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IRequestFilter> filter;
|
||||
private Mock<IKeyGenerator> keyGenerator;
|
||||
private Mock<ILogger> logger;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
private ResourceHandler resourceHandler;
|
||||
private Mock<IText> text;
|
||||
private TestableRequestHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
resourceHandler = new ResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, default, settings, windowSettings, text.Object);
|
||||
|
||||
sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustBlockSpecialWindowDispositions()
|
||||
{
|
||||
Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewBackgroundTab, default));
|
||||
Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewPopup, default));
|
||||
Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewWindow, default));
|
||||
Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.SaveToDisk, default));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDetectQuitUrl()
|
||||
{
|
||||
var eventFired = false;
|
||||
var quitUrl = "http://www.byebye.com";
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
request.SetupGet(r => r.Url).Returns(quitUrl);
|
||||
settings.QuitUrl = quitUrl;
|
||||
sut.QuitUrlVisited += (url) => eventFired = true;
|
||||
|
||||
var blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
Assert.IsTrue(blocked);
|
||||
Assert.IsTrue(eventFired);
|
||||
|
||||
blocked = false;
|
||||
eventFired = false;
|
||||
request.SetupGet(r => r.Url).Returns("http://www.bye.com");
|
||||
|
||||
blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
Assert.IsFalse(blocked);
|
||||
Assert.IsFalse(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustIgnoreTrailingSlashForQuitUrl()
|
||||
{
|
||||
var eventFired = false;
|
||||
var quitUrl = "http://www.byebye.com";
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
request.SetupGet(r => r.Url).Returns($"{quitUrl}/");
|
||||
settings.QuitUrl = quitUrl;
|
||||
sut.QuitUrlVisited += (url) => eventFired = true;
|
||||
|
||||
var blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
Assert.IsTrue(blocked);
|
||||
Assert.IsTrue(eventFired);
|
||||
|
||||
blocked = false;
|
||||
eventFired = false;
|
||||
request.SetupGet(r => r.Url).Returns(quitUrl);
|
||||
settings.QuitUrl = $"{quitUrl}/";
|
||||
|
||||
blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
Assert.IsTrue(blocked);
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustFilterMainRequests()
|
||||
{
|
||||
var eventFired = false;
|
||||
var request = new Mock<IRequest>();
|
||||
var url = "https://www.test.org";
|
||||
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
filter.Setup(f => f.Process(It.Is<Request>(r => r.Url.Equals(url)))).Returns(FilterResult.Block);
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.MainFrame);
|
||||
request.SetupGet(r => r.Url).Returns(url);
|
||||
settings.Filter.ProcessContentRequests = false;
|
||||
settings.Filter.ProcessMainRequests = true;
|
||||
sut.RequestBlocked += (u) => eventFired = true;
|
||||
|
||||
var blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
filter.Verify(f => f.Process(It.Is<Request>(r => r.Url.Equals(url))), Times.Once);
|
||||
|
||||
Assert.IsTrue(blocked);
|
||||
Assert.IsTrue(eventFired);
|
||||
|
||||
blocked = false;
|
||||
eventFired = false;
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.SubFrame);
|
||||
|
||||
blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
filter.Verify(f => f.Process(It.Is<Request>(r => r.Url.Equals(url))), Times.Once);
|
||||
|
||||
Assert.IsFalse(blocked);
|
||||
Assert.IsFalse(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustFilterContentRequests()
|
||||
{
|
||||
var eventFired = false;
|
||||
var request = new Mock<IRequest>();
|
||||
var url = "https://www.test.org";
|
||||
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
filter.Setup(f => f.Process(It.Is<Request>(r => r.Url.Equals(url)))).Returns(FilterResult.Block);
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.SubFrame);
|
||||
request.SetupGet(r => r.Url).Returns(url);
|
||||
settings.Filter.ProcessContentRequests = true;
|
||||
settings.Filter.ProcessMainRequests = false;
|
||||
sut.RequestBlocked += (u) => eventFired = true;
|
||||
|
||||
var blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
filter.Verify(f => f.Process(It.Is<Request>(r => r.Url.Equals(url))), Times.Once);
|
||||
|
||||
Assert.IsTrue(blocked);
|
||||
Assert.IsFalse(eventFired);
|
||||
|
||||
blocked = false;
|
||||
eventFired = false;
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.MainFrame);
|
||||
|
||||
blocked = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
filter.Verify(f => f.Process(It.Is<Request>(r => r.Url.Equals(url))), Times.Once);
|
||||
|
||||
Assert.IsFalse(blocked);
|
||||
Assert.IsFalse(eventFired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitiateDataUriConfigurationFileDownload()
|
||||
{
|
||||
var browser = new Mock<IBrowser>();
|
||||
var host = new Mock<IBrowserHost>();
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
appConfig.ConfigurationFileExtension = ".xyz";
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
appConfig.SebUriScheme = "abc";
|
||||
appConfig.SebUriSchemeSecure = "abcd";
|
||||
browser.Setup(b => b.GetHost()).Returns(host.Object);
|
||||
request.SetupGet(r => r.Url).Returns($"{appConfig.SebUriSchemeSecure}://{appConfig.ConfigurationFileMimeType};base64,H4sIAAAAAAAAE41WbXPaRhD...");
|
||||
|
||||
var handled = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), browser.Object, Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
host.Verify(h => h.StartDownload(It.Is<string>(u => u == $"data:{appConfig.ConfigurationFileMimeType};base64,H4sIAAAAAAAAE41WbXPaRhD...")));
|
||||
Assert.IsTrue(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitiateHttpConfigurationFileDownload()
|
||||
{
|
||||
var browser = new Mock<IBrowser>();
|
||||
var host = new Mock<IBrowserHost>();
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
appConfig.ConfigurationFileExtension = ".xyz";
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
appConfig.SebUriScheme = "abc";
|
||||
appConfig.SebUriSchemeSecure = "abcd";
|
||||
browser.Setup(b => b.GetHost()).Returns(host.Object);
|
||||
request.SetupGet(r => r.Url).Returns($"{appConfig.SebUriScheme}://host.com/path/file{appConfig.ConfigurationFileExtension}");
|
||||
|
||||
var handled = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), browser.Object, Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
host.Verify(h => h.StartDownload(It.Is<string>(u => u == $"{Uri.UriSchemeHttp}://host.com/path/file{appConfig.ConfigurationFileExtension}")));
|
||||
Assert.IsTrue(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitiateHttpsConfigurationFileDownload()
|
||||
{
|
||||
var browser = new Mock<IBrowser>();
|
||||
var host = new Mock<IBrowserHost>();
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
appConfig.ConfigurationFileExtension = ".xyz";
|
||||
appConfig.ConfigurationFileMimeType = "application/seb";
|
||||
appConfig.SebUriScheme = "abc";
|
||||
appConfig.SebUriSchemeSecure = "abcd";
|
||||
browser.Setup(b => b.GetHost()).Returns(host.Object);
|
||||
request.SetupGet(r => r.Url).Returns($"{appConfig.SebUriSchemeSecure}://host.com/path/file{appConfig.ConfigurationFileExtension}");
|
||||
|
||||
var handled = sut.OnBeforeBrowse(Mock.Of<IWebBrowser>(), browser.Object, Mock.Of<IFrame>(), request.Object, false, false);
|
||||
|
||||
host.Verify(h => h.StartDownload(It.Is<string>(u => u == $"{Uri.UriSchemeHttps}://host.com/path/file{appConfig.ConfigurationFileExtension}")));
|
||||
Assert.IsTrue(handled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustReturnResourceHandler()
|
||||
{
|
||||
var disableDefaultHandling = default(bool);
|
||||
var handler = sut.GetResourceRequestHandler(default, default, default, default, default, default, default, ref disableDefaultHandling);
|
||||
|
||||
Assert.AreSame(resourceHandler, handler);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustUseProxyCredentials()
|
||||
{
|
||||
var callback = new Mock<IAuthCallback>();
|
||||
var proxy1 = new ProxyConfiguration { Host = "www.test.com", Username = "Sepp", Password = "1234", Port = 10, RequiresAuthentication = true };
|
||||
var proxy2 = new ProxyConfiguration { Host = "www.nope.com", Username = "Peter", Password = "4321", Port = 10, RequiresAuthentication = false };
|
||||
|
||||
settings.Proxy.Proxies.Add(proxy1);
|
||||
settings.Proxy.Proxies.Add(proxy2);
|
||||
|
||||
var result = sut.GetAuthCredentials(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), default, true, "WWW.tEst.Com", 10, default, default, callback.Object);
|
||||
|
||||
callback.Verify(c => c.Cancel(), Times.Never);
|
||||
callback.Verify(c => c.Continue(It.Is<string>(u => u.Equals(proxy1.Username)), It.Is<string>(p => p.Equals(proxy1.Password))), Times.Once);
|
||||
callback.Verify(c => c.Continue(It.Is<string>(u => u.Equals(proxy2.Username)), It.Is<string>(p => p.Equals(proxy2.Password))), Times.Never);
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotUseProxyCredentialsIfNoProxy()
|
||||
{
|
||||
var callback = new Mock<IAuthCallback>();
|
||||
|
||||
sut.GetAuthCredentials(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), default, false, default, default, default, default, callback.Object);
|
||||
|
||||
callback.Verify(c => c.Cancel(), Times.Never);
|
||||
callback.Verify(c => c.Continue(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
private class TestableRequestHandler : RequestHandler
|
||||
{
|
||||
internal TestableRequestHandler(AppConfig appConfig, IRequestFilter filter, ILogger logger, ResourceHandler resourceHandler, BrowserSettings settings, WindowSettings windowSettings) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public new bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
|
||||
{
|
||||
return base.GetAuthCredentials(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback);
|
||||
}
|
||||
|
||||
public new IResourceRequestHandler GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
|
||||
{
|
||||
return base.GetResourceRequestHandler(webBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling);
|
||||
}
|
||||
|
||||
public new bool OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect)
|
||||
{
|
||||
return base.OnBeforeBrowse(webBrowser, browser, frame, request, userGesture, isRedirect);
|
||||
}
|
||||
|
||||
public new bool OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
|
||||
{
|
||||
return base.OnOpenUrlFromTab(webBrowser, browser, frame, targetUrl, targetDisposition, userGesture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Net.Mime;
|
||||
using System.Threading;
|
||||
using CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||
using Request = SafeExamBrowser.Browser.Contracts.Filters.Request;
|
||||
using ResourceHandler = SafeExamBrowser.Browser.Handlers.ResourceHandler;
|
||||
|
||||
namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
{
|
||||
[TestClass]
|
||||
public class ResourceHandlerTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IRequestFilter> filter;
|
||||
private Mock<IKeyGenerator> keyGenerator;
|
||||
private Mock<ILogger> logger;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
private Mock<IText> text;
|
||||
private TestableResourceHandler sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
|
||||
sut = new TestableResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, SessionMode.Server, settings, windowSettings, text.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustAppendCustomHeadersForSameDomain()
|
||||
{
|
||||
var browser = new Mock<IWebBrowser>();
|
||||
var headers = default(NameValueCollection);
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
browser.SetupGet(b => b.Address).Returns("http://www.host.org");
|
||||
keyGenerator.Setup(g => g.CalculateBrowserExamKeyHash(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
keyGenerator.Setup(g => g.CalculateConfigurationKeyHash(It.IsAny<string>(), It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
request.SetupGet(r => r.Headers).Returns(new NameValueCollection());
|
||||
request.SetupGet(r => r.Url).Returns("http://www.host.org");
|
||||
request.SetupSet(r => r.Headers = It.IsAny<NameValueCollection>()).Callback<NameValueCollection>((h) => headers = h);
|
||||
settings.SendConfigurationKey = true;
|
||||
settings.SendBrowserExamKey = true;
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(browser.Object, Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
request.VerifyGet(r => r.Headers, Times.AtLeastOnce);
|
||||
request.VerifySet(r => r.Headers = It.IsAny<NameValueCollection>(), Times.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Continue, result);
|
||||
Assert.IsNotNull(headers["X-SafeExamBrowser-ConfigKeyHash"]);
|
||||
Assert.IsNotNull(headers["X-SafeExamBrowser-RequestHash"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustAppendCustomHeadersForCrossDomainResourceRequestAndMainFrame()
|
||||
{
|
||||
var browser = new Mock<IWebBrowser>();
|
||||
var headers = new NameValueCollection();
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
browser.SetupGet(b => b.Address).Returns("http://www.otherhost.org");
|
||||
keyGenerator.Setup(g => g.CalculateBrowserExamKeyHash(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
keyGenerator.Setup(g => g.CalculateConfigurationKeyHash(It.IsAny<string>(), It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.MainFrame);
|
||||
request.SetupGet(r => r.Headers).Returns(new NameValueCollection());
|
||||
request.SetupGet(r => r.Url).Returns("http://www.host.org");
|
||||
request.SetupSet(r => r.Headers = It.IsAny<NameValueCollection>()).Callback<NameValueCollection>((h) => headers = h);
|
||||
settings.SendConfigurationKey = true;
|
||||
settings.SendBrowserExamKey = true;
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(browser.Object, Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
request.VerifyGet(r => r.Headers, Times.AtLeastOnce);
|
||||
request.VerifySet(r => r.Headers = It.IsAny<NameValueCollection>(), Times.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Continue, result);
|
||||
Assert.IsNotNull(headers["X-SafeExamBrowser-ConfigKeyHash"]);
|
||||
Assert.IsNotNull(headers["X-SafeExamBrowser-RequestHash"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotAppendCustomHeadersForCrossDomainResourceRequestAndSubResource()
|
||||
{
|
||||
var browser = new Mock<IWebBrowser>();
|
||||
var headers = new NameValueCollection();
|
||||
var request = new Mock<IRequest>();
|
||||
|
||||
browser.SetupGet(b => b.Address).Returns("http://www.otherhost.org");
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.SubResource);
|
||||
request.SetupGet(r => r.Headers).Returns(new NameValueCollection());
|
||||
request.SetupGet(r => r.Url).Returns("http://www.host.org");
|
||||
request.SetupSet(r => r.Headers = It.IsAny<NameValueCollection>()).Callback<NameValueCollection>((h) => headers = h);
|
||||
settings.SendConfigurationKey = true;
|
||||
settings.SendBrowserExamKey = true;
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(browser.Object, Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
request.VerifyGet(r => r.Headers, Times.Never);
|
||||
request.VerifySet(r => r.Headers = It.IsAny<NameValueCollection>(), Times.Never);
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Continue, result);
|
||||
Assert.IsNull(headers["X-SafeExamBrowser-ConfigKeyHash"]);
|
||||
Assert.IsNull(headers["X-SafeExamBrowser-RequestHash"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustBlockMailToUrls()
|
||||
{
|
||||
var request = new Mock<IRequest>();
|
||||
var url = $"{Uri.UriSchemeMailto}:someone@somewhere.org";
|
||||
|
||||
request.SetupGet(r => r.Url).Returns(url);
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Cancel, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustFilterContentRequests()
|
||||
{
|
||||
var request = new Mock<IRequest>();
|
||||
var url = "http://www.test.org";
|
||||
|
||||
filter.Setup(f => f.Process(It.Is<Request>(r => r.Url.Equals(url)))).Returns(FilterResult.Block);
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.SubFrame);
|
||||
request.SetupGet(r => r.Url).Returns(url);
|
||||
settings.Filter.ProcessContentRequests = true;
|
||||
settings.Filter.ProcessMainRequests = true;
|
||||
|
||||
var resourceHandler = sut.GetResourceHandler(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object);
|
||||
|
||||
filter.Verify(f => f.Process(It.Is<Request>(r => r.Url.Equals(url))), Times.Once);
|
||||
|
||||
Assert.IsNotNull(resourceHandler);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustLetOperatingSystemHandleUnknownProtocols()
|
||||
{
|
||||
Assert.IsTrue(sut.OnProtocolExecution(default, default, default, default));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustRedirectToDisablePdfToolbar()
|
||||
{
|
||||
var frame = new Mock<IFrame>();
|
||||
var headers = new NameValueCollection { { "Content-Type", MediaTypeNames.Application.Pdf } };
|
||||
var request = new Mock<IRequest>();
|
||||
var response = new Mock<IResponse>();
|
||||
var url = "http://www.host.org/some-document";
|
||||
|
||||
request.SetupGet(r => r.ResourceType).Returns(ResourceType.MainFrame);
|
||||
request.SetupGet(r => r.Url).Returns(url);
|
||||
response.SetupGet(r => r.Headers).Returns(headers);
|
||||
settings.AllowPdfReader = true;
|
||||
settings.AllowPdfReaderToolbar = false;
|
||||
|
||||
var result = sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), frame.Object, request.Object, response.Object);
|
||||
|
||||
frame.Verify(b => b.LoadUrl(It.Is<string>(s => s.Equals($"{url}#toolbar=0"))), Times.Once);
|
||||
Assert.IsTrue(result);
|
||||
|
||||
frame.Reset();
|
||||
request.SetupGet(r => r.Url).Returns($"{url}#toolbar=0");
|
||||
|
||||
result = sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), frame.Object, request.Object, response.Object);
|
||||
|
||||
frame.Verify(b => b.LoadUrl(It.IsAny<string>()), Times.Never);
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustReplaceSebScheme()
|
||||
{
|
||||
var request = new Mock<IRequest>();
|
||||
var url = default(string);
|
||||
|
||||
appConfig.SebUriScheme = "abc";
|
||||
appConfig.SebUriSchemeSecure = "abcs";
|
||||
request.SetupGet(r => r.Headers).Returns(new NameValueCollection());
|
||||
request.SetupGet(r => r.Url).Returns($"{appConfig.SebUriScheme}://www.host.org");
|
||||
request.SetupSet(r => r.Url = It.IsAny<string>()).Callback<string>(u => url = u);
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Continue, result);
|
||||
Assert.AreEqual("http://www.host.org/", url);
|
||||
|
||||
request.SetupGet(r => r.Url).Returns($"{appConfig.SebUriSchemeSecure}://www.host.org");
|
||||
request.SetupSet(r => r.Url = It.IsAny<string>()).Callback<string>(u => url = u);
|
||||
|
||||
result = sut.OnBeforeResourceLoad(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
Assert.AreEqual(CefReturnValue.Continue, result);
|
||||
Assert.AreEqual("https://www.host.org/", url);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSearchGenericLmsSessionIdentifier()
|
||||
{
|
||||
var @event = new AutoResetEvent(false);
|
||||
var headers = new NameValueCollection();
|
||||
var newUrl = default(string);
|
||||
var request = new Mock<IRequest>();
|
||||
var response = new Mock<IResponse>();
|
||||
var userId = default(string);
|
||||
|
||||
headers.Add("X-LMS-USER-ID", "some-session-id-123");
|
||||
request.SetupGet(r => r.Url).Returns("https://www.somelms.org");
|
||||
response.SetupGet(r => r.Headers).Returns(headers);
|
||||
sut.UserIdentifierDetected += (id) =>
|
||||
{
|
||||
userId = id;
|
||||
@event.Set();
|
||||
};
|
||||
|
||||
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("some-session-id-123", userId);
|
||||
|
||||
headers.Clear();
|
||||
headers.Add("X-LMS-USER-ID", "other-session-id-123");
|
||||
userId = default;
|
||||
|
||||
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("other-session-id-123", userId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSearchEdxSessionIdentifier()
|
||||
{
|
||||
var @event = new AutoResetEvent(false);
|
||||
var headers = new NameValueCollection();
|
||||
var newUrl = default(string);
|
||||
var request = new Mock<IRequest>();
|
||||
var response = new Mock<IResponse>();
|
||||
var userId = default(string);
|
||||
|
||||
headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-123\\\"}\"; expires");
|
||||
request.SetupGet(r => r.Url).Returns("https://www.somelms.org");
|
||||
response.SetupGet(r => r.Headers).Returns(headers);
|
||||
sut.UserIdentifierDetected += (id) =>
|
||||
{
|
||||
userId = id;
|
||||
@event.Set();
|
||||
};
|
||||
|
||||
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("edx-123", userId);
|
||||
|
||||
headers.Clear();
|
||||
headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-345\\\"}\"; expires");
|
||||
userId = default;
|
||||
|
||||
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("edx-345", userId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSearchMoodleSessionIdentifier()
|
||||
{
|
||||
var @event = new AutoResetEvent(false);
|
||||
var headers = new NameValueCollection();
|
||||
var newUrl = default(string);
|
||||
var request = new Mock<IRequest>();
|
||||
var response = new Mock<IResponse>();
|
||||
var userId = default(string);
|
||||
|
||||
headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=123");
|
||||
request.SetupGet(r => r.Url).Returns("https://www.some-moodle-instance.org");
|
||||
response.SetupGet(r => r.Headers).Returns(headers);
|
||||
sut.UserIdentifierDetected += (id) =>
|
||||
{
|
||||
userId = id;
|
||||
@event.Set();
|
||||
};
|
||||
|
||||
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("123", userId);
|
||||
|
||||
headers.Clear();
|
||||
headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=456");
|
||||
userId = default;
|
||||
|
||||
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
|
||||
@event.WaitOne();
|
||||
Assert.AreEqual("456", userId);
|
||||
}
|
||||
|
||||
private class TestableResourceHandler : ResourceHandler
|
||||
{
|
||||
internal TestableResourceHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
IKeyGenerator keyGenerator,
|
||||
ILogger logger,
|
||||
SessionMode sessionMode,
|
||||
BrowserSettings settings,
|
||||
WindowSettings windowSettings,
|
||||
IText text) : base(appConfig, filter, keyGenerator, logger, sessionMode, settings, windowSettings, text)
|
||||
{
|
||||
}
|
||||
|
||||
public new IResourceHandler GetResourceHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request)
|
||||
{
|
||||
return base.GetResourceHandler(webBrowser, browser, frame, request);
|
||||
}
|
||||
|
||||
public new CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
|
||||
{
|
||||
return base.OnBeforeResourceLoad(webBrowser, browser, frame, request, callback);
|
||||
}
|
||||
|
||||
public new bool OnProtocolExecution(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request)
|
||||
{
|
||||
return base.OnProtocolExecution(webBrowser, browser, frame, request);
|
||||
}
|
||||
|
||||
public new void OnResourceRedirect(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, ref string newUrl)
|
||||
{
|
||||
base.OnResourceRedirect(webBrowser, browser, frame, request, response, ref newUrl);
|
||||
}
|
||||
|
||||
public new bool OnResourceResponse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response)
|
||||
{
|
||||
return base.OnResourceResponse(webBrowser, browser, frame, request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
SafeExamBrowser.Browser.UnitTests/Properties/AssemblyInfo.cs
Normal file
17
SafeExamBrowser.Browser.UnitTests/Properties/AssemblyInfo.cs
Normal file
@@ -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 © 2024 ETH Zürich, IT Services")]
|
||||
|
||||
[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")]
|
@@ -0,0 +1,220 @@
|
||||
<?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.3.2.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SafeExamBrowser.Browser.UnitTests</RootNamespace>
|
||||
<AssemblyName>SafeExamBrowser.Browser.UnitTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp, Version=121.3.130.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.121.3.130\lib\net462\CefSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.Core, Version=121.3.130.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.121.3.130\lib\net462\CefSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform.MSBuild, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.2.2\lib\net462\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.3.2.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.20.70.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.20.70\lib\net462\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.9.1.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.9.1\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.8.0.0\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.8.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.8.0.0\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<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.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Filters\LegacyFilter.cs" />
|
||||
<Compile Include="Filters\RequestFilterTests.cs" />
|
||||
<Compile Include="Filters\RuleFactoryTests.cs" />
|
||||
<Compile Include="Filters\Rules\RegexRuleTests.cs" />
|
||||
<Compile Include="Filters\Rules\SimplifiedRuleTests.cs" />
|
||||
<Compile Include="Handlers\ContextMenuHandlerTests.cs" />
|
||||
<Compile Include="Handlers\DialogHandlerTests.cs" />
|
||||
<Compile Include="Handlers\DisplayHandlerTests.cs" />
|
||||
<Compile Include="Handlers\DownloadHandlerTests.cs" />
|
||||
<Compile Include="Handlers\KeyboardHandlerTests.cs" />
|
||||
<Compile Include="Handlers\RequestHandlerTests.cs" />
|
||||
<Compile Include="Handlers\ResourceHandlerTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Browser.Contracts\SafeExamBrowser.Browser.Contracts.csproj">
|
||||
<Project>{5fb5273d-277c-41dd-8593-a25ce1aff2e9}</Project>
|
||||
<Name>SafeExamBrowser.Browser.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Browser\SafeExamBrowser.Browser.csproj">
|
||||
<Project>{04e653f1-98e6-4e34-9dd7-7f2bc1a8b767}</Project>
|
||||
<Name>SafeExamBrowser.Browser</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
|
||||
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
|
||||
<Name>SafeExamBrowser.Configuration.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.I18n.Contracts\SafeExamBrowser.I18n.Contracts.csproj">
|
||||
<Project>{1858ddf3-bc2a-4bff-b663-4ce2ffeb8b7d}</Project>
|
||||
<Name>SafeExamBrowser.I18n.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
|
||||
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
|
||||
<Name>SafeExamBrowser.Logging.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
|
||||
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
||||
<Name>SafeExamBrowser.Settings</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
|
||||
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface.Contracts</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<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\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" />
|
||||
</Project>
|
51
SafeExamBrowser.Browser.UnitTests/app.config
Normal file
51
SafeExamBrowser.Browser.UnitTests/app.config
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="CefSharp" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-118.6.80.0" newVersion="118.6.80.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="CefSharp.Core" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-118.6.80.0" newVersion="118.6.80.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.11.3.1" newVersion="5.11.3.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration>
|
26
SafeExamBrowser.Browser.UnitTests/packages.config
Normal file
26
SafeExamBrowser.Browser.UnitTests/packages.config
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="121.3.130" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="121.3.13" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="121.3.13" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.9.0" targetFramework="net48" />
|
||||
<package id="Moq" version="4.20.70" targetFramework="net48" />
|
||||
<package id="MSTest.TestAdapter" version="3.2.2" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.2.2" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.9.1" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="8.0.0" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
|
||||
</packages>
|
Reference in New Issue
Block a user