diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index a85367c..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: ["https://web.satispay.com/app/match/link/user/S6Y-CON--88923C30-BEC8-487E-9814-68A5449F7D83?amount=500¤cy=EUR"] diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 1379a16..78d4791 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,18 +1,17 @@ --- name: Bug Report -about: Create a bug report to help us improve the Safe Exam Browser Patch +about: Create a bug report to help us improve Safe Exam Browser. title: '' -labels: bug -assignees: theitaliandeveloper +labels: '' +assignees: dbuechel --- > [!IMPORTANT] -> - Please _always_ consult the FAQs before creating an issue: https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/wiki/FAQs. +> - Please _always_ consult the documentation first before creating a bug report: https://safeexambrowser.org/windows/win_usermanual_en.html. > - Please _always_ attach the log file(s) of the affected session(s)! They can be found under `%LocalAppData%\SafeExamBrowser\Logs`. -> - Please follow this issue template. Saves me some work while reading all issues. -**Bug Description** +**Describe the Bug** A clear and concise description of what the bug is. **Steps to Reproduce** @@ -30,18 +29,7 @@ If applicable, add screenshots to help explain your problem. **Version Information** - OS: [e.g. Windows 10 Professional, Version 1803] - - SEB version: [e.g. SEB 3.0.1] - - SEB patch version: [e.g. 1.5.1] + - SEB-Version [e.g. SEB 3.0.1] **Additional Context** Add any other context about the problem here. - -**SEB Logs** -``` -Paste here the SEB logs -``` - -**SEB Patcher logs (optional, read below)** -``` -Paste here the SEB patcher logs (if issue is SEB patcher related, else just ignore this section) -``` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index e6c5858..0000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: FAQs - url: https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/wiki/FAQs - about: Before opening an issue, check out the FAQs. Any issue corresponding to the FAQs will be ignored and closed. - - name: Questions - url: https://github.com/theitaliandeveloper/SEBPatch/discussions/categories/q-a - about: Ask questions here about the Safe Exam Browser Patch - - name: Feature Requests - url: https://github.com/theitaliandeveloper/SEBPatch/discussions/categories/ideas - about: Propose a feature or a change for the Safe Exam Browser Patch. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..325de6b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea or new feature for Safe Exam Browser. +title: '' +labels: '' +assignees: dbuechel + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..57f0fc6 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -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}}" diff --git a/SafeExamBrowser.Applications.Contracts/FactoryResult.cs b/SafeExamBrowser.Applications.Contracts/FactoryResult.cs index 0286fe7..3624615 100644 --- a/SafeExamBrowser.Applications.Contracts/FactoryResult.cs +++ b/SafeExamBrowser.Applications.Contracts/FactoryResult.cs @@ -18,6 +18,11 @@ namespace SafeExamBrowser.Applications.Contracts /// Error, + /// + /// The application has been found but is invalid (e.g. because it is not the correct version or has been manipulated). + /// + Invalid, + /// /// The application could not be found on the system. /// diff --git a/SafeExamBrowser.Applications.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml b/SafeExamBrowser.Applications.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml deleted file mode 100644 index a42d7f0..0000000 --- a/SafeExamBrowser.Applications.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/SafeExamBrowser.Applications.UnitTests/SafeExamBrowser.Applications.UnitTests.csproj b/SafeExamBrowser.Applications.UnitTests/SafeExamBrowser.Applications.UnitTests.csproj index 466b469..9edb9a2 100644 --- a/SafeExamBrowser.Applications.UnitTests/SafeExamBrowser.Applications.UnitTests.csproj +++ b/SafeExamBrowser.Applications.UnitTests/SafeExamBrowser.Applications.UnitTests.csproj @@ -1,9 +1,9 @@  - - - - + + + + Debug @@ -63,78 +63,82 @@ - ..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll + ..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll - - ..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll + + ..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll - - ..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll + + ..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll - - ..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll + + ..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll - - ..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll + + ..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll - - ..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll + + ..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll - - ..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll + + ..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll + + + ..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll + True - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - ..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + ..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - ..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + ..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll ..\packages\Moq.4.20.72\lib\net462\Moq.dll - - ..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll + + ..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll - - ..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll + + ..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll - - ..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll - - ..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll + + ..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - ..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll @@ -184,11 +188,8 @@ - - - - - + + @@ -196,13 +197,17 @@ 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}. - - - - - - + + + + + + + + - - + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Applications.UnitTests/app.config b/SafeExamBrowser.Applications.UnitTests/app.config index 4a41c0d..259d7bd 100644 --- a/SafeExamBrowser.Applications.UnitTests/app.config +++ b/SafeExamBrowser.Applications.UnitTests/app.config @@ -4,7 +4,7 @@ - + @@ -12,31 +12,31 @@ - + - + - + - + - + - + - + diff --git a/SafeExamBrowser.Applications.UnitTests/packages.config b/SafeExamBrowser.Applications.UnitTests/packages.config index da1800f..32e6e53 100644 --- a/SafeExamBrowser.Applications.UnitTests/packages.config +++ b/SafeExamBrowser.Applications.UnitTests/packages.config @@ -1,24 +1,25 @@  - - - - - - - - + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Applications/ApplicationFactory.cs b/SafeExamBrowser.Applications/ApplicationFactory.cs index 497600d..cb940b2 100644 --- a/SafeExamBrowser.Applications/ApplicationFactory.cs +++ b/SafeExamBrowser.Applications/ApplicationFactory.cs @@ -8,7 +8,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Security.Cryptography.X509Certificates; using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Applications; @@ -43,42 +45,62 @@ namespace SafeExamBrowser.Applications public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application) { var name = $"'{settings.DisplayName}' ({settings.ExecutableName})"; + var result = FactoryResult.Error; application = default; 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.Initialize(); - - logger.Debug($"Successfully initialized application {name}."); - - return FactoryResult.Success; + application = InitializeApplication(executablePath, settings); } - logger.Error($"Could not find application {name}!"); - - return FactoryResult.NotFound; + result = DetermineResult(name, found, valid); } catch (Exception e) { logger.Error($"Unexpected error while trying to initialize application {name}!", e); } - return FactoryResult.Error; + return result; } - private IApplication 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 InitializeApplication(string executablePath, WhitelistApplication settings) { const int ONE_SECOND = 1000; var applicationLogger = logger.CloneFor(settings.DisplayName); var application = new ExternalApplication(applicationMonitor, executablePath, applicationLogger, nativeMethods, processFactory, settings, ONE_SECOND); + application.Initialize(); + return application; } @@ -143,5 +165,99 @@ namespace SafeExamBrowser.Applications 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; + } } } diff --git a/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs b/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs index 941c5b5..c674dcf 100644 --- a/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Filters/RequestFilterTests.cs @@ -102,13 +102,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters } [TestMethod] - [ExpectedException(typeof(NotImplementedException))] public void MustNotAllowUnsupportedResult() { var rule = new Mock(); rule.SetupGet(r => r.Result).Returns((FilterResult) (-1)); - sut.Load(rule.Object); + + Assert.ThrowsExactly(() => sut.Load(rule.Object)); } } } diff --git a/SafeExamBrowser.Browser.UnitTests/Filters/RuleFactoryTests.cs b/SafeExamBrowser.Browser.UnitTests/Filters/RuleFactoryTests.cs index 85c3b33..86f731e 100644 --- a/SafeExamBrowser.Browser.UnitTests/Filters/RuleFactoryTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Filters/RuleFactoryTests.cs @@ -33,10 +33,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters } [TestMethod] - [ExpectedException(typeof(NotImplementedException))] public void MustNotAllowUnsupportedFilterType() { - sut.CreateRule((FilterRuleType) (-1)); + Assert.ThrowsExactly(() => sut.CreateRule((FilterRuleType) (-1))); } } } diff --git a/SafeExamBrowser.Browser.UnitTests/Filters/Rules/RegexRuleTests.cs b/SafeExamBrowser.Browser.UnitTests/Filters/Rules/RegexRuleTests.cs index d4af886..0c3d612 100644 --- a/SafeExamBrowser.Browser.UnitTests/Filters/Rules/RegexRuleTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Filters/Rules/RegexRuleTests.cs @@ -51,17 +51,15 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] public void MustNotAllowUndefinedExpression() { - sut.Initialize(new FilterRuleSettings()); + Assert.ThrowsExactly(() => sut.Initialize(new FilterRuleSettings())); } [TestMethod] - [ExpectedException(typeof(ArgumentException))] public void MustValidateExpression() { - sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" }); + Assert.ThrowsExactly(() => sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" })); } } } diff --git a/SafeExamBrowser.Browser.UnitTests/Filters/Rules/SimplifiedRuleTests.cs b/SafeExamBrowser.Browser.UnitTests/Filters/Rules/SimplifiedRuleTests.cs index 4d49b26..d28a48c 100644 --- a/SafeExamBrowser.Browser.UnitTests/Filters/Rules/SimplifiedRuleTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Filters/Rules/SimplifiedRuleTests.cs @@ -51,10 +51,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] public void MustNotAllowUndefinedExpression() { - sut.Initialize(new FilterRuleSettings()); + Assert.ThrowsExactly(() => sut.Initialize(new FilterRuleSettings())); } [TestMethod] @@ -73,7 +72,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules foreach (var expression in invalid) { - Assert.ThrowsException(() => sut.Initialize(new FilterRuleSettings { Expression = expression })); + Assert.ThrowsExactly(() => sut.Initialize(new FilterRuleSettings { Expression = expression })); } } diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs index 64c554f..440451c 100644 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs @@ -12,6 +12,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Browser.Contracts.Filters; using SafeExamBrowser.Browser.Handlers; +using SafeExamBrowser.Browser.Integrations; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.I18n.Contracts; @@ -41,6 +42,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers [TestInitialize] public void Initialize() { + var integrations = new Integration[] + { + new GenericIntegration(new Mock().Object), + new EdxIntegration(new Mock().Object), + new MoodleIntegration(new Mock().Object) + }; + appConfig = new AppConfig(); filter = new Mock(); keyGenerator = new Mock(); @@ -48,7 +56,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers settings = new BrowserSettings(); windowSettings = new WindowSettings(); text = new Mock(); - 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); } @@ -285,7 +293,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers 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) { } diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/ResourceHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/ResourceHandlerTests.cs index 92b3111..c8ec7b7 100644 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/ResourceHandlerTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Handlers/ResourceHandlerTests.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Net.Mime; using System.Threading; @@ -14,6 +15,7 @@ using CefSharp; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Browser.Contracts.Filters; +using SafeExamBrowser.Browser.Integrations; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.I18n.Contracts; @@ -42,6 +44,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers [TestInitialize] public void Initialize() { + var integrations = new Integration[] + { + new GenericIntegration(new Mock().Object), + new EdxIntegration(new Mock().Object), + new MoodleIntegration(new Mock().Object) + }; + appConfig = new AppConfig(); filter = new Mock(); keyGenerator = new Mock(); @@ -50,7 +59,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers windowSettings = new WindowSettings(); text = new Mock(); - 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] @@ -325,12 +334,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers internal TestableResourceHandler( AppConfig appConfig, IRequestFilter filter, + IEnumerable integrations, IKeyGenerator keyGenerator, ILogger logger, SessionMode sessionMode, BrowserSettings settings, 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) { } diff --git a/SafeExamBrowser.Browser.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml b/SafeExamBrowser.Browser.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml deleted file mode 100644 index a42d7f0..0000000 --- a/SafeExamBrowser.Browser.UnitTests/ILLink/ILLink.Descriptors.LibraryBuild.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj index df98d1c..41b0683 100644 --- a/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj +++ b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj @@ -1,12 +1,12 @@  - - - - - - - + + + + + + + Debug @@ -66,84 +66,88 @@ - ..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll + ..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll - - ..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll + + ..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll - - ..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll + + ..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll - - ..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll + + ..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll - - ..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll + + ..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll - - ..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll + + ..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll - - ..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll + + ..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll - - ..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll + + ..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll - - ..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll + + ..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll + + + ..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll + True - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll - ..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + ..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - ..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + ..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - ..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + ..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll ..\packages\Moq.4.20.72\lib\net462\Moq.dll - - ..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll + + ..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll - - ..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll + + ..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll - - ..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll - - ..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll + + ..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - ..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll @@ -200,11 +204,8 @@ - - - - - + + @@ -212,18 +213,22 @@ 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}. - - - - - - - - - - + + + + + + + + + + + + - - - + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Browser.UnitTests/app.config b/SafeExamBrowser.Browser.UnitTests/app.config index ee510e7..4e06ece 100644 --- a/SafeExamBrowser.Browser.UnitTests/app.config +++ b/SafeExamBrowser.Browser.UnitTests/app.config @@ -4,11 +4,11 @@ - + - + @@ -16,11 +16,11 @@ - + - + @@ -28,27 +28,27 @@ - + - + - + - + - + - + diff --git a/SafeExamBrowser.Browser.UnitTests/packages.config b/SafeExamBrowser.Browser.UnitTests/packages.config index da5a1d8..71db1cb 100644 --- a/SafeExamBrowser.Browser.UnitTests/packages.config +++ b/SafeExamBrowser.Browser.UnitTests/packages.config @@ -1,27 +1,28 @@  - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Browser/BrowserApplication.cs b/SafeExamBrowser.Browser/BrowserApplication.cs index b27ed63..17c8962 100644 --- a/SafeExamBrowser.Browser/BrowserApplication.cs +++ b/SafeExamBrowser.Browser/BrowserApplication.cs @@ -18,6 +18,7 @@ using SafeExamBrowser.Applications.Contracts.Events; using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Browser.Contracts.Events; using SafeExamBrowser.Browser.Events; +using SafeExamBrowser.Browser.Integrations; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Core.Contracts.Resources.Icons; @@ -119,6 +120,7 @@ namespace SafeExamBrowser.Browser InitializeCookies(); InitializeDownAndUploadDirectory(); InitializeIntegrityKeys(); + InitializePreferences(); logger.Info("Initialized browser."); } @@ -167,6 +169,12 @@ namespace SafeExamBrowser.Browser private void CreateNewWindow(PopupRequestedEventArgs args = default) { 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 startUrl = GenerateStartUrl(); var windowLogger = logger.CloneFor($"Browser Window #{id}"); @@ -176,6 +184,7 @@ namespace SafeExamBrowser.Browser fileSystemDialog, hashAlgorithm, id, + integrations, isMainWindow, keyGenerator, windowLogger, @@ -197,7 +206,7 @@ namespace SafeExamBrowser.Browser window.InitializeControl(); windows.Add(window); - if (args != default(PopupRequestedEventArgs)) + if (args != default) { args.Window = window; } @@ -213,6 +222,7 @@ namespace SafeExamBrowser.Browser private void DeleteCookies() { var callback = new TaskDeleteCookiesCallback(); + var cookieManager = Cef.GetGlobalCookieManager(); callback.Task.ContinueWith(task => { @@ -226,7 +236,7 @@ namespace SafeExamBrowser.Browser } }); - if (Cef.GetGlobalCookieManager().DeleteCookies(callback: callback)) + if (cookieManager != default && cookieManager.DeleteCookies(callback: callback)) { logger.Debug("Successfully initiated cookie deletion."); } @@ -316,7 +326,6 @@ namespace SafeExamBrowser.Browser cefSettings.AcceptLanguageList = CultureInfo.CurrentUICulture.Name; cefSettings.CachePath = appConfig.BrowserCachePath; - cefSettings.CefCommandLineArgs.Add("touch-events", "enabled"); cefSettings.LogFile = appConfig.BrowserLogFilePath; cefSettings.LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info); cefSettings.PersistSessionCookies = !settings.DeleteCookiesOnStartup || !settings.DeleteCookiesOnShutdown; @@ -339,6 +348,7 @@ namespace SafeExamBrowser.Browser cefSettings.CefCommandLineArgs.Add("enable-media-stream"); cefSettings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing"); + cefSettings.CefCommandLineArgs.Add("touch-events", "enabled"); cefSettings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream"); InitializeProxySettings(cefSettings); @@ -417,6 +427,18 @@ namespace SafeExamBrowser.Browser } } + private void InitializePreferences() + { + Cef.UIThreadTaskFactory.StartNew(() => + { + using (var requestContext = Cef.GetGlobalRequestContext()) + { + requestContext.SetPreference("autofill.credit_card_enabled", false, out _); + requestContext.SetPreference("autofill.profile_enabled", false, out _); + } + }); + } + private void InitializeProxySettings(CefSettings cefSettings) { if (settings.Proxy.Policy == ProxyPolicy.Custom) @@ -506,6 +528,7 @@ namespace SafeExamBrowser.Browser private void Window_ResetRequested() { logger.Info("Attempting to reset browser..."); + AwaitReady(); foreach (var window in windows) diff --git a/SafeExamBrowser.Browser/BrowserControl.cs b/SafeExamBrowser.Browser/BrowserControl.cs index 445d4b0..823dc66 100644 --- a/SafeExamBrowser.Browser/BrowserControl.cs +++ b/SafeExamBrowser.Browser/BrowserControl.cs @@ -23,6 +23,7 @@ namespace SafeExamBrowser.Browser { private readonly Clipboard clipboard; private readonly ICefSharpControl control; + private readonly IContextMenuHandler contextMenuHandler; private readonly IDialogHandler dialogHandler; private readonly IDisplayHandler displayHandler; private readonly IDownloadHandler downloadHandler; @@ -47,6 +48,7 @@ namespace SafeExamBrowser.Browser public BrowserControl( Clipboard clipboard, ICefSharpControl control, + IContextMenuHandler contextMenuHandler, IDialogHandler dialogHandler, IDisplayHandler displayHandler, IDownloadHandler downloadHandler, @@ -58,8 +60,9 @@ namespace SafeExamBrowser.Browser IRenderProcessMessageHandler renderProcessMessageHandler, IRequestHandler requestHandler) { - this.control = control; this.clipboard = clipboard; + this.control = control; + this.contextMenuHandler = contextMenuHandler; this.dialogHandler = dialogHandler; this.displayHandler = displayHandler; this.downloadHandler = downloadHandler; @@ -89,31 +92,20 @@ namespace SafeExamBrowser.Browser { control.BrowserCore.EvaluateScriptAsync(code).ContinueWith(t => { - callback?.Invoke(new JavaScriptResult - { - Message = t.Result.Message, - Result = t.Result.Result, - Success = t.Result.Success - }); + callback?.Invoke(new JavaScriptResult { Message = t.Result.Message, Result = t.Result.Result, Success = t.Result.Success }); }); } else { - Task.Run(() => callback?.Invoke(new JavaScriptResult - { - Message = "JavaScript can't be executed in main frame!", - Success = false - })); + Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = "Could not execute JavaScript in main frame!", Success = false })); } } catch (Exception e) { - logger.Error($"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'!", e); - Task.Run(() => callback?.Invoke(new JavaScriptResult - { - Message = $"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'! Reason: {e.Message}", - Success = false - })); + var message = "Failed to execute JavaScript in main frame!"; + + logger.Error(message, e); + Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = $"{message} Reason: {e.Message}", Success = false })); } } @@ -129,10 +121,13 @@ namespace SafeExamBrowser.Browser 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.BeforeBrowse += (w, b, f, r, u, i, a) => a.Value = requestHandler.OnBeforeBrowse(w, b, f, r, u, i); + control.BeforeContextMenu += (w, b, f, p, m) => contextMenuHandler.OnBeforeContextMenu(w, b, f, p, m); control.BeforeDownload += (w, b, d, c, a) => a.Value = a.Value = downloadHandler.OnBeforeDownload(w, b, d, c); control.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.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.DialogClosed += (w, b) => javaScriptDialogHandler.OnDialogClosed(w, b); control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c); @@ -152,6 +147,7 @@ namespace SafeExamBrowser.Browser control.PreKeyEvent += (IWebBrowser w, IBrowser b, KeyType t, int k, int n, CefEventFlags m, bool i, ref bool s, GenericEventArgs a) => a.Value = keyboardHandler.OnPreKeyEvent(w, b, t, k, n, m, i, ref s); control.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.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); @@ -193,9 +189,21 @@ namespace SafeExamBrowser.Browser 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