From eff00514699ab2b3f4aa5f99ec79ad0a008aec2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Fri, 21 Jul 2023 09:31:59 +0200 Subject: [PATCH] SEBWIN-612, #625: Implemented basic clipboard functionality. --- SafeExamBrowser.Browser/Content/Api.js | 10 +- SafeExamBrowser.Browser/Content/Clipboard.js | 172 ++++++++++++++++++ .../Content/ContentLoader.cs | 23 ++- .../Handlers/RenderProcessMessageHandler.cs | 6 + .../SafeExamBrowser.Browser.csproj | 3 + .../Operations/ClipboardOperationTests.cs | 17 +- SafeExamBrowser.Client/CompositionRoot.cs | 4 +- .../Operations/ClipboardOperation.cs | 28 +-- .../ConfigurationData/DataProcessor.cs | 14 +- .../ConfigurationData/DataValues.cs | 1 + SafeExamBrowser.I18n.Contracts/TextKey.cs | 3 +- SafeExamBrowser.I18n/Data/de.xml | 9 +- SafeExamBrowser.I18n/Data/en.xml | 9 +- SafeExamBrowser.I18n/Data/es.xml | 9 +- SafeExamBrowser.I18n/Data/fr.xml | 9 +- SafeExamBrowser.I18n/Data/it.xml | 9 +- SafeExamBrowser.I18n/Data/zh.xml | 9 +- .../IClipboard.cs | 28 +++ ...afeExamBrowser.Monitoring.Contracts.csproj | 1 + SafeExamBrowser.Monitoring/Clipboard.cs | 77 ++++++++ .../Keyboard/KeyboardInterceptor.cs | 11 +- .../SafeExamBrowser.Monitoring.csproj | 1 + .../Browser/BrowserSettings.cs | 5 + .../Monitoring/KeyboardSettings.cs | 15 ++ .../SafeExamBrowser.Settings.csproj | 1 + .../Security/ClipboardPolicy.cs | 31 ++++ .../Security/SecuritySettings.cs | 5 + 27 files changed, 463 insertions(+), 47 deletions(-) create mode 100644 SafeExamBrowser.Browser/Content/Clipboard.js create mode 100644 SafeExamBrowser.Monitoring.Contracts/IClipboard.cs create mode 100644 SafeExamBrowser.Monitoring/Clipboard.cs create mode 100644 SafeExamBrowser.Settings/Security/ClipboardPolicy.cs diff --git a/SafeExamBrowser.Browser/Content/Api.js b/SafeExamBrowser.Browser/Content/Api.js index ea2daacb..c18af7de 100644 --- a/SafeExamBrowser.Browser/Content/Api.js +++ b/SafeExamBrowser.Browser/Content/Api.js @@ -1,4 +1,12 @@ -SafeExamBrowser = { +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * 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/. + */ + +SafeExamBrowser = { version: 'SEB_Windows_%%_VERSION_%%', security: { browserExamKey: '%%_BEK_%%', diff --git a/SafeExamBrowser.Browser/Content/Clipboard.js b/SafeExamBrowser.Browser/Content/Clipboard.js new file mode 100644 index 00000000..59c54251 --- /dev/null +++ b/SafeExamBrowser.Browser/Content/Clipboard.js @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 ETH Zrich, Educational Development and Technology (LET) + * + * 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/. + * + * Original code taken and slightly adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215. + */ + +SafeExamBrowser.clipboard = { + clear: function () { + ranges = []; + text = ""; + }, + ranges: [], + text: "" +} + +function copySelectedData(e) { + if (e.target.contentEditable && e.target.setRangeText) { + SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd); + SafeExamBrowser.clipboard.ranges = []; + } else { + var selection = e.target.ownerDocument.defaultView.getSelection(); + var text = ""; + + for (var i = 0; i < selection.rangeCount; i++) { + SafeExamBrowser.clipboard.ranges[i] = selection.getRangeAt(i).cloneContents(); + text += SafeExamBrowser.clipboard.ranges[i].textContent; + } + + SafeExamBrowser.clipboard.text = text; + } +} + +function cutSelectedData(e) { + if (e.target.contentEditable && e.target.setRangeText) { + e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select'); + } else { + var designMode = e.target.ownerDocument.designMode; + var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]'); + var selection = e.target.ownerDocument.defaultView.getSelection(); + + for (var i = 0; i < selection.rangeCount; i++) { + var range = selection.getRangeAt(i); + + if (designMode === 'on') { + range.deleteContents(); + } else { + if (contentEditables.length) { + contentEditables.forEach(node => { + if (node.contains(range.commonAncestorContainer)) { + range.deleteContents(); + } + }); + } + } + } + } +} + +function pasteSelectedData(e) { + if (e.target.contentEditable && e.target.setRangeText) { + 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'); + } else { + var w = e.target.ownerDocument.defaultView; + var designMode = e.target.ownerDocument.designMode; + var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]'); + var selection = w.getSelection(); + + for (var i = 0; i < selection.rangeCount; i++) { + var r = selection.getRangeAt(i); + + if (designMode === 'on') { + r.deleteContents(); + } else { + if (contentEditables.length) { + contentEditables.forEach(node => { + if (node.contains(r.commonAncestorContainer)) { + r.deleteContents(); + } + }); + } + } + } + + if (designMode === 'on') { + var range = w.getSelection().getRangeAt(0); + + if (SafeExamBrowser.clipboard.ranges.length > 0) { + SafeExamBrowser.clipboard.ranges.map(r => { + range = w.getSelection().getRangeAt(0); + range.collapse(); + const newNode = r.cloneNode(true); + range.insertNode(newNode); + range.collapse(); + }); + } else { + range.collapse(); + range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text)); + range.collapse(); + } + } else { + if (contentEditables.length) { + contentEditables.forEach(node => { + var range = w.getSelection().getRangeAt(0); + + if (node.contains(range.commonAncestorContainer)) { + if (SafeExamBrowser.clipboard.ranges.length > 0) { + SafeExamBrowser.clipboard.ranges.map(r => { + range = w.getSelection().getRangeAt(0); + range.collapse(); + const newNode = r.cloneNode(true); + range.insertNode(newNode); + range.collapse(); + }); + } else { + range = w.getSelection().getRangeAt(0); + range.collapse(); + range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text)); + range.collapse(); + } + } + }); + } + } + } +} + +function onCopy(e) { + SafeExamBrowser.clipboard.clear(); + + try { + copySelectedData(e); + } finally { + e.preventDefault(); + e.returnValue = false; + } + + return false; +} + +function onCut(e) { + SafeExamBrowser.clipboard.clear(); + + try { + copySelectedData(e); + cutSelectedData(e); + } finally { + e.preventDefault(); + e.returnValue = false; + } + + return false; +} + +function onPaste(e) { + try { + pasteSelectedData(e); + } finally { + e.preventDefault(); + e.returnValue = false; + } + + return false; +} + +window.document.addEventListener("copy", onCopy, true); +window.document.addEventListener("cut", onCut, true); +window.document.addEventListener("paste", onPaste, true); diff --git a/SafeExamBrowser.Browser/Content/ContentLoader.cs b/SafeExamBrowser.Browser/Content/ContentLoader.cs index bb1833c4..0c8ed8ca 100644 --- a/SafeExamBrowser.Browser/Content/ContentLoader.cs +++ b/SafeExamBrowser.Browser/Content/ContentLoader.cs @@ -14,8 +14,10 @@ namespace SafeExamBrowser.Browser.Content { internal class ContentLoader { + private readonly IText text; + private string api; - private IText text; + private string clipboard; internal ContentLoader(IText text) { @@ -24,7 +26,7 @@ namespace SafeExamBrowser.Browser.Content internal string LoadApi(string browserExamKey, string configurationKey, string version) { - if (api == default(string)) + if (api == default) { var assembly = Assembly.GetAssembly(typeof(ContentLoader)); var path = $"{typeof(ContentLoader).Namespace}.Api.js"; @@ -78,5 +80,22 @@ namespace SafeExamBrowser.Browser.Content return html; } } + + internal string LoadClipboard() + { + if (clipboard == default) + { + var assembly = Assembly.GetAssembly(typeof(ContentLoader)); + var path = $"{typeof(ContentLoader).Namespace}.Clipboard.js"; + + using (var stream = assembly.GetManifestResourceStream(path)) + using (var reader = new StreamReader(stream)) + { + clipboard = reader.ReadToEnd(); + } + } + + return clipboard; + } } } diff --git a/SafeExamBrowser.Browser/Handlers/RenderProcessMessageHandler.cs b/SafeExamBrowser.Browser/Handlers/RenderProcessMessageHandler.cs index f92260b3..8aa30a25 100644 --- a/SafeExamBrowser.Browser/Handlers/RenderProcessMessageHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/RenderProcessMessageHandler.cs @@ -37,6 +37,7 @@ namespace SafeExamBrowser.Browser.Handlers var browserExamKey = keyGenerator.CalculateBrowserExamKeyHash(settings.ConfigurationKey, settings.BrowserExamKeySalt, frame.Url); var configurationKey = keyGenerator.CalculateConfigurationKeyHash(settings.ConfigurationKey, frame.Url); var api = contentLoader.LoadApi(browserExamKey, configurationKey, appConfig.ProgramBuildVersion); + var clipboard = contentLoader.LoadClipboard(); frame.ExecuteJavaScriptAsync(api); @@ -44,6 +45,11 @@ namespace SafeExamBrowser.Browser.Handlers { frame.ExecuteJavaScriptAsync($"window.print = function(){{ alert('{text.Get(TextKey.Browser_PrintNotAllowed)}') }}"); } + + if (settings.UseIsolatedClipboard) + { + frame.ExecuteJavaScriptAsync(clipboard); + } } public void OnContextReleased(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame) diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj index 70454295..9dd0af59 100644 --- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj +++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj @@ -189,6 +189,9 @@ + + + diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ClipboardOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ClipboardOperationTests.cs index 0b6a00f4..ccc13792 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ClipboardOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ClipboardOperationTests.cs @@ -10,43 +10,44 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.WindowsApi.Contracts; +using SafeExamBrowser.Monitoring.Contracts; +using SafeExamBrowser.Settings; +using SafeExamBrowser.Settings.Security; namespace SafeExamBrowser.Client.UnitTests.Operations { [TestClass] public class ClipboardOperationTests { + private Mock clipboard; private ClientContext context; private Mock loggerMock; - private Mock nativeMethodsMock; private ClipboardOperation sut; [TestInitialize] public void Initialize() { + clipboard = new Mock(); context = new ClientContext(); + context.Settings = new AppSettings(); loggerMock = new Mock(); - nativeMethodsMock = new Mock(); - sut = new ClipboardOperation(context, loggerMock.Object, nativeMethodsMock.Object); + sut = new ClipboardOperation(context, clipboard.Object, loggerMock.Object); } [TestMethod] public void MustPerformCorrectly() { sut.Perform(); - - nativeMethodsMock.Verify(n => n.EmptyClipboard(), Times.Once); + clipboard.Verify(n => n.Initialize(It.IsAny()), Times.Once); } [TestMethod] public void MustRevertCorrectly() { sut.Revert(); - - nativeMethodsMock.Verify(n => n.EmptyClipboard(), Times.Once); + clipboard.Verify(n => n.Terminate(), Times.Once); } } } diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index a4b06926..60a97fd4 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -28,6 +28,7 @@ using SafeExamBrowser.I18n; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging; using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring; using SafeExamBrowser.Monitoring.Applications; using SafeExamBrowser.Monitoring.Display; using SafeExamBrowser.Monitoring.Keyboard; @@ -110,6 +111,7 @@ namespace SafeExamBrowser.Client var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory); var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory, new Registry(ModuleLogger(nameof(Registry)))); + var clipboard = new Clipboard(ModuleLogger(nameof(Clipboard)), nativeMethods); var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo); var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods); var fileSystemDialog = BuildFileSystemDialog(); @@ -136,7 +138,7 @@ namespace SafeExamBrowser.Client operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new LazyInitializationOperation(BuildServerOperation)); operations.Enqueue(new LazyInitializationOperation(BuildProctoringOperation)); - operations.Enqueue(new ClipboardOperation(context, logger, nativeMethods)); + operations.Enqueue(new ClipboardOperation(context, clipboard, logger)); var sequence = new OperationSequence(logger, operations); diff --git a/SafeExamBrowser.Client/Operations/ClipboardOperation.cs b/SafeExamBrowser.Client/Operations/ClipboardOperation.cs index 9a9c6c2e..f30717c0 100644 --- a/SafeExamBrowser.Client/Operations/ClipboardOperation.cs +++ b/SafeExamBrowser.Client/Operations/ClipboardOperation.cs @@ -10,44 +10,50 @@ using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.WindowsApi.Contracts; +using SafeExamBrowser.Monitoring.Contracts; namespace SafeExamBrowser.Client.Operations { internal class ClipboardOperation : ClientOperation { - private ILogger logger; - private INativeMethods nativeMethods; + private readonly IClipboard clipboard; + private readonly ILogger logger; public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event StatusChangedEventHandler StatusChanged; - public ClipboardOperation(ClientContext context, ILogger logger, INativeMethods nativeMethods) : base(context) + public ClipboardOperation(ClientContext context, IClipboard clipboard, ILogger logger) : base(context) { + this.clipboard = clipboard; this.logger = logger; - this.nativeMethods = nativeMethods; } public override OperationResult Perform() { - EmptyClipboard(); + InitializeClipboard(); return OperationResult.Success; } public override OperationResult Revert() { - EmptyClipboard(); + FinalizeClipboard(); return OperationResult.Success; } - private void EmptyClipboard() + private void InitializeClipboard() { - logger.Info("Emptying clipboard..."); - StatusChanged?.Invoke(TextKey.OperationStatus_EmptyClipboard); + logger.Info("Initializing clipboard..."); + StatusChanged?.Invoke(TextKey.OperationStatus_InitializeClipboard); + clipboard.Initialize(Context.Settings.Security.ClipboardPolicy); + } - nativeMethods.EmptyClipboard(); + private void FinalizeClipboard() + { + logger.Info("Finalizing clipboard..."); + StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeClipboard); + clipboard.Terminate(); } } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs index e3ff7909..93866272 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs @@ -13,6 +13,7 @@ using System.Security.Cryptography; using SafeExamBrowser.Settings; using SafeExamBrowser.Settings.Applications; using SafeExamBrowser.Settings.Proctoring; +using SafeExamBrowser.Settings.Security; namespace SafeExamBrowser.Configuration.ConfigurationData { @@ -22,7 +23,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { AllowBrowserToolbarForReloading(settings); CalculateConfigurationKey(rawData, settings); - HandleBrowserHomeFunctionality(settings); + InitializeBrowserHomeFunctionality(settings); + InitializeClipboardSettings(settings); InitializeProctoringSettings(settings); RemoveLegacyBrowsers(settings); } @@ -58,12 +60,20 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void HandleBrowserHomeFunctionality(AppSettings settings) + private void InitializeBrowserHomeFunctionality(AppSettings settings) { settings.Browser.MainWindow.ShowHomeButton = settings.Browser.UseStartUrlAsHomeUrl || !string.IsNullOrWhiteSpace(settings.Browser.HomeUrl); settings.Browser.HomePasswordHash = settings.Security.QuitPasswordHash; } + private void InitializeClipboardSettings(AppSettings settings) + { + settings.Browser.UseIsolatedClipboard = settings.Security.ClipboardPolicy == ClipboardPolicy.Isolated; + settings.Keyboard.AllowCtrlC = settings.Security.ClipboardPolicy != ClipboardPolicy.Block; + settings.Keyboard.AllowCtrlV = settings.Security.ClipboardPolicy != ClipboardPolicy.Block; + settings.Keyboard.AllowCtrlX = settings.Security.ClipboardPolicy != ClipboardPolicy.Block; + } + private void InitializeProctoringSettings(AppSettings settings) { settings.Proctoring.Enabled = settings.Proctoring.JitsiMeet.Enabled || settings.Proctoring.Zoom.Enabled; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index 04cb7e64..2de2cea7 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -260,6 +260,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData settings.Security.AllowApplicationLogAccess = false; settings.Security.AllowTermination = true; settings.Security.AllowReconfiguration = false; + settings.Security.ClipboardPolicy = ClipboardPolicy.Isolated; settings.Security.KioskMode = KioskMode.CreateNewDesktop; settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny; diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 831f4bd7..0d212b4e 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -172,13 +172,14 @@ namespace SafeExamBrowser.I18n.Contracts Notification_ProctoringLowerHand, Notification_ProctoringRaiseHand, OperationStatus_CloseRuntimeConnection, - OperationStatus_EmptyClipboard, OperationStatus_FinalizeApplications, + OperationStatus_FinalizeClipboard, OperationStatus_FinalizeServer, OperationStatus_FinalizeServiceSession, OperationStatus_FinalizeSystemEvents, OperationStatus_InitializeApplications, OperationStatus_InitializeBrowser, + OperationStatus_InitializeClipboard, OperationStatus_InitializeConfiguration, OperationStatus_InitializeKioskMode, OperationStatus_InitializeProctoring, diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml index 9af3685a..761709ca 100644 --- a/SafeExamBrowser.I18n/Data/de.xml +++ b/SafeExamBrowser.I18n/Data/de.xml @@ -474,12 +474,12 @@ Schliesse Verbindung zur Runtime - - Lösche Zwischenablage - Beende Applikationen + + Finalisiere Zwischenablage + Finalisiere SEB-Server @@ -495,6 +495,9 @@ Initialisiere Browser + + Initialisiere Zwischenablage + Initialisiere Konfiguration diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml index dbe59b7a..57becdbc 100644 --- a/SafeExamBrowser.I18n/Data/en.xml +++ b/SafeExamBrowser.I18n/Data/en.xml @@ -474,12 +474,12 @@ Closing runtime connection - - Emptying clipboard - Finalizing applications + + Finalizing clipboard + Finalizing SEB-Server @@ -495,6 +495,9 @@ Initializing browser + + Initializing clipboard + Initializing configuration diff --git a/SafeExamBrowser.I18n/Data/es.xml b/SafeExamBrowser.I18n/Data/es.xml index 188a25d6..c9e8c65a 100644 --- a/SafeExamBrowser.I18n/Data/es.xml +++ b/SafeExamBrowser.I18n/Data/es.xml @@ -474,12 +474,12 @@ Cierre de la conexión con runtime - - Vaciado del portapapeles - Finalización de las aplicaciones + + Finalización del portapapeles + Finalización del SEB-Server @@ -495,6 +495,9 @@ Inicialización del navegador + + Inicialización del portapapeles + Inicialización de la configuración diff --git a/SafeExamBrowser.I18n/Data/fr.xml b/SafeExamBrowser.I18n/Data/fr.xml index 4ff928be..e1dc84a3 100644 --- a/SafeExamBrowser.I18n/Data/fr.xml +++ b/SafeExamBrowser.I18n/Data/fr.xml @@ -474,12 +474,12 @@ Fermeture de la connexion - - Vidage du presse-papiers - Finalisation des applications + + Finalisation du presse-papiers + Finalisation du serveur SEB @@ -495,6 +495,9 @@ Initialisation du navigateur + + Initialisation du presse-papiers + Initialisation de la configuration diff --git a/SafeExamBrowser.I18n/Data/it.xml b/SafeExamBrowser.I18n/Data/it.xml index 534165ee..6a9ee373 100644 --- a/SafeExamBrowser.I18n/Data/it.xml +++ b/SafeExamBrowser.I18n/Data/it.xml @@ -474,12 +474,12 @@ Chiusura della connessione runtime - - Eliminazione appunti - Finalizzazione delle applicazioni + + Finalizzazione degli appunti + Finalizzazione server SEB @@ -495,6 +495,9 @@ Inizializzazione del browser + + Inizializzazione degli appunti + Inizializzazione della configurazione diff --git a/SafeExamBrowser.I18n/Data/zh.xml b/SafeExamBrowser.I18n/Data/zh.xml index c5b0fe4b..79c29adf 100644 --- a/SafeExamBrowser.I18n/Data/zh.xml +++ b/SafeExamBrowser.I18n/Data/zh.xml @@ -438,12 +438,12 @@ 关闭运行时连接 - - 清空剪贴板 - 完成应用程序(运行已定型) + + 剪贴板定稿 + 完成服务会话(运行已定型) @@ -453,6 +453,9 @@ 初始化浏览器 + + 初始化剪贴板 + 初始化配置 diff --git a/SafeExamBrowser.Monitoring.Contracts/IClipboard.cs b/SafeExamBrowser.Monitoring.Contracts/IClipboard.cs new file mode 100644 index 00000000..8ba3b816 --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/IClipboard.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Settings.Security; + +namespace SafeExamBrowser.Monitoring.Contracts +{ + /// + /// Monitors the system clipboard and provides related functionality. + /// + public interface IClipboard + { + /// + /// Initializes the system clipboard according to the given policy. + /// + void Initialize(ClipboardPolicy policy); + + /// + /// Finalizes the system clipboard. + /// + void Terminate(); + } +} diff --git a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj index 71636a0d..cf850617 100644 --- a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj +++ b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj @@ -62,6 +62,7 @@ + diff --git a/SafeExamBrowser.Monitoring/Clipboard.cs b/SafeExamBrowser.Monitoring/Clipboard.cs new file mode 100644 index 00000000..735042dd --- /dev/null +++ b/SafeExamBrowser.Monitoring/Clipboard.cs @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Timers; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts; +using SafeExamBrowser.Settings.Security; +using SafeExamBrowser.WindowsApi.Contracts; + +namespace SafeExamBrowser.Monitoring +{ + public class Clipboard : IClipboard + { + private readonly ILogger logger; + private readonly INativeMethods nativeMethods; + private readonly Timer timer; + + private ClipboardPolicy policy; + + public Clipboard(ILogger logger, INativeMethods nativeMethods, int timeout_ms = 50) + { + this.logger = logger; + this.nativeMethods = nativeMethods; + this.timer = new Timer(timeout_ms); + } + + public void Initialize(ClipboardPolicy policy) + { + this.policy = policy; + + nativeMethods.EmptyClipboard(); + logger.Debug("Cleared clipboard."); + + if (policy != ClipboardPolicy.Allow) + { + timer.Elapsed += Timer_Elapsed; + timer.Start(); + + logger.Debug($"Started clipboard monitoring with interval {timer.Interval} ms."); + } + else + { + logger.Debug("Clipboard is allowed, not starting monitoring."); + } + + logger.Info($"Initialized clipboard for policy '{policy}'."); + } + + public void Terminate() + { + nativeMethods.EmptyClipboard(); + logger.Debug("Cleared clipboard."); + + if (policy != ClipboardPolicy.Allow) + { + timer.Stop(); + logger.Debug("Stopped clipboard monitoring."); + } + else + { + logger.Debug("Clipboard monitoring was not active."); + } + + logger.Info($"Finalized clipboard."); + } + + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + nativeMethods.EmptyClipboard(); + } + } +} diff --git a/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs b/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs index 74b01fd7..320c2ee9 100644 --- a/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs +++ b/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs @@ -20,9 +20,9 @@ namespace SafeExamBrowser.Monitoring.Keyboard public class KeyboardInterceptor : IKeyboardInterceptor { private Guid? hookId; - private ILogger logger; - private INativeMethods nativeMethods; - private KeyboardSettings settings; + private readonly ILogger logger; + private readonly INativeMethods nativeMethods; + private readonly KeyboardSettings settings; public KeyboardInterceptor(ILogger logger, INativeMethods nativeMethods, KeyboardSettings settings) { @@ -66,11 +66,16 @@ namespace SafeExamBrowser.Monitoring.Keyboard block |= key == Key.LWin && !settings.AllowSystemKey; block |= key == Key.PrintScreen && !settings.AllowPrintScreen; block |= key == Key.RWin && !settings.AllowSystemKey; + block |= modifier.HasFlag(KeyModifier.Alt) && key == Key.Escape && !settings.AllowAltEsc; block |= modifier.HasFlag(KeyModifier.Alt) && key == Key.F4 && !settings.AllowAltF4; block |= modifier.HasFlag(KeyModifier.Alt) && key == Key.Space; block |= modifier.HasFlag(KeyModifier.Alt) && key == Key.Tab; + + block |= modifier.HasFlag(KeyModifier.Ctrl) && key == Key.C && !settings.AllowCtrlC; block |= modifier.HasFlag(KeyModifier.Ctrl) && key == Key.Escape && !settings.AllowCtrlEsc; + block |= modifier.HasFlag(KeyModifier.Ctrl) && key == Key.V && !settings.AllowCtrlV; + block |= modifier.HasFlag(KeyModifier.Ctrl) && key == Key.X && !settings.AllowCtrlX; if (block) { diff --git a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj index acc431dc..7c363d8b 100644 --- a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj +++ b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj @@ -58,6 +58,7 @@ + diff --git a/SafeExamBrowser.Settings/Browser/BrowserSettings.cs b/SafeExamBrowser.Settings/Browser/BrowserSettings.cs index 23cc3bf8..8c53188b 100644 --- a/SafeExamBrowser.Settings/Browser/BrowserSettings.cs +++ b/SafeExamBrowser.Settings/Browser/BrowserSettings.cs @@ -203,6 +203,11 @@ namespace SafeExamBrowser.Settings.Browser /// public bool UseCustomUserAgent { get; set; } + /// + /// Determines whether the browser application will use an isolated clipboard only working within the browser itself. + /// + public bool UseIsolatedClipboard { get; set; } + /// /// Determines whether the will be appended to the . /// diff --git a/SafeExamBrowser.Settings/Monitoring/KeyboardSettings.cs b/SafeExamBrowser.Settings/Monitoring/KeyboardSettings.cs index 53e5c2a2..2e8deeec 100644 --- a/SafeExamBrowser.Settings/Monitoring/KeyboardSettings.cs +++ b/SafeExamBrowser.Settings/Monitoring/KeyboardSettings.cs @@ -31,11 +31,26 @@ namespace SafeExamBrowser.Settings.Monitoring /// public bool AllowAltTab { get; set; } + /// + /// Determines whether the user may use the CTRL+C shortcut. + /// + public bool AllowCtrlC { get; set; } + /// /// Determines whether the user may use the CTRL+ESC shortcut. /// public bool AllowCtrlEsc { get; set; } + /// + /// Determines whether the user may use the CTRL+V shortcut. + /// + public bool AllowCtrlV { get; set; } + + /// + /// Determines whether the user may use the CTRL+X shortcut. + /// + public bool AllowCtrlX { get; set; } + /// /// Determines whether the user may use the escape key. /// diff --git a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj index 6fc16b9e..a3d852e9 100644 --- a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj +++ b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj @@ -77,6 +77,7 @@ + diff --git a/SafeExamBrowser.Settings/Security/ClipboardPolicy.cs b/SafeExamBrowser.Settings/Security/ClipboardPolicy.cs new file mode 100644 index 00000000..67adee90 --- /dev/null +++ b/SafeExamBrowser.Settings/Security/ClipboardPolicy.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Settings.Security +{ + /// + /// Defines all policies with respect to the usage of the clipboard. + /// + public enum ClipboardPolicy + { + /// + /// Allows the usage of the system clipboard without restrictions. + /// + Allow, + + /// + /// Completely blocks the usage of the system clipboard by continuously clearing its content and blocking all related keyboard shortcuts. + /// + Block, + + /// + /// Continuously clears the content of the system clipboard and enables an isolated clipboard only working within the browser application. + /// + Isolated + } +} diff --git a/SafeExamBrowser.Settings/Security/SecuritySettings.cs b/SafeExamBrowser.Settings/Security/SecuritySettings.cs index f3bb9b5b..7ce2edbe 100644 --- a/SafeExamBrowser.Settings/Security/SecuritySettings.cs +++ b/SafeExamBrowser.Settings/Security/SecuritySettings.cs @@ -37,6 +37,11 @@ namespace SafeExamBrowser.Settings.Security /// public bool AllowReconfiguration { get; set; } + /// + /// Determines whether the user is allowed to use the system clipboard, a custom clipboard or no clipboard at all. + /// + public ClipboardPolicy ClipboardPolicy { get; set; } + /// /// The kiosk mode which determines how the computer is locked down. ///