26 Commits
v1.5.2 ... v1.8

Author SHA1 Message Date
eaa78d1583 Load browser exam key from a text file + log masking
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2025-10-18 16:25:19 +02:00
c6b0deb359 Update README.md
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
2025-10-03 19:44:21 +00:00
213ab539cf v1.7
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2025-09-16 16:51:14 +02:00
9a9b753049 Sign files
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2025-09-16 16:43:02 +02:00
5148abede7 Remove leftovers
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2025-09-16 16:36:09 +02:00
dd82d45ed8 Update Safe Exam Browser Patch to 3.10.0.826 2025-09-16 16:32:31 +02:00
4827ae1afc Update README.md 2025-09-16 08:45:41 +00:00
f79146d6fb Update README.md
Some checks reported errors
continuous-integration/appveyor/branch AppVeyor build cancelled
2025-09-08 09:33:55 +00:00
59747140c7 Upload files to "/"
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2025-09-08 09:26:54 +00:00
ae5dc61021 Delete appveyor.yml 2025-09-07 15:37:09 +00:00
626b351dff Update appveyor.yml
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
2025-09-07 15:33:59 +00:00
1a350b6c13 Upload files to "/"
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
2025-09-07 15:20:38 +00:00
Vichingo455
9ed5135f69 Bug and UI fixes
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
2025-06-24 18:07:56 +02:00
25495595ba Update README.md 2025-06-24 12:47:53 +00:00
8a09160d31 Update .github/ISSUE_TEMPLATE/config.yml 2025-06-24 12:44:53 +00:00
354f731a6f Update .github/ISSUE_TEMPLATE/bug-report.md 2025-06-24 12:43:48 +00:00
9b2367a4a3 Update README.md 2025-06-24 12:12:07 +00:00
4948d5946f Update README.md 2025-06-23 15:36:59 +00:00
84b26a2295 v1.6 2025-06-23 17:31:39 +02:00
811aea6bd2 Reduce UI size 2025-06-23 17:13:30 +02:00
b71e8bd82f v1.6 2025-06-23 17:07:12 +02:00
a6b60d72bd Almost done first beta 2025-06-23 16:03:48 +02:00
2d36fecb45 Missing files + start working on offline patcher 2025-06-23 13:42:14 +02:00
058d48196a Downgrade patcher to .NET 4.5 from .NET 4.7 2025-06-22 22:39:41 +02:00
332c6460c7 Add resource files which got ignored by Git 2025-06-22 22:39:05 +02:00
fc6a2f7509 Update README.md 2025-06-01 13:04:37 +00:00
340 changed files with 19373 additions and 5160 deletions

View File

@@ -1,18 +1,17 @@
--- ---
name: Bug Report name: Bug Report
about: Create a bug report to help us improve the Safe Exam Browser Patch about: Create a bug report to help us improve Safe Exam Browser.
title: '' title: ''
labels: bug labels: ''
assignees: usefulstuffs assignees: dbuechel
--- ---
> [!IMPORTANT] > [!IMPORTANT]
> - Please _always_ consult the FAQs before creating an issue: https://github.com/school-cheating/SEBPatch/wiki/FAQs. > - Please _always_ consult the documentation first before creating a bug report: https://safeexambrowser.org/windows/win_usermanual_en.html.
> - Please _always_ attach the log file(s) of the affected session(s)! They can be found under `%LocalAppData%\SafeExamBrowser\Logs`. > - Please _always_ attach the log file(s) of the affected session(s)! They can be found under `%LocalAppData%\SafeExamBrowser\Logs`.
> - Please follow this issue template. Saves me some work while reading all issues.
**Bug Description** **Describe the Bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**Steps to Reproduce** **Steps to Reproduce**
@@ -30,18 +29,7 @@ If applicable, add screenshots to help explain your problem.
**Version Information** **Version Information**
- OS: [e.g. Windows 10 Professional, Version 1803] - OS: [e.g. Windows 10 Professional, Version 1803]
- SEB version: [e.g. SEB 3.0.1] - SEB-Version [e.g. SEB 3.0.1]
- SEB patch version: [e.g. 1.5.1]
**Additional Context** **Additional Context**
Add any other context about the problem here. Add any other context about the problem here.
**SEB Logs**
```
Paste here the SEB logs
```
**SEB Patcher logs (optional, read below)**
```
Paste here the SEB patcher logs (if issue is SEB patcher related, else just ignore this section)
```

View File

@@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: FAQs
url: https://github.com/school-cheating/SEBPatch/wiki/FAQs
about: Before opening an issue, check out the FAQs. Any issue corresponding to the FAQs will be ignored and closed.
- name: Questions
url: https://github.com/school-cheating/SEBPatch/discussions/categories/q-a
about: Ask questions here about the Safe Exam Browser Patch
- name: Feature Requests
url: https://github.com/school-cheating/SEBPatch/discussions/categories/ideas
about: Propose a feature or a change for the Safe Exam Browser Patch.

View File

@@ -0,0 +1,20 @@
---
name: Feature Request
about: Suggest an idea or new feature for Safe Exam Browser.
title: ''
labels: ''
assignees: dbuechel
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

54
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: "CodeQL"
on:
push:
branches: [ "master", "*" ]
pull_request:
branches: [ "master", "*" ]
schedule:
- cron: '0 0 * * 1'
jobs:
analyze:
name: Analyze
runs-on: "windows-latest"
timeout-minutes: 360
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'csharp', 'javascript-typescript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,15 +1,20 @@
# Safe Exam Browser Patch # Safe Exam Browser Patch
[![Build status](https://ci.vichingo455.freeddns.org/api/projects/status/qcwguer3ng9ox4a0?svg=true)](https://ci.vichingo455.freeddns.org/project/Vichingo455/sebpatch)
A patch to bypass Safe Exam Browser restrictions. A patch to bypass Safe Exam Browser restrictions.
- Currently supported SEB version: 3.9.0.787 - Currently supported SEB version: 3.10.0.826
## [HOW TO INSTALL](https://github.com/school-cheating/SEBPatch/wiki/Instructions) ## Downloading and installing
I put this bigger so you actually read it and stop asking how to install the patch. You can get the latest executable from [releases](https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/releases/latest). If you need help about the installation, check the [wiki](https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/wiki).
## Mirrors ## Mirrors
In case you can't download from the latest release, here is a list of mirrors (will be updated eventually): In case you can't download from the latest release, here is a list of mirrors (will be updated eventually):
* [Vichingo455's Software Repository](https://software-repository-website.vercel.app/Random%20Files/Projects/SEBPatch/) * [Vichingo455's Software Repository](https://software-repository-website.vercel.app/Random%20Files/Projects/SEBPatch/)
## Reporting an issue
The issues reporting has been moved from GitHub to [Bugzilla](https://bugs.vichingo455.freeddns.org/describecomponents.cgi?product=Safe%20Exam%20Browser%20Patch). Click [here](https://bugs.vichingo455.freeddns.org/describecomponents.cgi?product=Safe%20Exam%20Browser%20Patch) to go to Bugzilla.
## Credits ## Credits
This project uses the same license as Safe Exam Browser, so it's completely legal. This project uses the same license as Safe Exam Browser, so it's completely legal.
However, it should be used with caution. I don't recommend cheating in exams as it could lead to educational consequences. However, it should be used with caution. I don't recommend cheating in exams as it could lead to educational consequences.

View File

@@ -18,6 +18,11 @@ namespace SafeExamBrowser.Applications.Contracts
/// </summary> /// </summary>
Error, Error,
/// <summary>
/// The application has been found but is invalid (e.g. because it is not the correct version or has been manipulated).
/// </summary>
Invalid,
/// <summary> /// <summary>
/// The application could not be found on the system. /// The application could not be found on the system.
/// </summary> /// </summary>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" /> <Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -63,78 +63,82 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <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> <HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath> <HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> <Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath> <HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath> <HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath> <HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath> <HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime" /> <Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
@@ -184,11 +188,8 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -196,13 +197,17 @@
<PropertyGroup> <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> <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> </PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" /> <Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
</Target> </Target>
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
</Project> </Project>

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -12,31 +12,31 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" /> <bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@@ -1,24 +1,25 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Castle.Core" version="5.1.1" targetFramework="net48" /> <package id="Castle.Core" version="5.2.1" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" /> <package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" /> <package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
<package id="Moq" version="4.20.72" targetFramework="net48" /> <package id="Moq" version="4.20.72" targetFramework="net48" />
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" /> <package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" /> <package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
<package id="System.Buffers" version="4.6.0" targetFramework="net48" /> <package id="System.Buffers" version="4.6.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" /> <package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" /> <package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
<package id="System.Memory" version="4.6.0" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" /> <package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" /> <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
</packages> </packages>

View File

@@ -8,7 +8,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Security.Cryptography.X509Certificates;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Applications;
@@ -43,42 +45,62 @@ namespace SafeExamBrowser.Applications
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication<IApplicationWindow> application) public FactoryResult TryCreate(WhitelistApplication settings, out IApplication<IApplicationWindow> application)
{ {
var name = $"'{settings.DisplayName}' ({settings.ExecutableName})"; var name = $"'{settings.DisplayName}' ({settings.ExecutableName})";
var result = FactoryResult.Error;
application = default; application = default;
try try
{ {
var success = TryFindApplication(settings, out var executablePath); var found = TryFindApplication(settings, out var executablePath);
var valid = found && VerifyApplication(executablePath, name, settings);
if (success) if (found && valid)
{ {
application = BuildApplication(executablePath, settings); application = InitializeApplication(executablePath, settings);
application.Initialize();
logger.Debug($"Successfully initialized application {name}.");
return FactoryResult.Success;
} }
logger.Error($"Could not find application {name}!"); result = DetermineResult(name, found, valid);
return FactoryResult.NotFound;
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error($"Unexpected error while trying to initialize application {name}!", e); logger.Error($"Unexpected error while trying to initialize application {name}!", e);
} }
return FactoryResult.Error; return result;
} }
private IApplication<IApplicationWindow> BuildApplication(string executablePath, WhitelistApplication settings) private FactoryResult DetermineResult(string name, bool found, bool valid)
{
var result = default(FactoryResult);
if (!found)
{
result = FactoryResult.NotFound;
logger.Error($"Could not find application {name}!");
}
else if (!valid)
{
result = FactoryResult.Invalid;
logger.Error($"The application {name} is not valid or has been manipulated!");
}
else
{
result = FactoryResult.Success;
logger.Debug($"Successfully initialized application {name}.");
}
return result;
}
private IApplication<IApplicationWindow> InitializeApplication(string executablePath, WhitelistApplication settings)
{ {
const int ONE_SECOND = 1000; const int ONE_SECOND = 1000;
var applicationLogger = logger.CloneFor(settings.DisplayName); var applicationLogger = logger.CloneFor(settings.DisplayName);
var application = new ExternalApplication(applicationMonitor, executablePath, applicationLogger, nativeMethods, processFactory, settings, ONE_SECOND); var application = new ExternalApplication(applicationMonitor, executablePath, applicationLogger, nativeMethods, processFactory, settings, ONE_SECOND);
application.Initialize();
return application; return application;
} }
@@ -143,5 +165,99 @@ namespace SafeExamBrowser.Applications
return default; return default;
} }
private bool VerifyApplication(string executablePath, string name, WhitelistApplication settings)
{
var valid = true;
valid &= VerifyName(executablePath, name, settings);
valid &= VerifyOriginalName(executablePath, name, settings);
valid &= VerifySignature(executablePath, name, settings);
return valid;
}
private bool VerifyName(string executablePath, string name, WhitelistApplication settings)
{
var valid = Path.GetFileName(executablePath).Equals(settings.ExecutableName, StringComparison.OrdinalIgnoreCase);
if (!valid)
{
logger.Warn($"The executable name of application {name} at '{executablePath}' does not match the configured value!");
}
return valid;
}
private bool VerifyOriginalName(string executablePath, string name, WhitelistApplication settings)
{
var ignoreOriginalName = string.IsNullOrWhiteSpace(settings.OriginalName);
var valid = ignoreOriginalName;
if (!ignoreOriginalName && TryLoadOriginalName(executablePath, out var originalName))
{
valid = originalName.Equals(settings.OriginalName, StringComparison.OrdinalIgnoreCase);
}
if (!valid)
{
logger.Warn($"The original name of application {name} at '{executablePath}' does not match the configured value!");
}
return valid;
}
private bool VerifySignature(string executablePath, string name, WhitelistApplication settings)
{
var ignoreSignature = string.IsNullOrWhiteSpace(settings.Signature);
var valid = ignoreSignature;
if (!ignoreSignature && TryLoadSignature(executablePath, out var signature))
{
valid = signature.Equals(settings.Signature, StringComparison.OrdinalIgnoreCase);
}
if (!valid)
{
logger.Warn($"The signature of application {name} at '{executablePath}' does not match the configured value!");
}
return valid;
}
private bool TryLoadOriginalName(string path, out string originalName)
{
originalName = default;
try
{
originalName = FileVersionInfo.GetVersionInfo(path).OriginalFilename;
}
catch (Exception e)
{
logger.Error($"Failed to load original name for '{path}'!", e);
}
return originalName != default;
}
private bool TryLoadSignature(string path, out string signature)
{
signature = default;
try
{
using (var certificate = X509Certificate.CreateFromSignedFile(path))
{
signature = certificate.GetCertHashString();
}
}
catch (Exception e)
{
logger.Error($"Failed to load signature for '{path}'!", e);
}
return signature != default;
}
} }
} }

View File

@@ -102,13 +102,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void MustNotAllowUnsupportedResult() public void MustNotAllowUnsupportedResult()
{ {
var rule = new Mock<IRule>(); var rule = new Mock<IRule>();
rule.SetupGet(r => r.Result).Returns((FilterResult) (-1)); rule.SetupGet(r => r.Result).Returns((FilterResult) (-1));
sut.Load(rule.Object);
Assert.ThrowsExactly<NotImplementedException>(() => sut.Load(rule.Object));
} }
} }
} }

View File

@@ -33,10 +33,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void MustNotAllowUnsupportedFilterType() public void MustNotAllowUnsupportedFilterType()
{ {
sut.CreateRule((FilterRuleType) (-1)); Assert.ThrowsExactly<NotImplementedException>(() => sut.CreateRule((FilterRuleType) (-1)));
} }
} }
} }

View File

@@ -51,17 +51,15 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAllowUndefinedExpression() public void MustNotAllowUndefinedExpression()
{ {
sut.Initialize(new FilterRuleSettings()); Assert.ThrowsExactly<ArgumentNullException>(() => sut.Initialize(new FilterRuleSettings()));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MustValidateExpression() public void MustValidateExpression()
{ {
sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" }); Assert.ThrowsExactly<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" }));
} }
} }
} }

View File

@@ -51,10 +51,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAllowUndefinedExpression() public void MustNotAllowUndefinedExpression()
{ {
sut.Initialize(new FilterRuleSettings()); Assert.ThrowsExactly<ArgumentNullException>(() => sut.Initialize(new FilterRuleSettings()));
} }
[TestMethod] [TestMethod]
@@ -73,7 +72,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
foreach (var expression in invalid) foreach (var expression in invalid)
{ {
Assert.ThrowsException<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = expression })); Assert.ThrowsExactly<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = expression }));
} }
} }

View File

@@ -12,6 +12,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using SafeExamBrowser.Browser.Contracts.Filters; using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Handlers; using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
@@ -41,6 +42,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
[TestInitialize] [TestInitialize]
public void Initialize() public void Initialize()
{ {
var integrations = new Integration[]
{
new GenericIntegration(new Mock<ILogger>().Object),
new EdxIntegration(new Mock<ILogger>().Object),
new MoodleIntegration(new Mock<ILogger>().Object)
};
appConfig = new AppConfig(); appConfig = new AppConfig();
filter = new Mock<IRequestFilter>(); filter = new Mock<IRequestFilter>();
keyGenerator = new Mock<IKeyGenerator>(); keyGenerator = new Mock<IKeyGenerator>();
@@ -48,7 +56,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
settings = new BrowserSettings(); settings = new BrowserSettings();
windowSettings = new WindowSettings(); windowSettings = new WindowSettings();
text = new Mock<IText>(); text = new Mock<IText>();
resourceHandler = new ResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, default, settings, windowSettings, text.Object); resourceHandler = new ResourceHandler(appConfig, filter.Object, integrations, keyGenerator.Object, logger.Object, default, settings, windowSettings, text.Object);
sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings); sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings);
} }
@@ -285,7 +293,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
private class TestableRequestHandler : RequestHandler 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) internal TestableRequestHandler(
AppConfig appConfig,
IRequestFilter filter,
ILogger logger,
ResourceHandler resourceHandler,
BrowserSettings settings,
WindowSettings windowSettings) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings)
{ {
} }

View File

@@ -7,6 +7,7 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Net.Mime; using System.Net.Mime;
using System.Threading; using System.Threading;
@@ -14,6 +15,7 @@ using CefSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using SafeExamBrowser.Browser.Contracts.Filters; using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
@@ -42,6 +44,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
[TestInitialize] [TestInitialize]
public void Initialize() public void Initialize()
{ {
var integrations = new Integration[]
{
new GenericIntegration(new Mock<ILogger>().Object),
new EdxIntegration(new Mock<ILogger>().Object),
new MoodleIntegration(new Mock<ILogger>().Object)
};
appConfig = new AppConfig(); appConfig = new AppConfig();
filter = new Mock<IRequestFilter>(); filter = new Mock<IRequestFilter>();
keyGenerator = new Mock<IKeyGenerator>(); keyGenerator = new Mock<IKeyGenerator>();
@@ -50,7 +59,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
windowSettings = new WindowSettings(); windowSettings = new WindowSettings();
text = new Mock<IText>(); text = new Mock<IText>();
sut = new TestableResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, SessionMode.Server, settings, windowSettings, text.Object); sut = new TestableResourceHandler(appConfig, filter.Object, integrations, keyGenerator.Object, logger.Object, SessionMode.Server, settings, windowSettings, text.Object);
} }
[TestMethod] [TestMethod]
@@ -325,12 +334,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
internal TestableResourceHandler( internal TestableResourceHandler(
AppConfig appConfig, AppConfig appConfig,
IRequestFilter filter, IRequestFilter filter,
IEnumerable<Integration> integrations,
IKeyGenerator keyGenerator, IKeyGenerator keyGenerator,
ILogger logger, ILogger logger,
SessionMode sessionMode, SessionMode sessionMode,
BrowserSettings settings, BrowserSettings settings,
WindowSettings windowSettings, WindowSettings windowSettings,
IText text) : base(appConfig, filter, keyGenerator, logger, sessionMode, settings, windowSettings, text) IText text) : base(appConfig, filter, integrations, keyGenerator, logger, sessionMode, settings, windowSettings, text)
{ {
} }

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" /> <Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" /> <Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" /> <Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" /> <Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -66,84 +66,88 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <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> <HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="CefSharp, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"> <Reference Include="CefSharp, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll</HintPath> <HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="CefSharp.Core, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"> <Reference Include="CefSharp.Core, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll</HintPath> <HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath> <HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> <Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath> <HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath> <HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath> <HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath> <HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime" /> <Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@@ -200,11 +204,8 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -212,18 +213,22 @@
<PropertyGroup> <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> <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> </PropertyGroup>
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props'))" /> <Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props'))" />
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props'))" /> <Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props'))" /> <Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets'))" /> <Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" /> <Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
</Target> </Target>
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" /> <Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
</Project> </Project>

View File

@@ -4,11 +4,11 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -16,11 +16,11 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="CefSharp" publicKeyToken="40c4b6fc221f4138" culture="neutral" /> <assemblyIdentity name="CefSharp" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-129.0.110.0" newVersion="129.0.110.0" /> <bindingRedirect oldVersion="0.0.0.0-138.0.170.0" newVersion="138.0.170.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="CefSharp.Core" publicKeyToken="40c4b6fc221f4138" culture="neutral" /> <assemblyIdentity name="CefSharp.Core" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-129.0.110.0" newVersion="129.0.110.0" /> <bindingRedirect oldVersion="0.0.0.0-138.0.170.0" newVersion="138.0.170.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -28,27 +28,27 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" /> <bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@@ -1,27 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Castle.Core" version="5.1.1" targetFramework="net48" /> <package id="Castle.Core" version="5.2.1" targetFramework="net48" />
<package id="CefSharp.Common" version="131.3.50" targetFramework="net48" /> <package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x64" version="131.3.5" targetFramework="net48" /> <package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x86" version="131.3.5" targetFramework="net48" /> <package id="chromiumembeddedframework.runtime.win-x86" version="139.0.28" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" /> <package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" /> <package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
<package id="Moq" version="4.20.72" targetFramework="net48" /> <package id="Moq" version="4.20.72" targetFramework="net48" />
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" /> <package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" /> <package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
<package id="System.Buffers" version="4.6.0" targetFramework="net48" /> <package id="System.Buffers" version="4.6.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" /> <package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" /> <package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
<package id="System.Memory" version="4.6.0" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" /> <package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" /> <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
</packages> </packages>

View File

@@ -18,6 +18,7 @@ using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events; using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Events; using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.Resources.Icons; using SafeExamBrowser.Core.Contracts.Resources.Icons;
@@ -119,6 +120,7 @@ namespace SafeExamBrowser.Browser
InitializeCookies(); InitializeCookies();
InitializeDownAndUploadDirectory(); InitializeDownAndUploadDirectory();
InitializeIntegrityKeys(); InitializeIntegrityKeys();
InitializePreferences();
logger.Info("Initialized browser."); logger.Info("Initialized browser.");
} }
@@ -167,6 +169,12 @@ namespace SafeExamBrowser.Browser
private void CreateNewWindow(PopupRequestedEventArgs args = default) private void CreateNewWindow(PopupRequestedEventArgs args = default)
{ {
var id = ++windowIdCounter; var id = ++windowIdCounter;
var integrations = new Integration[]
{
new GenericIntegration(logger.CloneFor($"{nameof(GenericIntegration)} #{id}")),
new EdxIntegration(logger.CloneFor($"{nameof(EdxIntegration)} #{id}")),
new MoodleIntegration(logger.CloneFor($"{nameof(MoodleIntegration)} #{id}"))
};
var isMainWindow = windows.Count == 0; var isMainWindow = windows.Count == 0;
var startUrl = GenerateStartUrl(); var startUrl = GenerateStartUrl();
var windowLogger = logger.CloneFor($"Browser Window #{id}"); var windowLogger = logger.CloneFor($"Browser Window #{id}");
@@ -176,6 +184,7 @@ namespace SafeExamBrowser.Browser
fileSystemDialog, fileSystemDialog,
hashAlgorithm, hashAlgorithm,
id, id,
integrations,
isMainWindow, isMainWindow,
keyGenerator, keyGenerator,
windowLogger, windowLogger,
@@ -197,7 +206,7 @@ namespace SafeExamBrowser.Browser
window.InitializeControl(); window.InitializeControl();
windows.Add(window); windows.Add(window);
if (args != default(PopupRequestedEventArgs)) if (args != default)
{ {
args.Window = window; args.Window = window;
} }
@@ -213,6 +222,7 @@ namespace SafeExamBrowser.Browser
private void DeleteCookies() private void DeleteCookies()
{ {
var callback = new TaskDeleteCookiesCallback(); var callback = new TaskDeleteCookiesCallback();
var cookieManager = Cef.GetGlobalCookieManager();
callback.Task.ContinueWith(task => callback.Task.ContinueWith(task =>
{ {
@@ -226,7 +236,7 @@ namespace SafeExamBrowser.Browser
} }
}); });
if (Cef.GetGlobalCookieManager().DeleteCookies(callback: callback)) if (cookieManager != default && cookieManager.DeleteCookies(callback: callback))
{ {
logger.Debug("Successfully initiated cookie deletion."); logger.Debug("Successfully initiated cookie deletion.");
} }
@@ -316,7 +326,6 @@ namespace SafeExamBrowser.Browser
cefSettings.AcceptLanguageList = CultureInfo.CurrentUICulture.Name; cefSettings.AcceptLanguageList = CultureInfo.CurrentUICulture.Name;
cefSettings.CachePath = appConfig.BrowserCachePath; cefSettings.CachePath = appConfig.BrowserCachePath;
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
cefSettings.LogFile = appConfig.BrowserLogFilePath; cefSettings.LogFile = appConfig.BrowserLogFilePath;
cefSettings.LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info); cefSettings.LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info);
cefSettings.PersistSessionCookies = !settings.DeleteCookiesOnStartup || !settings.DeleteCookiesOnShutdown; cefSettings.PersistSessionCookies = !settings.DeleteCookiesOnStartup || !settings.DeleteCookiesOnShutdown;
@@ -339,6 +348,7 @@ namespace SafeExamBrowser.Browser
cefSettings.CefCommandLineArgs.Add("enable-media-stream"); cefSettings.CefCommandLineArgs.Add("enable-media-stream");
cefSettings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing"); cefSettings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing");
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
cefSettings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream"); cefSettings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream");
InitializeProxySettings(cefSettings); InitializeProxySettings(cefSettings);
@@ -417,6 +427,18 @@ namespace SafeExamBrowser.Browser
} }
} }
private void InitializePreferences()
{
Cef.UIThreadTaskFactory.StartNew(() =>
{
using (var requestContext = Cef.GetGlobalRequestContext())
{
requestContext.SetPreference("autofill.credit_card_enabled", false, out _);
requestContext.SetPreference("autofill.profile_enabled", false, out _);
}
});
}
private void InitializeProxySettings(CefSettings cefSettings) private void InitializeProxySettings(CefSettings cefSettings)
{ {
if (settings.Proxy.Policy == ProxyPolicy.Custom) if (settings.Proxy.Policy == ProxyPolicy.Custom)
@@ -506,6 +528,7 @@ namespace SafeExamBrowser.Browser
private void Window_ResetRequested() private void Window_ResetRequested()
{ {
logger.Info("Attempting to reset browser..."); logger.Info("Attempting to reset browser...");
AwaitReady(); AwaitReady();
foreach (var window in windows) foreach (var window in windows)

View File

@@ -23,6 +23,7 @@ namespace SafeExamBrowser.Browser
{ {
private readonly Clipboard clipboard; private readonly Clipboard clipboard;
private readonly ICefSharpControl control; private readonly ICefSharpControl control;
private readonly IContextMenuHandler contextMenuHandler;
private readonly IDialogHandler dialogHandler; private readonly IDialogHandler dialogHandler;
private readonly IDisplayHandler displayHandler; private readonly IDisplayHandler displayHandler;
private readonly IDownloadHandler downloadHandler; private readonly IDownloadHandler downloadHandler;
@@ -47,6 +48,7 @@ namespace SafeExamBrowser.Browser
public BrowserControl( public BrowserControl(
Clipboard clipboard, Clipboard clipboard,
ICefSharpControl control, ICefSharpControl control,
IContextMenuHandler contextMenuHandler,
IDialogHandler dialogHandler, IDialogHandler dialogHandler,
IDisplayHandler displayHandler, IDisplayHandler displayHandler,
IDownloadHandler downloadHandler, IDownloadHandler downloadHandler,
@@ -58,8 +60,9 @@ namespace SafeExamBrowser.Browser
IRenderProcessMessageHandler renderProcessMessageHandler, IRenderProcessMessageHandler renderProcessMessageHandler,
IRequestHandler requestHandler) IRequestHandler requestHandler)
{ {
this.control = control;
this.clipboard = clipboard; this.clipboard = clipboard;
this.control = control;
this.contextMenuHandler = contextMenuHandler;
this.dialogHandler = dialogHandler; this.dialogHandler = dialogHandler;
this.displayHandler = displayHandler; this.displayHandler = displayHandler;
this.downloadHandler = downloadHandler; this.downloadHandler = downloadHandler;
@@ -89,31 +92,20 @@ namespace SafeExamBrowser.Browser
{ {
control.BrowserCore.EvaluateScriptAsync(code).ContinueWith(t => control.BrowserCore.EvaluateScriptAsync(code).ContinueWith(t =>
{ {
callback?.Invoke(new JavaScriptResult callback?.Invoke(new JavaScriptResult { Message = t.Result.Message, Result = t.Result.Result, Success = t.Result.Success });
{
Message = t.Result.Message,
Result = t.Result.Result,
Success = t.Result.Success
});
}); });
} }
else else
{ {
Task.Run(() => callback?.Invoke(new JavaScriptResult Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = "Could not execute JavaScript in main frame!", Success = false }));
{
Message = "JavaScript can't be executed in main frame!",
Success = false
}));
} }
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error($"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'!", e); var message = "Failed to execute JavaScript in main frame!";
Task.Run(() => callback?.Invoke(new JavaScriptResult
{ logger.Error(message, e);
Message = $"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'! Reason: {e.Message}", Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = $"{message} Reason: {e.Message}", Success = false }));
Success = false
}));
} }
} }
@@ -129,10 +121,13 @@ namespace SafeExamBrowser.Browser
control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address); control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address);
control.AuthCredentialsRequired += (w, b, o, i, h, p, r, s, c, a) => a.Value = requestHandler.GetAuthCredentials(w, b, o, i, h, p, r, s, c); control.AuthCredentialsRequired += (w, b, o, i, h, p, r, s, c, a) => a.Value = requestHandler.GetAuthCredentials(w, b, o, i, h, p, r, s, c);
control.BeforeBrowse += (w, b, f, r, u, i, a) => a.Value = requestHandler.OnBeforeBrowse(w, b, f, r, u, i); control.BeforeBrowse += (w, b, f, r, u, i, a) => a.Value = requestHandler.OnBeforeBrowse(w, b, f, r, u, i);
control.BeforeContextMenu += (w, b, f, p, m) => contextMenuHandler.OnBeforeContextMenu(w, b, f, p, m);
control.BeforeDownload += (w, b, d, c, a) => a.Value = a.Value = downloadHandler.OnBeforeDownload(w, b, d, c); control.BeforeDownload += (w, b, d, c, a) => a.Value = a.Value = downloadHandler.OnBeforeDownload(w, b, d, c);
control.BeforeUnloadDialog += (w, b, m, r, c, a) => a.Value = javaScriptDialogHandler.OnBeforeUnloadDialog(w, b, m, r, c); control.BeforeUnloadDialog += (w, b, m, r, c, a) => a.Value = javaScriptDialogHandler.OnBeforeUnloadDialog(w, b, m, r, c);
control.CanDownload += (w, b, u, r, a) => a.Value = downloadHandler.CanDownload(w, b, u, r); control.CanDownload += (w, b, u, r, a) => a.Value = downloadHandler.CanDownload(w, b, u, r);
control.ContextCreated += (w, b, f) => renderProcessMessageHandler.OnContextCreated(w, b, f); control.ContextCreated += (w, b, f) => renderProcessMessageHandler.OnContextCreated(w, b, f);
control.ContextMenuCommand += (w, b, f, p, c, e, a) => a.Value = contextMenuHandler.OnContextMenuCommand(w, b, f, p, c, e);
control.ContextMenuDismissed += (w, b, f) => contextMenuHandler.OnContextMenuDismissed(w, b, f);
control.ContextReleased += (w, b, f) => renderProcessMessageHandler.OnContextReleased(w, b, f); control.ContextReleased += (w, b, f) => renderProcessMessageHandler.OnContextReleased(w, b, f);
control.DialogClosed += (w, b) => javaScriptDialogHandler.OnDialogClosed(w, b); control.DialogClosed += (w, b) => javaScriptDialogHandler.OnDialogClosed(w, b);
control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c); control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c);
@@ -152,6 +147,7 @@ namespace SafeExamBrowser.Browser
control.PreKeyEvent += (IWebBrowser w, IBrowser b, KeyType t, int k, int n, CefEventFlags m, bool i, ref bool s, GenericEventArgs a) => a.Value = keyboardHandler.OnPreKeyEvent(w, b, t, k, n, m, i, ref s); control.PreKeyEvent += (IWebBrowser w, IBrowser b, KeyType t, int k, int n, CefEventFlags m, bool i, ref bool s, GenericEventArgs a) => a.Value = keyboardHandler.OnPreKeyEvent(w, b, t, k, n, m, i, ref s);
control.ResetDialogState += (w, b) => javaScriptDialogHandler.OnResetDialogState(w, b); control.ResetDialogState += (w, b) => javaScriptDialogHandler.OnResetDialogState(w, b);
control.ResourceRequestHandlerRequired += (IWebBrowser w, IBrowser b, IFrame f, IRequest r, bool n, bool d, string i, ref bool h, ResourceRequestEventArgs a) => a.Handler = requestHandler.GetResourceRequestHandler(w, b, f, r, n, d, i, ref h); control.ResourceRequestHandlerRequired += (IWebBrowser w, IBrowser b, IFrame f, IRequest r, bool n, bool d, string i, ref bool h, ResourceRequestEventArgs a) => a.Handler = requestHandler.GetResourceRequestHandler(w, b, f, r, n, d, i, ref h);
control.RunContextMenu += (w, b, f, p, m, c, a) => a.Value = contextMenuHandler.RunContextMenu(w, b, f, p, m, c);
control.SetFocus += (w, b, s, a) => a.Value = focusHandler.OnSetFocus(w, b, s); control.SetFocus += (w, b, s, a) => a.Value = focusHandler.OnSetFocus(w, b, s);
control.TakeFocus += (w, b, n) => focusHandler.OnTakeFocus(w, b, n); control.TakeFocus += (w, b, n) => focusHandler.OnTakeFocus(w, b, n);
control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title); control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title);
@@ -193,9 +189,21 @@ namespace SafeExamBrowser.Browser
control.BrowserCore.SetZoomLevel(level); control.BrowserCore.SetZoomLevel(level);
} }
private void Clipboard_Changed(long id) private void Clipboard_Changed(string id)
{ {
ExecuteJavaScript($"SafeExamBrowser.clipboard.update({id}, '{clipboard.Content}');"); try
{
var script = $"SafeExamBrowser.clipboard.update('{id}', '{clipboard.Content}');";
foreach (var frame in control.BrowserCore?.GetAllFrames() ?? Enumerable.Empty<IFrame>())
{
frame.EvaluateScriptAsync(script);
}
}
catch (Exception e)
{
logger.Error($"Failed to update JavaScript clipboard!", e);
}
} }
private void Control_IsBrowserInitializedChanged(object sender, EventArgs e) private void Control_IsBrowserInitializedChanged(object sender, EventArgs e)
@@ -208,7 +216,7 @@ namespace SafeExamBrowser.Browser
private void WebBrowser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e) private void WebBrowser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
{ {
clipboard.Process(e); clipboard.Update(e);
} }
} }
} }

View File

@@ -19,6 +19,7 @@ using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Events; using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Filters; using SafeExamBrowser.Browser.Filters;
using SafeExamBrowser.Browser.Handlers; using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Browser.Wrapper; using SafeExamBrowser.Browser.Wrapper;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
@@ -52,6 +53,7 @@ namespace SafeExamBrowser.Browser
private readonly IFileSystemDialog fileSystemDialog; private readonly IFileSystemDialog fileSystemDialog;
private readonly IHashAlgorithm hashAlgorithm; private readonly IHashAlgorithm hashAlgorithm;
private readonly HttpClient httpClient; private readonly HttpClient httpClient;
private readonly IEnumerable<Integration> integrations;
private readonly IKeyGenerator keyGenerator; private readonly IKeyGenerator keyGenerator;
private readonly IModuleLogger logger; private readonly IModuleLogger logger;
private readonly IMessageBox messageBox; private readonly IMessageBox messageBox;
@@ -97,6 +99,7 @@ namespace SafeExamBrowser.Browser
IFileSystemDialog fileSystemDialog, IFileSystemDialog fileSystemDialog,
IHashAlgorithm hashAlgorithm, IHashAlgorithm hashAlgorithm,
int id, int id,
IEnumerable<Integration> integrations,
bool isMainWindow, bool isMainWindow,
IKeyGenerator keyGenerator, IKeyGenerator keyGenerator,
IModuleLogger logger, IModuleLogger logger,
@@ -113,6 +116,7 @@ namespace SafeExamBrowser.Browser
this.hashAlgorithm = hashAlgorithm; this.hashAlgorithm = hashAlgorithm;
this.httpClient = new HttpClient(); this.httpClient = new HttpClient();
this.Id = id; this.Id = id;
this.integrations = integrations;
this.IsMainWindow = isMainWindow; this.IsMainWindow = isMainWindow;
this.keyGenerator = keyGenerator; this.keyGenerator = keyGenerator;
this.logger = logger; this.logger = logger;
@@ -153,6 +157,7 @@ namespace SafeExamBrowser.Browser
{ {
var cefSharpControl = default(ICefSharpControl); var cefSharpControl = default(ICefSharpControl);
var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} #{Id}"); var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} #{Id}");
var contextMenuHandler = new ContextMenuHandler();
var dialogHandler = new DialogHandler(); var dialogHandler = new DialogHandler();
var displayHandler = new DisplayHandler(); var displayHandler = new DisplayHandler();
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}"); var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
@@ -164,7 +169,7 @@ namespace SafeExamBrowser.Browser
var renderHandler = new RenderProcessMessageHandler(appConfig, clipboard, keyGenerator, settings, text); var renderHandler = new RenderProcessMessageHandler(appConfig, clipboard, keyGenerator, settings, text);
var requestFilter = new RequestFilter(); var requestFilter = new RequestFilter();
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}"); var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}");
var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, sessionMode, settings, WindowSettings, text); var resourceHandler = new ResourceHandler(appConfig, requestFilter, integrations, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings); var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings);
Icon = new BrowserIconResource(); Icon = new BrowserIconResource();
@@ -202,6 +207,7 @@ namespace SafeExamBrowser.Browser
Control = new BrowserControl( Control = new BrowserControl(
clipboard, clipboard,
cefSharpControl, cefSharpControl,
contextMenuHandler,
dialogHandler, dialogHandler,
displayHandler, displayHandler,
downloadHandler, downloadHandler,
@@ -218,12 +224,13 @@ namespace SafeExamBrowser.Browser
Control.TitleChanged += Control_TitleChanged; Control.TitleChanged += Control_TitleChanged;
Control.Initialize(); Control.Initialize();
logger.Debug("Initialized browser control."); logger.Debug("Initialized browser control.");
} }
internal void InitializeWindow() internal void InitializeWindow()
{ {
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, this.logger); window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, logger);
window.AddressChanged += Window_AddressChanged; window.AddressChanged += Window_AddressChanged;
window.BackwardNavigationRequested += Window_BackwardNavigationRequested; window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
window.Closed += Window_Closed; window.Closed += Window_Closed;
@@ -242,6 +249,7 @@ namespace SafeExamBrowser.Browser
window.BringToForeground(); window.BringToForeground();
Handle = window.Handle; Handle = window.Handle;
InitiateCookieTraversal();
logger.Debug("Initialized browser window."); logger.Debug("Initialized browser window.");
} }
@@ -256,6 +264,22 @@ namespace SafeExamBrowser.Browser
.Build(); .Build();
} }
private void InitiateCookieTraversal()
{
var visitor = new CookieVisitor(integrations);
visitor.UserIdentifierDetected += (id) => UserIdentifierDetected?.Invoke(id);
if (Cef.GetGlobalCookieManager().VisitAllCookies(visitor))
{
logger.Debug("Successfully initiated cookie traversal.");
}
else
{
logger.Warn("Failed to initiate cookie traversal!");
}
}
private void InitializeRequestFilter(IRequestFilter requestFilter) private void InitializeRequestFilter(IRequestFilter requestFilter)
{ {
if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests) if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests)
@@ -297,8 +321,6 @@ namespace SafeExamBrowser.Browser
window.UpdateTitle(address); window.UpdateTitle(address);
TitleChanged?.Invoke(address); TitleChanged?.Invoke(address);
} }
AutoFind();
} }
private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url) private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url)
@@ -834,14 +856,6 @@ namespace SafeExamBrowser.Browser
} }
} }
private void AutoFind()
{
if (settings.AllowFind && !string.IsNullOrEmpty(findParameters.term) && !CLEAR_FIND_TERM.Equals(findParameters.term, StringComparison.OrdinalIgnoreCase))
{
Control.Find(findParameters.term, findParameters.isInitial, findParameters.caseSensitive, findParameters.forward);
}
}
private double CalculateZoomPercentage() private double CalculateZoomPercentage()
{ {
return (zoomLevel * 25.0) + 100.0; return (zoomLevel * 25.0) + 100.0;

View File

@@ -30,7 +30,7 @@ namespace SafeExamBrowser.Browser
this.settings = settings; this.settings = settings;
} }
internal void Process(JavascriptMessageReceivedEventArgs message) internal void Update(JavascriptMessageReceivedEventArgs message)
{ {
if (settings.UseIsolatedClipboard) if (settings.UseIsolatedClipboard)
{ {
@@ -65,7 +65,7 @@ namespace SafeExamBrowser.Browser
private class Data private class Data
{ {
public string Content { get; set; } public string Content { get; set; }
public long Id { get; set; } public string Id { get; set; }
public string Type { get; set; } public string Type { get; set; }
} }
} }

View File

@@ -5,11 +5,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Original code taken and slightly adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215. * Original code taken and adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215.
*/ */
if (typeof SafeExamBrowser.clipboard === 'undefined') {
SafeExamBrowser.clipboard = { SafeExamBrowser.clipboard = {
id: Math.round((Date.now() + Math.random()) * 1000), id: crypto.randomUUID(),
ranges: [], ranges: [],
text: "", text: "",
@@ -35,8 +36,10 @@ SafeExamBrowser.clipboard = {
} }
} }
} }
}
function copySelectedData(e) { if (typeof copySelection === 'undefined') {
function copySelection(e) {
if (e.target.contentEditable && e.target.setRangeText) { if (e.target.contentEditable && e.target.setRangeText) {
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd); SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
SafeExamBrowser.clipboard.ranges = []; SafeExamBrowser.clipboard.ranges = [];
@@ -52,8 +55,10 @@ function copySelectedData(e) {
SafeExamBrowser.clipboard.text = text; SafeExamBrowser.clipboard.text = text;
} }
} }
}
function cutSelectedData(e) { if (typeof cutSelection === 'undefined') {
function cutSelection(e) {
if (e.target.contentEditable && e.target.setRangeText) { if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select'); e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
} else { } else {
@@ -78,16 +83,18 @@ function cutSelectedData(e) {
} }
} }
} }
}
function pasteSelectedData(e) { if (typeof pasteContent === 'undefined') {
function pasteContent(e) {
if (e.target.contentEditable && e.target.setRangeText) { if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select'); e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end'); e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
} else { } else {
var w = e.target.ownerDocument.defaultView; var targetWindow = e.target.ownerDocument.defaultView;
var designMode = e.target.ownerDocument.designMode; var designMode = e.target.ownerDocument.designMode;
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]'); var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
var selection = w.getSelection(); var selection = targetWindow.getSelection();
for (var i = 0; i < selection.rangeCount; i++) { for (var i = 0; i < selection.rangeCount; i++) {
var r = selection.getRangeAt(i); var r = selection.getRangeAt(i);
@@ -106,11 +113,11 @@ function pasteSelectedData(e) {
} }
if (designMode === 'on') { if (designMode === 'on') {
var range = w.getSelection().getRangeAt(0); var range = targetWindow.getSelection().getRangeAt(0);
if (SafeExamBrowser.clipboard.ranges.length > 0) { if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => { SafeExamBrowser.clipboard.ranges.map(r => {
range = w.getSelection().getRangeAt(0); range = targetWindow.getSelection().getRangeAt(0);
range.collapse(); range.collapse();
const newNode = r.cloneNode(true); const newNode = r.cloneNode(true);
range.insertNode(newNode); range.insertNode(newNode);
@@ -118,27 +125,29 @@ function pasteSelectedData(e) {
}); });
} else { } else {
range.collapse(); range.collapse();
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text)); range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse(); range.collapse();
} }
} else { } else {
if (contentEditables.length) { if (contentEditables.length) {
contentEditables.forEach(node => { contentEditables.forEach(node => {
var range = w.getSelection().getRangeAt(0); var range = targetWindow.getSelection().getRangeAt(0);
if (node.contains(range.commonAncestorContainer)) { if (node.contains(range.commonAncestorContainer)) {
if (SafeExamBrowser.clipboard.ranges.length > 0) { if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => { SafeExamBrowser.clipboard.ranges.map(r => {
range = w.getSelection().getRangeAt(0); range = targetWindow.getSelection().getRangeAt(0);
range.collapse(); range.collapse();
const newNode = r.cloneNode(true); const newNode = r.cloneNode(true);
range.insertNode(newNode); range.insertNode(newNode);
range.collapse(); range.collapse();
}); });
} else { } else {
range = w.getSelection().getRangeAt(0); range = targetWindow.getSelection().getRangeAt(0);
range.collapse(); range.collapse();
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text)); range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse(); range.collapse();
} }
} }
@@ -147,49 +156,56 @@ function pasteSelectedData(e) {
} }
} }
} }
}
if (typeof onCopy === 'undefined') {
function onCopy(e) { function onCopy(e) {
try {
SafeExamBrowser.clipboard.clear(); SafeExamBrowser.clipboard.clear();
try { copySelection(e);
copySelectedData(e);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() }); CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally { } finally {
e.preventDefault(); e.preventDefault();
e.returnValue = false;
}
return false;
}
function onCut(e) {
SafeExamBrowser.clipboard.clear();
try {
copySelectedData(e);
cutSelectedData(e);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
e.returnValue = false;
}
return false;
}
function onPaste(e) {
try {
pasteSelectedData(e);
} finally {
e.preventDefault();
e.returnValue = false;
} }
return false; return false;
} }
window.document.addEventListener("copy", onCopy, true); window.document.addEventListener("copy", onCopy, true);
}
if (typeof onCut === 'undefined') {
function onCut(e) {
try {
SafeExamBrowser.clipboard.clear();
copySelection(e);
cutSelection(e);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
}
return false;
}
window.document.addEventListener("cut", onCut, true); window.document.addEventListener("cut", onCut, true);
}
if (typeof onPaste === 'undefined') {
function onPaste(e) {
try {
pasteContent(e);
} finally {
e.preventDefault();
}
return false;
}
window.document.addEventListener("paste", onPaste, true); window.document.addEventListener("paste", onPaste, true);
}

View File

@@ -8,5 +8,5 @@
namespace SafeExamBrowser.Browser.Events namespace SafeExamBrowser.Browser.Events
{ {
internal delegate void ClipboardChangedEventHandler(long id); internal delegate void ClipboardChangedEventHandler(string id);
} }

View File

@@ -6,12 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Core.Contracts.OperationModel.Events; namespace SafeExamBrowser.Browser.Events
namespace SafeExamBrowser.Runtime.Operations.Events
{ {
internal class ConfigurationCompletedEventArgs : ActionRequiredEventArgs internal class JavaScriptDialogRequestedEventArgs
{ {
public bool AbortStartup { get; set; } internal bool Success { get; set; }
internal JavaScriptDialogType Type { get; set; }
} }
} }

View File

@@ -0,0 +1,12 @@
/*
* Copyright (c) 2025 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/.
*/
namespace SafeExamBrowser.Browser.Events
{
internal delegate void JavaScriptDialogRequestedEventHandler(JavaScriptDialogRequestedEventArgs args);
}

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) 2025 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/.
*/
namespace SafeExamBrowser.Browser.Events
{
internal enum JavaScriptDialogType
{
LeavePage,
Reload
}
}

View File

@@ -12,21 +12,21 @@ namespace SafeExamBrowser.Browser.Handlers
{ {
internal class ContextMenuHandler : IContextMenuHandler internal class ContextMenuHandler : IContextMenuHandler
{ {
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{ {
model.Clear(); model.Clear();
} }
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
{ {
return false; return false;
} }
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{ {
} }
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{ {
return false; return false;
} }

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 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.Tasks;
using CefSharp;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Integrations;
namespace SafeExamBrowser.Browser.Handlers
{
internal class CookieVisitor : ICookieVisitor
{
private readonly IEnumerable<Integration> integrations;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
internal CookieVisitor(IEnumerable<Integration> integrations)
{
this.integrations = integrations;
}
public void Dispose()
{
}
public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie)
{
foreach (var integration in integrations)
{
var success = integration.TrySearchUserIdentifier(cookie, out var userIdentifier);
if (success)
{
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
break;
}
}
return true;
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025 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 CefSharp;
using CefSharp.Enums;
namespace SafeExamBrowser.Browser.Handlers
{
internal class DragHandler : IDragHandler
{
public bool OnDragEnter(IWebBrowser chromiumWebBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask)
{
return !(dragData.IsFragment && mask.HasFlag(DragOperationsMask.Move));
}
public void OnDraggableRegionsChanged(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions)
{
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Handlers
{
internal class FocusHandler : IFocusHandler
{
internal FocusHandler()
{
}
public void OnGotFocus(IWebBrowser webBrowser, IBrowser browser)
{
}
public bool OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source)
{
return false;
}
public void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next)
{
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025 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.Threading.Tasks;
using CefSharp;
using SafeExamBrowser.Browser.Events;
namespace SafeExamBrowser.Browser.Handlers
{
internal class JavaScriptDialogHandler : IJsDialogHandler
{
internal event JavaScriptDialogRequestedEventHandler DialogRequested;
public bool OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback)
{
var args = new JavaScriptDialogRequestedEventArgs
{
Type = isReload ? JavaScriptDialogType.Reload : JavaScriptDialogType.LeavePage
};
Task.Run(() =>
{
DialogRequested?.Invoke(args);
using (callback)
{
callback.Continue(args.Success);
}
});
return true;
}
public void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser)
{
}
public bool OnJSDialog(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress)
{
return false;
}
public void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser)
{
}
}
}

View File

@@ -7,18 +7,16 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.Mime; using System.Net.Mime;
using System.Threading.Tasks; using System.Threading.Tasks;
using CefSharp; using CefSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SafeExamBrowser.Browser.Content; using SafeExamBrowser.Browser.Content;
using SafeExamBrowser.Browser.Contracts.Events; using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Contracts.Filters; using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
@@ -36,6 +34,7 @@ namespace SafeExamBrowser.Browser.Handlers
private readonly AppConfig appConfig; private readonly AppConfig appConfig;
private readonly ContentLoader contentLoader; private readonly ContentLoader contentLoader;
private readonly IRequestFilter filter; private readonly IRequestFilter filter;
private readonly IEnumerable<Integration> integrations;
private readonly IKeyGenerator keyGenerator; private readonly IKeyGenerator keyGenerator;
private readonly ILogger logger; private readonly ILogger logger;
private readonly SessionMode sessionMode; private readonly SessionMode sessionMode;
@@ -44,13 +43,13 @@ namespace SafeExamBrowser.Browser.Handlers
private IResourceHandler contentHandler; private IResourceHandler contentHandler;
private IResourceHandler pageHandler; private IResourceHandler pageHandler;
private string userIdentifier;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected; internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
internal ResourceHandler( internal ResourceHandler(
AppConfig appConfig, AppConfig appConfig,
IRequestFilter filter, IRequestFilter filter,
IEnumerable<Integration> integrations,
IKeyGenerator keyGenerator, IKeyGenerator keyGenerator,
ILogger logger, ILogger logger,
SessionMode sessionMode, SessionMode sessionMode,
@@ -60,6 +59,7 @@ namespace SafeExamBrowser.Browser.Handlers
{ {
this.appConfig = appConfig; this.appConfig = appConfig;
this.filter = filter; this.filter = filter;
this.integrations = integrations;
this.contentLoader = new ContentLoader(text); this.contentLoader = new ContentLoader(text);
this.keyGenerator = keyGenerator; this.keyGenerator = keyGenerator;
this.logger = logger; this.logger = logger;
@@ -235,217 +235,17 @@ namespace SafeExamBrowser.Browser.Handlers
private void SearchUserIdentifier(IRequest request, IResponse response) private void SearchUserIdentifier(IRequest request, IResponse response)
{ {
var success = TrySearchGenericUserIdentifier(response); foreach (var integration in integrations)
if (!success)
{ {
success = TrySearchEdxUserIdentifier(response); var success = integration.TrySearchUserIdentifier(request, response, out var userIdentifier);
}
if (!success) if (success)
{ {
TrySearchMoodleUserIdentifier(request, response);
}
}
private bool TrySearchGenericUserIdentifier(IResponse response)
{
var ids = response.Headers.GetValues("X-LMS-USER-ID");
var success = false;
if (ids != default(string[]))
{
var userId = ids.FirstOrDefault();
if (userId != default && userIdentifier != userId)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier)); Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Generic LMS user identifier detected.");
success = true;
}
}
return success; break;
}
private bool TrySearchEdxUserIdentifier(IResponse response)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[]))
{
try
{
var userInfo = cookies.FirstOrDefault(c => c.Contains("edx-user-info"));
if (userInfo != default)
{
var start = userInfo.IndexOf("=") + 1;
var end = userInfo.IndexOf("; expires");
var cookie = userInfo.Substring(start, end - start);
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
var userName = json["username"].Value<string>();
if (userIdentifier != userName)
{
userIdentifier = userName;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("EdX user identifier detected.");
success = true;
} }
} }
} }
catch (Exception e)
{
logger.Error("Failed to parse edX user identifier!", e);
}
}
return success;
}
private bool TrySearchMoodleUserIdentifier(IRequest request, IResponse response)
{
var success = TrySearchMoodleUserIdentifierByLocation(response);
if (!success)
{
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Plugin, request, response);
}
if (!success)
{
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Theme, request, response);
}
return success;
}
private bool TrySearchMoodleUserIdentifierByLocation(IResponse response)
{
var locations = response.Headers.GetValues("Location");
if (locations != default(string[]))
{
try
{
var location = locations.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
if (location != default)
{
var userId = location.Substring(location.IndexOf("=") + 1);
if (userIdentifier != userId)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Moodle user identifier detected by location.");
}
return true;
}
}
catch (Exception e)
{
logger.Error("Failed to parse Moodle user identifier by location!", e);
}
}
return false;
}
private bool TrySearchMoodleUserIdentifierByRequest(MoodleRequestType type, IRequest request, IResponse response)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[]))
{
var session = cookies.FirstOrDefault(c => c.Contains("MoodleSession"));
if (session != default)
{
var userId = ExecuteMoodleUserIdentifierRequest(request.Url, session, type);
if (int.TryParse(userId, out var id) && id > 0 && userIdentifier != userId)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info($"Moodle user identifier detected by request ({type}).");
success = true;
}
}
}
return success;
}
private string ExecuteMoodleUserIdentifierRequest(string requestUrl, string session, MoodleRequestType type)
{
var userId = default(string);
try
{
Task.Run(async () =>
{
try
{
var endpointUrl = default(string);
var start = session.IndexOf("=") + 1;
var end = session.IndexOf(";");
var name = session.Substring(0, start - 1);
var value = session.Substring(start, end - start);
var uri = new Uri(requestUrl);
if (type == MoodleRequestType.Plugin)
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/mod/quiz/accessrule/sebserver/classes/external/user.php";
}
else
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/theme/boost_ethz/sebuser.php";
}
var message = new HttpRequestMessage(HttpMethod.Get, endpointUrl);
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"{name}={value}");
var result = await client.SendAsync(message);
if (result.IsSuccessStatusCode)
{
userId = await result.Content.ReadAsStringAsync();
}
else if (result.StatusCode != HttpStatusCode.NotFound)
{
logger.Error($"Failed to retrieve Moodle user identifier by request ({type})! Response: {(int) result.StatusCode} {result.ReasonPhrase}");
}
}
}
catch (Exception e)
{
logger.Error($"Failed to parse Moodle user identifier by request ({type})!", e);
}
}).GetAwaiter().GetResult();
}
catch (Exception e)
{
logger.Error($"Failed to execute Moodle user identifier request ({type})!", e);
}
return userId;
}
private enum MoodleRequestType
{
Plugin,
Theme
}
} }
} }

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2025 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.Linq;
using CefSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Browser.Integrations
{
internal class EdxIntegration : Integration
{
private readonly ILogger logger;
public EdxIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
userIdentifier = default;
return false;
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var userInfo = cookies?.FirstOrDefault(c => c.Contains("edx-user-info"));
userIdentifier = default;
if (TryParseCookie(userInfo, out var id) && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by session cookie on response.");
}
return userIdentifier != default;
}
private bool TryParseCookie(string userInfo, out string userIdentifier)
{
userIdentifier = default;
try
{
if (userInfo != default)
{
var start = userInfo.IndexOf("=") + 1;
var end = userInfo.IndexOf("; expires");
var cookie = userInfo.Substring(start, end - start);
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
userIdentifier = json["username"].Value<string>();
}
}
catch (Exception e)
{
logger.Error("Failed to parse user identifier!", e);
}
return userIdentifier != default;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 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.Linq;
using CefSharp;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Browser.Integrations
{
internal class GenericIntegration : Integration
{
private readonly ILogger logger;
public GenericIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
userIdentifier = default;
return false;
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var ids = response.Headers.GetValues("X-LMS-USER-ID");
var id = ids?.FirstOrDefault();
userIdentifier = default;
if (HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by header of response.");
}
return userIdentifier != default;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Integrations
{
internal abstract class Integration
{
private static string activeUserIdentifier;
internal abstract bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier);
internal abstract bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier);
protected bool HasChanged(string userIdentifier)
{
var current = activeUserIdentifier;
if (userIdentifier != default && activeUserIdentifier != userIdentifier)
{
activeUserIdentifier = userIdentifier;
}
return activeUserIdentifier != current;
}
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2025 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.Linq;
using System.Net;
using System.Net.Http;
using CefSharp;
using SafeExamBrowser.Logging.Contracts;
using Cookie = CefSharp.Cookie;
namespace SafeExamBrowser.Browser.Integrations
{
internal class MoodleIntegration : Integration
{
private const string PLUGIN_PATH = "/mod/quiz/accessrule/sebserver/classes/external/user.php";
private const string SESSION_COOKIE_NAME = "MoodleSession";
private const string THEME_PATH = "/theme/boost_ethz/sebuser.php";
private readonly ILogger logger;
public MoodleIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
return TrySearchByCookie(cookie, out userIdentifier);
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var success = TrySearchByLocation(response, out userIdentifier);
if (!success)
{
success = TrySearchByRequests(request, response, out userIdentifier);
}
return success;
}
private bool TrySearchByCookie(Cookie cookie, out string userIdentifier)
{
var id = default(string);
var type = default(RequestType);
var isSession = cookie.Name.Contains(SESSION_COOKIE_NAME);
var url = $"{(cookie.Secure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{cookie.Domain}{cookie.Path}";
var hasId = isSession && TryExecuteRequests(url, (cookie.Name, cookie.Value), out type, out id);
userIdentifier = default;
if (hasId && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by request on cookie traversal ({type}).");
}
return userIdentifier != default;
}
private bool TrySearchByLocation(IResponse response, out string userIdentifier)
{
var locations = response.Headers.GetValues("Location");
var location = locations?.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
userIdentifier = default;
if (TryParseLocation(location, out var id) && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by location header of response.");
}
return userIdentifier != default;
}
private bool TrySearchByRequests(IRequest request, IResponse response, out string userIdentifier)
{
var id = default(string);
var type = default(RequestType);
var cookies = response.Headers.GetValues("Set-Cookie");
var session = cookies?.FirstOrDefault(c => c.Contains(SESSION_COOKIE_NAME));
var hasCookie = TryParseCookie(session, out var cookie);
var hasId = hasCookie && TryExecuteRequests(request.Url, cookie, out type, out id);
userIdentifier = default;
if (hasId && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by request on response ({type}).");
}
return userIdentifier != default;
}
private bool TryExecuteRequests(string originUrl, (string name, string value) session, out RequestType requestType, out string userId)
{
var order = new[] { RequestType.Plugin, RequestType.Theme };
requestType = default;
userId = default;
foreach (var type in order)
{
try
{
var url = BuildUrl(originUrl, type);
using (var response = ExecuteRequest(url, session))
{
if (TryParseResponse(response, type, out var id))
{
requestType = type;
userId = id;
break;
}
}
}
catch (Exception e)
{
logger.Error($"Failed to execute user identifier request ({type})!", e);
}
}
return userId != default;
}
private string BuildUrl(string originUrl, RequestType type)
{
var uri = new Uri(originUrl);
var endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}{(type == RequestType.Plugin ? PLUGIN_PATH : THEME_PATH)}";
return endpointUrl;
}
private HttpResponseMessage ExecuteRequest(string url, (string name, string value) session)
{
using (var message = new HttpRequestMessage(HttpMethod.Get, url))
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"{session.name}={session.value}");
return client.SendAsync(message).GetAwaiter().GetResult();
}
}
private bool TryParseCookie(string session, out (string name, string value) cookie)
{
cookie = default;
try
{
if (session != default)
{
var start = session.IndexOf("=") + 1;
var end = session.IndexOf(";");
cookie.name = session.Substring(0, start - 1);
cookie.value = session.Substring(start, end - start);
}
}
catch (Exception e)
{
logger.Error("Failed to parse session cookie!", e);
}
return cookie.name != default && cookie.value != default;
}
private bool TryParseLocation(string location, out string userId)
{
userId = default;
try
{
if (location != default)
{
userId = location.Substring(location.IndexOf("=") + 1);
}
}
catch (Exception e)
{
logger.Error("Failed to parse location!", e);
}
return userId != default;
}
private bool TryParseResponse(HttpResponseMessage response, RequestType type, out string userId)
{
userId = default;
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (int.TryParse(content, out var id) && id > 0)
{
userId = content;
}
}
else if (response.StatusCode != HttpStatusCode.NotFound)
{
logger.Error($"Failed to retrieve user identifier by request ({type})! Response: {(int) response.StatusCode} {response.ReasonPhrase}");
}
return userId != default;
}
private enum RequestType
{
Plugin,
Theme
}
}
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" /> <Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" /> <Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" /> <Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -53,14 +53,14 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="CefSharp, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"> <Reference Include="CefSharp, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll</HintPath> <HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="CefSharp.Core, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"> <Reference Include="CefSharp.Core, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll</HintPath> <HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="CefSharp.WinForms, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"> <Reference Include="CefSharp.WinForms, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.WinForms.131.3.50\lib\net462\CefSharp.WinForms.dll</HintPath> <HintPath>..\packages\CefSharp.WinForms.139.0.280\lib\net462\CefSharp.WinForms.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -103,6 +103,7 @@
<Compile Include="Handlers\ContextMenuHandler.cs" /> <Compile Include="Handlers\ContextMenuHandler.cs" />
<Compile Include="BrowserControl.cs" /> <Compile Include="BrowserControl.cs" />
<Compile Include="BrowserIconResource.cs" /> <Compile Include="BrowserIconResource.cs" />
<Compile Include="Handlers\CookieVisitor.cs" />
<Compile Include="Handlers\DialogHandler.cs" /> <Compile Include="Handlers\DialogHandler.cs" />
<Compile Include="Handlers\DisplayHandler.cs" /> <Compile Include="Handlers\DisplayHandler.cs" />
<Compile Include="Handlers\DownloadHandler.cs" /> <Compile Include="Handlers\DownloadHandler.cs" />
@@ -113,6 +114,10 @@
<Compile Include="Handlers\RequestHandler.cs" /> <Compile Include="Handlers\RequestHandler.cs" />
<Compile Include="Handlers\ResourceHandler.cs" /> <Compile Include="Handlers\ResourceHandler.cs" />
<Compile Include="Content\ContentLoader.cs" /> <Compile Include="Content\ContentLoader.cs" />
<Compile Include="Integrations\EdxIntegration.cs" />
<Compile Include="Integrations\GenericIntegration.cs" />
<Compile Include="Integrations\Integration.cs" />
<Compile Include="Integrations\MoodleIntegration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Wrapper\CefSharpBrowserControl.cs"> <Compile Include="Wrapper\CefSharpBrowserControl.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
@@ -122,10 +127,13 @@
</Compile> </Compile>
<Compile Include="Wrapper\Events\AuthCredentialsEventHandler.cs" /> <Compile Include="Wrapper\Events\AuthCredentialsEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeBrowseEventHandler.cs" /> <Compile Include="Wrapper\Events\BeforeBrowseEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeContextMenuEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeDownloadEventHandler.cs" /> <Compile Include="Wrapper\Events\BeforeDownloadEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeUnloadDialogEventHandler.cs" /> <Compile Include="Wrapper\Events\BeforeUnloadDialogEventHandler.cs" />
<Compile Include="Wrapper\Events\CanDownloadEventHandler.cs" /> <Compile Include="Wrapper\Events\CanDownloadEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextCreatedEventHandler.cs" /> <Compile Include="Wrapper\Events\ContextCreatedEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextMenuCommandEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextMenuDismissedEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextReleasedEventHandler.cs" /> <Compile Include="Wrapper\Events\ContextReleasedEventHandler.cs" />
<Compile Include="Wrapper\Events\DialogClosedEventHandler.cs" /> <Compile Include="Wrapper\Events\DialogClosedEventHandler.cs" />
<Compile Include="Wrapper\Events\DownloadUpdatedEventHandler.cs" /> <Compile Include="Wrapper\Events\DownloadUpdatedEventHandler.cs" />
@@ -144,10 +152,12 @@
<Compile Include="Wrapper\Events\ResetDialogStateEventHandler.cs" /> <Compile Include="Wrapper\Events\ResetDialogStateEventHandler.cs" />
<Compile Include="Wrapper\Events\ResourceRequestEventArgs.cs" /> <Compile Include="Wrapper\Events\ResourceRequestEventArgs.cs" />
<Compile Include="Wrapper\Events\ResourceRequestEventHandler.cs" /> <Compile Include="Wrapper\Events\ResourceRequestEventHandler.cs" />
<Compile Include="Wrapper\Events\RunContextMenuEventHandler.cs" />
<Compile Include="Wrapper\Events\SetFocusEventHandler.cs" /> <Compile Include="Wrapper\Events\SetFocusEventHandler.cs" />
<Compile Include="Wrapper\Events\TakeFocusEventHandler.cs" /> <Compile Include="Wrapper\Events\TakeFocusEventHandler.cs" />
<Compile Include="Wrapper\Events\UncaughtExceptionEventHandler.cs" /> <Compile Include="Wrapper\Events\UncaughtExceptionEventHandler.cs" />
<Compile Include="Wrapper\Extensions.cs" /> <Compile Include="Wrapper\Extensions.cs" />
<Compile Include="Wrapper\Handlers\ContextMenuHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DialogHandlerSwitch.cs" /> <Compile Include="Wrapper\Handlers\DialogHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DisplayHandlerSwitch.cs" /> <Compile Include="Wrapper\Handlers\DisplayHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DownloadHandlerSwitch.cs" /> <Compile Include="Wrapper\Handlers\DownloadHandlerSwitch.cs" />
@@ -227,10 +237,10 @@
<PropertyGroup> <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> <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> </PropertyGroup>
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props'))" /> <Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props'))" />
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props'))" /> <Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props'))" /> <Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets'))" /> <Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets'))" />
</Target> </Target>
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" /> <Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" />
</Project> </Project>

View File

@@ -10,7 +10,6 @@ using System.Collections.Generic;
using CefSharp; using CefSharp;
using CefSharp.Enums; using CefSharp.Enums;
using CefSharp.WinForms; using CefSharp.WinForms;
using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Wrapper.Events; using SafeExamBrowser.Browser.Wrapper.Events;
using SafeExamBrowser.Browser.Wrapper.Handlers; using SafeExamBrowser.Browser.Wrapper.Handlers;
@@ -20,10 +19,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{ {
public event AuthCredentialsEventHandler AuthCredentialsRequired; public event AuthCredentialsEventHandler AuthCredentialsRequired;
public event BeforeBrowseEventHandler BeforeBrowse; public event BeforeBrowseEventHandler BeforeBrowse;
public event BeforeContextMenuEventHandler BeforeContextMenu;
public event BeforeDownloadEventHandler BeforeDownload; public event BeforeDownloadEventHandler BeforeDownload;
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog; public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
public event CanDownloadEventHandler CanDownload; public event CanDownloadEventHandler CanDownload;
public event ContextCreatedEventHandler ContextCreated; public event ContextCreatedEventHandler ContextCreated;
public event ContextMenuCommandEventHandler ContextMenuCommand;
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
public event ContextReleasedEventHandler ContextReleased; public event ContextReleasedEventHandler ContextReleased;
public event DialogClosedEventHandler DialogClosed; public event DialogClosedEventHandler DialogClosed;
public event DownloadUpdatedEventHandler DownloadUpdated; public event DownloadUpdatedEventHandler DownloadUpdated;
@@ -40,6 +42,7 @@ namespace SafeExamBrowser.Browser.Wrapper
public event PreKeyEventHandler PreKeyEvent; public event PreKeyEventHandler PreKeyEvent;
public event ResetDialogStateEventHandler ResetDialogState; public event ResetDialogStateEventHandler ResetDialogState;
public event ResourceRequestEventHandler ResourceRequestHandlerRequired; public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
public event RunContextMenuEventHandler RunContextMenu;
public event SetFocusEventHandler SetFocus; public event SetFocusEventHandler SetFocus;
public event TakeFocusEventHandler TakeFocus; public event TakeFocusEventHandler TakeFocus;
public event UncaughtExceptionEventHandler UncaughtExceptionEvent; public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -54,7 +57,7 @@ namespace SafeExamBrowser.Browser.Wrapper
JsDialogHandler = new JavaScriptDialogHandlerSwitch(); JsDialogHandler = new JavaScriptDialogHandlerSwitch();
KeyboardHandler = new KeyboardHandlerSwitch(); KeyboardHandler = new KeyboardHandlerSwitch();
LifeSpanHandler = lifeSpanHandler; LifeSpanHandler = lifeSpanHandler;
MenuHandler = new ContextMenuHandler(); MenuHandler = new ContextMenuHandlerSwitch();
RenderProcessMessageHandler = new RenderProcessMessageHandlerSwitch(); RenderProcessMessageHandler = new RenderProcessMessageHandlerSwitch();
RequestHandler = new RequestHandlerSwitch(); RequestHandler = new RequestHandlerSwitch();
} }
@@ -79,6 +82,11 @@ namespace SafeExamBrowser.Browser.Wrapper
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args); BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
} }
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
}
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args) public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
{ {
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args); BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
@@ -99,6 +107,16 @@ namespace SafeExamBrowser.Browser.Wrapper
ContextCreated?.Invoke(webBrowser, browser, frame); ContextCreated?.Invoke(webBrowser, browser, frame);
} }
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
{
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
}
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame) public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{ {
ContextReleased?.Invoke(webBrowser, browser, frame); ContextReleased?.Invoke(webBrowser, browser, frame);
@@ -174,6 +192,11 @@ namespace SafeExamBrowser.Browser.Wrapper
ResetDialogState?.Invoke(webBrowser, browser); ResetDialogState?.Invoke(webBrowser, browser);
} }
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
{
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
}
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args) public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
{ {
SetFocus?.Invoke(webBrowser, browser, source, args); SetFocus?.Invoke(webBrowser, browser, source, args);

View File

@@ -18,10 +18,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{ {
public event AuthCredentialsEventHandler AuthCredentialsRequired; public event AuthCredentialsEventHandler AuthCredentialsRequired;
public event BeforeBrowseEventHandler BeforeBrowse; public event BeforeBrowseEventHandler BeforeBrowse;
public event BeforeContextMenuEventHandler BeforeContextMenu;
public event BeforeDownloadEventHandler BeforeDownload; public event BeforeDownloadEventHandler BeforeDownload;
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog; public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
public event CanDownloadEventHandler CanDownload; public event CanDownloadEventHandler CanDownload;
public event ContextCreatedEventHandler ContextCreated; public event ContextCreatedEventHandler ContextCreated;
public event ContextMenuCommandEventHandler ContextMenuCommand;
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
public event ContextReleasedEventHandler ContextReleased; public event ContextReleasedEventHandler ContextReleased;
public event DialogClosedEventHandler DialogClosed; public event DialogClosedEventHandler DialogClosed;
public event DownloadUpdatedEventHandler DownloadUpdated; public event DownloadUpdatedEventHandler DownloadUpdated;
@@ -38,6 +41,7 @@ namespace SafeExamBrowser.Browser.Wrapper
public event PreKeyEventHandler PreKeyEvent; public event PreKeyEventHandler PreKeyEvent;
public event ResetDialogStateEventHandler ResetDialogState; public event ResetDialogStateEventHandler ResetDialogState;
public event ResourceRequestEventHandler ResourceRequestHandlerRequired; public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
public event RunContextMenuEventHandler RunContextMenu;
public event SetFocusEventHandler SetFocus; public event SetFocusEventHandler SetFocus;
public event TakeFocusEventHandler TakeFocus; public event TakeFocusEventHandler TakeFocus;
public event UncaughtExceptionEventHandler UncaughtExceptionEvent; public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -70,6 +74,11 @@ namespace SafeExamBrowser.Browser.Wrapper
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args); BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
} }
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
}
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args) public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
{ {
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args); BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
@@ -90,6 +99,16 @@ namespace SafeExamBrowser.Browser.Wrapper
ContextCreated?.Invoke(webBrowser, browser, frame); ContextCreated?.Invoke(webBrowser, browser, frame);
} }
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
{
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
}
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame) public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{ {
ContextReleased?.Invoke(webBrowser, browser, frame); ContextReleased?.Invoke(webBrowser, browser, frame);
@@ -165,6 +184,11 @@ namespace SafeExamBrowser.Browser.Wrapper
ResetDialogState?.Invoke(webBrowser, browser); ResetDialogState?.Invoke(webBrowser, browser);
} }
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
{
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
}
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args) public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
{ {
SetFocus?.Invoke(webBrowser, browser, source, args); SetFocus?.Invoke(webBrowser, browser, source, args);

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void BeforeContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void BeforeUnloadDialogEventHandler(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void ContextMenuCommandEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void ContextMenuDismissedEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void DialogClosedEventHandler(IWebBrowser webBrowser, IBrowser browser);
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) 2025 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 CefSharp.Enums;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void DragEnterEventHandler(IWebBrowser webBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask, GenericEventArgs args);
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) 2025 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 CefSharp;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void DraggableRegionsChangedEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void GotFocusEventHandler(IWebBrowser webBrowser, IBrowser browser);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void JavaScriptDialogEventHandler(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void ResetDialogStateEventHandler(IWebBrowser webBrowser, IBrowser browser);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void RunContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void SetFocusEventHandler(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 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;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void TakeFocusEventHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next);
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2025 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 CefSharp.WinForms;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Browser.Wrapper.Events;
namespace SafeExamBrowser.Browser.Wrapper.Handlers
{
internal class ContextMenuHandlerSwitch : IContextMenuHandler
{
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnBeforeContextMenu(webBrowser, browser, frame, parameters, model);
}
public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
{
var args = new GenericEventArgs();
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnContextMenuCommand(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
return args.Value;
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnContextMenuDismissed(webBrowser, browser, frame);
}
public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{
var args = new GenericEventArgs();
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnRunContextMenu(webBrowser, browser, frame, parameters, model, callback, args);
return args.Value;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2025 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 CefSharp;
using CefSharp.Enums;
using CefSharp.WinForms;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Browser.Wrapper.Events;
namespace SafeExamBrowser.Browser.Wrapper.Handlers
{
internal class DragHandlerSwitch : IDragHandler
{
public bool OnDragEnter(IWebBrowser webBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask)
{
var args = new GenericEventArgs();
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnDragEnter(webBrowser, browser, dragData, mask, args);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnDragEnter(webBrowser, browser, dragData, mask, args);
}
return args.Value;
}
public void OnDraggableRegionsChanged(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions)
{
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnDraggableRegionsChanged(webBrowser, browser, frame, regions);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnDraggableRegionsChanged(webBrowser, browser, frame, regions);
}
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2025 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 CefSharp.WinForms;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Browser.Wrapper.Events;
namespace SafeExamBrowser.Browser.Wrapper.Handlers
{
internal class FocusHandlerSwitch : IFocusHandler
{
public void OnGotFocus(IWebBrowser webBrowser, IBrowser browser)
{
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnGotFocus(webBrowser, browser);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnGotFocus(webBrowser, browser);
}
}
public bool OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source)
{
var args = new GenericEventArgs { Value = false };
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnSetFocus(webBrowser, browser, source, args);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnSetFocus(webBrowser, browser, source, args);
}
return args.Value;
}
public void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next)
{
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnTakeFocus(webBrowser, browser, next);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnTakeFocus(webBrowser, browser, next);
}
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2025 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 CefSharp.WinForms;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Browser.Wrapper.Events;
namespace SafeExamBrowser.Browser.Wrapper.Handlers
{
internal class JavaScriptDialogHandlerSwitch : IJsDialogHandler
{
public bool OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback)
{
var args = new GenericEventArgs { Value = false };
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnBeforeUnloadDialog(webBrowser, browser, message, isReload, callback, args);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnBeforeUnloadDialog(webBrowser, browser, message, isReload, callback, args);
}
return args.Value;
}
public void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser)
{
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnDialogClosed(webBrowser, browser);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnDialogClosed(webBrowser, browser);
}
}
public bool OnJSDialog(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress)
{
var args = new GenericEventArgs { Value = false };
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnJavaScriptDialog(webBrowser, browser, originUrl, type, message, promptText, callback, ref suppress, args);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnJavaScriptDialog(webBrowser, browser, originUrl, type, message, promptText, callback, ref suppress, args);
}
return args.Value;
}
public void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser)
{
if (browser.IsPopup)
{
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
control?.OnResetDialogState(webBrowser, browser);
}
else
{
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
control?.OnResetDialogState(webBrowser, browser);
}
}
}
}

View File

@@ -21,10 +21,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{ {
event AuthCredentialsEventHandler AuthCredentialsRequired; event AuthCredentialsEventHandler AuthCredentialsRequired;
event BeforeBrowseEventHandler BeforeBrowse; event BeforeBrowseEventHandler BeforeBrowse;
event BeforeContextMenuEventHandler BeforeContextMenu;
event BeforeDownloadEventHandler BeforeDownload; event BeforeDownloadEventHandler BeforeDownload;
event BeforeUnloadDialogEventHandler BeforeUnloadDialog; event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
event CanDownloadEventHandler CanDownload; event CanDownloadEventHandler CanDownload;
event ContextCreatedEventHandler ContextCreated; event ContextCreatedEventHandler ContextCreated;
event ContextMenuCommandEventHandler ContextMenuCommand;
event ContextMenuDismissedEventHandler ContextMenuDismissed;
event ContextReleasedEventHandler ContextReleased; event ContextReleasedEventHandler ContextReleased;
event DialogClosedEventHandler DialogClosed; event DialogClosedEventHandler DialogClosed;
event DownloadUpdatedEventHandler DownloadUpdated; event DownloadUpdatedEventHandler DownloadUpdated;
@@ -41,6 +44,7 @@ namespace SafeExamBrowser.Browser.Wrapper
event PreKeyEventHandler PreKeyEvent; event PreKeyEventHandler PreKeyEvent;
event ResetDialogStateEventHandler ResetDialogState; event ResetDialogStateEventHandler ResetDialogState;
event ResourceRequestEventHandler ResourceRequestHandlerRequired; event ResourceRequestEventHandler ResourceRequestHandlerRequired;
event RunContextMenuEventHandler RunContextMenu;
event SetFocusEventHandler SetFocus; event SetFocusEventHandler SetFocus;
event TakeFocusEventHandler TakeFocus; event TakeFocusEventHandler TakeFocus;
event UncaughtExceptionEventHandler UncaughtExceptionEvent; event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -50,10 +54,13 @@ namespace SafeExamBrowser.Browser.Wrapper
void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args); void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args);
void Load(string address); void Load(string address);
void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args); void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args);
void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args); void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args);
void OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args); void OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args);
void OnCanDownload(IWebBrowser webBrowser, IBrowser browser, string url, string requestMethod, GenericEventArgs args); void OnCanDownload(IWebBrowser webBrowser, IBrowser browser, string url, string requestMethod, GenericEventArgs args);
void OnContextCreated(IWebBrowser webBrowser, IBrowser browser, IFrame frame); void OnContextCreated(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
void OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame);
void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame); void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser); void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser);
void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback); void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback);
@@ -69,6 +76,7 @@ namespace SafeExamBrowser.Browser.Wrapper
void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args); void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args);
void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args); void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args);
void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser); void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser);
void OnRunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args); void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args);
void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next); void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next);
void OnUncaughtException(IWebBrowser webBrowser, IBrowser browser, IFrame frame, JavascriptException exception); void OnUncaughtException(IWebBrowser webBrowser, IBrowser browser, IFrame frame, JavascriptException exception);

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="CefSharp.Common" version="131.3.50" targetFramework="net48" /> <package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
<package id="CefSharp.WinForms" version="131.3.50" targetFramework="net48" /> <package id="CefSharp.WinForms" version="139.0.280" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x64" version="131.3.5" targetFramework="net48" /> <package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x86" version="131.3.5" targetFramework="net48" /> <package id="chromiumembeddedframework.runtime.win-x86" version="139.0.28" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" /> <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Syroot.Windows.IO.KnownFolders" version="1.3.0" targetFramework="net48" /> <package id="Syroot.Windows.IO.KnownFolders" version="1.3.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" /> <package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" />

View File

@@ -15,6 +15,8 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts; using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings; using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Server; using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@@ -22,26 +24,33 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public class ServerOperationTests public class ServerOperationTests
{ {
private AppConfig appConfig; private AppConfig appConfig;
private Mock<IActionCenter> actionCenter;
private ClientContext context; private ClientContext context;
private Mock<IInvigilator> invigilator;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<IServerProxy> server; private Mock<IServerProxy> server;
private AppSettings settings; private AppSettings settings;
private Mock<ITaskbar> taskbar;
private Mock<IUserInterfaceFactory> uiFactory;
private ServerOperation sut; private ServerOperation sut;
[TestInitialize] [TestInitialize]
public void Initialize() public void Initialize()
{ {
appConfig = new AppConfig(); appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>();
context = new ClientContext(); context = new ClientContext();
invigilator = new Mock<IInvigilator>();
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
server = new Mock<IServerProxy>(); server = new Mock<IServerProxy>();
settings = new AppSettings(); settings = new AppSettings();
taskbar = new Mock<ITaskbar>();
uiFactory = new Mock<IUserInterfaceFactory>();
context.AppConfig = appConfig; context.AppConfig = appConfig;
context.Settings = settings; context.Settings = settings;
sut = new ServerOperation(context, logger.Object, server.Object); sut = new ServerOperation(actionCenter.Object, context, invigilator.Object, logger.Object, server.Object, taskbar.Object, uiFactory.Object);
} }
[TestMethod] [TestMethod]

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class ApplicationResponsibilityTests
{
private ClientContext context;
private ApplicationsResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
context = new ClientContext();
sut = new ApplicationsResponsibility(context, logger.Object);
}
[TestMethod]
public void MustAutoStartApplications()
{
var application1 = new Mock<IApplication<IApplicationWindow>>();
var application2 = new Mock<IApplication<IApplicationWindow>>();
var application3 = new Mock<IApplication<IApplicationWindow>>();
application1.SetupGet(a => a.AutoStart).Returns(true);
application2.SetupGet(a => a.AutoStart).Returns(false);
application3.SetupGet(a => a.AutoStart).Returns(true);
context.Applications.Add(application1.Object);
context.Applications.Add(application2.Object);
context.Applications.Add(application3.Object);
sut.Assume(ClientTask.AutoStartApplications);
application1.Verify(a => a.Start(), Times.Once);
application2.Verify(a => a.Start(), Times.Never);
application3.Verify(a => a.Start(), Times.Once);
}
}
}

View File

@@ -0,0 +1,350 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class BrowserResponsibilityTests
{
private AppConfig appConfig;
private Mock<IBrowserApplication> browser;
private ClientContext context;
private Mock<ICoordinator> coordinator;
private Mock<IMessageBox> messageBox;
private Mock<IRuntimeProxy> runtime;
private Mock<IServerProxy> server;
private AppSettings settings;
private Mock<ISplashScreen> splashScreen;
private Mock<ITaskbar> taskbar;
private BrowserResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
appConfig = new AppConfig();
browser = new Mock<IBrowserApplication>();
context = new ClientContext();
coordinator = new Mock<ICoordinator>();
messageBox = new Mock<IMessageBox>();
runtime = new Mock<IRuntimeProxy>();
server = new Mock<IServerProxy>();
settings = new AppSettings();
splashScreen = new Mock<ISplashScreen>();
taskbar = new Mock<ITaskbar>();
context.AppConfig = appConfig;
context.Browser = browser.Object;
context.Responsibilities = responsibilities.Object;
context.Runtime = runtime.Object;
context.Server = server.Object;
context.Settings = settings;
sut = new BrowserResponsibility(
context,
coordinator.Object,
logger.Object,
messageBox.Object,
runtime.Object,
splashScreen.Object,
taskbar.Object);
sut.Assume(ClientTask.RegisterEvents);
}
[TestMethod]
public void MustAutoStartBrowser()
{
settings.Browser.EnableBrowser = true;
browser.SetupGet(b => b.AutoStart).Returns(true);
sut.Assume(ClientTask.AutoStartApplications);
browser.Verify(b => b.Start(), Times.Once);
browser.Reset();
browser.SetupGet(b => b.AutoStart).Returns(false);
sut.Assume(ClientTask.AutoStartApplications);
browser.Verify(b => b.Start(), Times.Never);
}
[TestMethod]
public void MustNotAutoStartBrowserIfNotEnabled()
{
settings.Browser.EnableBrowser = false;
browser.SetupGet(b => b.AutoStart).Returns(true);
sut.Assume(ClientTask.AutoStartApplications);
browser.Verify(b => b.Start(), Times.Never);
}
[TestMethod]
public void Browser_MustHandleUserIdentifierDetection()
{
var counter = 0;
var identifier = "abc123";
settings.SessionMode = SessionMode.Server;
server.Setup(s => s.SendUserIdentifier(It.IsAny<string>())).Returns(() => new ServerResponse(++counter == 3));
browser.Raise(b => b.UserIdentifierDetected += null, identifier);
server.Verify(s => s.SendUserIdentifier(It.Is<string>(id => id == identifier)), Times.Exactly(3));
}
[TestMethod]
public void Browser_MustTerminateIfRequested()
{
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
browser.Raise(b => b.TerminationRequested += null);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void Reconfiguration_MustAllowIfNoQuitPasswordSet()
{
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
args.Callback(true, string.Empty);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
Assert.IsTrue(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustNotAllowWithQuitPasswordAndNoUrl()
{
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
settings.Security.AllowReconfiguration = true;
settings.Security.QuitPasswordHash = "abc123";
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
Assert.IsFalse(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustNotAllowConcurrentExecution()
{
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(false);
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
args.Callback?.Invoke(true, string.Empty);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
Assert.IsFalse(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustAllowIfUrlMatches()
{
var args = new DownloadEventArgs { Url = "sebs://www.somehost.org/some/path/some_configuration.seb?query=123" };
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
settings.Security.AllowReconfiguration = true;
settings.Security.QuitPasswordHash = "abc123";
settings.Security.ReconfigurationUrl = "sebs://www.somehost.org/some/path/*.seb?query=123";
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
args.Callback(true, string.Empty);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
Assert.IsTrue(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustDenyIfNotAllowed()
{
var args = new DownloadEventArgs();
settings.Security.AllowReconfiguration = false;
settings.Security.QuitPasswordHash = "abc123";
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
Assert.IsFalse(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustDenyIfUrlDoesNotMatch()
{
var args = new DownloadEventArgs { Url = "sebs://www.somehost.org/some/path/some_configuration.seb?query=123" };
settings.Security.AllowReconfiguration = false;
settings.Security.QuitPasswordHash = "abc123";
settings.Security.ReconfigurationUrl = "sebs://www.somehost.org/some/path/other_configuration.seb?query=123";
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
Assert.IsFalse(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustCorrectlyHandleDownload()
{
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
var downloadUrl = @"https://www.host.abc/someresource.seb";
var filename = "filepath.seb";
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestReconfiguration(
It.Is<string>(p => p == downloadPath),
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
settings.Security.AllowReconfiguration = true;
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
args.Callback(true, downloadUrl, downloadPath);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
runtime.Verify(r => r.RequestReconfiguration(It.Is<string>(p => p == downloadPath), It.Is<string>(u => u == downloadUrl)), Times.Once);
Assert.AreEqual(downloadPath, args.DownloadPath);
Assert.IsTrue(args.AllowDownload);
}
[TestMethod]
public void Reconfiguration_MustCorrectlyHandleFailedDownload()
{
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
var downloadUrl = @"https://www.host.abc/someresource.seb";
var filename = "filepath.seb";
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestReconfiguration(
It.Is<string>(p => p == downloadPath),
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
settings.Security.AllowReconfiguration = true;
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
args.Callback(false, downloadPath);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Once);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[TestMethod]
public void Reconfiguration_MustCorrectlyHandleFailedRequest()
{
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
var downloadUrl = @"https://www.host.abc/someresource.seb";
var filename = "filepath.seb";
var args = new DownloadEventArgs();
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestReconfiguration(
It.Is<string>(p => p == downloadPath),
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(false));
settings.Security.AllowReconfiguration = true;
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
args.Callback(true, downloadUrl, downloadPath);
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Once);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Once);
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
[TestMethod]
public void MustNotFailIfDependencyIsNull()
{
context.Browser = null;
sut.Assume(ClientTask.DeregisterEvents);
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2025 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 Moq;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Events;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class CommunicationResponsibilityTests
{
private Mock<IClientHost> clientHost;
private ClientContext context;
private Mock<ICoordinator> coordinator;
private Mock<IMessageBox> messageBox;
private Mock<IRuntimeProxy> runtimeProxy;
private Mock<Action> shutdown;
private Mock<ISplashScreen> splashScreen;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private CommunicationResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
clientHost = new Mock<IClientHost>();
context = new ClientContext();
coordinator = new Mock<ICoordinator>();
messageBox = new Mock<IMessageBox>();
runtimeProxy = new Mock<IRuntimeProxy>();
shutdown = new Mock<Action>();
splashScreen = new Mock<ISplashScreen>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
context.ClientHost = clientHost.Object;
sut = new CommunicationResponsibility(
context,
coordinator.Object,
logger.Object,
messageBox.Object,
runtimeProxy.Object,
shutdown.Object,
splashScreen.Object,
text.Object,
uiFactory.Object);
sut.Assume(ClientTask.RegisterEvents);
}
[TestMethod]
public void Communication_MustCorrectlyHandleExamSelection()
{
var args = new ExamSelectionRequestEventArgs
{
Exams = new List<(string id, string lms, string name, string url)> { ("", "", "", "") },
RequestId = Guid.NewGuid()
};
var dialog = new Mock<IExamSelectionDialog>();
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(new ExamSelectionDialogResult { Success = true });
uiFactory.Setup(f => f.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>())).Returns(dialog.Object);
clientHost.Raise(c => c.ExamSelectionRequested += null, args);
runtimeProxy.Verify(p => p.SubmitExamSelectionResult(It.Is<Guid>(g => g == args.RequestId), true, null), Times.Once);
uiFactory.Verify(f => f.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>()), Times.Once);
}
[TestMethod]
public void Communication_MustCorrectlyHandleMessageBoxRequest()
{
var args = new MessageBoxRequestEventArgs
{
Action = (int) MessageBoxAction.YesNo,
Icon = (int) MessageBoxIcon.Question,
Message = "Some question to be answered",
RequestId = Guid.NewGuid(),
Title = "A Title"
};
messageBox.Setup(m => m.Show(
It.Is<string>(s => s == args.Message),
It.Is<string>(s => s == args.Title),
It.Is<MessageBoxAction>(a => a == (MessageBoxAction) args.Action),
It.Is<MessageBoxIcon>(i => i == (MessageBoxIcon) args.Icon),
It.IsAny<IWindow>())).Returns(MessageBoxResult.No);
clientHost.Raise(c => c.MessageBoxRequested += null, args);
runtimeProxy.Verify(p => p.SubmitMessageBoxResult(
It.Is<Guid>(g => g == args.RequestId),
It.Is<int>(r => r == (int) MessageBoxResult.No)), Times.Once);
}
[TestMethod]
public void Communication_MustCorrectlyHandlePasswordRequest()
{
var args = new PasswordRequestEventArgs
{
Purpose = PasswordRequestPurpose.LocalSettings,
RequestId = Guid.NewGuid()
};
var dialog = new Mock<IPasswordDialog>();
var result = new PasswordDialogResult { Password = "blubb", Success = true };
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(result);
uiFactory.Setup(f => f.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>())).Returns(dialog.Object);
clientHost.Raise(c => c.PasswordRequested += null, args);
runtimeProxy.Verify(p => p.SubmitPassword(
It.Is<Guid>(g => g == args.RequestId),
It.Is<bool>(b => b == result.Success),
It.Is<string>(s => s == result.Password)), Times.Once);
}
[TestMethod]
public void Communication_MustCorrectlyHandleAbortedReconfiguration()
{
clientHost.Raise(c => c.ReconfigurationAborted += null);
splashScreen.Verify(s => s.Hide(), Times.AtLeastOnce);
}
[TestMethod]
public void Communication_MustInformUserAboutDeniedReconfiguration()
{
var args = new ReconfigurationEventArgs
{
ConfigurationPath = @"C:\Some\File\Path.seb"
};
clientHost.Raise(c => c.ReconfigurationDenied += null, args);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>()), Times.Once);
}
[TestMethod]
public void Communication_MustCorrectlyHandleServerCommunicationFailure()
{
var args = new ServerFailureActionRequestEventArgs { RequestId = Guid.NewGuid() };
var dialog = new Mock<IServerFailureDialog>();
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(new ServerFailureDialogResult());
uiFactory.Setup(f => f.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>())).Returns(dialog.Object);
clientHost.Raise(c => c.ServerFailureActionRequested += null, args);
runtimeProxy.Verify(r => r.SubmitServerFailureActionResult(It.Is<Guid>(g => g == args.RequestId), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Once);
uiFactory.Verify(f => f.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>()), Times.Once);
}
[TestMethod]
public void Communication_MustCorrectlyInitiateShutdown()
{
clientHost.Raise(c => c.Shutdown += null);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void Communication_MustShutdownOnLostConnection()
{
runtimeProxy.Raise(p => p.ConnectionLost += null);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>()), Times.Once);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void MustNotFailIfDependencyIsNull()
{
context.ClientHost = null;
sut.Assume(ClientTask.DeregisterEvents);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class IntegrityResponsibilityTests
{
private Mock<IText> text;
private Mock<IIntegrityModule> integrityModule;
[TestInitialize]
public void Initialize()
{
var context = new ClientContext();
var logger = new Mock<ILogger>();
var valid = true;
text = new Mock<IText>();
integrityModule = new Mock<IIntegrityModule>();
integrityModule.Setup(m => m.TryVerifySessionIntegrity(It.IsAny<string>(), It.IsAny<string>(), out valid)).Returns(true);
var sut = new IntegrityResponsibility(context, logger.Object, text.Object);
}
}
}

View File

@@ -0,0 +1,448 @@
/*
* Copyright (c) 2025 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 System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Win32;
using Moq;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.System;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.WindowsApi.Contracts;
using IWindow = SafeExamBrowser.UserInterface.Contracts.Windows.IWindow;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class MonitoringResponsibilityTests
{
private Mock<IActionCenter> actionCenter;
private Mock<IApplicationMonitor> applicationMonitor;
private ClientContext context;
private Mock<ICoordinator> coordinator;
private Mock<IDisplayMonitor> displayMonitor;
private Mock<IExplorerShell> explorerShell;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<IMessageBox> messageBox;
private Mock<IRuntimeProxy> runtime;
private Mock<ISystemSentinel> sentinel;
private AppSettings settings;
private Mock<ITaskbar> taskbar;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private MonitoringResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
actionCenter = new Mock<IActionCenter>();
applicationMonitor = new Mock<IApplicationMonitor>();
context = new ClientContext();
coordinator = new Mock<ICoordinator>();
displayMonitor = new Mock<IDisplayMonitor>();
explorerShell = new Mock<IExplorerShell>();
hashAlgorithm = new Mock<IHashAlgorithm>();
messageBox = new Mock<IMessageBox>();
runtime = new Mock<IRuntimeProxy>();
sentinel = new Mock<ISystemSentinel>();
settings = new AppSettings();
taskbar = new Mock<ITaskbar>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
context.HashAlgorithm = hashAlgorithm.Object;
context.MessageBox = messageBox.Object;
context.Responsibilities = responsibilities.Object;
context.Runtime = runtime.Object;
context.Settings = settings;
context.UserInterfaceFactory = uiFactory.Object;
sut = new MonitoringResponsibility(
actionCenter.Object,
applicationMonitor.Object,
context,
coordinator.Object,
displayMonitor.Object,
explorerShell.Object,
logger.Object,
sentinel.Object,
taskbar.Object,
text.Object);
sut.Assume(ClientTask.RegisterEvents);
sut.Assume(ClientTask.StartMonitoring);
}
[TestMethod]
public void ApplicationMonitor_MustCorrectlyHandleExplorerStartWithTaskbar()
{
var boundsActionCenter = 0;
var boundsTaskbar = 0;
var height = 30;
var order = 0;
var shell = 0;
var workingArea = 0;
settings.UserInterface.Taskbar.EnableTaskbar = true;
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == height))).Callback(() => workingArea = ++order);
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
applicationMonitor.Raise(a => a.ExplorerStarted += null);
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
explorerShell.Verify(e => e.Terminate(), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Never);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Once);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Once);
Assert.IsTrue(shell == 1);
Assert.IsTrue(workingArea == 2);
Assert.IsTrue(boundsActionCenter == 3);
Assert.IsTrue(boundsTaskbar == 4);
}
[TestMethod]
public void ApplicationMonitor_MustCorrectlyHandleExplorerStartWithoutTaskbar()
{
var boundsActionCenter = 0;
var boundsTaskbar = 0;
var height = 30;
var order = 0;
var shell = 0;
var workingArea = 0;
settings.UserInterface.Taskbar.EnableTaskbar = false;
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == 0))).Callback(() => workingArea = ++order);
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
applicationMonitor.Raise(a => a.ExplorerStarted += null);
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
explorerShell.Verify(e => e.Terminate(), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Never);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Never);
Assert.IsTrue(shell == 1);
Assert.IsTrue(workingArea == 2);
Assert.IsTrue(boundsActionCenter == 3);
Assert.IsTrue(boundsTaskbar == 4);
}
[TestMethod]
public void ApplicationMonitor_MustPermitApplicationIfChosenByUserAfterFailedTermination()
{
var lockScreen = new Mock<ILockScreen>();
var result = new LockScreenResult();
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object)
.Callback<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.First().Id);
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
runtime.Verify(p => p.RequestShutdown(), Times.Never);
}
[TestMethod]
public void ApplicationMonitor_MustRequestShutdownIfChosenByUserAfterFailedTermination()
{
var lockScreen = new Mock<ILockScreen>();
var result = new LockScreenResult();
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object)
.Callback<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.Last().Id);
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void ApplicationMonitor_MustShowLockScreenIfTerminationFailed()
{
var activator1 = new Mock<IActivator>();
var activator2 = new Mock<IActivator>();
var activator3 = new Mock<IActivator>();
var lockScreen = new Mock<ILockScreen>();
var result = new LockScreenResult();
var order = 0;
var pause = 0;
var show = 0;
var wait = 0;
var close = 0;
var resume = 0;
activator1.Setup(a => a.Pause()).Callback(() => pause = ++order);
activator1.Setup(a => a.Resume()).Callback(() => resume = ++order);
context.Activators.Add(activator1.Object);
context.Activators.Add(activator2.Object);
context.Activators.Add(activator3.Object);
lockScreen.Setup(l => l.Show()).Callback(() => show = ++order);
lockScreen.Setup(l => l.WaitForResult()).Callback(() => wait = ++order).Returns(result);
lockScreen.Setup(l => l.Close()).Callback(() => close = ++order);
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
activator1.Verify(a => a.Pause(), Times.Once);
activator1.Verify(a => a.Resume(), Times.Once);
activator2.Verify(a => a.Pause(), Times.Once);
activator2.Verify(a => a.Resume(), Times.Once);
activator3.Verify(a => a.Pause(), Times.Once);
activator3.Verify(a => a.Resume(), Times.Once);
lockScreen.Verify(l => l.Show(), Times.Once);
lockScreen.Verify(l => l.WaitForResult(), Times.Once);
lockScreen.Verify(l => l.Close(), Times.Once);
Assert.IsTrue(pause == 1);
Assert.IsTrue(show == 2);
Assert.IsTrue(wait == 3);
Assert.IsTrue(close == 4);
Assert.IsTrue(resume == 5);
}
[TestMethod]
public void ApplicationMonitor_MustValidateQuitPasswordIfTerminationFailed()
{
var hash = "12345";
var lockScreen = new Mock<ILockScreen>();
var result = new LockScreenResult { Password = "test" };
var attempt = 0;
var correct = new Random().Next(1, 50);
var lockScreenResult = new Func<LockScreenResult>(() => ++attempt == correct ? result : new LockScreenResult());
context.Settings.Security.QuitPasswordHash = hash;
hashAlgorithm.Setup(a => a.GenerateHashFor(It.Is<string>(p => p == result.Password))).Returns(hash);
lockScreen.Setup(l => l.WaitForResult()).Returns(lockScreenResult);
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
hashAlgorithm.Verify(a => a.GenerateHashFor(It.Is<string>(p => p == result.Password)), Times.Once);
hashAlgorithm.Verify(a => a.GenerateHashFor(It.Is<string>(p => p != result.Password)), Times.Exactly(attempt - 1));
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.Is<IWindow>(w => w == lockScreen.Object)), Times.Exactly(attempt - 1));
}
[TestMethod]
public void DisplayMonitor_MustCorrectlyHandleDisplayChangeWithTaskbar()
{
var boundsActionCenter = 0;
var boundsTaskbar = 0;
var height = 25;
var order = 0;
var workingArea = 0;
settings.UserInterface.Taskbar.EnableTaskbar = true;
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
displayMonitor.Setup(m => m.InitializePrimaryDisplay(It.Is<int>(h => h == height))).Callback(() => workingArea = ++order);
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
displayMonitor.Raise(d => d.DisplayChanged += null);
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Never);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Once);
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Once);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
Assert.IsTrue(workingArea == 1);
Assert.IsTrue(boundsActionCenter == 2);
Assert.IsTrue(boundsTaskbar == 3);
}
[TestMethod]
public void DisplayMonitor_MustCorrectlyHandleDisplayChangeWithoutTaskbar()
{
var boundsActionCenter = 0;
var boundsTaskbar = 0;
var height = 25;
var order = 0;
var workingArea = 0;
settings.UserInterface.Taskbar.EnableTaskbar = false;
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == 0))).Callback(() => workingArea = ++order);
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
displayMonitor.Raise(d => d.DisplayChanged += null);
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Once);
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Never);
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Never);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
Assert.IsTrue(workingArea == 1);
Assert.IsTrue(boundsActionCenter == 2);
Assert.IsTrue(boundsTaskbar == 3);
}
[TestMethod]
public void DisplayMonitor_MustShowLockScreenOnDisplayChange()
{
var lockScreen = new Mock<ILockScreen>();
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = false });
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
displayMonitor.Raise(d => d.DisplayChanged += null);
lockScreen.Verify(l => l.Show(), Times.Once);
}
[TestMethod]
public void SystemMonitor_MustShowLockScreenOnSessionSwitch()
{
var lockScreen = new Mock<ILockScreen>();
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
settings.Service.IgnoreService = true;
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleDisconnect);
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
lockScreen.Verify(l => l.Show(), Times.Once);
}
[TestMethod]
public void SystemMonitor_MustTerminateIfRequestedByUser()
{
var lockScreen = new Mock<ILockScreen>();
var result = new LockScreenResult();
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
settings.Service.IgnoreService = true;
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Callback(new Action<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((message, title, options, settings) => result.OptionId = options.Last().Id))
.Returns(lockScreen.Object);
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
lockScreen.Verify(l => l.Show(), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void SystemMonitor_MustDoNothingIfSessionSwitchAllowed()
{
var lockScreen = new Mock<ILockScreen>();
settings.Service.IgnoreService = false;
settings.Service.DisableUserLock = false;
settings.Service.DisableUserSwitch = false;
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
lockScreen.Verify(l => l.Show(), Times.Never);
}
[TestMethod]
public void SystemMonitor_MustDoNothingIfSessionLockUnlockWithoutService()
{
var lockScreen = new Mock<ILockScreen>();
settings.Service.IgnoreService = true;
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.SessionLock);
lockScreen.Verify(l => l.Show(), Times.Never);
}
[TestMethod]
public void SystemMonitor_MustShowLockscreenIfNotSessionLockUnlockWithoutService()
{
var lockScreen = new Mock<ILockScreen>();
settings.Service.IgnoreService = true;
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
uiFactory
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
.Returns(lockScreen.Object);
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
lockScreen.Verify(l => l.Show(), Times.Once);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class NetworkResponsibilityTests
{
private Mock<INetworkAdapter> networkAdapter;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
[TestInitialize]
public void Initialize()
{
var context = new ClientContext();
var logger = new Mock<ILogger>();
networkAdapter = new Mock<INetworkAdapter>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
var sut = new NetworkResponsibility(context, logger.Object, networkAdapter.Object, text.Object, uiFactory.Object);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class ProctoringResponsibilityTests
{
private ClientContext context;
private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox;
private Mock<IUserInterfaceFactory> uiFactory;
private ProctoringResponsibility sut;
[TestInitialize]
public void Initialize()
{
context = new ClientContext();
logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>();
uiFactory = new Mock<IUserInterfaceFactory>();
sut = new ProctoringResponsibility(context, logger.Object, messageBox.Object, uiFactory.Object);
}
[TestMethod]
public void MustNotFailIfDependencyIsNull()
{
context.Proctoring = null;
sut.Assume(ClientTask.PrepareShutdown_Wave1);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class ServerResponsibilityTests
{
private ClientContext context;
private Mock<ICoordinator> coordinator;
private Mock<IRuntimeProxy> runtime;
private Mock<IServerProxy> server;
private Mock<IText> text;
private ServerResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
context = new ClientContext();
coordinator = new Mock<ICoordinator>();
runtime = new Mock<IRuntimeProxy>();
server = new Mock<IServerProxy>();
text = new Mock<IText>();
context.Responsibilities = responsibilities.Object;
context.Runtime = runtime.Object;
context.Server = server.Object;
sut = new ServerResponsibility(context, coordinator.Object, logger.Object, text.Object);
sut.Assume(ClientTask.RegisterEvents);
}
[TestMethod]
public void Server_MustInitiateShutdownOnEvent()
{
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
server.Raise(s => s.TerminationRequested += null);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void MustNotFailIfDependencyIsNull()
{
context.Server = null;
sut.Assume(ClientTask.DeregisterEvents);
}
}
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright (c) 2025 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Responsibilities;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
{
[TestClass]
public class ShellResponsibilityTests
{
private Mock<IActionCenter> actionCenter;
private ClientContext context;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<IMessageBox> messageBox;
private Mock<IRuntimeProxy> runtime;
private AppSettings settings;
private Mock<ITaskbar> taskbar;
private Mock<IUserInterfaceFactory> uiFactory;
private ShellResponsibility sut;
[TestInitialize]
public void Initialize()
{
var logger = new Mock<ILogger>();
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
actionCenter = new Mock<IActionCenter>();
context = new ClientContext();
hashAlgorithm = new Mock<IHashAlgorithm>();
messageBox = new Mock<IMessageBox>();
runtime = new Mock<IRuntimeProxy>();
settings = new AppSettings();
taskbar = new Mock<ITaskbar>();
uiFactory = new Mock<IUserInterfaceFactory>();
context.HashAlgorithm = hashAlgorithm.Object;
context.MessageBox = messageBox.Object;
context.Responsibilities = responsibilities.Object;
context.Runtime = runtime.Object;
context.Settings = settings;
sut = new ShellResponsibility(
actionCenter.Object,
context,
hashAlgorithm.Object,
logger.Object,
messageBox.Object,
taskbar.Object,
uiFactory.Object);
sut.Assume(ClientTask.RegisterEvents);
}
[TestMethod]
public void Shutdown_MustAskUserToConfirm()
{
var args = new System.ComponentModel.CancelEventArgs();
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
Assert.IsFalse(args.Cancel);
}
[TestMethod]
public void Shutdown_MustNotInitiateIfNotWishedByUser()
{
var args = new System.ComponentModel.CancelEventArgs();
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.No);
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
runtime.Verify(p => p.RequestShutdown(), Times.Never);
Assert.IsTrue(args.Cancel);
}
[TestMethod]
public void Shutdown_MustCloseActionCenterAndTaskbarIfEnabled()
{
settings.UserInterface.ActionCenter.EnableActionCenter = true;
settings.UserInterface.Taskbar.EnableTaskbar = true;
sut.Assume(ClientTask.CloseShell);
actionCenter.Verify(a => a.Close(), Times.Once);
taskbar.Verify(o => o.Close(), Times.Once);
}
[TestMethod]
public void Shutdown_MustNotCloseActionCenterAndTaskbarIfNotEnabled()
{
settings.UserInterface.ActionCenter.EnableActionCenter = false;
settings.UserInterface.Taskbar.EnableTaskbar = false;
sut.Assume(ClientTask.CloseShell);
actionCenter.Verify(a => a.Close(), Times.Never);
taskbar.Verify(o => o.Close(), Times.Never);
}
[TestMethod]
public void Shutdown_MustShowErrorMessageOnCommunicationFailure()
{
var args = new System.ComponentModel.CancelEventArgs();
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(false));
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void Shutdown_MustAskUserForQuitPassword()
{
var args = new System.ComponentModel.CancelEventArgs();
var dialog = new Mock<IPasswordDialog>();
var dialogResult = new PasswordDialogResult { Password = "blobb", Success = true };
settings.Security.QuitPasswordHash = "1234";
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
hashAlgorithm.Setup(h => h.GenerateHashFor(It.Is<string>(s => s == dialogResult.Password))).Returns(settings.Security.QuitPasswordHash);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
Assert.IsFalse(args.Cancel);
}
[TestMethod]
public void Shutdown_MustAbortAskingUserForQuitPassword()
{
var args = new System.ComponentModel.CancelEventArgs();
var dialog = new Mock<IPasswordDialog>();
var dialogResult = new PasswordDialogResult { Success = false };
settings.Security.QuitPasswordHash = "1234";
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Never);
Assert.IsTrue(args.Cancel);
}
[TestMethod]
public void Shutdown_MustNotInitiateIfQuitPasswordIncorrect()
{
var args = new System.ComponentModel.CancelEventArgs();
var dialog = new Mock<IPasswordDialog>();
var dialogResult = new PasswordDialogResult { Password = "blobb", Success = true };
settings.Security.QuitPasswordHash = "1234";
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
hashAlgorithm.Setup(h => h.GenerateHashFor(It.IsAny<string>())).Returns("9876");
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Warning),
It.IsAny<IWindow>()), Times.Once);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Never);
Assert.IsTrue(args.Cancel);
}
[TestMethod]
public void Startup_MustCorrectlyShowTaskbar()
{
settings.UserInterface.Taskbar.EnableTaskbar = true;
sut.Assume(ClientTask.ShowShell);
taskbar.Verify(t => t.Show(), Times.Once);
taskbar.Reset();
settings.UserInterface.Taskbar.EnableTaskbar = false;
sut.Assume(ClientTask.ShowShell);
taskbar.Verify(t => t.Show(), Times.Never);
}
[TestMethod]
public void Startup_MustCorrectlyShowActionCenter()
{
settings.UserInterface.ActionCenter.EnableActionCenter = true;
sut.Assume(ClientTask.ShowShell);
actionCenter.Verify(t => t.Promote(), Times.Once);
actionCenter.Verify(t => t.Show(), Times.Never);
actionCenter.Reset();
settings.UserInterface.ActionCenter.EnableActionCenter = false;
sut.Assume(ClientTask.ShowShell);
actionCenter.Verify(t => t.Promote(), Times.Never);
actionCenter.Verify(t => t.Show(), Times.Never);
}
[TestMethod]
public void TerminationActivator_MustCorrectlyInitiateShutdown()
{
var order = 0;
var pause = 0;
var resume = 0;
var terminationActivator = new Mock<ITerminationActivator>();
context.Activators.Add(terminationActivator.Object);
messageBox.Setup(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.IsAny<MessageBoxIcon>(),
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
terminationActivator.Setup(t => t.Pause()).Callback(() => pause = ++order);
terminationActivator.Setup(t => t.Resume()).Callback(() => resume = ++order);
sut.Assume(ClientTask.RegisterEvents);
terminationActivator.Raise(t => t.Activated += null);
Assert.AreEqual(1, pause);
Assert.AreEqual(2, resume);
terminationActivator.Verify(t => t.Pause(), Times.Once);
terminationActivator.Verify(t => t.Resume(), Times.Once);
runtime.Verify(p => p.RequestShutdown(), Times.Once);
}
}
}

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" /> <Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -61,89 +61,90 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <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> <HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath> <HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> <Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath> <HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath> <HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath> <HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.IO.Packaging, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.IO.Packaging, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Packaging.9.0.1\lib\net462\System.IO.Packaging.dll</HintPath> <HintPath>..\packages\System.IO.Packaging.9.0.8\lib\net462\System.IO.Packaging.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath> <HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath> <HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime" /> <Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
@@ -254,11 +255,8 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -266,13 +264,19 @@
<PropertyGroup> <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> <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> </PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" /> <Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
</Target> </Target>
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
</Project> </Project>

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
@@ -12,7 +12,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -28,27 +28,27 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" /> <bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" /> <assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.3.0.0" newVersion="8.3.0.0" /> <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" /> <assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" />
@@ -56,7 +56,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@@ -1,27 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Castle.Core" version="5.1.1" targetFramework="net48" /> <package id="Castle.Core" version="5.2.1" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" /> <package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" /> <package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
<package id="Moq" version="4.20.72" targetFramework="net48" /> <package id="Moq" version="4.20.72" targetFramework="net48" />
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" /> <package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" /> <package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" /> <package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
<package id="System.Buffers" version="4.6.0" targetFramework="net48" /> <package id="System.Buffers" version="4.6.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" /> <package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" /> <package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" /> <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.IO.Packaging" version="9.0.1" targetFramework="net48" /> <package id="System.IO.Packaging" version="9.0.8" targetFramework="net48" />
<package id="System.Memory" version="4.6.0" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" /> <package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" /> <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" /> <package id="System.ValueTuple" version="4.6.1" targetFramework="net48" />
</packages> </packages>

View File

@@ -15,7 +15,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" /> <assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.3.0.0" newVersion="8.3.0.0" /> <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" /> <assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" />

View File

@@ -82,6 +82,11 @@ namespace SafeExamBrowser.Client
/// </summary> /// </summary>
internal IProctoringController Proctoring { get; set; } internal IProctoringController Proctoring { get; set; }
/// <summary>
/// Indicates that the user already provided the correct quit password.
/// </summary>
internal bool QuitPasswordValidated { get; set; }
/// <summary> /// <summary>
/// The client responsibilities. /// The client responsibilities.
/// </summary> /// </summary>

View File

@@ -58,6 +58,7 @@ using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows; using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Shared;
using SafeExamBrowser.UserInterface.Shared.Activators; using SafeExamBrowser.UserInterface.Shared.Activators;
using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
@@ -92,6 +93,7 @@ namespace SafeExamBrowser.Client
private ITaskview taskview; private ITaskview taskview;
private IUserInfo userInfo; private IUserInfo userInfo;
private IText text; private IText text;
private WindowGuard windowGuard;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
internal ClientController ClientController { get; private set; } internal ClientController ClientController { get; private set; }
@@ -105,6 +107,7 @@ namespace SafeExamBrowser.Client
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
windowGuard = new WindowGuard(ModuleLogger(nameof(WindowGuard)));
uiFactory = BuildUserInterfaceFactory(); uiFactory = BuildUserInterfaceFactory();
actionCenter = uiFactory.CreateActionCenter(); actionCenter = uiFactory.CreateActionCenter();
context = new ClientContext(); context = new ClientContext();
@@ -162,6 +165,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new I18nOperation(logger, text)); operations.Enqueue(new I18nOperation(logger, text));
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken)); operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy)); operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
operations.Enqueue(new WindowGuardOperation(context, logger, windowGuard));
operations.Enqueue(new DelegateOperation(UpdateAppConfig)); operations.Enqueue(new DelegateOperation(UpdateAppConfig));
operations.Enqueue(new DelegateOperation(BuildIntegrityModule)); operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
operations.Enqueue(new DelegateOperation(BuildPowerSupply)); operations.Enqueue(new DelegateOperation(BuildPowerSupply));
@@ -169,6 +173,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS)); operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new PermissionOperation(context, logger, networkAdapter));
operations.Enqueue(new ApplicationOperation(context, applicationFactory, fileSystemDialog, logger, messageBox, applicationMonitor, splashScreen, text)); operations.Enqueue(new ApplicationOperation(context, applicationFactory, fileSystemDialog, logger, messageBox, applicationMonitor, splashScreen, text));
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar)); operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation)); operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
@@ -196,7 +201,7 @@ namespace SafeExamBrowser.Client
responsibilities.Enqueue(new IntegrityResponsibility(context, ModuleLogger(nameof(IntegrityResponsibility)), text)); responsibilities.Enqueue(new IntegrityResponsibility(context, ModuleLogger(nameof(IntegrityResponsibility)), text));
responsibilities.Enqueue(new MonitoringResponsibility(actionCenter, applicationMonitor, context, coordinator, displayMonitor, explorerShell, ModuleLogger(nameof(MonitoringResponsibility)), sentinel, taskbar, text)); responsibilities.Enqueue(new MonitoringResponsibility(actionCenter, applicationMonitor, context, coordinator, displayMonitor, explorerShell, ModuleLogger(nameof(MonitoringResponsibility)), sentinel, taskbar, text));
responsibilities.Enqueue(new NetworkResponsibility(context, ModuleLogger(nameof(NetworkResponsibility)), networkAdapter, text, uiFactory)); responsibilities.Enqueue(new NetworkResponsibility(context, ModuleLogger(nameof(NetworkResponsibility)), networkAdapter, text, uiFactory));
responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), uiFactory)); responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), messageBox, uiFactory));
responsibilities.Enqueue(new ServerResponsibility(context, coordinator, ModuleLogger(nameof(ServerResponsibility)), text)); responsibilities.Enqueue(new ServerResponsibility(context, coordinator, ModuleLogger(nameof(ServerResponsibility)), text));
responsibilities.Enqueue(new ShellResponsibility(actionCenter, context, new HashAlgorithm(), ModuleLogger(nameof(ShellResponsibility)), messageBox, taskbar, uiFactory)); responsibilities.Enqueue(new ShellResponsibility(actionCenter, context, new HashAlgorithm(), ModuleLogger(nameof(ShellResponsibility)), messageBox, taskbar, uiFactory));
@@ -337,7 +342,8 @@ namespace SafeExamBrowser.Client
{ {
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator))); var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)));
var server = new ServerProxy(context.AppConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter); var server = new ServerProxy(context.AppConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter);
var operation = new ServerOperation(context, logger, server); var invigilator = new Invigilator(ModuleLogger(nameof(Invigilator)), server);
var operation = new ServerOperation(actionCenter, context, invigilator, logger, server, taskbar, uiFactory);
context.Server = server; context.Server = server;
@@ -381,9 +387,9 @@ namespace SafeExamBrowser.Client
switch (uiMode) switch (uiMode)
{ {
case UserInterfaceMode.Mobile: case UserInterfaceMode.Mobile:
return new Mobile.FileSystemDialogFactory(systemInfo, text); return new Mobile.FileSystemDialogFactory(systemInfo, text, windowGuard);
default: default:
return new Desktop.FileSystemDialogFactory(systemInfo, text); return new Desktop.FileSystemDialogFactory(systemInfo, text, windowGuard);
} }
} }
@@ -403,9 +409,9 @@ namespace SafeExamBrowser.Client
switch (uiMode) switch (uiMode)
{ {
case UserInterfaceMode.Mobile: case UserInterfaceMode.Mobile:
return new Mobile.UserInterfaceFactory(text); return new Mobile.UserInterfaceFactory(text, windowGuard);
default: default:
return new Desktop.UserInterfaceFactory(text); return new Desktop.UserInterfaceFactory(text, windowGuard);
} }
} }

View File

@@ -16,12 +16,7 @@ namespace SafeExamBrowser.Client.Operations
/// </summary> /// </summary>
internal abstract class ClientOperation : IOperation internal abstract class ClientOperation : IOperation
{ {
protected ClientContext Context { get; private set; } protected ClientContext Context { get; }
/// <summary>
/// TODO: In case this event is neither used by the runtime, either remove it completely or then move it to a separate interface!
/// </summary>
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public abstract event StatusChangedEventHandler StatusChanged; public abstract event StatusChangedEventHandler StatusChanged;

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.Core.OperationModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Client.Operations
{
internal class ClientOperationSequence : OperationSequence<IOperation>
{
private readonly ISplashScreen splashScreen;
public ClientOperationSequence(ILogger logger, IEnumerable<IOperation> operations, ISplashScreen splashScreen) : base(logger, operations)
{
this.splashScreen = splashScreen;
ProgressChanged += Operations_ProgressChanged;
StatusChanged += Operations_StatusChanged;
}
private void Operations_ProgressChanged(ProgressChangedEventArgs args)
{
if (args.CurrentValue.HasValue)
{
splashScreen.SetValue(args.CurrentValue.Value);
}
if (args.IsIndeterminate == true)
{
splashScreen.SetIndeterminate();
}
if (args.MaxValue.HasValue)
{
splashScreen.SetMaxValue(args.MaxValue.Value);
}
if (args.Progress == true)
{
splashScreen.Progress();
}
if (args.Regress == true)
{
splashScreen.Regress();
}
}
private void Operations_StatusChanged(TextKey status)
{
splashScreen.UpdateStatus(status, true);
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.Client.Operations
{
internal class PermissionOperation : ClientOperation
{
private readonly ILogger logger;
private readonly INetworkAdapter networkAdapter;
public override event StatusChangedEventHandler StatusChanged;
public PermissionOperation(ClientContext context, ILogger logger, INetworkAdapter networkAdapter) : base(context)
{
this.logger = logger;
this.networkAdapter = networkAdapter;
}
public override OperationResult Perform()
{
logger.Info("Initializing permissions...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializePermissions);
RequestNetworkAdapterAccess();
return OperationResult.Success;
}
public override OperationResult Revert()
{
return OperationResult.Success;
}
private void RequestNetworkAdapterAccess()
{
var granted = networkAdapter.RequestAccess();
if (granted)
{
logger.Info("Permission to access the wireless networking functionality has been granted.");
}
else
{
logger.Warn("Permission to access the wireless networking functionality has not been granted! " +
"If required, please grant the location permission manually under 'Privacy & Security' in the system settings.");
}
}
}
}

View File

@@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts; using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -51,12 +50,6 @@ namespace SafeExamBrowser.Client.Operations
controller.Initialize(Context.Settings.Proctoring); controller.Initialize(Context.Settings.Proctoring);
if (Context.Settings.SessionMode == SessionMode.Server && Context.Settings.Proctoring.ShowRaiseHandNotification)
{
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.ActionCenter, Context.Settings.Proctoring));
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.Taskbar, Context.Settings.Proctoring));
}
foreach (var notification in controller.Notifications) foreach (var notification in controller.Notifications)
{ {
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter)); actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter));

View File

@@ -12,20 +12,37 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts; using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings; using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {
internal class ServerOperation : ClientOperation internal class ServerOperation : ClientOperation
{ {
private readonly IActionCenter actionCenter;
private readonly IInvigilator invigilator;
private readonly ILogger logger; private readonly ILogger logger;
private readonly IServerProxy server; private readonly IServerProxy server;
private readonly ITaskbar taskbar;
private readonly IUserInterfaceFactory uiFactory;
public override event StatusChangedEventHandler StatusChanged; public override event StatusChangedEventHandler StatusChanged;
public ServerOperation(ClientContext context, ILogger logger, IServerProxy server) : base(context) public ServerOperation(
IActionCenter actionCenter,
ClientContext context,
IInvigilator invigilator,
ILogger logger,
IServerProxy server,
ITaskbar taskbar,
IUserInterfaceFactory uiFactory) : base(context)
{ {
this.actionCenter = actionCenter;
this.invigilator = invigilator;
this.logger = logger; this.logger = logger;
this.server = server; this.server = server;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
} }
public override OperationResult Perform() public override OperationResult Perform()
@@ -42,6 +59,14 @@ namespace SafeExamBrowser.Client.Operations
Context.AppConfig.ServerOauth2Token, Context.AppConfig.ServerOauth2Token,
Context.Settings.Server); Context.Settings.Server);
server.StartConnectivity(); server.StartConnectivity();
if (Context.Settings.Server.Invigilation.ShowRaiseHandNotification)
{
invigilator.Initialize(Context.Settings.Server.Invigilation);
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.ActionCenter, Context.Settings.Server.Invigilation));
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.Taskbar, Context.Settings.Server.Invigilation));
}
} }
return OperationResult.Success; return OperationResult.Success;

View File

@@ -83,7 +83,7 @@ namespace SafeExamBrowser.Client.Operations
InitializeSystemComponents(); InitializeSystemComponents();
InitializeActionCenter(); InitializeActionCenter();
InitializeTaskbar(); InitializeTaskbar();
InitializeTaskview(); //InitializeTaskview();
InitializeActivators(); InitializeActivators();
InitializeAlwaysOnState(); InitializeAlwaysOnState();
@@ -112,11 +112,11 @@ namespace SafeExamBrowser.Client.Operations
actionCenterActivator.Start(); actionCenterActivator.Start();
} }
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator) //if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
{ //{
taskview.Register(taskViewActivator); // taskview.Register(taskViewActivator);
taskViewActivator.Start(); // taskViewActivator.Start();
} //}
if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator) if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator)
{ {

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
namespace SafeExamBrowser.Client.Operations
{
internal class WindowGuardOperation : ClientOperation
{
private readonly ILogger logger;
private readonly IWindowGuard guard;
public WindowGuardOperation(ClientContext context, ILogger logger, IWindowGuard guard) : base(context)
{
this.logger = logger;
this.guard = guard;
}
public override event StatusChangedEventHandler StatusChanged;
public override OperationResult Perform()
{
logger.Info("Initializing window guard...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWindowGuard);
if (Context.Settings.Proctoring.Enabled || Context.Settings.Security.AllowWindowCapture)
{
guard.Deactivate();
logger.Info($"Deactivated window guard because {(Context.Settings.Proctoring.Enabled ? "proctoring" : "window capturing")} is enabled.");
}
else
{
guard.Activate();
logger.Info("Activated window guard.");
}
return OperationResult.Success;
}
public override OperationResult Revert()
{
return OperationResult.Success;
}
}
}

View File

@@ -51,6 +51,6 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.9.0.787")] [assembly: AssemblyVersion("3.10.0.826")]
[assembly: AssemblyFileVersion("3.9.0.787")] [assembly: AssemblyFileVersion("3.10.0.826")]
[assembly: AssemblyInformationalVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0.0")]

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class ApplicationsResponsibility : ClientResponsibility
{
public ApplicationsResponsibility(ClientContext context, ILogger logger) : base(context, logger)
{
}
public override void Assume(ClientTask task)
{
if (task == ClientTask.AutoStartApplications)
{
AutoStart();
}
}
private void AutoStart()
{
foreach (var application in Context.Applications)
{
if (application.AutoStart)
{
Logger.Info($"Auto-starting '{application.Name}'...");
application.Start();
}
}
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2025 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.Text.RegularExpressions;
using System.Threading;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class BrowserResponsibility : ClientResponsibility
{
private readonly ICoordinator coordinator;
private readonly IMessageBox messageBox;
private readonly IRuntimeProxy runtime;
private readonly ISplashScreen splashScreen;
private readonly ITaskbar taskbar;
private IBrowserApplication Browser => Context.Browser;
public BrowserResponsibility(
ClientContext context,
ICoordinator coordinator,
ILogger logger,
IMessageBox messageBox,
IRuntimeProxy runtime,
ISplashScreen splashScreen,
ITaskbar taskbar) : base(context, logger)
{
this.coordinator = coordinator;
this.messageBox = messageBox;
this.runtime = runtime;
this.splashScreen = splashScreen;
this.taskbar = taskbar;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.AutoStartApplications:
AutoStartBrowser();
break;
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
}
}
private void AutoStartBrowser()
{
if (Settings.Browser.EnableBrowser && Browser.AutoStart)
{
Logger.Info("Auto-starting browser...");
Browser.Start();
}
}
private void DeregisterEvents()
{
if (Browser != default)
{
Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested;
Browser.LoseFocusRequested -= Browser_LoseFocusRequested;
Browser.TerminationRequested -= Browser_TerminationRequested;
Browser.UserIdentifierDetected -= Browser_UserIdentifierDetected;
}
}
private void RegisterEvents()
{
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
Browser.LoseFocusRequested += Browser_LoseFocusRequested;
Browser.TerminationRequested += Browser_TerminationRequested;
Browser.UserIdentifierDetected += Browser_UserIdentifierDetected;
}
private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
{
args.AllowDownload = false;
if (IsAllowedToReconfigure(args.Url))
{
if (coordinator.RequestReconfigurationLock())
{
args.AllowDownload = true;
args.Callback = Browser_ConfigurationDownloadFinished;
args.DownloadPath = Path.Combine(Context.AppConfig.TemporaryDirectory, fileName);
splashScreen.Show();
splashScreen.BringToForeground();
splashScreen.SetIndeterminate();
splashScreen.UpdateStatus(TextKey.OperationStatus_InitializeSession, true);
Logger.Info($"Allowed download request for configuration file '{fileName}'.");
}
else
{
Logger.Warn($"A reconfiguration is already in progress, denied download request for configuration file '{fileName}'!");
}
}
else
{
Logger.Info($"Reconfiguration is not allowed, denied download request for configuration file '{fileName}'.");
}
}
private bool IsAllowedToReconfigure(string url)
{
var allow = false;
var hasQuitPassword = !string.IsNullOrWhiteSpace(Settings.Security.QuitPasswordHash);
var hasUrl = !string.IsNullOrWhiteSpace(Settings.Security.ReconfigurationUrl);
if (hasQuitPassword)
{
if (hasUrl)
{
var expression = Regex.Escape(Settings.Security.ReconfigurationUrl).Replace(@"\*", ".*");
var regex = new Regex($"^{expression}$", RegexOptions.IgnoreCase);
var sebUrl = url.Replace(Uri.UriSchemeHttps, Context.AppConfig.SebUriSchemeSecure).Replace(Uri.UriSchemeHttp, Context.AppConfig.SebUriScheme);
allow = Settings.Security.AllowReconfiguration && (regex.IsMatch(url) || regex.IsMatch(sebUrl));
}
else
{
Logger.Warn("The active configuration does not contain a valid reconfiguration URL!");
}
}
else
{
allow = Settings.ConfigurationMode == ConfigurationMode.ConfigureClient || Settings.Security.AllowReconfiguration;
}
return allow;
}
private void Browser_ConfigurationDownloadFinished(bool success, string url, string filePath = null)
{
if (success)
{
PrepareShutdown();
var communication = runtime.RequestReconfiguration(filePath, url);
if (communication.Success)
{
Logger.Info($"Sent reconfiguration request for '{filePath}' to the runtime.");
}
else
{
Logger.Error($"Failed to communicate reconfiguration request for '{filePath}'!");
messageBox.Show(TextKey.MessageBox_ReconfigurationError, TextKey.MessageBox_ReconfigurationErrorTitle, icon: MessageBoxIcon.Error, parent: splashScreen);
splashScreen.Hide();
coordinator.ReleaseReconfigurationLock();
}
}
else
{
Logger.Error($"Failed to download configuration file '{filePath}'!");
messageBox.Show(TextKey.MessageBox_ConfigurationDownloadError, TextKey.MessageBox_ConfigurationDownloadErrorTitle, icon: MessageBoxIcon.Error, parent: splashScreen);
splashScreen.Hide();
coordinator.ReleaseReconfigurationLock();
}
}
private void Browser_LoseFocusRequested(bool forward)
{
taskbar.Focus(forward);
}
private void Browser_UserIdentifierDetected(string identifier)
{
if (Settings.SessionMode == SessionMode.Server)
{
var response = Context.Server.SendUserIdentifier(identifier);
while (!response.Success)
{
Logger.Error($"Failed to communicate user identifier with server! {response.Message}");
Thread.Sleep(Settings.Server.RequestAttemptInterval);
response = Context.Server.SendUserIdentifier(identifier);
}
}
}
private void Browser_TerminationRequested()
{
Logger.Info("Attempting to shutdown as requested by the browser...");
TryRequestShutdown();
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.Core.Contracts.ResponsibilityModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.Responsibilities
{
internal abstract class ClientResponsibility : IResponsibility<ClientTask>
{
protected ClientContext Context { get; private set; }
protected ILogger Logger { get; private set; }
protected AppSettings Settings => Context.Settings;
internal ClientResponsibility(ClientContext context, ILogger logger)
{
Context = context;
Logger = logger;
}
public abstract void Assume(ClientTask task);
protected void PauseActivators()
{
foreach (var activator in Context.Activators)
{
activator.Pause();
}
}
protected bool IsValidQuitPassword(string password)
{
var actual = Context.HashAlgorithm.GenerateHashFor(password);
var expected = Settings.Security.QuitPasswordHash;
var valid = expected.Equals(actual, StringComparison.OrdinalIgnoreCase);
if (valid)
{
Context.QuitPasswordValidated = true;
}
return valid;
}
protected void PrepareShutdown()
{
Context.Responsibilities.Delegate(ClientTask.PrepareShutdown_Wave1);
Context.Responsibilities.Delegate(ClientTask.PrepareShutdown_Wave2);
}
protected void ResumeActivators()
{
foreach (var activator in Context.Activators)
{
activator.Resume();
}
}
protected LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
{
Logger.Info("Showing lock screen...");
PauseActivators();
Context.LockScreen = Context.UserInterfaceFactory.CreateLockScreen(message, title, options, Settings.UserInterface.LockScreen);
Context.LockScreen.Show();
if (Settings.SessionMode == SessionMode.Server)
{
SendLockScreenNotification(message);
}
var result = WaitForLockScreenResolution();
Context.LockScreen.Close();
Context.LockScreen = default;
ResumeActivators();
if (Settings.SessionMode == SessionMode.Server)
{
SendLockScreenConfirmation();
}
Logger.Info("Closed lock screen.");
return result;
}
protected bool TryRequestShutdown()
{
PrepareShutdown();
var communication = Context.Runtime.RequestShutdown();
if (!communication.Success)
{
Logger.Error("Failed to communicate shutdown request to the runtime!");
Context.MessageBox.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error);
}
return communication.Success;
}
private void SendLockScreenConfirmation()
{
var response = Context.Server.ConfirmLockScreen();
if (!response.Success)
{
Logger.Error($"Failed to send lock screen confirmation to server! Message: {response.Message}.");
}
}
private void SendLockScreenNotification(string message)
{
var response = Context.Server.LockScreen(message);
if (!response.Success)
{
Logger.Error($"Failed to send lock screen notification to server! Message: {response.Message}.");
}
}
private LockScreenResult WaitForLockScreenResolution()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var result = default(LockScreenResult);
for (var unlocked = false; !unlocked;)
{
result = Context.LockScreen.WaitForResult();
if (result.Canceled)
{
Logger.Info("The lock screen has been canceled automatically.");
unlocked = true;
}
else if (hasQuitPassword)
{
var passwordHash = Context.HashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
Logger.Info("The user entered the correct unlock password.");
unlocked = true;
}
else
{
Logger.Info("The user entered a wrong unlock password.");
Context.MessageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: Context.LockScreen);
}
}
else
{
Logger.Warn($"No unlock password is defined, allowing user to resume session!");
unlocked = true;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2025 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/.
*/
namespace SafeExamBrowser.Client.Responsibilities
{
/// <summary>
/// Defines all tasks assumed by the responsibilities of the client application.
/// </summary>
internal enum ClientTask
{
/// <summary>
/// Auto-start the browser and any potential third-party applications.
/// </summary>
AutoStartApplications,
/// <summary>
/// Close the shell.
/// </summary>
CloseShell,
/// <summary>
/// Deregister all event handlers during application termination.
/// </summary>
DeregisterEvents,
/// <summary>
/// Execute wave 1 of the application shutdown preparation. It should be used for potentially long-running operations which require all
/// other (security) functionalities to continue working normally.
/// </summary>
PrepareShutdown_Wave1,
/// <summary>
/// Execute wave 2 of the application shutdown preparation. It should be used by all remaining responsibilities which must continue to work
/// normally during the execution of wave 1.
/// </summary>
PrepareShutdown_Wave2,
/// <summary>
/// Register all event handlers during application initialization.
/// </summary>
RegisterEvents,
/// <summary>
/// Schedule the verification of the application integrity.
/// </summary>
ScheduleIntegrityVerification,
/// <summary>
/// Show the shell.
/// </summary>
ShowShell,
/// <summary>
/// Start the monitoring of different (security) aspects.
/// </summary>
StartMonitoring,
/// <summary>
/// Update the session integrity during application termination.
/// </summary>
UpdateSessionIntegrity,
/// <summary>
/// Verify the session integrity during application initialization.
/// </summary>
VerifySessionIntegrity
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2025 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.Linq;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Events;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class CommunicationResponsibility : ClientResponsibility
{
private readonly ICoordinator coordinator;
private readonly IMessageBox messageBox;
private readonly IRuntimeProxy runtime;
private readonly Action shutdown;
private readonly ISplashScreen splashScreen;
private readonly IText text;
private readonly IUserInterfaceFactory uiFactory;
private IClientHost ClientHost => Context.ClientHost;
public CommunicationResponsibility(
ClientContext context,
ICoordinator coordinator,
ILogger logger,
IMessageBox messageBox,
IRuntimeProxy runtime,
Action shutdown,
ISplashScreen splashScreen,
IText text,
IUserInterfaceFactory uiFactory) : base(context, logger)
{
this.coordinator = coordinator;
this.messageBox = messageBox;
this.runtime = runtime;
this.shutdown = shutdown;
this.splashScreen = splashScreen;
this.text = text;
this.uiFactory = uiFactory;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
}
}
private void DeregisterEvents()
{
if (ClientHost != default)
{
ClientHost.ExamSelectionRequested -= ClientHost_ExamSelectionRequested;
ClientHost.MessageBoxRequested -= ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested -= ClientHost_PasswordRequested;
ClientHost.ReconfigurationAborted -= ClientHost_ReconfigurationAborted;
ClientHost.ReconfigurationDenied -= ClientHost_ReconfigurationDenied;
ClientHost.ServerFailureActionRequested -= ClientHost_ServerFailureActionRequested;
ClientHost.Shutdown -= ClientHost_Shutdown;
}
runtime.ConnectionLost -= Runtime_ConnectionLost;
}
private void RegisterEvents()
{
ClientHost.ExamSelectionRequested += ClientHost_ExamSelectionRequested;
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
ClientHost.ReconfigurationAborted += ClientHost_ReconfigurationAborted;
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
ClientHost.ServerFailureActionRequested += ClientHost_ServerFailureActionRequested;
ClientHost.Shutdown += ClientHost_Shutdown;
runtime.ConnectionLost += Runtime_ConnectionLost;
}
private void ClientHost_ExamSelectionRequested(ExamSelectionRequestEventArgs args)
{
Logger.Info($"Received exam selection request with id '{args.RequestId}'.");
var exams = args.Exams.Select(e => new Exam { Id = e.id, LmsName = e.lms, Name = e.name, Url = e.url });
var dialog = uiFactory.CreateExamSelectionDialog(exams);
var result = dialog.Show();
runtime.SubmitExamSelectionResult(args.RequestId, result.Success, result.SelectedExam?.Id);
Logger.Info($"Exam selection request with id '{args.RequestId}' is complete.");
}
private void ClientHost_MessageBoxRequested(MessageBoxRequestEventArgs args)
{
Logger.Info($"Received message box request with id '{args.RequestId}'.");
var action = (MessageBoxAction) args.Action;
var icon = (MessageBoxIcon) args.Icon;
var result = messageBox.Show(args.Message, args.Title, action, icon, parent: splashScreen);
runtime.SubmitMessageBoxResult(args.RequestId, (int) result);
Logger.Info($"Message box request with id '{args.RequestId}' yielded result '{result}'.");
}
private void ClientHost_PasswordRequested(PasswordRequestEventArgs args)
{
var message = default(TextKey);
var title = default(TextKey);
Logger.Info($"Received input request with id '{args.RequestId}' for the {args.Purpose.ToString().ToLower()} password.");
switch (args.Purpose)
{
case PasswordRequestPurpose.LocalAdministrator:
message = TextKey.PasswordDialog_LocalAdminPasswordRequired;
title = TextKey.PasswordDialog_LocalAdminPasswordRequiredTitle;
break;
case PasswordRequestPurpose.LocalSettings:
message = TextKey.PasswordDialog_LocalSettingsPasswordRequired;
title = TextKey.PasswordDialog_LocalSettingsPasswordRequiredTitle;
break;
case PasswordRequestPurpose.Settings:
message = TextKey.PasswordDialog_SettingsPasswordRequired;
title = TextKey.PasswordDialog_SettingsPasswordRequiredTitle;
break;
}
var dialog = uiFactory.CreatePasswordDialog(text.Get(message), text.Get(title));
var result = dialog.Show();
runtime.SubmitPassword(args.RequestId, result.Success, result.Password);
Logger.Info($"Password request with id '{args.RequestId}' was {(result.Success ? "successful" : "aborted by the user")}.");
}
private void ClientHost_ReconfigurationAborted()
{
Logger.Info("The reconfiguration was aborted by the runtime.");
splashScreen.Hide();
coordinator.ReleaseReconfigurationLock();
}
private void ClientHost_ReconfigurationDenied(ReconfigurationEventArgs args)
{
Logger.Info($"The reconfiguration request for '{args.ConfigurationPath}' was denied by the runtime!");
messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle, parent: splashScreen);
splashScreen.Hide();
coordinator.ReleaseReconfigurationLock();
}
private void ClientHost_ServerFailureActionRequested(ServerFailureActionRequestEventArgs args)
{
Logger.Info($"Received server failure action request with id '{args.RequestId}'.");
var dialog = uiFactory.CreateServerFailureDialog(args.Message, args.ShowFallback);
var result = dialog.Show();
runtime.SubmitServerFailureActionResult(args.RequestId, result.Abort, result.Fallback, result.Retry);
Logger.Info($"Server failure action request with id '{args.RequestId}' is complete, the user chose to {(result.Abort ? "abort" : (result.Fallback ? "fallback" : "retry"))}.");
}
private void ClientHost_Shutdown()
{
shutdown.Invoke();
}
private void Runtime_ConnectionLost()
{
Logger.Error("Lost connection to the runtime!");
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
shutdown.Invoke();
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2025 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.Linq;
using System.Threading.Tasks;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class IntegrityResponsibility : ClientResponsibility
{
private readonly IText text;
private IIntegrityModule IntegrityModule => Context.IntegrityModule;
public IntegrityResponsibility(ClientContext context, ILogger logger, IText text) : base(context, logger)
{
this.text = text;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.ScheduleIntegrityVerification:
ScheduleIntegrityVerification();
break;
case ClientTask.UpdateSessionIntegrity:
UpdateSessionIntegrity();
break;
case ClientTask.VerifySessionIntegrity:
VerifySessionIntegrity();
break;
}
}
private void ScheduleIntegrityVerification()
{
const int FIVE_MINUTES = 300000;
const int TEN_MINUTES = 600000;
var timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += (o, args) => VerifyApplicationIntegrity();
timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
timer.Start();
}
private void UpdateSessionIntegrity()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings?.Security.QuitPasswordHash);
if (hasQuitPassword)
{
IntegrityModule?.ClearSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
}
}
private void VerifyApplicationIntegrity()
{
Logger.Info($"Attempting to verify application integrity...");
if (IntegrityModule.TryVerifyCodeSignature(out var isValid))
{
if (isValid)
{
Logger.Info("Application integrity successfully verified.");
}
else
{
Logger.Warn("Application integrity is compromised!");
ShowLockScreen(text.Get(TextKey.LockScreen_ApplicationIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
}
}
else
{
Logger.Warn("Failed to verify application integrity!");
}
}
private void VerifySessionIntegrity()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
if (hasQuitPassword && Settings.Security.VerifySessionIntegrity)
{
Logger.Info($"Attempting to verify session integrity...");
if (IntegrityModule.TryVerifySessionIntegrity(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl, out var isValid))
{
if (isValid)
{
Logger.Info("Session integrity successfully verified.");
IntegrityModule.CacheSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
}
else
{
Logger.Warn("Session integrity is compromised!");
Task.Delay(1000).ContinueWith(_ =>
{
ShowLockScreen(text.Get(TextKey.LockScreen_SessionIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
});
}
}
else
{
Logger.Warn("Failed to verify session integrity!");
}
}
}
}
}

View File

@@ -0,0 +1,334 @@
/*
* Copyright (c) 2025 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 System.Linq;
using Microsoft.Win32;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.System;
using SafeExamBrowser.Monitoring.Contracts.System.Events;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class MonitoringResponsibility : ClientResponsibility
{
private readonly IActionCenter actionCenter;
private readonly IApplicationMonitor applicationMonitor;
private readonly ICoordinator coordinator;
private readonly IDisplayMonitor displayMonitor;
private readonly IExplorerShell explorerShell;
private readonly ISystemSentinel sentinel;
private readonly ITaskbar taskbar;
private readonly IText text;
public MonitoringResponsibility(
IActionCenter actionCenter,
IApplicationMonitor applicationMonitor,
ClientContext context,
ICoordinator coordinator,
IDisplayMonitor displayMonitor,
IExplorerShell explorerShell,
ILogger logger,
ISystemSentinel sentinel,
ITaskbar taskbar,
IText text) : base(context, logger)
{
this.actionCenter = actionCenter;
this.applicationMonitor = applicationMonitor;
this.coordinator = coordinator;
this.displayMonitor = displayMonitor;
this.explorerShell = explorerShell;
this.sentinel = sentinel;
this.taskbar = taskbar;
this.text = text;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.PrepareShutdown_Wave2:
StopMonitoring();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
case ClientTask.StartMonitoring:
StartMonitoring();
break;
}
}
private void DeregisterEvents()
{
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
sentinel.CursorChanged -= Sentinel_CursorChanged;
sentinel.EaseOfAccessChanged -= Sentinel_EaseOfAccessChanged;
sentinel.SessionChanged -= Sentinel_SessionChanged;
sentinel.StickyKeysChanged -= Sentinel_StickyKeysChanged;
}
private void StopMonitoring()
{
sentinel.StopMonitoring();
}
private void RegisterEvents()
{
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
applicationMonitor.TerminationFailed += ApplicationMonitor_TerminationFailed;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
sentinel.CursorChanged += Sentinel_CursorChanged;
sentinel.EaseOfAccessChanged += Sentinel_EaseOfAccessChanged;
sentinel.SessionChanged += Sentinel_SessionChanged;
sentinel.StickyKeysChanged += Sentinel_StickyKeysChanged;
}
private void StartMonitoring()
{
sentinel.StartMonitoringSystemEvents();
if (!Settings.Security.AllowStickyKeys)
{
sentinel.StartMonitoringStickyKeys();
}
if (Settings.Security.VerifyCursorConfiguration)
{
sentinel.StartMonitoringCursors();
}
if (Settings.Service.IgnoreService)
{
sentinel.StartMonitoringEaseOfAccess();
}
}
private void ApplicationMonitor_ExplorerStarted()
{
Logger.Info("Trying to terminate Windows explorer...");
explorerShell.Terminate();
Logger.Info("Re-initializing working area...");
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
Logger.Info("Re-initializing shell...");
actionCenter.InitializeBounds();
taskbar.InitializeBounds();
Logger.Info("Desktop successfully restored.");
}
private void ApplicationMonitor_TerminationFailed(IEnumerable<RunningApplication> applications)
{
var applicationList = string.Join(Environment.NewLine, applications.Select(a => $"- {a.Name}"));
var message = $"{text.Get(TextKey.LockScreen_ApplicationsMessage)}{Environment.NewLine}{Environment.NewLine}{applicationList}";
var title = text.Get(TextKey.LockScreen_Title);
var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsAllowOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsTerminateOption) };
Logger.Warn("Detected termination failure of blacklisted application(s)!");
var result = ShowLockScreen(message, title, new[] { allowOption, terminateOption });
if (result.OptionId == allowOption.Id)
{
Logger.Info($"The blacklisted application(s) {string.Join(", ", applications.Select(a => $"'{a.Name}'"))} will be temporarily allowed.");
}
else if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
}
private void DisplayMonitor_DisplaySettingsChanged()
{
Logger.Info("Re-initializing working area...");
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
Logger.Info("Re-initializing shell...");
actionCenter.InitializeBounds();
Context.LockScreen?.InitializeBounds();
taskbar.InitializeBounds();
Logger.Info("Desktop successfully restored.");
if (!displayMonitor.ValidateConfiguration(Settings.Display).IsAllowed)
{
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationTerminateOption) };
var message = text.Get(TextKey.LockScreen_DisplayConfigurationMessage);
var title = text.Get(TextKey.LockScreen_Title);
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
}
}
private void Sentinel_CursorChanged(SentinelEventArgs args)
{
if (coordinator.RequestSessionLock())
{
var message = text.Get(TextKey.LockScreen_CursorMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorTerminateOption) };
args.Allow = true;
Logger.Info("Cursor changed! Attempting to show lock screen...");
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == continueOption.Id)
{
Logger.Info("The session will be allowed to resume as requested by the user...");
}
else if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
Logger.Info("Cursor changed but lock screen is already active.");
}
}
private void Sentinel_EaseOfAccessChanged(SentinelEventArgs args)
{
if (coordinator.RequestSessionLock())
{
var message = text.Get(TextKey.LockScreen_EaseOfAccessMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_EaseOfAccessContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_EaseOfAccessTerminateOption) };
args.Allow = true;
Logger.Info("Ease of access changed! Attempting to show lock screen...");
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == continueOption.Id)
{
Logger.Info("The session will be allowed to resume as requested by the user...");
}
else if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
Logger.Info("Ease of access changed but lock screen is already active.");
}
}
private void Sentinel_SessionChanged(SessionSwitchReason reason)
{
var allowed = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
var disabled = Settings.Security.DisableSessionChangeLockScreen;
var ignore = Settings.Service.IgnoreService && (reason == SessionSwitchReason.SessionLock || reason == SessionSwitchReason.SessionUnlock);
if (allowed || disabled)
{
Logger.Info($"Detected user session change ({reason}), but {(allowed ? "session locking and/or switching is allowed" : "lock screen is disabled")}.");
}
else if (ignore)
{
Logger.Info($"Ignoring user session change ({reason}).");
}
else
{
var message = text.Get(Settings.Service.IgnoreService ? TextKey.LockScreen_UserSwitchMessage : TextKey.LockScreen_UserSessionMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionTerminateOption) };
Logger.Warn($"User session changed ({reason})! Attempting to show lock screen...");
if (coordinator.RequestSessionLock())
{
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == continueOption.Id)
{
Logger.Info("The session will be allowed to resume as requested by the user...");
}
else if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
Logger.Warn("User session changed but lock screen is already active.");
}
}
}
private void Sentinel_StickyKeysChanged(SentinelEventArgs args)
{
if (coordinator.RequestSessionLock())
{
var message = text.Get(TextKey.LockScreen_StickyKeysMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_StickyKeysContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_StickyKeysTerminateOption) };
args.Allow = true;
Logger.Info("Sticky keys changed! Attempting to show lock screen...");
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == continueOption.Id)
{
Logger.Info("The session will be allowed to resume as requested by the user...");
}
else if (result.OptionId == terminateOption.Id)
{
Logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
Logger.Info("Sticky keys changed but lock screen is already active.");
}
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 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 SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class NetworkResponsibility : ClientResponsibility
{
private readonly INetworkAdapter networkAdapter;
private readonly IText text;
private readonly IUserInterfaceFactory uiFactory;
public NetworkResponsibility(ClientContext context, ILogger logger, INetworkAdapter networkAdapter, IText text, IUserInterfaceFactory uiFactory) : base(context, logger)
{
this.networkAdapter = networkAdapter;
this.text = text;
this.uiFactory = uiFactory;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
}
}
private void DeregisterEvents()
{
networkAdapter.CredentialsRequired -= NetworkAdapter_CredentialsRequired;
}
private void RegisterEvents()
{
networkAdapter.CredentialsRequired += NetworkAdapter_CredentialsRequired;
}
private void NetworkAdapter_CredentialsRequired(CredentialsRequiredEventArgs args)
{
var message = text.Get(TextKey.CredentialsDialog_WirelessNetworkMessage).Replace("%%_NAME_%%", args.NetworkName);
var title = text.Get(TextKey.CredentialsDialog_WirelessNetworkTitle);
var dialog = uiFactory.CreateCredentialsDialog(CredentialsDialogPurpose.WirelessNetwork, message, title);
var result = dialog.Show();
args.Password = result.Password;
args.Success = result.Success;
args.Username = result.Username;
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2025 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.Threading.Tasks;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Proctoring.Contracts.Events;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class ProctoringResponsibility : ClientResponsibility
{
private readonly IMessageBox messageBox;
private readonly IUserInterfaceFactory uiFactory;
private bool cancel;
private IProctoringController Proctoring => Context.Proctoring;
public ProctoringResponsibility(
ClientContext context,
ILogger logger,
IMessageBox messageBox,
IUserInterfaceFactory uiFactory) : base(context, logger)
{
this.messageBox = messageBox;
this.uiFactory = uiFactory;
}
public override void Assume(ClientTask task)
{
if (task == ClientTask.PrepareShutdown_Wave1)
{
FinalizeProctoring();
}
}
private void FinalizeProctoring()
{
if (Proctoring != default && Proctoring.HasRemainingWork())
{
var dialog = uiFactory.CreateProctoringFinalizationDialog(!Context.QuitPasswordValidated);
var handler = new RemainingWorkUpdatedEventHandler((args) => Proctoring_RemainingWorkUpdated(dialog, args));
dialog.CancellationRequested += new CancellationRequestedEventHandler(() => Dialog_CancellationRequested(dialog));
Task.Run(() =>
{
Proctoring.RemainingWorkUpdated += handler;
Proctoring.ExecuteRemainingWork();
Proctoring.RemainingWorkUpdated -= handler;
});
dialog.Show();
}
}
private void Dialog_CancellationRequested(IProctoringFinalizationDialog dialog)
{
var alreadyValidated = Context.QuitPasswordValidated;
if (alreadyValidated || IsValidQuitPassword(dialog.QuitPassword))
{
cancel = true;
Logger.Info($"The user {(alreadyValidated ? "already " : "")}entered the correct quit password, cancelling remaining work...");
}
else
{
cancel = false;
Logger.Info("The user entered the wrong quit password, remaining work will continue.");
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning, parent: dialog);
}
}
private void Proctoring_RemainingWorkUpdated(IProctoringFinalizationDialog dialog, RemainingWorkUpdatedEventArgs args)
{
dialog.Update(args);
args.CancellationRequested = cancel;
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2025 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.Linq;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class ServerResponsibility : ClientResponsibility
{
private readonly ICoordinator coordinator;
private readonly IText text;
private IServerProxy Server => Context.Server;
public ServerResponsibility(ClientContext context, ICoordinator coordinator, ILogger logger, IText text) : base(context, logger)
{
this.coordinator = coordinator;
this.text = text;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
}
}
private void DeregisterEvents()
{
if (Server != default)
{
Server.LockScreenConfirmed -= Server_LockScreenConfirmed;
Server.LockScreenRequested -= Server_LockScreenRequested;
Server.TerminationRequested -= Server_TerminationRequested;
}
}
private void RegisterEvents()
{
Server.LockScreenConfirmed += Server_LockScreenConfirmed;
Server.LockScreenRequested += Server_LockScreenRequested;
Server.TerminationRequested += Server_TerminationRequested;
}
private void Server_LockScreenConfirmed()
{
Logger.Info("Closing lock screen as requested by the server...");
Context.LockScreen?.Cancel();
}
private void Server_LockScreenRequested(string message)
{
Logger.Info("Attempting to show lock screen as requested by the server...");
if (coordinator.RequestSessionLock())
{
ShowLockScreen(message, text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
coordinator.ReleaseSessionLock();
}
else
{
Logger.Info("Lock screen is already active.");
}
}
private void Server_TerminationRequested()
{
Logger.Info("Attempting to shutdown as requested by the server...");
TryRequestShutdown();
}
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (c) 2025 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.ComponentModel;
using System.Linq;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class ShellResponsibility : ClientResponsibility
{
private readonly IActionCenter actionCenter;
private readonly IHashAlgorithm hashAlgorithm;
private readonly IMessageBox messageBox;
private readonly ITaskbar taskbar;
private readonly IUserInterfaceFactory uiFactory;
public ShellResponsibility(
IActionCenter actionCenter,
ClientContext context,
IHashAlgorithm hashAlgorithm,
ILogger logger,
IMessageBox messageBox,
ITaskbar taskbar,
IUserInterfaceFactory uiFactory) : base(context, logger)
{
this.actionCenter = actionCenter;
this.hashAlgorithm = hashAlgorithm;
this.messageBox = messageBox;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
}
public override void Assume(ClientTask task)
{
switch (task)
{
case ClientTask.CloseShell:
CloseShell();
break;
case ClientTask.DeregisterEvents:
DeregisterEvents();
break;
case ClientTask.RegisterEvents:
RegisterEvents();
break;
case ClientTask.ShowShell:
ShowShell();
break;
}
}
private void DeregisterEvents()
{
actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked;
taskbar.LoseFocusRequested -= Taskbar_LoseFocusRequested;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
foreach (var activator in Context.Activators.OfType<ITerminationActivator>())
{
activator.Activated -= TerminationActivator_Activated;
}
}
private void RegisterEvents()
{
actionCenter.QuitButtonClicked += Shell_QuitButtonClicked;
taskbar.LoseFocusRequested += Taskbar_LoseFocusRequested;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
foreach (var activator in Context.Activators.OfType<ITerminationActivator>())
{
activator.Activated += TerminationActivator_Activated;
}
}
private void CloseShell()
{
if (Settings?.UserInterface.ActionCenter.EnableActionCenter == true)
{
actionCenter.Close();
}
if (Settings?.UserInterface.Taskbar.EnableTaskbar == true)
{
taskbar.Close();
}
}
private void ShowShell()
{
if (Settings.UserInterface.ActionCenter.EnableActionCenter)
{
actionCenter.Promote();
}
if (Settings.UserInterface.Taskbar.EnableTaskbar)
{
taskbar.Show();
}
}
private void Shell_QuitButtonClicked(CancelEventArgs args)
{
PauseActivators();
args.Cancel = !TryInitiateShutdown();
ResumeActivators();
}
private void Taskbar_LoseFocusRequested(bool forward)
{
Context.Browser.Focus(forward);
}
private void TerminationActivator_Activated()
{
PauseActivators();
TryInitiateShutdown();
ResumeActivators();
}
private bool TryInitiateShutdown()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var initiateShutdown = hasQuitPassword ? TryValidateQuitPassword() : TryConfirmShutdown();
var success = false;
if (initiateShutdown)
{
success = TryRequestShutdown();
}
return success;
}
private bool TryConfirmShutdown()
{
var result = messageBox.Show(TextKey.MessageBox_Quit, TextKey.MessageBox_QuitTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
var quit = result == MessageBoxResult.Yes;
if (quit)
{
Logger.Info("The user chose to terminate the application.");
}
return quit;
}
private bool TryValidateQuitPassword()
{
var dialog = uiFactory.CreatePasswordDialog(TextKey.PasswordDialog_QuitPasswordRequired, TextKey.PasswordDialog_QuitPasswordRequiredTitle);
var result = dialog.Show();
var success = false;
if (result.Success && IsValidQuitPassword(result.Password))
{
success = true;
Logger.Info("The user entered the correct quit password, the application will now terminate.");
}
else if (result.Success)
{
Logger.Info("The user entered the wrong quit password.");
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning);
}
return success;
}
}
}

View File

@@ -80,6 +80,7 @@
<Compile Include="Operations\ClientOperation.cs" /> <Compile Include="Operations\ClientOperation.cs" />
<Compile Include="Operations\ClientOperationSequence.cs" /> <Compile Include="Operations\ClientOperationSequence.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" /> <Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\PermissionOperation.cs" />
<Compile Include="Operations\ProctoringOperation.cs" /> <Compile Include="Operations\ProctoringOperation.cs" />
<Compile Include="Operations\RuntimeConnectionOperation.cs" /> <Compile Include="Operations\RuntimeConnectionOperation.cs" />
<Compile Include="Communication\ClientHost.cs" /> <Compile Include="Communication\ClientHost.cs" />
@@ -94,6 +95,7 @@
<Compile Include="Operations\ApplicationOperation.cs" /> <Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ServerOperation.cs" /> <Compile Include="Operations\ServerOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" /> <Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\WindowGuardOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

View File

@@ -53,12 +53,11 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(CommunicationException))]
public void MustCorrectlyHandleStartupException() public void MustCorrectlyHandleStartupException()
{ {
hostObject.Setup(h => h.Open()).Throws<Exception>(); hostObject.Setup(h => h.Open()).Throws<Exception>();
sut.Start(); Assert.ThrowsExactly<CommunicationException>(() => sut.Start());
} }
[TestMethod] [TestMethod]
@@ -71,13 +70,12 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(CommunicationException))]
public void MustCorrectlyHandleShutdownException() public void MustCorrectlyHandleShutdownException()
{ {
hostObject.Setup(h => h.Close()).Throws<Exception>(); hostObject.Setup(h => h.Close()).Throws<Exception>();
sut.Start(); sut.Start();
sut.Stop();
Assert.ThrowsExactly<CommunicationException>(() => sut.Stop());
} }
[TestMethod] [TestMethod]

View File

@@ -171,14 +171,12 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void MustFailToSendIfNotConnected() public void MustFailToSendIfNotConnected()
{ {
sut.Send(new Mock<Message>().Object); Assert.ThrowsExactly<InvalidOperationException>(() => sut.Send(new Mock<Message>().Object));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void MustFailToSendIfChannelNotOpen() public void MustFailToSendIfChannelNotOpen()
{ {
var proxy = new Mock<IProxyObject>(); var proxy = new Mock<IProxyObject>();
@@ -195,14 +193,14 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
var token = Guid.NewGuid(); var token = Guid.NewGuid();
sut.Connect(token); sut.Connect(token);
sut.Send(new Mock<Message>().Object);
Assert.ThrowsExactly<InvalidOperationException>(() => sut.Send(new Mock<Message>().Object));
} }
[TestMethod] [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAllowSendingNull() public void MustNotAllowSendingNull()
{ {
sut.Send(null); Assert.ThrowsExactly<ArgumentNullException>(() => sut.Send(null));
} }
[TestMethod] [TestMethod]

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" /> <Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -63,82 +63,83 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <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> <HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath> <HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath> <HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath> <HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath> <HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL"> <Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath> <HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath> <HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath> <HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath> <HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime" /> <Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
@@ -177,11 +178,8 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" /> <Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -189,13 +187,19 @@
<PropertyGroup> <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> <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> </PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" /> <Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
</Target> </Target>
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" /> <Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
</Project> </Project>

View File

@@ -8,11 +8,11 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -20,27 +20,27 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" /> <bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

Some files were not shown because too many files have changed in this diff Show More