Skip to content

Malformed third-party entry point crashes extension discovery for every extension, not just the offending one #213

Description

@codeforester

Summary

_descriptor_from_entry_point() raises a bare, uncaught ValueError when a single entry point declares more than one base-cli-api-vN extra. That exception is not a subclass of ExtensionDiscoveryError, and it is raised from _metadata_descriptors() — which every listing/loading entry point (list(), list_commands(), list_profiles(), list_plugins(), load(), load_all()) calls unconditionally before any per-descriptor error isolation kicks in.

Details

lib/python/base_cli/extensions.py:339-357:

def _descriptor_from_entry_point(entry_point: Any) -> ExtensionDescriptor:
    ...
    if len(api_versions) > 1:
        raise ValueError("an extension entry point may declare only one base-cli-api-vN extra")

This is called unconditionally from _metadata_descriptors() (extensions.py:273-284), which backs list() (:189-203) and therefore list_commands()/list_profiles()/list_plugins()/load()/load_all() alike. Reproduced directly: registering one fake entry point with extras = ("base-cli-api-v1", "base-cli-api-v2") in a single group makes ExtensionDiscovery.list_commands() raise ValueError uncaught, even though the caller only asked about one group and no other entry point is involved.

This directly contradicts the module's own isolation design: load() (:238-241) wraps its per-descriptor load in except BaseException and re-raises as ExtensionLoadError, and load_all() (:252-261) individually catches ExtensionLoadError/ExtensionCollisionError/ExtensionCompatibilityError per descriptor so one bad extension can't take down the others — but none of that isolation exists for this particular validation error, and it fires even earlier, during metadata-only listing before any extension code is loaded or executed.

Impact

One badly-packaged third-party plugin (a plausible packaging typo — declaring both an old and new base-cli-api-v* extra rather than malicious intent) crashes command/profile/plugin discovery for a host application that never touched that plugin's code, likely at CLI startup. A consumer defensively catching except ExtensionDiscoveryError around discovery calls won't catch this either, since the raised type isn't in that hierarchy.

Suggested fix

Either validate/skip malformed entry points per-descriptor inside _metadata_descriptors() (matching the isolation load_all() already provides), or raise an ExtensionDiscoveryError subclass instead of a bare ValueError so it's at least catchable by the documented exception hierarchy, and have list() skip and continue past the offending descriptor rather than aborting the whole call.

Metadata

Metadata

Assignees

Labels

bugSomething is not working

Type

No type

Projects

Status
Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions