Enabled plugins are trusted by NAME, so a session can swap the module behind one

Cerrado
#496 3 comentarios 1 reacción 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
python
Área
security

Línea de trabajo

Empieza por plugins/trust.py, plugins/loader.py y registry._construct; después, lee las referencias a config_digest en PR #495 y el contexto relacionado en #461. Un maintainer debe elegir entre rechazar los shadows originados en el proyecto y fijar el contenido de la implementación; se considera terminado cuando un nombre de plugin habilitado no puede hacer que se ejecuten bytes de módulo no confiables, con pruebas que cubran el modelo de confianza elegido.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

area:config bug needs-design P2

Split out of #461 point 4 / PR #495, where the config-integrity pin closes the config half of this but not the implementation half. Found by an automated reviewer on that PR and confirmed against the code.

The hole

trust.require_enabled gates in-process Python on the plugin name only:

def require_enabled(policy, name) -> None:
    if not is_enabled(policy, name):
        raise PluginUntrusted(...)

and plugins/loader.py discovery is an overlay whose highest precedence source is inside the agent-writable workspace:

builtin (bmad_loop.data/plugins/*)         lowest
entry_point                                (inert today)
project (<project>/.bmad-loop/plugins/*)   highest — same-name override

registry._construct then resolves Path(manifest.scripts_dir) / manifest.python.module and calls spec.loader.exec_module(module). For a project-origin manifest, scripts_dir is <project>/.bmad-loop/plugins/<name>/.

So given a project whose policy already has [plugins] enabled = ["tea"], a driven session can write .bmad-loop/plugins/tea/plugin.toml + its own module, and the next registry build imports and executes that module in the orchestrator process. The name never changed, so the allowlist is satisfied. The simpler variant needs no builtin to shadow: if enabled names a plugin that does not exist yet, the session just creates the directory.

Unlike the ungated-declarative-shell-hooks item (also out of scope on #495), this one is inside the trust tier that is supposed to be gated — plugins/trust.py's own docstring says a [python] plugin is "never imported or executed unless its name is in [plugins] enabled", and that is true of the name while saying nothing about the bytes.

Why PR #495 does not close it

#495's runsetup.config_digest hashes sorted(policy.plugins.enabled). That catches a session adding a name to the allowlist. It cannot catch a session changing what an already-allowlisted name resolves to, because no policy field moves. The gap is recorded in the config_digest docstring and the CHANGELOG entry so the pin is not read as stronger than it is.

Shape of a fix (undecided — needs a maintainer call)

Two directions, and this is a plugin-trust-model decision rather than a hashing one, which is why it is not folded into #495:

  1. Refuse the shadow. Reject a project-origin manifest whose name collides with a builtin, or refuse project-origin [python] plugins entirely and require in-process plugins to be installed outside the workspace. Cheapest and closest to the relay fix in #461 points 1-3 (move the executable surface out of the agent-writable tree), but it is a behavior change for anyone developing a plugin in-tree.
  2. Pin the implementation. Extend the trust record to a content digest of the manifest + the [python] module, established when the operator enables the plugin. Preserves in-tree development but needs a place to store the pin and an --accept path when the operator legitimately edits their own plugin. Watch for false positives if a plugin writes into its own {scripts} dir.

Either way the auto-sweep gate then inherits the guarantee for free, since it already refuses on a moved config digest.

Notes

  • Default [plugins] enabled is empty, so a stock project is unaffected. The exposure is limited to projects that have deliberately enabled a [python] plugin.
  • This is not a regression: it predates #495, which narrows the surrounding surface without widening this one.

Related: #461, PR #495.

Lenguaje dominante
Python
Estrellas
174
Forks
72
Merge medio
19 h 25 min
PR fusionados (30 d)
84

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de bmad-code-org/bmad-loop

Todos los issues de bmad-code-org/bmad-loop

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.