-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Description
Often in automation we need to use passwords or other values that should not be logged. To help with that, various libraries have keywords that avoid logging given values even though their keywords normally do that. For example, SeleniumLibrary has Input Password
that doesn't log the password and also temporarily disables Robot's logging to avoid the password being logged by the underlying Selenium on DEBUG level. This works fine otherwise, but if tests are run with --loglevel TRACE
, Robot anyway logs all argument values and the password will be visible. Browser's Fill Secret
tries to avoid that problem by requiring the value to be passed using a special $var
syntax so that Robot won't log the actual variable value. Unfortunately that doesn't work if the value is passed to Fill Secret
via a user keyword and only that keyword uses the $var
syntax like in the following example:
*** Variables ***
${PASSWORD} secret
*** Test Cases ***
Example
Keyword ${PASSWORD} # This value is logged on TRACE level
*** Keywords ***
Keyword
[Arguments] ${pwd}
Fill Secret selector $pwd
To make it easier to use values that should not be logged, I propose we add new variable syntax *{var}
to create "secret variables". The syntax could be used when creating variables in the Variables section, when creating local variables based on keyword return values, and with Set Global/Suite/Test/Local Variable
keywords. The syntax would be only used for creating variables, they would be used normally like ${var}
:
*** Variables ***
*{PASSWORD} secret
*** Test Cases ***
Example
Keyword ${PASSWORD}
*{local} = Another keyword
Keyword ${local}
*** Keywords ***
Keyword
[Arguments] ${pwd}
Fill Secret selector ${pwd}
Technically the *{var}
assignment would create a custom object with this kind of implementation:
class Secret:
def __init__(self, value, name=None):
self.value = value
self.name = name
def __str__(self):
return f'<Secret {self.name}>' if self.name else '<Secret>'
These objects would be used when the variable is passed from tests to user keywords and from user keywords to other user keywords. The real value would, however, be passed to library keywords so they would get values normally and wouldn't need to be changed to benefit from this new syntax.
Although this new syntax would work with existing keywords, we should make it possible for library keywords to require arguments to be "secret". That's basically what Fill Secret
does but this new syntax would avoid the problem that the value can be logged on higher level. A convenient way to support this would be type hints:
from robot.api.secrets import Secret
def example(secret: Secret):
...
This would work so that Robot would validate that the used value is a Secret
and fail if it's not. I believe we should pass the real value, not the actual Secret
object, to the underlying keyword also in this case. That can confuse type checkers but keywords can use typing.cast if that's a problem. To support normal type conversion, we should also allow parameterizing the type hint like Secret[int]
.
Keywords could also return secret values simply by returning Secret
instances:
from robot.api.secrets import Secret
def example():
return Secret('value')