Skip to content

Support creating "secret" variables that hide their values in data and log files #4537

@pekkaklarck

Description

@pekkaklarck

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')

Metadata

Metadata

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions