ADR-013 Phase 2: meta_path import guard (re-review fix)
This commit is contained in:
@@ -160,35 +160,50 @@ class PluginLoader:
|
||||
plugin_name=name,
|
||||
)
|
||||
|
||||
# THE import gate. Every import path reaches here, so an unsigned or
|
||||
# tampered plugin never executes under enforcement (findings #1, #2).
|
||||
# When enforcing: strip any bytecode (so only verified source can run),
|
||||
# verify the tree, and import without writing new bytecode.
|
||||
import contextlib
|
||||
from .packaging import strip_bytecode, no_bytecode
|
||||
# Under enforcement, verify plugin.py from a SINGLE read and exec exactly
|
||||
# those bytes (no re-open, no .pyc), so there is no verify-vs-exec gap on
|
||||
# this file. Its submodule imports (from .models import ...) and every
|
||||
# `from plugins.X...` in core code are verified by the sys.meta_path
|
||||
# guard installed at init_app - the import system, not this method, is
|
||||
# the real choke point (findings #1, #2, #3).
|
||||
enforcing = self.verifier is not None and self.verifier.require_signed
|
||||
if enforcing:
|
||||
from .packaging import strip_bytecode, no_bytecode
|
||||
from .importguard import PluginImportGuard, PluginVerificationError
|
||||
strip_bytecode(plugin_dir)
|
||||
if self.verifier is not None:
|
||||
ok, reason = self.verifier.check(name)
|
||||
if not ok:
|
||||
try:
|
||||
source = PluginImportGuard(
|
||||
self.plugins_dir, self.verifier).verified_source(
|
||||
name, 'plugin.py')
|
||||
except PluginVerificationError as e:
|
||||
raise PluginContractError(
|
||||
f'Plugin {name} failed signature verification: {reason}',
|
||||
f'Plugin {name} failed signature verification: {e}',
|
||||
plugin_name=name,
|
||||
)
|
||||
|
||||
try:
|
||||
with (no_bytecode() if enforcing else contextlib.nullcontext()):
|
||||
) from e
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f'plugins.{name}.plugin', plugin_module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
with no_bytecode():
|
||||
exec(compile(source, str(plugin_module_path), 'exec'),
|
||||
module.__dict__)
|
||||
except Exception as e:
|
||||
raise PluginContractError(
|
||||
f'Plugin {name} import failed: {e}',
|
||||
plugin_name=name,
|
||||
) from e
|
||||
else:
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f'plugins.{name}.plugin', plugin_module_path,
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
except Exception as e:
|
||||
raise PluginContractError(
|
||||
f'Plugin {name} import failed: {e}',
|
||||
plugin_name=name,
|
||||
) from e
|
||||
except Exception as e:
|
||||
raise PluginContractError(
|
||||
f'Plugin {name} import failed: {e}',
|
||||
plugin_name=name,
|
||||
) from e
|
||||
|
||||
for attr_name in dir(module):
|
||||
attr = getattr(module, attr_name)
|
||||
|
||||
Reference in New Issue
Block a user