A folder-dropped declarative plugin runs a shell hook with no allowlist entry, on a stock project
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
plugins/trust.py、loader._discover_project、registry._resolve、registry.hooks_for、HookBus.emit から始めて、プロジェクト由来の宣言的プラグインが再現された stock-project パスを通る経路を追跡します。スコープを決める前に、提案されている3つの trust アプローチを maintainer と確認します。合意したポリシーによって、allowlist にないフォルダーに置かれた hook が unattended パスで実行されないことが保証され、その再現がテストでカバーされれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Split out of #461 point 4 / PR #495. #496 names this as separate and out of scope there; it had no issue of its own. Found by a completeness pass over PR #495's config_digest and confirmed firsthand against the code.
Related to #496 but strictly wider: #496 needs a project that has already enabled a [python] plugin. This one needs nothing — [plugins] enabled empty (the default) is enough.
The hole
plugins/trust.py is explicit that this is the designed behavior:
Data-only / declarative plugins (no
[python]) carry no executable Python — they load on folder-drop. Their shell hooks are the same risk surface as today'sengine.toml *_cmdand run through the bus like any command.
That reasoning holds when a human drops the folder. It does not hold under #461's threat model, where a driven session can write anywhere in the workspace — and loader._discover_project reads manifests from <project>/.bmad-loop/plugins/*/plugin.toml, inside that writable tree.
The chain, with no trust gate at any hop:
loader._discover_projectyields the manifest (origin="project").registry._resolve—if manifest.python is None:returns aLoadedPluginimmediately, before thetrust.is_enabledcheck, which only guards the[python]branch.registry.hooks_for(stage)iterates every loaded plugin and filters onmanifest.hook_for(stage)only.HookBus.emit→_dispatch_declarative→_run_subprocess(cmd, shell=True).
Reproduced
Stock policy, empty allowlist, one file written:
# <project>/.bmad-loop/plugins/rogue/plugin.toml
[plugin]
name = "rogue"
api_version = 1
[hooks.pre_sweep_cycle]
cmd = "..."
policy [plugins] enabled = ()
digest moved : False
plugin discovered : True
trusted flag : True
hooks_for(stage) : [('rogue', '...')]
The child SweepEngine builds a fresh PluginRegistry per run and emits pre_sweep_cycle, so the command runs on the host in the unattended path — which is precisely the human-absent window PR #495's gate exists to protect.
Why PR #495 does not close it
config_digest hashes sorted(policy.plugins.enabled). Nothing in .bmad-loop/plugins/ is read at all, and no policy field moves, so the auto-sweep integrity gate passes.
PR #495's docstring previously claimed "Adding a plugin is caught; changing what an enabled one runs is not." That is true only of plugins declaring [python]. Corrected in b049133 — the docstring and CHANGELOG now scope the claim to the [python] tier and point here, so the pin is not read as stronger than it is.
Shape of a fix (undecided — needs a maintainer call)
The trust tiers were drawn on what the plugin contains (Python vs. data). The gap is that they ignore where it came from, and project origin is agent-writable. Options, roughly in increasing cost:
- Gate declarative hooks on
enabledtoo, at least fororigin="project"manifests. Smallest change and it makes the allowlist mean what the CHANGELOG implies. Behavior change for anyone relying on folder-drop for a data-only plugin. - Trust declarative folder-drop by origin: builtin/entry_point load freely, project-origin needs an allowlist entry. Keeps folder-drop working for anything shipped or installed outside the workspace.
- Digest the manifests — fold
.bmad-loop/plugins/*/plugin.tomlcontent intoconfig_digest. Closes the auto-sweep window only, not the parent run, and does nothing for a first run. Weakest of the three; noted for completeness.
Options 1 and 2 also subsume the [plugins.<name>] settings surface: those are overlaid onto manifest defaults by registry._resolve_settings and exported into the hook env as BMAD_LOOP_SETTING_*, and the Unity plugin turns one into an --editor-path argv token. PR #495 deliberately does not pin them (the TUI settings screen writes those same tables, so pinning would refuse an auto-sweep after a supported human edit) — they sit inside an already-trusted plugin's blast radius, which is the boundary this issue and #496 are about.
Notes
- Not a regression. Predates #495, which narrows the surrounding surface without widening this one.
- Unlike #496, a stock project with no plugins configured is exposed.
Related: #461, #496, PR #495.
- 主要言語
- Python
- スター
- 174
- フォーク
- 72
- 平均マージ
- 17時間 56分
- マージ済み PR(30日)
- 95
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
bmad-code-org/bmad-loop のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 69/100
bmad-code-org/bmad-loop#973 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
bmad-code-org/bmad-loop#965 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
bmad-code-org/bmad-loop#1004 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 36/100
bmad-code-org/bmad-loop#972 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 1〜2日 初心者へのやさしさ 49/100
bmad-code-org/bmad-loop#971 ·
メンテナーはふだん 1 日以内に返信
bmad-code-org/bmad-loop の issue をすべて見る
似ている issue
-
[BUG]: Compliance rules never see Unity Catalog views as views対応中かも @wagnercsantos が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
databrickslabs/ontos#1348 ·
メンテナーはふだん 3 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
ibm-granite/granite.build#649 ·
メンテナーはふだん 1 日以内に返信
-
`forum_pr_poster` action will always return `none` when checking for pre-existing feedback threads, even if a feedback thread already exists.対応中かも @pgmzeta が今日担当しました。 オープンE-Verified P-Major
難易度 2/5 1〜3時間 初心者へのやさしさ 79/100
goonstation/goonstation#28259 ·
メンテナーはふだん 2 日以内に返信
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
woonstadrotterdam/monumenten#70 ·
メンテナーはふだん 15 日以内に返信
-
binding:python bug
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信