PyPI / keyring / 25.7.0

keyring 25.7.0

Source
https://github.com/jaraco/keyring
Tag scanned
v25.7.0
Compared with
v25.6.0 (0 signals introduced since)
Files
52 scanned, 7 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 7 info
Scan time
6.5 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 5 of 5 warn and critical signals; 5 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 06:12

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review dynamic-execution keyring/backend_complete.bash:8:3

    command name is a variable () not set in this file — the caller or environment decides what runs (ast pass, confidence 0.70)

    	choices=$(
    		"${COMP_WORDS[0]}" --list-backends 2>/dev/null |
    			while IFS=$' \t' read -r backend rest; do
    				printf "%s\n" "$backend"
    dismissed LLM, confidence 0.93 Bash tab-completion helper that runs the keyring command with --list-backends to gather backend names.
    Reasoning

    The command name comes from COMP_WORDS[0], the program the user is already completing (keyring itself), and it is called with --list-backends to populate completion choices. This is the standard way a bash completion script queries its own CLI, not a hidden execution path.

  2. info warn → info by review dynamic-execution keyring/backend_complete.zsh:12:14

    command name is a variable (`$words`) not set in this file — the caller or environment decides what runs (ast pass, confidence 0.70)

    		choices+=(${${line/ \(priority: /\\\\:}/)/})
    	done <<< "$($words[1] --list-backends)"
    	_arguments "*:keyring priority:(($choices))"
    }
    dismissed LLM, confidence 0.93 Zsh completion function that runs the current keyring command with --list-backends to offer backend choices.
    Reasoning

    $words[1] is the command being completed in zsh's completion system, and the script invokes it with --list-backends to build the priority list. This matches the bash counterpart and is ordinary completion behaviour.

  3. info critical → info by review dynamic-import keyring/core.py:134:5

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

        module_name, sep, class_name = keyring_name.rpartition('.')
        __import__(module_name)
        module = sys.modules[module_name]
        return getattr(module, class_name)
    likely benign LLM, confidence 0.80 Backend loader that imports a module from a dotted keyring class name taken from configuration.
    Reasoning

    This is keyring's plugin loader: it splits a configured dotted backend name, imports the module, and fetches the class with getattr. The module name comes from configuration or entry-point names set by the user or installed packages, not from untrusted remote input. Flagged as critical, but the pattern is the expected design of a plugin system. Residual doubt is that any config-supplied name can cause an import.

  4. info unicode-homoglyph keyring/testing/backend.py:19:6

    identifier `ξεσκεπάζωτηνψυχοφθόραβδελυγμία` contains homoglyph candidates: κ (03BA)→k, η (03B7)→n, ν (03BD)→v, ο (03BF)→o (in string/comment) (raw pass, confidence 0.35)

        "זהכיףסתםלשמועאיךתנצחקרפדעץטובבגן"
        "ξεσκεπάζωτηνψυχοφθόραβδελυγμία"
        "Съешьжеещёэтихмягкихфранцузскихбулокдавыпейчаю"
  5. info unicode-mixed-script keyring/testing/backend.py:19:6

    identifier `ξεσκεπάζωτηνψυχοφθόραβδελυγμία` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

        "זהכיףסתםלשמועאיךתנצחקרפדעץטובבגן"
        "ξεσκεπάζωτηνψυχοφθόραβδελυγμία"
        "Съешьжеещёэтихмягкихфранцузскихбулокдавыпейчаю"
  6. info warn → info by review dynamic-attribute keyring/util/platform_.py:39:13

    reach-by-name through `globals.get` (ast pass, confidence 0.60)

    # by default, use Unix convention
    data_root = globals().get('_data_root_' + platform.system(), _data_root_Linux)
    config_root = globals().get('_config_root_' + platform.system(), _config_root_Linux)
    likely benign LLM, confidence 0.90 Chooses the data-root path function for the current OS by looking up a name in module globals with a default.
    Reasoning

    The globals() lookup builds a name from a fixed prefix plus platform.system(), which returns a small set of OS names, and falls back to a Linux default. This is a simple, bounded per-platform dispatch with no attacker-controlled input.

  7. info warn → info by review dynamic-attribute keyring/util/platform_.py:40:15

    reach-by-name through `globals.get` (ast pass, confidence 0.60)

    data_root = globals().get('_data_root_' + platform.system(), _data_root_Linux)
    config_root = globals().get('_config_root_' + platform.system(), _config_root_Linux)
    likely benign LLM, confidence 0.90 Chooses the config-root path function for the current OS by looking up a name in module globals with a default.
    Reasoning

    Same bounded per-platform dispatch as the data_root line: a fixed prefix plus platform.system() selects the config-root function, with a Linux default. Nothing here is attacker-controlled or hidden.

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.1.0
git clone https://github.com/jaraco/keyring keyring && cd keyring
git checkout v25.7.0
disclude scan --severity info --diff v25.6.0 .