Back to skill

Security audit

a2a-Market-ACP-Lite-Negotiation

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent, but it needs review because it can automatically negotiate through a gateway while exposing tokens and sending negotiation context to a model tool.

Review before installing. Use only with a trusted local or HTTPS gateway, replace all default tokens with unique secrets, avoid passing API keys on the command line, and understand that the OpenClaw engine may send negotiation/session data to a model provider. Do not use it for real financial or business negotiations until token handling, TLS guidance, and policy checks around model decisions are tightened.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
src/cli/index.js:779
Finding
Gateway Authentication Token Exposed in Query Strings and Standard Output<![CDATA[ ## Vulnerability Details **File Location**: `src/cli/index.js:779-787` and `src/cli/index.js:916-917` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: High ### Vulnerable Code ```js process.stdout.write(`${JSON.stringify({ ok: true, mode: 'gateway-agent-loop', role, agentId, decisionEngine, gateway, pullUrl: registration.pullUrl ?? `${gateway}/agents/pull?agentId=${encodeURIComponent(agentId)}&token=${encodeURIComponent(token)}` })}\n`); ``` The token is also included in every polling URL: ```js const pullUrl = `${gateway}/agents/pull?agentId=${encodeURIComponent(agentId)}&token=${encodeURIComponent(token)}&timeoutMs=${pullTimeoutMs}`; const pull = await fetchJsonOrThrow(pullUrl); ``` ### Technical Analysis The gateway registration token is used as a bearer credential but is embedded in the query string of the polling URL. The generated credential-bearing URL is then printed to standard output when the agent starts. Secrets in query strings can be captured by gateway access logs, reverse proxies, network monitoring products, tracing systems, exception diagnostics, and other URL-oriented telemetry. Printing the URL further exposes the token to terminal capture, CI logs, process supervisors, and applications consuming the CLI output. The gateway is configurable and the implementation does not require TLS. Although the documented default is a loopback HTTP endpoint, configuring a non-loopback `http://` gateway would transmit the token and negotiation traffic without transport encryption. The network communication itself is necessary for the declared gateway-only negotiation functionality. Transmitting and logging the credential in a URL is not necessary and exceeds safe least-exposure practices. ### Attack Path 1. An operator starts the gateway agent loop. 2. The gateway returns an authentication token during registration. 3. The Skill inserts that token into the `/agents/pull` query string. 4. The Sk ...[truncated 844 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove credentials from URL query parameters. 2. Transmit the token in an authorization header, for example: ```js const pullUrl = `${gateway}/agents/pull?agentId=${encodeURIComponent(agentId)}&timeoutMs=${pullTimeoutMs}`; const pull = await fetchJsonOrThrow(pullUrl, { headers: { authorization: `Bearer ${token}` } }); ``` 3. Apply the same header-based authentication design to every authenticated gateway endpoint. 4. Never print a URL received from the gateway without parsing and redacting sensitive query parameters. 5. Replace `pullUrl` in startup output with a credential-free endpoint or a redacted value. 6. Reject non-HTTPS gateway URLs unless the hostname is a verified loopback address and the operator explicitly permits local plaintext communication. 7. Configure gateway tokens with short lifetimes and narrowly scoped permissions. 8. Revoke and rotate any tokens that may already have entered logs. 9. Ensure error messages and HTTP instrumentation also redact authorization values. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
src/cli/index.js:479
Finding
Untrusted Gateway Content Is Embedded Directly in the OpenClaw Instruction Prompt<![CDATA[ ## Vulnerability Details **File Location**: `src/cli/index.js:479-493` **Vulnerability Type**: `T01: Skill Instruction Hijacking` **Risk Level**: Medium ### Vulnerable Code ```js function buildOpenClawTurnPrompt({ turnInput, role, extraPrompt }) { const compactPayload = JSON.stringify(turnInput); const roleText = role === 'seller' ? 'seller' : 'buyer'; return [ `You are the ${roleText} side in an A2A pricing negotiation turn.`, 'Contract=turn-decision-v1.', 'Return ONLY JSON with exactly these keys: action, offerMinorUnits, utterance, reason.', 'action must be one of offer/counter/accept/reject.', 'For offer/counter, offerMinorUnits must be a positive integer.', 'For accept/reject, offerMinorUnits can be null.', 'Do not output markdown or any extra text.', 'Do not output session-level fields such as result/status/trace/dialogue/history.', extraPrompt || '', `Context: ${compactPayload}` ].filter(Boolean).join(' '); } ``` The resulting prompt is supplied to OpenClaw at `src/cli/index.js:548-557`: ```js const prompt = buildOpenClawTurnPrompt({ turnInput, role, extraPrompt }); const sessionId = String( turnInput?.message?.sessionId ?? turnInput?.session?.sessionId ?? turnInput?.sessionId ?? '' ).trim(); const runtime = resolveOpenClawBin(); const args = [...runtime.argsPrefix, 'agent', '--local', '--json', '--message', prompt]; ``` ### Technical Analysis The complete `turnInput` object is serialized and appended to an LLM prompt. This object includes session information and gateway-controlled message content. Text fields such as labels, product, goal, policy values, and message payload values are not isolated from model instructions, constrained by an allowlist, or subject to explicit length limits. A malicious counterparty or compromised gateway can therefore insert instruction-like text into negotiation data. The model may interpret that text as an instruction rather than inert context and p ...[truncated 1645 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Build the model context from a strict allowlist of fields required for a turn rather than serializing the entire `turnInput`. 2. Validate every field before prompt construction: - Require expected primitive types. - Enforce reasonable text-length limits. - Reject unexpected nested objects and properties. - Validate monetary values and round numbers against bounded ranges. 3. Place untrusted values in a clearly delimited data block and explicitly tell the model that content inside the block is data, not instructions. 4. Avoid including fields such as arbitrary policy objects, labels, or gateway metadata unless they are necessary for the decision. 5. Apply deterministic post-model policy enforcement: - Reject seller offers below the configured floor. - Reject buyer offers or acceptances above the configured ceiling. - Verify allowed actions for the current phase. - Verify that accepted amounts match a valid outstanding offer. 6. Fall back to the rule engine or reject the turn if the model output violates policy. 7. Treat `--openclaw-extra-prompt` as privileged configuration and prevent untrusted input sources from setting it. 8. Add tests containing common prompt-injection strings in every gateway-controlled text field. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/cli/index.js:740
Finding
Predictable Shared Default Used for Application-Level Authentication<![CDATA[ ## Vulnerability Details **File Location**: `src/cli/index.js:740` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```js const expectedAuthToken = String(input['auth-token'] ?? input.authToken ?? 'market-auth-token').trim(); ``` The responder compares inbound authentication envelopes against this value at `src/cli/index.js:629-634`: ```js case 'AUTH': return { ok: String(envelope?.payload?.token ?? '') === expectedAuthToken, role, agentId }; ``` The predictable default is also documented in `SKILL.md:31`: ```md - `--auth-token` (default `market-auth-token`) ``` ### Technical Analysis When the operator does not supply `--auth-token`, all default installations use the same publicly documented value, `market-auth-token`. This value cannot function as a meaningful secret because an attacker can derive it from the distributed source or documentation. The check is performed inside the responder after a message has been delivered by the gateway. Its practical impact therefore depends on how the gateway uses the returned `AUTH` result and whether unauthorized parties can enqueue authentication messages. Nevertheless, using a fixed shared default causes the application-level authentication mechanism to fail open from a credential-strength perspective. ### Attack Path 1. An operator starts the Skill without explicitly setting `--auth-token`. 2. The Skill uses the predictable value `market-auth-token`. 3. An attacker capable of submitting an `AUTH` envelope supplies that documented value in `envelope.payload.token`. 4. The equality comparison succeeds. 5. The Skill returns an authentication response containing `ok: true`. 6. If the gateway relies on this response to authorize negotiation participation, the attacker is treated as authenticated. ### Impact Assessment Successful exploitation may allow an unauthorized gateway participant to pass the Skill's application-level authen ...[truncated 276 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the hard-coded fallback authentication value. 2. Require an operator-supplied secret when protocol-level authentication is enabled, and terminate startup if it is missing. 3. Alternatively, generate a cryptographically random token during secure enrollment and communicate it through a protected channel. 4. Store the token in a dedicated secret environment variable or secret manager rather than routinely passing it on the command line. 5. Use a unique token per agent and deployment, with expiration and revocation support. 6. Compare secret values using a timing-safe comparison after validating equal buffer lengths. 7. Prefer gateway-issued challenge-response authentication or signed messages over a static token inside an envelope. 8. Update `SKILL.md` to state that authentication configuration is mandatory and must contain a high-entropy secret. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Hidden Instructions

High
Category
Prompt Injection
Content
---
name: a2a-market-acp-lite-negotiation
description: Gateway-only ACP negotiation skill with optional OpenClaw model-driven turn decisions.
---
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Ae1

High
Category
analysis-evasion
Content
node src/cli/index.js --role buyer --agent-id buyer-openclaw --gateway http://127.0.0.1:3085
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli/index.js --role buyer --agent-id buyer-openclaw --gateway http://127.0.0.1:3085
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli/index.js --role buyer --agent-id buyer-openclaw --gateway http://127.0.0.1:3085
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The documentation states the skill is now gateway-only and that single-turn local decision mode has been removed. However, the same file later documents `--decision-engine rule|openclaw` and explicitly says `NEGOTIATION_TURN` is decided by the selected engine, which indicates local decision logic remains part of the skill's behavior.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill documentation instructs users to register, poll, and respond over HTTP and references authentication tokens and optional direct API keys, but it does not warn about transmitting credentials or negotiation data to a gateway. In practice, this can lead users to run the skill against non-TLS endpoints, expose secrets in command history, or send sensitive business context to an untrusted gateway without understanding the risk.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The OpenClaw path can inject a directly supplied API key into the child process environment and invoke an external model provider without any explicit user-facing disclosure at the point of execution. In a skill/agent context, this can lead to unanticipated transmission of negotiation context and potentially sensitive session data to third-party LLM services, especially when execution is triggered through flags or stdin rather than an interactive consent flow.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/cli/index.js:572

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/cli/index.js:497