From 0b72324a35d46bd3a715e1e31992bb4cd86bae9e Mon Sep 17 00:00:00 2001 From: Sean Budd Date: Tue, 30 Aug 2022 10:29:26 +1000 Subject: [PATCH] rename _isSecureObjectWhileLockScreenActivated, mark as stable API (#14044) Follow up of GHSA-rmq3-vvhq-gp32 Summary of the issue: The function _isSecureObjectWhileLockScreenActivated may be useful to add-on authors. Therefore the API should become stable, i.e. not prefixed with an underscore. The function is also not named in a way that makes it clear what a "secure object" is. Description of user facing changes None Description of development approach Rename _isSecureObjectWhileLockScreenActivated to objectBelowLockScreenAndWindowsIsLocked --- source/NVDAObjects/lockscreen.py | 2 +- source/api.py | 14 +++++++------- source/braille.py | 20 ++++++++++---------- source/eventHandler.py | 8 ++++---- source/mathPres/mathPlayer.py | 4 ++-- source/speech/speech.py | 6 +++--- source/utils/security.py | 28 +++++++++++++++++++++------- 7 files changed, 48 insertions(+), 34 deletions(-) diff --git a/source/NVDAObjects/lockscreen.py b/source/NVDAObjects/lockscreen.py index 523286b64b7..42ba192029f 100644 --- a/source/NVDAObjects/lockscreen.py +++ b/source/NVDAObjects/lockscreen.py @@ -13,7 +13,7 @@ class LockScreenObject(NVDAObject): """ Prevent users from object navigating outside of the lock screen. - While usages of `_isSecureObjectWhileLockScreenActivated` in the api module prevent + While usages of `api.objectBelowLockScreenAndWindowsIsLocked` prevent the user from moving to the object, this overlay class prevents reading neighbouring objects. """ diff --git a/source/api.py b/source/api.py index 7d28f91b018..bfc9bd33830 100644 --- a/source/api.py +++ b/source/api.py @@ -28,7 +28,7 @@ import appModuleHandler import cursorManager from typing import Any, Optional -from utils.security import _isSecureObjectWhileLockScreenActivated +from utils.security import objectBelowLockScreenAndWindowsIsLocked if typing.TYPE_CHECKING: import documentBase @@ -66,7 +66,7 @@ def setForegroundObject(obj: NVDAObjects.NVDAObject) -> bool: if not isinstance(obj, NVDAObjects.NVDAObject): log.error("Object is not a valid NVDAObject") return False - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return False globalVars.foregroundObject=obj return True @@ -86,7 +86,7 @@ def setFocusObject(obj: NVDAObjects.NVDAObject) -> bool: # noqa: C901 if not isinstance(obj, NVDAObjects.NVDAObject): log.error("Object is not a valid NVDAObject") return False - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return False if globalVars.focusObject: eventHandler.executeEvent("loseFocus",globalVars.focusObject) @@ -194,7 +194,7 @@ def setMouseObject(obj: NVDAObjects.NVDAObject) -> bool: if not isinstance(obj, NVDAObjects.NVDAObject): log.error("Object is not a valid NVDAObject") return False - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return False globalVars.mouseObject=obj return True @@ -207,7 +207,7 @@ def getDesktopObject() -> NVDAObjects.NVDAObject: def setDesktopObject(obj: NVDAObjects.NVDAObject) -> None: """Tells NVDA to remember the given object as the desktop object. - We cannot prevent setting this when _isSecureObjectWhileLockScreenActivated is True, + We cannot prevent setting this when objectBelowLockScreenAndWindowsIsLocked is True, as NVDA needs to set the desktopObject on start, and NVDA may start from the lockscreen. """ globalVars.desktopObject=obj @@ -270,7 +270,7 @@ def getNavigatorObject() -> NVDAObjects.NVDAObject: except (NotImplementedError, LookupError): obj = globalVars.reviewPosition.obj nextObj = getattr(obj, 'rootNVDAObject', None) or obj - if _isSecureObjectWhileLockScreenActivated(nextObj): + if objectBelowLockScreenAndWindowsIsLocked(nextObj): return globalVars.navigatorObject globalVars.navigatorObject = nextObj return globalVars.navigatorObject @@ -289,7 +289,7 @@ def setNavigatorObject(obj: NVDAObjects.NVDAObject, isFocus: bool = False) -> bo if not isinstance(obj, NVDAObjects.NVDAObject): log.error("Object is not a valid NVDAObject") return False - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return False globalVars.navigatorObject=obj globalVars.reviewPosition=None diff --git a/source/braille.py b/source/braille.py index c14d77ac2ec..f1ad2f5cec1 100644 --- a/source/braille.py +++ b/source/braille.py @@ -48,7 +48,7 @@ import queueHandler import brailleViewer from autoSettingsUtils.driverSetting import BooleanDriverSetting, NumericDriverSetting -from utils.security import _isSecureObjectWhileLockScreenActivated +from utils.security import objectBelowLockScreenAndWindowsIsLocked if TYPE_CHECKING: from NVDAObjects import NVDAObject @@ -343,7 +343,7 @@ def NVDAObjectHasUsefulText(obj: "NVDAObject") -> bool: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return False import displayModel if issubclass(obj.TextInfo,displayModel.DisplayModelTextInfo): @@ -643,7 +643,7 @@ def __init__(self, obj: "NVDAObject", appendText: str = ""): @param obj: The associated NVDAObject. @param appendText: Text which should always be appended to the NVDAObject text, useful if this region will always precede other regions. """ - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): raise RuntimeError("NVDA object is secure and should not be initialized as a braille region") super().__init__() self.obj = obj @@ -915,7 +915,7 @@ class TextInfoRegion(Region): allowPageTurns=True #: True if a page turn should be tried when a TextInfo cannot move anymore and the object supports page turns. def __init__(self, obj: "NVDAObject"): - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): raise RuntimeError("NVDA object is secure and should not be initialized as a braille region") super().__init__() self.obj = obj @@ -1629,7 +1629,7 @@ def getFocusContextRegions( obj: "NVDAObject", oldFocusRegions: Optional[List[Region]] = None, ) -> Generator[Region, None, None]: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return global _cachedFocusAncestorsEnd # Late import to avoid circular import. @@ -1702,7 +1702,7 @@ def getFocusRegions( obj: "NVDAObject", review: bool = False, ) -> Generator[Region, None, None]: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return # Allow objects to override normal behaviour. try: @@ -2069,7 +2069,7 @@ def _dismissMessage(self): def handleGainFocus(self, obj: "NVDAObject", shouldAutoTether: bool = True) -> None: if not self.enabled: return - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return if shouldAutoTether: self.setTether(self.TETHER_FOCUS, auto=True) @@ -2111,7 +2111,7 @@ def handleCaretMove( ) -> None: if not self.enabled: return - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return prevTether = self._tether if shouldAutoTether: @@ -2162,7 +2162,7 @@ def _handleProgressBarUpdate( self, obj: "NVDAObject", ) -> None: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return oldTime = getattr(self, "_lastProgressBarUpdateTime", None) newTime = time.time() @@ -2178,7 +2178,7 @@ def _handleProgressBarUpdate( def handleUpdate(self, obj: "NVDAObject") -> None: if not self.enabled: return - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return # Optimisation: It is very likely that it is the focus object that is being updated. # If the focus object is in the braille buffer, it will be the last region, so scan the regions backwards. diff --git a/source/eventHandler.py b/source/eventHandler.py index b73a330b2f7..97c2f99f964 100755 --- a/source/eventHandler.py +++ b/source/eventHandler.py @@ -22,7 +22,7 @@ import winUser import extensionPoints import oleacc -from utils.security import _isSecureObjectWhileLockScreenActivated +from utils.security import objectBelowLockScreenAndWindowsIsLocked if typing.TYPE_CHECKING: import NVDAObjects @@ -158,7 +158,7 @@ def _trackFocusObject(eventName: str, obj: "NVDAObjects.NVDAObject") -> None: if ( eventName == "gainFocus" - and not _isSecureObjectWhileLockScreenActivated( + and not objectBelowLockScreenAndWindowsIsLocked( obj, shouldLog=config.conf["debugLog"]["events"], ) @@ -282,7 +282,7 @@ def executeEvent( @param obj: the object the event is for @param kwargs: Additional event parameters as keyword arguments. """ - if _isSecureObjectWhileLockScreenActivated( + if objectBelowLockScreenAndWindowsIsLocked( obj, shouldLog=config.conf["debugLog"]["events"], ): @@ -304,7 +304,7 @@ def executeEvent( def doPreGainFocus(obj: "NVDAObjects.NVDAObject", sleepMode: bool = False) -> bool: - if _isSecureObjectWhileLockScreenActivated( + if objectBelowLockScreenAndWindowsIsLocked( obj, shouldLog=config.conf["debugLog"]["events"], ): diff --git a/source/mathPres/mathPlayer.py b/source/mathPres/mathPlayer.py index 8b99cfacb4f..e0ca49e74e4 100644 --- a/source/mathPres/mathPlayer.py +++ b/source/mathPres/mathPlayer.py @@ -30,7 +30,7 @@ CharacterModeCommand, PhonemeCommand, ) -from utils.security import _isSecureObjectWhileLockScreenActivated +from utils.security import objectBelowLockScreenAndWindowsIsLocked RE_MP_SPEECH = re.compile( @@ -103,7 +103,7 @@ def getBrailleRegions( self, review: bool = False, ) -> Generator[braille.Region, None, None]: - if _isSecureObjectWhileLockScreenActivated(self): + if objectBelowLockScreenAndWindowsIsLocked(self): return yield braille.NVDAObjectRegion(self, appendText=" ") region = braille.Region() diff --git a/source/speech/speech.py b/source/speech/speech.py index ecf155a149f..1652b76c8a5 100644 --- a/source/speech/speech.py +++ b/source/speech/speech.py @@ -60,7 +60,7 @@ from enum import IntEnum from dataclasses import dataclass from copy import copy -from utils.security import _isSecureObjectWhileLockScreenActivated +from utils.security import objectBelowLockScreenAndWindowsIsLocked if typing.TYPE_CHECKING: import NVDAObjects @@ -457,7 +457,7 @@ def getObjectPropertiesSpeech( # noqa: C901 _prefixSpeechCommand: Optional[SpeechCommand] = None, **allowedProperties ) -> SpeechSequence: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return [] #Fetch the values for all wanted properties newPropertyValues={} @@ -621,7 +621,7 @@ def getObjectSpeech( # noqa: C901 reason: OutputReason = OutputReason.QUERY, _prefixSpeechCommand: Optional[SpeechCommand] = None, ) -> SpeechSequence: - if _isSecureObjectWhileLockScreenActivated(obj): + if objectBelowLockScreenAndWindowsIsLocked(obj): return [] role=obj.role # Choose when we should report the content of this object's textInfo, rather than just the object's value diff --git a/source/utils/security.py b/source/utils/security.py index f52845134f0..eda61c18758 100644 --- a/source/utils/security.py +++ b/source/utils/security.py @@ -78,17 +78,23 @@ def _isLockAppAndAlive(appModule: "appModuleHandler.AppModule") -> bool: return appModule.appName == "lockapp" and appModule.isAlive -# TODO: mark this API as public when it becomes stable (i.e. remove the underscore). -# Add-on authors may require this function to make their code secure. -# Consider renaming (e.g. objectOutsideOfLockScreenAndWindowsIsLocked). -def _isSecureObjectWhileLockScreenActivated( +def objectBelowLockScreenAndWindowsIsLocked( obj: "NVDAObjects.NVDAObject", shouldLog: bool = True, ) -> bool: """ - While Windows is locked, Windows 10 and 11 doesn't prevent object navigation outside of the lockscreen. - As such, NVDA must prevent accessing and reading objects outside of the lockscreen when Windows is locked. - @return: C{True} if the Windows 10/11 lockscreen is active and C{obj} is outside of the lock screen. + While Windows is locked, the current user session is still running, and below the lockscreen + exists the current user's desktop. + + Windows 10 and 11 doesn't prevent object navigation below the lockscreen. + + If an object is above the lockscreen, it is accessible and visible to the user + through the Windows UX while Windows is locked. + An object below the lockscreen should only be accessible when Windows is unlocked, + as it may contain sensitive information. + + As such, NVDA must prevent accessing and reading objects below the lockscreen when Windows is locked. + @return: C{True} if the Windows 10/11 lockscreen is active and C{obj} is below the lock screen. """ if isWindowsLocked() and not isObjectAboveLockScreen(obj): if shouldLog and log.isEnabledFor(log.DEBUG): @@ -101,8 +107,16 @@ def _isSecureObjectWhileLockScreenActivated( def isObjectAboveLockScreen(obj: "NVDAObjects.NVDAObject") -> bool: """ + While Windows is locked, the current user session is still running, and below the lockscreen + exists the current user's desktop. + When Windows is locked, the foreground Window is usually LockApp, but other Windows can be focused (e.g. Windows Magnifier). + + If an object is above the lockscreen, it is accessible and visible to the user + through the Windows UX while Windows is locked. + An object below the lockscreen should only be accessible when Windows is unlocked, + as it may contain sensitive information. """ import appModuleHandler from NVDAObjects.IAccessible import TaskListIcon