Back to skill

Security audit

OpenClaw Output Metrics Footer

Security checks for vulnerabilities and agentic risk

Overview

This skill provides the advertised metrics footer, but it also reads a local OAuth token, makes authenticated network requests, and can leak limited usage metadata across sessions.

Install only if you are comfortable with this plugin reading your OpenClaw Codex OAuth profile and contacting chatgpt.com for quota data. Prefer restricting enabledChannels and disabledConversations, disabling appendSubagents in shared or multi-user gateways, and using it only where cross-session usage metadata disclosure is acceptable.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
assets/extension-template/index.ts:219
Finding

Cross-session LLM usage metadata disclosure

Content
View full analysis

Vulnerability Details

File Location: assets/extension-template/index.ts, lines 219-231
Vulnerability Type: Cross-session information disclosure caused by global usage correlation
Risk Level: Medium

Vulnerable Code

typescript
if (!event.content || typeof event.content !== "string") return;

const usage = latestUsage();
const quota = await fetchCodexQuota(quotaCacheMs);
const label = modelLabel(usage?.provider, usage?.model);
const win = contextWindow(usage?.model, usage?.provider);
const total = usage?.total ?? ((usage?.input ?? 0) + (usage?.output ?? 0));
const effectiveTotal = Math.max(0, total + reserve);
const ctxPct = win ? Math.round((effectiveTotal / win) * 100) : undefined;
const status = worst(colorForUsage(ctxPct), colorForRemaining(quota?.percent));
const parts = [
  `${status} ↑${fmt(usage?.input)} ↓${fmt(usage?.output)}`,
  `${Number.isFinite(ctxPct) ? ctxPct : "n/a"}%ctx`
];
if (quota?.percent != null) parts.push(`${quota.resetLabel ?? "5h"} ${quota.percent}%`);
parts.push(label);
const sub = cfg.appendSubagents === false ? null : aggregateSubagents(usage, cacheMs);

The underlying fallback and aggregation logic is:

typescript
function latestUsage(sessionKey?: string): Usage | undefined {
  if (sessionKey && recentBySession.has(sessionKey)) return recentBySession.get(sessionKey);
  return recentOutputs.at(-1);
}

function aggregateSubagents(root?: Usage, cacheMs = 120000): { input: number; output: number } | null {
  if (!root) return null;
  const cutoff = Date.now() - cacheMs;
  let input = 0;
  let output = 0;
  for (const u of recentOutputs) {
    if (u === root || u.ts < cutoff) continue;
    if (Math.abs(root.ts - u.ts) > cacheMs) continue;
    if (u.sessionKey && root.sessionKey && u.sessionKey === root.sessionKey) continue;
    input += u.input ?? 0;
    output += u.output ?? 0;
  }
  return input || outpu
...[truncated 3521 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pass the current session identity into the lookup and do not fall back to the globally latest output:

    typescript
    const usage = latestUsage(ctx.sessionKey);
    if (!usage) return;
    
  2. Change latestUsage() to fail closed when no session key or exact session match exists:

    typescript
    function latestUsage(sessionKey?: string): Usage | undefined {
      if (!sessionKey) return undefined;
      return recentBySession.get(sessionKey);
    }
    
  3. Do not infer subagent relationships solely from timestamps. Disable appendSubagents by default until OpenClaw supplies trustworthy parent/root run identifiers.

  4. When parent-run metadata is available, store it with each usage record and aggregate only records whose root-run ID, tenant identity, and owning session match the current delivery context.

  5. Partition all caches by an appropriate security boundary, such as tenant plus session, rather than maintaining a process-global list usable by every conversation.

  6. Remove stale entries from both recentOutputs and recentBySession after the configured retention period to reduce residual metadata exposure.

  7. Add concurrency tests that interleave llm_output and message_sending events from multiple sessions. Verify that no footer contains model or token metrics originating from another session.

  8. Validate channel and conversation identifiers using normalized strings and adopt an explicit provider allowlist by default where multi-tenant gateways are used.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The documented purpose is a UI footer, but the detected behavior includes reading a local auth file to extract Codex bearer tokens and making a request to an external ChatGPT backend endpoint. That is a material expansion of privilege and sensitivity beyond formatting output: it touches credentials, local filesystem secrets, and remote account data. In skill context, this is more dangerous because it is presented as a cosmetic telemetry add-on, so users are less likely to expect or scrutinize secret access and outbound authenticated traffic.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code directly reads OAuth access tokens from the user's local auth profile via auth-profiles.json, even though token access is not necessary for simply appending message metrics. Access to bearer tokens is highly sensitive: any code path that can read them can potentially misuse account privileges or exfiltrate credentials if the plugin is modified or compromised.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

Reading a local OAuth access token from the auth profile store is credential access, even if the stated purpose is quota lookup and the document says not to print the token. Accessing bearer tokens expands the extension's privilege boundary: if the extension is compromised, misimplemented, or logs/debug output leak, the token could be exposed or abused for unauthorized API calls.

Content

Scanner excerpt · references/implementation.md (reported line 77)May include surrounding context.

Codex quota

The extension reads the local OpenClaw auth profile store to find an OpenAI Codex OAuth access token, then fetches:

text
https://chatgpt.com/backend-api/wham/usage

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 57)May include surrounding context.

Install this skill into your OpenClaw workspace, then copy the bundled extension template:

bash
mkdir -p ~/.openclaw/extensions/openclaw-output-metrics-footer
cp -R assets/extension-template/* ~/.openclaw/extensions/openclaw-output-metrics-footer/

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares no explicit tool scope or permissions despite behavior indicating network access. In a plugin that is installed into a persistent agent workspace, undeclared capabilities reduce transparency and make it harder for operators to evaluate trust boundaries, especially when the same skill also references quota fetching and provider integrations. This is dangerous because users may install it believing it only formats local output, while it can reach external services or leverage broader runtime capabilities.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

Copy the bundled extension template:

bash
mkdir -p ~/.openclaw/extensions/openclaw-output-metrics-footer
cp -R assets/extension-template/* ~/.openclaw/extensions/openclaw-output-metrics-footer/

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill’s stated purpose is only to render a compact output footer, but it also reads local authentication material and makes a remote quota request to chatgpt.com. That hidden expansion of scope is dangerous because users enabling a cosmetic/UI plugin would not reasonably expect local secret access or outbound authenticated network activity, creating a trust and data-exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The plugin takes a locally stored access token and sends it as a Bearer token in an outbound request without any visible user-facing notice, consent flow, or runtime disclosure. Even if sent only to the intended service, this pattern is risky because it normalizes silent credential use by a low-risk-looking plugin and increases the blast radius if the endpoint, dependency chain, or plugin behavior changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly states it reads a local auth profile to obtain an OAuth access token and makes a request to an external endpoint, but the user-facing description does not clearly warn about this sensitive behavior. That creates a transparency and consent problem: users may install a seemingly cosmetic footer extension without realizing it accesses local credentials and performs network activity.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.potential_exfiltration

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
assets/extension-template/index.ts:121