Summary
Auto-discovered project-local external tools are executable with a single UI click and there is no trust/approval step. A malicious rf-sim-extensions/ folder in an untrusted project registers attacker-controlled tools; duplicate ids let a project-local manifest silently replace an existing record.
Severity: Important (security)
Evidence
app/src/extension_manager.cpp scanRoots() always adds project_root / "rf-sim-extensions" after built-in/global roots.
app/src/extension_manager.cpp loadRoot() replaces an existing record when a later manifest reuses the same id (m_records[existing->second] = std::move(record)).
app/src/app.cpp renders every externalTools() entry into the UI and calls runExternalTool() on click.
app/src/external_tool_runner.cpp executes manifest.entry_path (or via python) with no approval/allowlist step.
tests/test_extensions.cpp ("extension manager prefers project-local copies over built-in copies") pins the override behavior.
Impact
Opening an untrusted project can register attacker-controlled tools in the app UI; one normal click runs local code as the user. Duplicate-id replacement makes a malicious project-local copy indistinguishable from an existing extension record.
Suggested fix
Add an extension trust model: disable project-local executable extensions by default, require explicit per-extension approval before first run, and block duplicate-id overrides for external-tool manifests unless the user re-authorizes the replacement.
Summary
Auto-discovered project-local external tools are executable with a single UI click and there is no trust/approval step. A malicious
rf-sim-extensions/folder in an untrusted project registers attacker-controlled tools; duplicateids let a project-local manifest silently replace an existing record.Severity: Important (security)
Evidence
app/src/extension_manager.cppscanRoots()always addsproject_root / "rf-sim-extensions"after built-in/global roots.app/src/extension_manager.cpploadRoot()replaces an existing record when a later manifest reuses the sameid(m_records[existing->second] = std::move(record)).app/src/app.cpprenders everyexternalTools()entry into the UI and callsrunExternalTool()on click.app/src/external_tool_runner.cppexecutesmanifest.entry_path(or via python) with no approval/allowlist step.tests/test_extensions.cpp("extension manager prefers project-local copies over built-in copies") pins the override behavior.Impact
Opening an untrusted project can register attacker-controlled tools in the app UI; one normal click runs local code as the user. Duplicate-id replacement makes a malicious project-local copy indistinguishable from an existing extension record.
Suggested fix
Add an extension trust model: disable project-local executable extensions by default, require explicit per-extension approval before first run, and block duplicate-id overrides for
external-toolmanifests unless the user re-authorizes the replacement.