Merge pull request #1579 from asottile/forbid-invalid-plugin-codes

forbid invalid plugin prefixes in plugin loading
This commit is contained in:
Anthony Sottile 2022-04-11 18:34:13 -04:00 committed by GitHub
commit 7e1e8b34af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 0 deletions

View file

@ -1,7 +1,9 @@
"""Functions related to finding and loading plugins.""" """Functions related to finding and loading plugins."""
import configparser import configparser
import inspect import inspect
import itertools
import logging import logging
import re
import sys import sys
from typing import Any from typing import Any
from typing import Dict from typing import Dict
@ -20,6 +22,8 @@ from flake8.exceptions import FailedToLoadPlugin
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
VALID_CODE = re.compile("^[A-Z]{1,3}[0-9]{0,3}$", re.ASCII)
FLAKE8_GROUPS = frozenset(("flake8.extension", "flake8.report")) FLAKE8_GROUPS = frozenset(("flake8.extension", "flake8.report"))
BANNED_PLUGINS = { BANNED_PLUGINS = {
@ -328,6 +332,13 @@ def _classify_plugins(
else: else:
raise NotImplementedError(f"what plugin type? {loaded}") raise NotImplementedError(f"what plugin type? {loaded}")
for loaded in itertools.chain(tree, logical_line, physical_line):
if not VALID_CODE.match(loaded.entry_name):
raise ExecutionError(
f"plugin code for `{loaded.display_name}` does not match "
f"{VALID_CODE.pattern}"
)
return Plugins( return Plugins(
checkers=Checkers( checkers=Checkers(
tree=tree, tree=tree,

View file

@ -29,6 +29,37 @@ def _loaded(plugin=None, obj=None, parameters=None):
return finder.LoadedPlugin(plugin, obj, parameters) return finder.LoadedPlugin(plugin, obj, parameters)
@pytest.mark.parametrize(
"s",
(
"E",
"E1",
"E123",
"ABC",
"ABC1",
"ABC123",
),
)
def test_valid_plugin_prefixes(s):
assert finder.VALID_CODE.match(s)
@pytest.mark.parametrize(
"s",
(
"",
"A1234",
"ABCD",
"abc",
"a-b",
"",
"A𝟗",
),
)
def test_invalid_plugin_prefixes(s):
assert finder.VALID_CODE.match(s) is None
def test_loaded_plugin_entry_name_vs_display_name(): def test_loaded_plugin_entry_name_vs_display_name():
loaded = _loaded(_plugin(package="package-name", ep=_ep(name="Q"))) loaded = _loaded(_plugin(package="package-name", ep=_ep(name="Q")))
assert loaded.entry_name == "Q" assert loaded.entry_name == "Q"
@ -761,6 +792,35 @@ def test_classify_plugins_enable_a_disabled_plugin():
) )
def test_classify_plugins_does_not_error_on_reporter_prefix():
# these are ok, don't check their name
plugin = _plugin(ep=_ep(name="report-er", group="flake8.report"))
loaded = _loaded(plugin=plugin)
opts = finder.PluginOptions.blank()
classified = finder._classify_plugins([loaded], opts)
assert classified == finder.Plugins(
checkers=finder.Checkers([], [], []),
reporters={"report-er": loaded},
disabled=[],
)
def test_classify_plugins_errors_on_incorrect_checker_name():
plugin = _plugin(ep=_ep(name="INVALID", group="flake8.extension"))
loaded = _loaded(plugin=plugin, parameters={"tree": True})
with pytest.raises(ExecutionError) as excinfo:
finder._classify_plugins([loaded], finder.PluginOptions.blank())
(msg,) = excinfo.value.args
assert msg == (
"plugin code for `local[INVALID]` "
"does not match ^[A-Z]{1,3}[0-9]{0,3}$"
)
@pytest.mark.usefixtures("reset_sys") @pytest.mark.usefixtures("reset_sys")
def test_load_plugins(): def test_load_plugins():
plugin = _plugin(ep=_ep(value="aplugin:ExtensionTestPlugin2")) plugin = _plugin(ep=_ep(value="aplugin:ExtensionTestPlugin2"))