Skip to content

Keyword call banner is silently disabled when Browser is imported with AS alias or subclassed #5154

Description

@aaltat

Three listener hooks compare the library name against the literal string "Browser":

  • Browser/browser.py:935or attrs["libname"] != "Browser" in _start_keyword, gating self._show_keyword_call(attrs)
  • Browser/browser.py:939if "secret" in attrs["kwname"].lower() and attrs["libname"] == "Browser":self._set_logging(False)
  • Browser/browser.py:1031 — the same condition in _end_keyword, restoring the log level

The listener itself is registered as self (Browser/browser.py:509), so the hooks do fire in these cases — only the name comparison fails.

That comparison is false in two ordinary situations:

  1. Library Browser AS PW
  2. a user library built by subclassing Browser, imported under its own name

1. Keyword-call banner is silently a no-op — confirmed

The banner is a <style id="kwCallBanner"> element injected into <head> (KW_CALL_BANNER_FUNCTION, Browser/browser.py:109). Same suite, same resource file, show_keyword_call_banner=True, real robot runs, webkit headless, probed with Get Element Count css=style#kwCallBanner:

Import Banner element count
Library Browser 1textContent starts body::before {\n content: 'Get Property css=style#kwCallBanner textContent';
Library Browser AS PW 0
Library MyLib where class MyLib(Browser) 0

The element is never created. Nothing appears in the page, in videos, in traces, or in the log, and no warning is issued — show_keyword_call_banner=True and Enable Presenter Mode simply do nothing for anyone using an alias or a subclass.

2. Secret log suppression is skipped — but no secret was disclosed

The _set_logging suppression at :939 / :1031 genuinely does not run for those imports. Observed in output.xml for the same three keywords and arguments:

Library Browser:   -- Fill Secret owner=Browser  args=['id=pw', '$PWD']    (no messages logged)
Library MyLib:     -- Fill Secret owner=MyLib    args=['id=pw', '$PWD']
                        [ TRACE ] Arguments: [ 'id=pw' | '$PWD' ]
                        [ TRACE ] Presenter mode: False, selector: id=pw, strict: True
                        [ TRACE ] Return: None

The secret value itself never appeared in clear text in either run. Grepping output.xml and log.html for both secret values, at TRACE:INFO, gave 0 occurrences in every configuration, including a forced failure path (Fill Secret against a missing selector, so the timeout error and the on-failure screenshot were exercised).

Three independent defences already keep the value out: the Robot Framework argument is the unresolved literal $PWD / %ENV (resolution happens inside resolve_secret, Browser/base/librarycomponent.py:243), an RF 7.4 Secret renders as Secret(value=<secret>), and fill_secret / type_secret pass log_response=False (Browser/keywords/interaction.py:846). _set_logging is belt-and-braces on top of those.

So this half is a consistency and robustness problem rather than a disclosure: any future code path that does log a resolved secret would be unprotected for aliased and subclassed imports. Worth noting that losing the suppression also changes the failure output in the other direction — plain Browser swallows the timeout traceback entirely, while the aliased/subclassed import shows it.

Suggested fix

Compare identity rather than the name — resolve the library instance for attrs["libname"] and check isinstance(instance, Browser), or compare against the instance's own resolved name — so aliases and subclasses behave like Browser. Browser/keywords/waiter.py:299-304 already does the isinstance form for Wait For Condition and can serve as the pattern.

Environment

Browser 20.3.0 (3b3ec7a9), Robot Framework 7.4.1, Python 3.14.7, macOS, webkit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions