25 Commits

Author SHA1 Message Date
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
00707825b4 Restore SEBPatch 2025-06-01 11:56:28 +02:00
1133 changed files with 22922 additions and 10218 deletions

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Wiki
url: https://github.com/school-cheating/SEBPatch/wiki
about: Before opening an issue, check out the wiki to see if your issue is listed there.

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,10 +1,12 @@
# 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.
- Patch for version 3.8.0.742 has been deprecated and will not receive updates in the future, please upgrade to Safe Exam Browser 3.9.0.787. - Currently supported SEB version: 3.10.0.826
## How to use ## Downloading and installing
Check out the [Wiki](https://github.com/school-cheating/SEBPatch/wiki) 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):

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Applications.Contracts")] [assembly: AssemblyProduct("SafeExamBrowser.Applications.Contracts")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Applications.UnitTests")] [assembly: AssemblyProduct("SafeExamBrowser.Applications.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View File

@@ -1,8 +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.2.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.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.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\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.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>
@@ -62,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.Telemetry, Version=1.0.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.Extensions.Telemetry.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.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.TrxReport.Abstractions, Version=1.0.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.TrxReport.Abstractions.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.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.VSTestBridge, Version=1.0.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.VSTestBridge.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.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.Platform, Version=1.0.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.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.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.MSBuild, Version=1.0.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.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.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.9.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.9.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.9.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.2.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.2.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.70.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.70\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.9.1.3, 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.9.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.3.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.5.1\lib\net461\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=8.0.0.0, 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.8.0.0\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=8.0.0.0, 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.8.0.0\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.1.2, 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.5.5\lib\net461\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.4.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.5.0\lib\net46\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=8.0.0.0, 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.8.0.0\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.0.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.0.0\lib\net461\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.0.1, 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.5.4\lib\net461\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>
@@ -182,18 +187,27 @@
<Name>SafeExamBrowser.WindowsApi.Contracts</Name> <Name>SafeExamBrowser.WindowsApi.Contracts</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</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" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<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.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.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.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\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.2.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\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.2.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\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.2.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.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.0.0" newVersion="6.0.0.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,23 +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-8.0.0.0" newVersion="8.0.0.0" /> <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-8.0.0.0" newVersion="8.0.0.0" /> <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.1.2" newVersion="4.0.1.2" /> <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-8.0.0.0" newVersion="8.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@@ -1,23 +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.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.9.0" targetFramework="net48" /> <package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
<package id="Moq" version="4.20.70" targetFramework="net48" /> <package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
<package id="MSTest.TestAdapter" version="3.2.2" targetFramework="net48" /> <package id="Moq" version="4.20.72" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.2.2" targetFramework="net48" /> <package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
<package id="NuGet.Frameworks" version="6.9.1" targetFramework="net48" /> <package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" /> <package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net48" /> <package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="8.0.0" targetFramework="net48" /> <package id="System.Buffers" version="4.6.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" /> <package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" /> <package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" /> <package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
</packages> </packages>

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Applications")] [assembly: AssemblyProduct("SafeExamBrowser.Applications")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Browser.Contracts")] [assembly: AssemblyProduct("SafeExamBrowser.Browser.Contracts")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla internal * This Source Code Form is subject to the terms of the Mozilla internal
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -80,7 +80,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
threadId = Thread.CurrentThread.ManagedThreadId; threadId = Thread.CurrentThread.ManagedThreadId;
}; };
var status = sut.OnFileDialog(default, default, mode, title, initialPath, default, callback.Object); var status = sut.OnFileDialog(default, default, mode, title, initialPath, default, default, default, callback.Object);
sync.WaitOne(); sync.WaitOne();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -108,7 +108,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
}; };
sut.DownloadUpdated += (state) => failed = true; sut.DownloadUpdated += (state) => failed = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
sync.WaitOne(); sync.WaitOne();
callback.VerifyNoOtherCalls(); callback.VerifyNoOtherCalls();
@@ -145,7 +145,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
sut.ConfigurationDownloadRequested += (f, a) => failed = true; sut.ConfigurationDownloadRequested += (f, a) => failed = true;
sut.DownloadUpdated += (state) => failed = true; sut.DownloadUpdated += (state) => failed = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
sync.WaitOne(); sync.WaitOne();
callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(downloadPath)), false), Times.Once); callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(downloadPath)), false), Times.Once);
@@ -180,7 +180,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
sut.ConfigurationDownloadRequested += (f, a) => failed = true; sut.ConfigurationDownloadRequested += (f, a) => failed = true;
sut.DownloadUpdated += (state) => failed = true; sut.DownloadUpdated += (state) => failed = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
sync.WaitOne(); sync.WaitOne();
var downloadPath = Path.Combine(Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory), item.SuggestedFileName); var downloadPath = Path.Combine(Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory), item.SuggestedFileName);
@@ -202,12 +202,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
Url = "https://somehost.org/somefile.abc" Url = "https://somehost.org/somefile.abc"
}; };
appConfig.ConfigurationFileMimeType = "application/seb";
settings.AllowDownloads = false; settings.AllowDownloads = false;
settings.AllowConfigurationDownloads = false; settings.AllowConfigurationDownloads = false;
sut.ConfigurationDownloadRequested += (file, args) => fail = true; sut.ConfigurationDownloadRequested += (file, args) => fail = true;
sut.DownloadUpdated += (state) => fail = true; sut.DownloadUpdated += (state) => fail = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
callback.VerifyNoOtherCalls(); callback.VerifyNoOtherCalls();
Assert.IsFalse(fail); Assert.IsFalse(fail);
@@ -233,7 +234,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
settings.AllowConfigurationDownloads = false; settings.AllowConfigurationDownloads = false;
sut.ConfigurationDownloadRequested += (f, a) => failed = true; sut.ConfigurationDownloadRequested += (f, a) => failed = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
sync.WaitOne(); sync.WaitOne();
Assert.IsFalse(failed); Assert.IsFalse(failed);
@@ -246,7 +247,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
}; };
item.PercentComplete = 10; item.PercentComplete = 10;
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback)); sut.OnDownloadUpdated(default, default, item, default);
sync.WaitOne(); sync.WaitOne();
Assert.IsFalse(state.IsCancelled); Assert.IsFalse(state.IsCancelled);
@@ -255,7 +256,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId); Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
item.PercentComplete = 20; item.PercentComplete = 20;
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback)); sut.OnDownloadUpdated(default, default, item, default);
sync.WaitOne(); sync.WaitOne();
Assert.IsFalse(state.IsCancelled); Assert.IsFalse(state.IsCancelled);
@@ -265,7 +266,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
item.PercentComplete = 50; item.PercentComplete = 50;
item.IsCancelled = true; item.IsCancelled = true;
sut.OnDownloadUpdated(default(IWebBrowser), default(IBrowser), item, default(IDownloadItemCallback)); sut.OnDownloadUpdated(default, default, item, default);
sync.WaitOne(); sync.WaitOne();
Assert.IsFalse(failed); Assert.IsFalse(failed);
@@ -297,7 +298,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
}; };
sut.DownloadUpdated += (state) => failed = true; sut.DownloadUpdated += (state) => failed = true;
sut.OnBeforeDownload(default(IWebBrowser), default(IBrowser), item, callback.Object); sut.OnBeforeDownload(default, default, item, callback.Object);
sync.WaitOne(); sync.WaitOne();
callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(args.DownloadPath)), false), Times.Once); callback.Verify(c => c.Continue(It.Is<string>(p => p.Equals(args.DownloadPath)), false), Times.Once);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Browser.UnitTests")] [assembly: AssemblyProduct("SafeExamBrowser.Browser.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View File

@@ -1,11 +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.2.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.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.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" /> <Import Project="..\packages\Microsoft.Testing.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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" /> <Import Project="..\packages\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\CefSharp.Common.121.3.130\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.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\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" /> <Import Project="..\packages\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-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" /> <Import Project="..\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.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>
@@ -65,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=121.3.130.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.121.3.130\lib\net462\CefSharp.dll</HintPath> <HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="CefSharp.Core, Version=121.3.130.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.121.3.130\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.Telemetry, Version=1.0.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.Extensions.Telemetry.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.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.TrxReport.Abstractions, Version=1.0.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.TrxReport.Abstractions.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.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.VSTestBridge, Version=1.0.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.VSTestBridge.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.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.Platform, Version=1.0.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.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.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.MSBuild, Version=1.0.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.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.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.9.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.9.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.9.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.2.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.2.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.70.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.70\lib\net462\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NuGet.Frameworks, Version=6.9.1.3, 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.9.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.3.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.5.1\lib\net461\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=8.0.0.0, 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.8.0.0\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=8.0.0.0, 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.8.0.0\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.1.2, 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.5.5\lib\net461\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.4.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.5.0\lib\net46\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=8.0.0.0, 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.8.0.0\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.0.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.0.0\lib\net461\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.0.1, 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.5.4\lib\net461\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" />
@@ -198,23 +203,32 @@
<Name>SafeExamBrowser.UserInterface.Contracts</Name> <Name>SafeExamBrowser.UserInterface.Contracts</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
</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" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<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.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props'))" /> <Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-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.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props'))" /> <Error Condition="!Exists('..\packages\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.121.3.130\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props'))" /> <Error Condition="!Exists('..\packages\CefSharp.Common.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.121.3.130\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.targets'))" /> <Error Condition="!Exists('..\packages\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.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Testing.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.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" /> <Error Condition="!Exists('..\packages\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.2.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props'))" /> <Error Condition="!Exists('..\packages\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.2.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.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\CefSharp.Common.121.3.130\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.121.3.130\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.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" /> <Import Project="..\packages\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.2.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.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.0.1" newVersion="4.2.0.1" /> <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.0.0" newVersion="6.0.0.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-118.6.80.0" newVersion="118.6.80.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-118.6.80.0" newVersion="118.6.80.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,23 +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-8.0.0.0" newVersion="8.0.0.0" /> <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-8.0.0.0" newVersion="8.0.0.0" /> <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.1.2" newVersion="4.0.1.2" /> <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-8.0.0.0" newVersion="8.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@@ -1,26 +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="121.3.130" targetFramework="net48" /> <package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x64" version="121.3.13" targetFramework="net48" /> <package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x86" version="121.3.13" 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.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.0.2" targetFramework="net48" /> <package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.9.0" targetFramework="net48" /> <package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
<package id="Moq" version="4.20.70" targetFramework="net48" /> <package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
<package id="MSTest.TestAdapter" version="3.2.2" targetFramework="net48" /> <package id="Moq" version="4.20.72" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.2.2" targetFramework="net48" /> <package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
<package id="NuGet.Frameworks" version="6.9.1" targetFramework="net48" /> <package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" /> <package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net48" /> <package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="8.0.0" targetFramework="net48" /> <package id="System.Buffers" version="4.6.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" /> <package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" /> <package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" /> <package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
</packages> </packages>

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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;
@@ -116,17 +117,10 @@ namespace SafeExamBrowser.Browser
if (success) if (success)
{ {
InitializeCookies();
InitializeDownAndUploadDirectory();
InitializeIntegrityKeys(); InitializeIntegrityKeys();
InitializePreferences();
if (settings.DeleteCookiesOnStartup)
{
DeleteCookies();
}
if (settings.UseTemporaryDownAndUploadDirectory)
{
CreateTemporaryDownAndUploadDirectory();
}
logger.Info("Initialized browser."); logger.Info("Initialized browser.");
} }
@@ -144,36 +138,23 @@ namespace SafeExamBrowser.Browser
public void Terminate() public void Terminate()
{ {
logger.Info("Initiating termination..."); logger.Info("Initiating termination...");
AwaitReady(); AwaitReady();
foreach (var window in windows) foreach (var window in windows)
{ {
window.Closed -= Window_Closed; window.Closed -= Window_Closed;
window.Close(); window.Close();
logger.Info($"Closed browser window #{window.Id}."); logger.Info($"Closed browser window #{window.Id}.");
} }
if (settings.UseTemporaryDownAndUploadDirectory) FinalizeCookies();
{ FinalizeDownAndUploadDirectory();
DeleteTemporaryDownAndUploadDirectory();
}
if (settings.DeleteCookiesOnShutdown)
{
DeleteCookies();
}
Cef.Shutdown(); Cef.Shutdown();
logger.Info("Terminated browser."); FinalizeCache();
if (settings.DeleteCacheOnShutdown && settings.DeleteCookiesOnShutdown) logger.Info("Terminated browser.");
{
DeleteCache();
}
else
{
logger.Info("Retained browser cache.");
}
} }
private void AwaitReady() private void AwaitReady()
@@ -188,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}");
@@ -197,6 +184,7 @@ namespace SafeExamBrowser.Browser
fileSystemDialog, fileSystemDialog,
hashAlgorithm, hashAlgorithm,
id, id,
integrations,
isMainWindow, isMainWindow,
keyGenerator, keyGenerator,
windowLogger, windowLogger,
@@ -218,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;
} }
@@ -231,49 +219,10 @@ namespace SafeExamBrowser.Browser
WindowsChanged?.Invoke(); WindowsChanged?.Invoke();
} }
private void CreateTemporaryDownAndUploadDirectory()
{
try
{
settings.DownAndUploadDirectory = Path.Combine(appConfig.TemporaryDirectory, Path.GetRandomFileName());
Directory.CreateDirectory(settings.DownAndUploadDirectory);
logger.Info($"Created temporary down- and upload directory.");
}
catch (Exception e)
{
logger.Error("Failed to create temporary down- and upload directory!", e);
}
}
private void DeleteTemporaryDownAndUploadDirectory()
{
try
{
Directory.Delete(settings.DownAndUploadDirectory, true);
logger.Info("Deleted temporary down- and upload directory.");
}
catch (Exception e)
{
logger.Error("Failed to delete temporary down- and upload directory!", e);
}
}
private void DeleteCache()
{
try
{
Directory.Delete(appConfig.BrowserCachePath, true);
logger.Info("Deleted browser cache.");
}
catch (Exception e)
{
logger.Error("Failed to delete browser cache!", e);
}
}
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 =>
{ {
@@ -287,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.");
} }
@@ -297,6 +246,50 @@ namespace SafeExamBrowser.Browser
} }
} }
private void FinalizeCache()
{
if (settings.DeleteCacheOnShutdown && settings.DeleteCookiesOnShutdown)
{
try
{
Directory.Delete(appConfig.BrowserCachePath, true);
logger.Info("Deleted browser cache.");
}
catch (Exception e)
{
logger.Error("Failed to delete browser cache!", e);
}
}
else
{
logger.Info("Retained browser cache.");
}
}
private void FinalizeCookies()
{
if (settings.DeleteCookiesOnShutdown)
{
DeleteCookies();
}
}
private void FinalizeDownAndUploadDirectory()
{
if (settings.UseTemporaryDownAndUploadDirectory)
{
try
{
Directory.Delete(settings.DownAndUploadDirectory, true);
logger.Info("Deleted temporary down- and upload directory.");
}
catch (Exception e)
{
logger.Error("Failed to delete temporary down- and upload directory!", e);
}
}
}
private string GenerateStartUrl() private string GenerateStartUrl()
{ {
var url = settings.StartUrl; var url = settings.StartUrl;
@@ -333,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;
@@ -356,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);
@@ -371,6 +364,53 @@ namespace SafeExamBrowser.Browser
return cefSettings; return cefSettings;
} }
private void InitializeCookies()
{
if (settings.DeleteCookiesOnStartup)
{
DeleteCookies();
}
}
private void InitializeDownAndUploadDirectory()
{
if (settings.UseTemporaryDownAndUploadDirectory)
{
InitializeTemporaryDownAndUploadDirectory();
}
else if (!string.IsNullOrEmpty(settings.DownAndUploadDirectory))
{
InitializeCustomDownAndUploadDirectory();
}
}
private void InitializeCustomDownAndUploadDirectory()
{
if (!Directory.Exists(Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory)))
{
logger.Warn("The configured down- and upload directory does not exist! Falling back to the default directory...");
settings.DownAndUploadDirectory = default;
}
else
{
logger.Debug("Using custom down- and upload directory as defined in the active configuration.");
}
}
private void InitializeTemporaryDownAndUploadDirectory()
{
try
{
settings.DownAndUploadDirectory = Path.Combine(appConfig.TemporaryDirectory, Path.GetRandomFileName());
Directory.CreateDirectory(settings.DownAndUploadDirectory);
logger.Info($"Created temporary down- and upload directory.");
}
catch (Exception e)
{
logger.Error("Failed to create temporary down- and upload directory!", e);
}
}
private void InitializeIntegrityKeys() private void InitializeIntegrityKeys()
{ {
logger.Debug($"Browser Exam Key (BEK) transmission is {(settings.SendBrowserExamKey ? "enabled" : "disabled")}."); logger.Debug($"Browser Exam Key (BEK) transmission is {(settings.SendBrowserExamKey ? "enabled" : "disabled")}.");
@@ -387,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)
@@ -476,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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -23,9 +23,13 @@ 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;
private readonly IDragHandler dragHandler;
private readonly IFocusHandler focusHandler;
private readonly IJsDialogHandler javaScriptDialogHandler;
private readonly IKeyboardHandler keyboardHandler; private readonly IKeyboardHandler keyboardHandler;
private readonly ILogger logger; private readonly ILogger logger;
private readonly IRenderProcessMessageHandler renderProcessMessageHandler; private readonly IRenderProcessMessageHandler renderProcessMessageHandler;
@@ -44,19 +48,27 @@ 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,
IDragHandler dragHandler,
IFocusHandler focusHandler,
IJsDialogHandler javaScriptDialogHandler,
IKeyboardHandler keyboardHandler, IKeyboardHandler keyboardHandler,
ILogger logger, ILogger logger,
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;
this.dragHandler = dragHandler;
this.focusHandler = focusHandler;
this.javaScriptDialogHandler = javaScriptDialogHandler;
this.keyboardHandler = keyboardHandler; this.keyboardHandler = keyboardHandler;
this.logger = logger; this.logger = logger;
this.renderProcessMessageHandler = renderProcessMessageHandler; this.renderProcessMessageHandler = renderProcessMessageHandler;
@@ -67,6 +79,7 @@ namespace SafeExamBrowser.Browser
{ {
if (!control.IsDisposed) if (!control.IsDisposed)
{ {
control.CloseDevTools();
control.Dispose(true); control.Dispose(true);
} }
} }
@@ -79,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
}));
} }
} }
@@ -119,22 +121,35 @@ 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.BeforeDownload += (w, b, d, c) => downloadHandler.OnBeforeDownload(w, b, d, c); 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.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.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c); control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c);
control.DragEnterCefSharp += (w, b, d, m, a) => a.Value = dragHandler.OnDragEnter(w, b, d, m);
control.DraggableRegionsChanged += (w, b, f, r) => dragHandler.OnDraggableRegionsChanged(w, b, f, r);
control.FaviconUrlChanged += (w, b, u) => displayHandler.OnFaviconUrlChange(w, b, u); control.FaviconUrlChanged += (w, b, u) => displayHandler.OnFaviconUrlChange(w, b, u);
control.FileDialogRequested += (w, b, m, t, d, f, c) => dialogHandler.OnFileDialog(w, b, m, t, d, f, c); control.FileDialogRequested += (w, b, m, t, p, f, e, d, c) => dialogHandler.OnFileDialog(w, b, m, t, p, f, e, d, c);
control.FocusedNodeChanged += (w, b, f, n) => renderProcessMessageHandler.OnFocusedNodeChanged(w, b, f, n); control.FocusedNodeChanged += (w, b, f, n) => renderProcessMessageHandler.OnFocusedNodeChanged(w, b, f, n);
control.GotFocusCefSharp += (w, b) => focusHandler.OnGotFocus(w, b);
control.IsBrowserInitializedChanged += Control_IsBrowserInitializedChanged; control.IsBrowserInitializedChanged += Control_IsBrowserInitializedChanged;
control.JavaScriptDialog += (IWebBrowser w, IBrowser b, string u, CefJsDialogType t, string m, string p, IJsDialogCallback c, ref bool s, GenericEventArgs a) => a.Value = javaScriptDialogHandler.OnJSDialog(w, b, u, t, m, p, c, ref s);
control.KeyEvent += (w, b, t, k, n, m, s) => keyboardHandler.OnKeyEvent(w, b, t, k, n, m, s); control.KeyEvent += (w, b, t, k, n, m, s) => keyboardHandler.OnKeyEvent(w, b, t, k, n, m, s);
control.LoadError += (o, e) => LoadFailed?.Invoke((int) e.ErrorCode, e.ErrorText, e.Frame.IsMain, e.FailedUrl); control.LoadError += (o, e) => LoadFailed?.Invoke((int) e.ErrorCode, e.ErrorText, e.Frame.IsMain, e.FailedUrl);
control.LoadingProgressChanged += (w, b, p) => displayHandler.OnLoadingProgressChange(w, b, p); control.LoadingProgressChanged += (w, b, p) => displayHandler.OnLoadingProgressChange(w, b, p);
control.LoadingStateChanged += (o, e) => LoadingStateChanged?.Invoke(e.IsLoading); control.LoadingStateChanged += (o, e) => LoadingStateChanged?.Invoke(e.IsLoading);
control.OpenUrlFromTab += (w, b, f, u, t, g, a) => a.Value = requestHandler.OnOpenUrlFromTab(w, b, f, u, t, g); control.OpenUrlFromTab += (w, b, f, u, t, g, a) => a.Value = requestHandler.OnOpenUrlFromTab(w, b, f, u, t, g);
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.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.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);
control.UncaughtExceptionEvent += (w, b, f, e) => renderProcessMessageHandler.OnUncaughtException(w, b, f, e); control.UncaughtExceptionEvent += (w, b, f, e) => renderProcessMessageHandler.OnUncaughtException(w, b, f, e);
@@ -174,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)
@@ -189,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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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,15 +157,19 @@ 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}");
var downloadHandler = new DownloadHandler(appConfig, downloadLogger, settings, WindowSettings); var downloadHandler = new DownloadHandler(appConfig, downloadLogger, settings, WindowSettings);
var dragHandler = new DragHandler();
var focusHandler = new FocusHandler();
var javaScriptDialogHandler = new JavaScriptDialogHandler();
var keyboardHandler = new KeyboardHandler(); var keyboardHandler = new KeyboardHandler();
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();
@@ -181,6 +189,7 @@ namespace SafeExamBrowser.Browser
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested; downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted; downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted;
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated; downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
javaScriptDialogHandler.DialogRequested += JavaScriptDialogHandler_DialogRequested;
keyboardHandler.FindRequested += KeyboardHandler_FindRequested; keyboardHandler.FindRequested += KeyboardHandler_FindRequested;
keyboardHandler.FocusAddressBarRequested += KeyboardHandler_FocusAddressBarRequested; keyboardHandler.FocusAddressBarRequested += KeyboardHandler_FocusAddressBarRequested;
keyboardHandler.HomeNavigationRequested += HomeNavigationRequested; keyboardHandler.HomeNavigationRequested += HomeNavigationRequested;
@@ -195,19 +204,33 @@ namespace SafeExamBrowser.Browser
InitializeRequestFilter(requestFilter); InitializeRequestFilter(requestFilter);
Control = new BrowserControl(clipboard, cefSharpControl, dialogHandler, displayHandler, downloadHandler, keyboardHandler, controlLogger, renderHandler, requestHandler); Control = new BrowserControl(
clipboard,
cefSharpControl,
contextMenuHandler,
dialogHandler,
displayHandler,
downloadHandler,
dragHandler,
focusHandler,
javaScriptDialogHandler,
keyboardHandler,
controlLogger,
renderHandler,
requestHandler);
Control.AddressChanged += Control_AddressChanged; Control.AddressChanged += Control_AddressChanged;
Control.LoadFailed += Control_LoadFailed; Control.LoadFailed += Control_LoadFailed;
Control.LoadingStateChanged += Control_LoadingStateChanged; Control.LoadingStateChanged += Control_LoadingStateChanged;
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;
@@ -226,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.");
} }
@@ -240,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)
@@ -281,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)
@@ -488,6 +526,20 @@ namespace SafeExamBrowser.Browser
} }
} }
private void JavaScriptDialogHandler_DialogRequested(JavaScriptDialogRequestedEventArgs args)
{
logger.Debug($"A JavaScript dialog of type '{args.Type}' has been requested...");
if (args.Type == JavaScriptDialogType.LeavePage)
{
args.Success = RequestPageLeave();
}
else
{
args.Success = RequestPageReload();
}
}
private void KeyboardHandler_FindRequested() private void KeyboardHandler_FindRequested()
{ {
if (settings.AllowFind) if (settings.AllowFind)
@@ -642,29 +694,61 @@ namespace SafeExamBrowser.Browser
private void ReloadRequested() private void ReloadRequested()
{ {
if (WindowSettings.AllowReloading && WindowSettings.ShowReloadWarning) logger.Debug("A reload of the current page has been requested...");
if (RequestPageReload())
{ {
var result = messageBox.Show(TextKey.MessageBox_ReloadConfirmation, TextKey.MessageBox_ReloadConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window); Control.Reload();
}
}
private bool RequestPageLeave()
{
var allow = false;
var result = messageBox.Show(TextKey.MessageBox_PageLeaveConfirmation, TextKey.MessageBox_PageLeaveConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window);
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
logger.Debug("The user confirmed reloading the current page..."); allow = true;
Control.Reload(); logger.Debug("The page leave has been granted by the user.");
} }
else else
{ {
logger.Debug("The user aborted reloading the current page."); logger.Debug("The page leave has been aborted by the user.");
}
return allow;
}
private bool RequestPageReload()
{
var allow = false;
if (WindowSettings.AllowReloading && WindowSettings.ShowReloadWarning)
{
var result = messageBox.Show(TextKey.MessageBox_PageReloadConfirmation, TextKey.MessageBox_PageReloadConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window);
if (result == MessageBoxResult.Yes)
{
allow = true;
logger.Debug("The page reload has been granted by the user.");
}
else
{
logger.Debug("The page reload has been aborted by the user.");
} }
} }
else if (WindowSettings.AllowReloading) else if (WindowSettings.AllowReloading)
{ {
logger.Debug("Reloading current page..."); allow = true;
Control.Reload(); logger.Debug("The page reload has been automatically granted.");
} }
else else
{ {
logger.Debug("Blocked reload attempt, as the user is not allowed to reload web pages."); logger.Debug("The page reload has been blocked, as the user is not allowed to reload web pages.");
} }
return allow;
} }
private void ShowDownUploadNotAllowedMessage(bool isDownload = true) private void ShowDownUploadNotAllowedMessage(bool isDownload = true)
@@ -772,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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -30,9 +30,24 @@ namespace SafeExamBrowser.Browser
this.settings = settings; this.settings = settings;
} }
internal void Process(JavascriptMessageReceivedEventArgs message) internal void Update(JavascriptMessageReceivedEventArgs message)
{ {
if (settings.UseIsolatedClipboard)
{
try
{
var data = message.ConvertMessageTo<Data>();
if (data != default && data.Type == "Clipboard" && TrySetContent(data.Content))
{
Task.Run(() => Changed?.Invoke(data.Id));
}
}
catch (Exception e)
{
logger.Error($"Failed to process browser message '{message}'!", e);
}
}
} }
private bool TrySetContent(object value) private bool TrySetContent(object value)
@@ -50,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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,15 +1,16 @@
/* /*
* Copyright (c) 2024 ETH Z<>rich, IT Services * Copyright (c) 2025 ETH Z<>rich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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.
*/ */
SafeExamBrowser.clipboard = { if (typeof SafeExamBrowser.clipboard === 'undefined') {
id: Math.round((Date.now() + Math.random()) * 1000), SafeExamBrowser.clipboard = {
id: crypto.randomUUID(),
ranges: [], ranges: [],
text: "", text: "",
@@ -34,9 +35,11 @@ SafeExamBrowser.clipboard = {
this.text = content; this.text = content;
} }
} }
}
} }
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 = [];
@@ -51,9 +54,11 @@ 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 {
@@ -77,17 +82,19 @@ 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();
} }
} }
@@ -146,50 +155,57 @@ function pasteSelectedData(e) {
} }
} }
} }
}
} }
function onCopy(e) { if (typeof onCopy === 'undefined') {
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; return false;
}
window.document.addEventListener("copy", onCopy, true);
} }
function onCut(e) { if (typeof onCut === 'undefined') {
function onCut(e) {
try {
SafeExamBrowser.clipboard.clear(); SafeExamBrowser.clipboard.clear();
try { copySelection(e);
copySelectedData(e); cutSelection(e);
cutSelectedData(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; return false;
}
window.document.addEventListener("cut", onCut, true);
} }
function onPaste(e) {
if (typeof onPaste === 'undefined') {
function onPaste(e) {
try { try {
pasteSelectedData(e); pasteContent(e);
} finally { } finally {
e.preventDefault(); e.preventDefault();
e.returnValue = false;
} }
return false; return false;
} }
window.document.addEventListener("copy", onCopy, true); window.document.addEventListener("paste", onPaste, true);
window.document.addEventListener("cut", onCut, true); }
window.document.addEventListener("paste", onPaste, true);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Z<>rich, IT Services * Copyright (c) 2025 ETH Z<>rich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

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 class JavaScriptDialogRequestedEventArgs
{
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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -18,7 +18,16 @@ namespace SafeExamBrowser.Browser.Handlers
{ {
internal event DialogRequestedEventHandler DialogRequested; internal event DialogRequestedEventHandler DialogRequested;
public bool OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, IFileDialogCallback callback) public bool OnFileDialog(
IWebBrowser webBrowser,
IBrowser browser,
CefFileDialogMode mode,
string title,
string defaultFilePath,
IReadOnlyCollection<string> acceptFilters,
IReadOnlyCollection<string> acceptExtensions,
IReadOnlyCollection<string> acceptDescriptions,
IFileDialogCallback callback)
{ {
var args = new DialogRequestedEventArgs var args = new DialogRequestedEventArgs
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -50,7 +50,7 @@ namespace SafeExamBrowser.Browser.Handlers
return true; return true;
} }
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) public bool OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
{ {
var fileExtension = Path.GetExtension(downloadItem.SuggestedFileName); var fileExtension = Path.GetExtension(downloadItem.SuggestedFileName);
var isConfigurationFile = false; var isConfigurationFile = false;
@@ -86,6 +86,8 @@ namespace SafeExamBrowser.Browser.Handlers
logger.Info($"Aborted download request{(windowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")}, as downloading is not allowed."); logger.Info($"Aborted download request{(windowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")}, as downloading is not allowed.");
Task.Run(() => DownloadAborted?.Invoke()); Task.Run(() => DownloadAborted?.Invoke());
} }
return true;
} }
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
@@ -100,6 +102,8 @@ namespace SafeExamBrowser.Browser.Handlers
FullPath = downloadItem.FullPath, FullPath = downloadItem.FullPath,
IsCancelled = downloadItem.IsCancelled, IsCancelled = downloadItem.IsCancelled,
IsComplete = downloadItem.IsComplete, IsComplete = downloadItem.IsComplete,
IsIndeterminate = downloadItem.PercentComplete < 0,
Size = downloadItem.ReceivedBytes,
Url = downloadItem.Url Url = downloadItem.Url
}; };

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

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 ETH Zürich, IT Services * Copyright (c) 2025 ETH Zürich, IT Services
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -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

@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("Safe Exam Browser")] [assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")] [assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Browser")] [assembly: AssemblyProduct("SafeExamBrowser.Browser")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")] [assembly: AssemblyCopyright("Copyright © 2025 ETH Zürich, IT Services")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from

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