openclaw - ✅(Solved) Fix Security audit false positive: plugins.allow_phantom_entries flags bundled stock plugins as phantom in CLI context [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#68786Fetched 2026-04-19 15:07:32
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×1referenced ×1

openclaw security audit --deep can incorrectly warn that plugins.allow contains phantom entries even when those plugin IDs are valid bundled stock plugins and are visibly loaded in openclaw plugins list.

The root cause appears to be that the audit check uses listChannelPlugins() as the bundled-plugin inventory source. In CLI/audit context, listChannelPlugins() returns only runtime-loaded channel plugins, and can return an empty list. That makes legitimate bundled plugins look missing.

Error Message

openclaw security audit --deep can incorrectly warn that plugins.allow contains phantom entries even when those plugin IDs are valid bundled stock plugins and are visibly loaded in openclaw plugins list. plugins.allow_phantom_entries should only warn for IDs that are truly absent from both: It should not warn for valid stock bundled plugins that happen not to be runtime-loaded in the current audit context.

Root Cause

The audit compares plugins.allow against:

  1. user-installed extension directories under ~/.openclaw/extensions
  2. listChannelPlugins()

But listChannelPlugins() is not a full bundled-plugin inventory. It only returns runtime-loaded channel plugins, and in CLI audit context it can return zero. That makes valid bundled stock plugins appear missing.

Fix Action

Fixed

PR fix notes

PR #68867: fix(security): use manifest registry for phantom plugin check

Description (problem / solution / changelog)

Summary

Fixes false-positive security audit warnings for bundled stock plugins.

Root Cause

The security audit used listChannelPlugins() to get bundled plugin IDs, but this function only returns runtime-loaded channel plugins and can return an empty list in CLI/audit context. This caused legitimate bundled plugins (telegram, discord, browser, anthropic, etc.) to be flagged as phantom entries.

Fix

Use loadPluginManifestRegistry() instead, which loads all plugin manifests from both bundled and installed sources. This ensures all bundled plugins are correctly identified and excluded from the phantom check.

Changes

  • Import loadPluginManifestRegistry from ../plugins/manifest-registry.js
  • Replace listChannelPlugins() call with registry-based filtering
  • Filter for origin === "bundled" to get all bundled plugin IDs
  • Updated comments explaining the fix

Test Plan

  • Security audit no longer flags bundled plugins as phantom
  • Installed plugins still correctly identified
  • Actual phantom plugins still detected

Closes openclaw#68786

Changed files

  • src/security/audit-extra.async.ts (modified, +9/-3)

Code Example

const { extensionsDir, pluginDirs } = await listInstalledPluginDirs({ stateDir: params.stateDir });
if (pluginDirs.length > 0) {
  const allow = params.cfg.plugins?.allow;
  const allowConfigured = Array.isArray(allow) && allow.length > 0;
  if (allowConfigured) {
    const installedPluginIds = new Set(pluginDirs.map((dir) => path.basename(dir).toLowerCase()));
    const bundledPluginIds = new Set(listChannelPlugins().map((p) => p.id.toLowerCase()));
    const phantomEntries = allow.filter((entry) => {
      if (typeof entry !== "string" || entry === "group:plugins") return false;
      const lower = entry.toLowerCase();
      if (installedPluginIds.has(lower) || bundledPluginIds.has(lower)) return false;
      const canonicalId = normalizeOptionalLowercaseString(normalizePluginId(entry)) ?? "";
      return !canonicalId || !bundledPluginIds.has(canonicalId);
    });

---

plugins.allow_phantom_entries plugins.allow contains entries with no matching installed plugin
The following plugins.allow entries do not correspond to any installed plugin: telegram, discord, exa, firecrawl, browser, anthropic, google, xai, openai, brave, memory-core, moonshot, codex, voice-call.

---

function listChannelPlugins() {
  return listLoadedChannelPlugins();
}

---

$ node --input-type=module <<'NODE'
import { r as listChannelPlugins } from '/opt/homebrew/lib/node_modules/openclaw/dist/registry-Delpa74L.js';
const ids=listChannelPlugins().map(p=>p.id);
console.log('count=', ids.length);
console.log(ids.join(','));
NODE
count= 0

---

anthropic   ... loaded   stock:anthropic/index.js
brave       ... loaded   stock:brave/index.js
browser     ... loaded   stock:browser/index.js
codex       ... loaded   stock:codex/index.js
discord     ... loaded   stock:discord/index.js
exa         ... loaded   stock:exa/index.js
firecrawl   ... loaded   stock:firecrawl/index.js
google      ... loaded   stock:google/index.js
memory-core ... loaded   stock:memory-core/index.js
moonshot    ... loaded   stock:moonshot/index.js
openai      ... loaded   stock:openai/index.js
telegram    ... loaded   stock:telegram/index.js
voice-call  ... loaded   stock:voice-call/index.js
xai         ... loaded   stock:xai/index.js
RAW_BUFFERClick to expand / collapse

Security audit false positive: plugins.allow_phantom_entries flags bundled stock plugins as phantom in CLI context

Summary

openclaw security audit --deep can incorrectly warn that plugins.allow contains phantom entries even when those plugin IDs are valid bundled stock plugins and are visibly loaded in openclaw plugins list.

The root cause appears to be that the audit check uses listChannelPlugins() as the bundled-plugin inventory source. In CLI/audit context, listChannelPlugins() returns only runtime-loaded channel plugins, and can return an empty list. That makes legitimate bundled plugins look missing.

Exact code path

File:

  • /opt/homebrew/lib/node_modules/openclaw/dist/audit-extra.async-zxtuaq1W.js

Relevant section:

const { extensionsDir, pluginDirs } = await listInstalledPluginDirs({ stateDir: params.stateDir });
if (pluginDirs.length > 0) {
  const allow = params.cfg.plugins?.allow;
  const allowConfigured = Array.isArray(allow) && allow.length > 0;
  if (allowConfigured) {
    const installedPluginIds = new Set(pluginDirs.map((dir) => path.basename(dir).toLowerCase()));
    const bundledPluginIds = new Set(listChannelPlugins().map((p) => p.id.toLowerCase()));
    const phantomEntries = allow.filter((entry) => {
      if (typeof entry !== "string" || entry === "group:plugins") return false;
      const lower = entry.toLowerCase();
      if (installedPluginIds.has(lower) || bundledPluginIds.has(lower)) return false;
      const canonicalId = normalizeOptionalLowercaseString(normalizePluginId(entry)) ?? "";
      return !canonicalId || !bundledPluginIds.has(canonicalId);
    });

Reproduction

  1. Configure plugins.allow with bundled stock plugin IDs like:
    • telegram
    • discord
    • exa
    • firecrawl
    • browser
    • anthropic
    • google
    • xai
    • openai
    • brave
    • memory-core
    • moonshot
    • codex
    • voice-call
  2. Run:
    • openclaw security audit --deep
  3. Observe false-positive warning:
    • plugins.allow contains entries with no matching installed plugin

Evidence

1. Audit warns these bundled IDs are phantom

Example audit output on Fas's machine:

plugins.allow_phantom_entries plugins.allow contains entries with no matching installed plugin
The following plugins.allow entries do not correspond to any installed plugin: telegram, discord, exa, firecrawl, browser, anthropic, google, xai, openai, brave, memory-core, moonshot, codex, voice-call.

2. listChannelPlugins() is empty in CLI context

/opt/homebrew/lib/node_modules/openclaw/dist/registry-Delpa74L.js

function listChannelPlugins() {
  return listLoadedChannelPlugins();
}

Direct check in CLI context:

$ node --input-type=module <<'NODE'
import { r as listChannelPlugins } from '/opt/homebrew/lib/node_modules/openclaw/dist/registry-Delpa74L.js';
const ids=listChannelPlugins().map(p=>p.id);
console.log('count=', ids.length);
console.log(ids.join(','));
NODE
count= 0

3. The same supposedly phantom plugins are visibly loaded from stock

Example output from openclaw plugins list:

anthropic   ... loaded   stock:anthropic/index.js
brave       ... loaded   stock:brave/index.js
browser     ... loaded   stock:browser/index.js
codex       ... loaded   stock:codex/index.js
discord     ... loaded   stock:discord/index.js
exa         ... loaded   stock:exa/index.js
firecrawl   ... loaded   stock:firecrawl/index.js
google      ... loaded   stock:google/index.js
memory-core ... loaded   stock:memory-core/index.js
moonshot    ... loaded   stock:moonshot/index.js
openai      ... loaded   stock:openai/index.js
telegram    ... loaded   stock:telegram/index.js
voice-call  ... loaded   stock:voice-call/index.js
xai         ... loaded   stock:xai/index.js

Root cause

The audit compares plugins.allow against:

  1. user-installed extension directories under ~/.openclaw/extensions
  2. listChannelPlugins()

But listChannelPlugins() is not a full bundled-plugin inventory. It only returns runtime-loaded channel plugins, and in CLI audit context it can return zero. That makes valid bundled stock plugins appear missing.

Proposed fix

Use the full plugin manifest registry (bundled + installed manifests) as the inventory source instead of listChannelPlugins().

Something in the family of:

  • loadPluginManifestRegistry(...)
  • collect all plugin IDs from bundled and installed manifests
  • compare plugins.allow against that complete set

In short:

  • replace runtime-loaded-channel enumeration
  • with full manifest-registry enumeration

Expected behavior

plugins.allow_phantom_entries should only warn for IDs that are truly absent from both:

  • bundled plugin manifests
  • installed extension manifests

It should not warn for valid stock bundled plugins that happen not to be runtime-loaded in the current audit context.

extent analysis

TL;DR

The most likely fix is to replace listChannelPlugins() with a function that loads the full plugin manifest registry, including both bundled and installed manifests, to accurately identify phantom entries in plugins.allow.

Guidance

  • Identify the correct function to load the full plugin manifest registry, such as loadPluginManifestRegistry(...), and use it to collect all plugin IDs from bundled and installed manifests.
  • Compare plugins.allow against the complete set of plugin IDs from the manifest registry to determine phantom entries.
  • Verify that the new implementation correctly identifies phantom entries by testing with known valid and invalid plugin IDs.
  • Consider adding logging or debugging statements to ensure the manifest registry is being loaded correctly and the comparison is working as expected.

Example

const pluginManifestRegistry = await loadPluginManifestRegistry({ stateDir: params.stateDir });
const allPluginIds = pluginManifestRegistry.map((plugin) => plugin.id.toLowerCase());
const phantomEntries = params.cfg.plugins.allow.filter((entry) => {
  const lower = entry.toLowerCase();
  return !allPluginIds.includes(lower);
});

Notes

The proposed fix assumes that loadPluginManifestRegistry is a valid function that returns a list of plugin manifests, including both bundled and installed plugins. If this function does not exist or does not return the expected data, additional implementation may be required.

Recommendation

Apply the workaround by replacing listChannelPlugins() with the full plugin manifest registry enumeration, as this will provide a more accurate identification of phantom entries in plugins.allow. This approach will help prevent false positives and ensure that only truly absent plugins are warned about.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

plugins.allow_phantom_entries should only warn for IDs that are truly absent from both:

  • bundled plugin manifests
  • installed extension manifests

It should not warn for valid stock bundled plugins that happen not to be runtime-loaded in the current audit context.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Security audit false positive: plugins.allow_phantom_entries flags bundled stock plugins as phantom in CLI context [1 pull requests, 1 participants]