fix(security): fix plugins/loader.py load_from_entry_point security #7476

Parse entry point targets before import so allowlist enforcement happens prior to execution and add a Behave regression scenario covering the disallowed-prefix path.

ISSUES CLOSED: #7476
This commit is contained in:
2026-04-12 03:34:09 +00:00
parent f67e8a2e07
commit 717a05f604
4 changed files with 91 additions and 6 deletions
+32 -1
View File
@@ -12,7 +12,7 @@ from __future__ import annotations
import contextlib
import threading
from typing import Any
from unittest.mock import MagicMock, patch
from unittest.mock import ANY, MagicMock, patch
from behave import given, then, when # type: ignore[import-untyped]
from behave.runner import Context # type: ignore[import-untyped]
@@ -429,6 +429,37 @@ def step_first_descriptor_name(context: Context, name: str) -> None:
assert context.discovered[0].name == name
@given('a mocked entry point group "{group}" with raw entry "{entry_spec}"')
def step_mock_raw_entry(context: Context, group: str, entry_spec: str) -> None:
name, value = entry_spec.split("=", 1)
mock_ep = MagicMock()
mock_ep.name = name
mock_ep.value = value
mock_ep.load = MagicMock(name="load")
context.mock_group = group
context.mock_eps = [mock_ep]
context.mock_ep_last = mock_ep
context.loader._logger = MagicMock()
@then("the mocked entry point load should not be called")
def step_entry_point_not_loaded(context: Context) -> None:
context.mock_ep_last.load.assert_not_called()
@then('a security warning should be emitted for disallowed entry point "{name}"')
def step_security_warning_emitted(context: Context, name: str) -> None:
logger_mock = context.loader._logger
logger_mock.warning.assert_any_call(
"plugin.entry_point_disallowed_prefix",
name=name,
value=context.mock_ep_last.value,
group=context.mock_group,
error=ANY,
)
# ---------------------------------------------------------------------------
# PluginManager — lifecycle
# ---------------------------------------------------------------------------