Compare commits
25 Commits
3.8.0.742
...
3.10.0.826
Author | SHA1 | Date | |
---|---|---|---|
213ab539cf | |||
9a9b753049 | |||
5148abede7 | |||
dd82d45ed8 | |||
4827ae1afc | |||
f79146d6fb | |||
59747140c7 | |||
ae5dc61021 | |||
626b351dff | |||
1a350b6c13 | |||
![]() |
9ed5135f69 | ||
25495595ba | |||
8a09160d31 | |||
354f731a6f | |||
9b2367a4a3 | |||
4948d5946f | |||
84b26a2295 | |||
811aea6bd2 | |||
b71e8bd82f | |||
a6b60d72bd | |||
2d36fecb45 | |||
058d48196a | |||
332c6460c7 | |||
fc6a2f7509 | |||
00707825b4 |
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
54
.github/workflows/codeql.yml
vendored
Normal 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}}"
|
@@ -1,10 +1,12 @@
|
|||||||
# Safe Exam Browser Patch
|
# Safe Exam Browser Patch
|
||||||
|
[](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):
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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>
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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,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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)]
|
||||||
|
|
||||||
|
@@ -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>
|
@@ -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>
|
||||||
|
@@ -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>
|
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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,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
|
||||||
|
@@ -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
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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)]
|
||||||
|
|
||||||
|
@@ -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>
|
@@ -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>
|
||||||
|
@@ -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>
|
@@ -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)
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (typeof SafeExamBrowser.clipboard === 'undefined') {
|
||||||
SafeExamBrowser.clipboard = {
|
SafeExamBrowser.clipboard = {
|
||||||
id: Math.round((Date.now() + Math.random()) * 1000),
|
id: crypto.randomUUID(),
|
||||||
ranges: [],
|
ranges: [],
|
||||||
text: "",
|
text: "",
|
||||||
|
|
||||||
@@ -35,8 +36,10 @@ SafeExamBrowser.clipboard = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function copySelectedData(e) {
|
if (typeof copySelection === 'undefined') {
|
||||||
|
function copySelection(e) {
|
||||||
if (e.target.contentEditable && e.target.setRangeText) {
|
if (e.target.contentEditable && e.target.setRangeText) {
|
||||||
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
||||||
SafeExamBrowser.clipboard.ranges = [];
|
SafeExamBrowser.clipboard.ranges = [];
|
||||||
@@ -52,8 +55,10 @@ function copySelectedData(e) {
|
|||||||
SafeExamBrowser.clipboard.text = text;
|
SafeExamBrowser.clipboard.text = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function cutSelectedData(e) {
|
if (typeof cutSelection === 'undefined') {
|
||||||
|
function cutSelection(e) {
|
||||||
if (e.target.contentEditable && e.target.setRangeText) {
|
if (e.target.contentEditable && e.target.setRangeText) {
|
||||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||||
} else {
|
} else {
|
||||||
@@ -78,16 +83,18 @@ function cutSelectedData(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function pasteSelectedData(e) {
|
if (typeof pasteContent === 'undefined') {
|
||||||
|
function pasteContent(e) {
|
||||||
if (e.target.contentEditable && e.target.setRangeText) {
|
if (e.target.contentEditable && e.target.setRangeText) {
|
||||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||||
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
|
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
|
||||||
} else {
|
} else {
|
||||||
var w = e.target.ownerDocument.defaultView;
|
var targetWindow = e.target.ownerDocument.defaultView;
|
||||||
var designMode = e.target.ownerDocument.designMode;
|
var designMode = e.target.ownerDocument.designMode;
|
||||||
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
||||||
var selection = w.getSelection();
|
var selection = targetWindow.getSelection();
|
||||||
|
|
||||||
for (var i = 0; i < selection.rangeCount; i++) {
|
for (var i = 0; i < selection.rangeCount; i++) {
|
||||||
var r = selection.getRangeAt(i);
|
var r = selection.getRangeAt(i);
|
||||||
@@ -106,11 +113,11 @@ function pasteSelectedData(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (designMode === 'on') {
|
if (designMode === 'on') {
|
||||||
var range = w.getSelection().getRangeAt(0);
|
var range = targetWindow.getSelection().getRangeAt(0);
|
||||||
|
|
||||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||||
range = w.getSelection().getRangeAt(0);
|
range = targetWindow.getSelection().getRangeAt(0);
|
||||||
range.collapse();
|
range.collapse();
|
||||||
const newNode = r.cloneNode(true);
|
const newNode = r.cloneNode(true);
|
||||||
range.insertNode(newNode);
|
range.insertNode(newNode);
|
||||||
@@ -118,27 +125,29 @@ function pasteSelectedData(e) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
range.collapse();
|
range.collapse();
|
||||||
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
|
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||||
range.collapse();
|
range.collapse();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (contentEditables.length) {
|
if (contentEditables.length) {
|
||||||
contentEditables.forEach(node => {
|
contentEditables.forEach(node => {
|
||||||
var range = w.getSelection().getRangeAt(0);
|
var range = targetWindow.getSelection().getRangeAt(0);
|
||||||
|
|
||||||
if (node.contains(range.commonAncestorContainer)) {
|
if (node.contains(range.commonAncestorContainer)) {
|
||||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||||
|
|
||||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||||
range = w.getSelection().getRangeAt(0);
|
range = targetWindow.getSelection().getRangeAt(0);
|
||||||
range.collapse();
|
range.collapse();
|
||||||
const newNode = r.cloneNode(true);
|
const newNode = r.cloneNode(true);
|
||||||
range.insertNode(newNode);
|
range.insertNode(newNode);
|
||||||
range.collapse();
|
range.collapse();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
range = w.getSelection().getRangeAt(0);
|
range = targetWindow.getSelection().getRangeAt(0);
|
||||||
range.collapse();
|
range.collapse();
|
||||||
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
|
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||||
range.collapse();
|
range.collapse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,49 +156,56 @@ function pasteSelectedData(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onCopy === 'undefined') {
|
||||||
function onCopy(e) {
|
function onCopy(e) {
|
||||||
|
try {
|
||||||
SafeExamBrowser.clipboard.clear();
|
SafeExamBrowser.clipboard.clear();
|
||||||
|
|
||||||
try {
|
copySelection(e);
|
||||||
copySelectedData(e);
|
|
||||||
|
|
||||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||||
} finally {
|
} finally {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.returnValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCut(e) {
|
|
||||||
SafeExamBrowser.clipboard.clear();
|
|
||||||
|
|
||||||
try {
|
|
||||||
copySelectedData(e);
|
|
||||||
cutSelectedData(e);
|
|
||||||
|
|
||||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
|
||||||
} finally {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPaste(e) {
|
|
||||||
try {
|
|
||||||
pasteSelectedData(e);
|
|
||||||
} finally {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.document.addEventListener("copy", onCopy, true);
|
window.document.addEventListener("copy", onCopy, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onCut === 'undefined') {
|
||||||
|
function onCut(e) {
|
||||||
|
try {
|
||||||
|
SafeExamBrowser.clipboard.clear();
|
||||||
|
|
||||||
|
copySelection(e);
|
||||||
|
cutSelection(e);
|
||||||
|
|
||||||
|
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||||
|
} finally {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
window.document.addEventListener("cut", onCut, true);
|
window.document.addEventListener("cut", onCut, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof onPaste === 'undefined') {
|
||||||
|
function onPaste(e) {
|
||||||
|
try {
|
||||||
|
pasteContent(e);
|
||||||
|
} finally {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
window.document.addEventListener("paste", onPaste, true);
|
window.document.addEventListener("paste", onPaste, true);
|
||||||
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
16
SafeExamBrowser.Browser/Events/JavaScriptDialogType.cs
Normal file
16
SafeExamBrowser.Browser/Events/JavaScriptDialogType.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
49
SafeExamBrowser.Browser/Handlers/CookieVisitor.cs
Normal file
49
SafeExamBrowser.Browser/Handlers/CookieVisitor.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
{
|
{
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
26
SafeExamBrowser.Browser/Handlers/DragHandler.cs
Normal file
26
SafeExamBrowser.Browser/Handlers/DragHandler.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
SafeExamBrowser.Browser/Handlers/FocusHandler.cs
Normal file
32
SafeExamBrowser.Browser/Handlers/FocusHandler.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
SafeExamBrowser.Browser/Handlers/JavaScriptDialogHandler.cs
Normal file
52
SafeExamBrowser.Browser/Handlers/JavaScriptDialogHandler.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
75
SafeExamBrowser.Browser/Integrations/EdxIntegration.cs
Normal file
75
SafeExamBrowser.Browser/Integrations/EdxIntegration.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
SafeExamBrowser.Browser/Integrations/GenericIntegration.cs
Normal file
47
SafeExamBrowser.Browser/Integrations/GenericIntegration.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
SafeExamBrowser.Browser/Integrations/Integration.cs
Normal file
32
SafeExamBrowser.Browser/Integrations/Integration.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
226
SafeExamBrowser.Browser/Integrations/MoodleIntegration.cs
Normal file
226
SafeExamBrowser.Browser/Integrations/MoodleIntegration.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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
Reference in New Issue
Block a user