security: extension capabilities are declared but never enforced #397
Description
Problem
ExtensionDescriptor.capabilities is parsed from entry-point extras and exposed publicly, but
nothing in the package ever reads it to make a decision. It looks like a permission model and grants
nothing.
_descriptor_from_entry_point() collects base-cli-cap-* extras into a sorted tuple
(lib/python/base_cli/extensions.py:364-370), and docs/extensions.md:41-51 tells authors to
"Declare the SDK version and capabilities in the entry-point extras". api_version is enforced —
ExtensionCompatibilityError is raised in load() when it is unsupported (line 236-237) — which
makes the neighbouring unenforced field more misleading, not less: an adopter reasonably infers that
declaring base-cli-cap-network means something.
A secondary weakness in the same area: _allowed() (lib/python/base_cli/extensions.py:312-319)
admits a descriptor if the allowlist contains the bare name, the group:name key, or the
distribution name. Allowlisting a bare name therefore admits that name from any group and any
distribution, and allowlisting a distribution admits every entry point it ships — including ones
added in a later release of that distribution. For an allowlist, which exists to be a trust
boundary, the loosest match winning is the wrong default.
Verified evidence
Reviewed 2026-09-30 at a58ec109349fa3f3d03eae5b0de078b39ea361a2.
$ grep -rn "capabilities" lib/python/base_cli/ docs/extensions.md
extensions.py:109: capabilities: tuple[str, ...] = ()
extensions.py:364: capabilities = tuple(
extensions.py:379: capabilities=capabilities,
docs/extensions.md:41:Declare the SDK version and capabilities in the entry-point extras. The
docs/extensions.md:51:`ExtensionDescriptor.api_version` and `.capabilities` expose this metadata.
Three references in the package, all construction or storage; no comparison, no filter, no gate.
Proposal
Pick one and make it explicit:
- Enforce them. Give
ExtensionDiscoveryasupported_capabilities/required_capabilities
parameter, refuse to load an extension declaring a capability the host has not granted, and raise a
typed error naming the capability — mirroring howapi_versionalready works. Note honestly in the
docs that this is declarative: a loaded Python extension runs in-process with full privileges, so
the gate expresses intent and enables audit, it does not sandbox. - Demote them. Document
capabilitiesas informational metadata only — useful for inventory and
for the CLI catalog work in [platform] Define signed CLI Catalog ingestion and governance boundaries #277 — and say plainly that it grants and restricts nothing.
Separately, tighten _allowed(): make group:name the canonical allowlist form, treat a bare name as
matching only within an explicitly requested group, and require distribution entries to be written as
something unambiguous such as dist:<name>. Document the precedence.
Acceptance criteria
capabilitiesis either enforced with a typed refusal and tests, or documented as
non-enforcing metadata indocs/extensions.md.- If enforced, the documentation states clearly that it is not a sandbox.
- Allowlist matching semantics are documented, and a test asserts a bare-name entry does not admit an
entry point from an unintended group or distribution. docs/security-threat-model.mdreflects whatever the extension trust boundary actually is.
Non-goals
- Do not build extension sandboxing or subprocess isolation here.
- Do not change
EXTENSION_API_VERSIONor the entry-point group names.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
codeforester commentedon Oct 22, 2026
Implementation evidence is in PR #462: #462
Capabilities are now explicitly informational metadata, allowlists require fully qualified
group:nameordist:distributionentries, and exact matching is covered by tests and security documentation. The issue-branch policy was also repaired by retaining only thesecuritycategory label; required hosted checks are now green. The issue remains In Review pending review/merge.codeforester commentedon Oct 23, 2026
Review follow-up completed in PR #462. Distribution allowlists now use exact installed metadata spelling and a precomputed approved set; a case-variant regression test and documentation were added. Commit: 10e1f7d. Focused and full pytest pass locally; all review threads are resolved.