ADR-013 Phase 2: meta_path import guard (re-review fix)

This commit is contained in:
2026-07-18 21:47:40 -04:00
parent 77ca2e0d2d
commit 83a5ce79d9
4 changed files with 368 additions and 19 deletions

View File

@@ -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)