Back to skill

Security audit

opencode-responses-bridge-skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local API bridge, but it stores sensitive chat/request data in local debug files by default and forwards credentials and prompts to a configurable upstream endpoint.

Install only if you are comfortable with a local proxy forwarding your prompts, tool data, images, and bearer token to the configured upstream API. Treat OPENCODE_UPSTREAM as highly sensitive, keep it HTTPS unless it is a trusted loopback service, do not expose PROXY_HOST publicly, and regularly delete or protect proxy-requests.log and proxy-last-*.json/txt because they may contain conversation content.

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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/proxy.py:398
Finding

Persistent Plaintext Logging of Sensitive Conversation and Tool Data

Content
View full analysis

Vulnerability Details

File Location: scripts/proxy.py:54-72, 398-424
Vulnerability Type: Sensitive data stored in plaintext diagnostic files
Risk Level: Medium

Technical Analysis

The proxy enables diagnostic logging unconditionally. It appends the first 8,000 characters of every inbound request body to proxy-requests.log and overwrites proxy-last-upstream.json with the complete translated upstream payload.

python
LOG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "proxy-requests.log")
FULL_REQ_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "proxy-last-request.json")
FULL_UP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "proxy-last-upstream.json")
FULL_ERR_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "proxy-last-error.txt")


def _log(msg):
    """Append a line to proxy-requests.log (best-effort, never raises)."""
    try:
        with open(LOG_PATH, "a", encoding="utf-8") as f:
            f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
    except Exception:
        pass


def _dump(path, text):
    """Overwrite a debug dump file (best-effort, never raises)."""
    try:
        with open(path, "w", encoding="utf-8") as f:
            f.write(text)
    except Exception:
        pass
python
# --- debug logging (auth redacted) ---
_log(f">>> {self.command} {self.path}")
_log(
    "headers: "
    + json.dumps(
        {
            k: (v if k.lower() != "authorization" else "Bearer ***")
            for k, v in self.headers.items()
        }
    )
)
_log("body(first 8000): " + body.decode("utf-8", "replace")[:8000])

try:
    req = json.loads(body.decode("utf-8") or "{}")
except json.JSONDecodeError:
    _log("<<< 400 bad json")
    self.send_error(400, "bad json")
    return

model = req.get("model", "gpt-5.6-luna")
auth = self.headers.get("Author
...[truncated 1993 chars]
Remediation
View remediation

Remediation Suggestions

  1. Disable body and payload logging by default.
  2. Require an explicit environment variable such as PROXY_DEBUG=1 before creating diagnostic files.
  3. Log only non-sensitive metadata such as timestamp, route, status, duration, model, and body size.
  4. If debug payload capture is necessary, recursively redact message content, instructions, tool arguments, tool outputs, image data, and fields whose names indicate credentials or secrets.
  5. Create files with owner-only permissions, such as mode 0600 on supported platforms.
  6. Add rotation, maximum file size, and short retention periods.
  7. Provide a documented cleanup procedure and warn users that diagnostic data can contain complete conversations.
  8. Correct the documentation so it accurately states what is stored.
  9. Remove the unused FULL_REQ_PATH declaration or implement only a safe, explicitly enabled version of that diagnostic feature.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/proxy.py:425
Finding

Bearer Credentials and Prompts Can Be Forwarded to a Plaintext HTTP Upstream

Content
View full analysis

Vulnerability Details

File Location: scripts/proxy.py:42, 425-434
Vulnerability Type: Missing transport-security validation for a credential-bearing upstream request
Risk Level: Medium

Technical Analysis

The upstream endpoint is accepted directly from the OPENCODE_UPSTREAM environment variable without validating its scheme or destination. The proxy then forwards the client's bearer credential and full translated request to that endpoint.

python
UPSTREAM = os.environ.get("OPENCODE_UPSTREAM", "https://opencode.ai/zen/go/v1/responses")
python
upstream_req = urllib.request.Request(UPSTREAM, data=data, method="POST")
upstream_req.add_header("Content-Type", "application/json")
upstream_req.add_header("User-Agent", BROWSER_UA)
upstream_req.add_header("Accept", "application/json, text/event-stream")
if token:
    upstream_req.add_header("Authorization", "Bearer " + token)

try:
    with urllib.request.urlopen(upstream_req, timeout=180) as resp:

The default endpoint uses HTTPS, but a user, deployment wrapper, service configuration, or compromised environment can set OPENCODE_UPSTREAM to an http:// URL. In that configuration, the authorization token and complete request payload are transmitted without encryption or server authentication.

This finding depends on configuration influence; the default configuration is not vulnerable to plaintext transport. Loopback HTTP may be legitimate for a local upstream, so enforcement should distinguish loopback endpoints from remote hosts.

Attack Path

  1. An attacker or unsafe deployment configuration influences OPENCODE_UPSTREAM, setting it to a remote plaintext HTTP endpoint.
  2. A client sends a request to the local proxy with a valid upstream bearer token.
  3. The proxy extracts the token from the inbound Authorization header.
  4. The proxy sends the token, prompts, conversation history, tool data, and image inputs to the configur ...[truncated 691 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse OPENCODE_UPSTREAM with urllib.parse.urlparse before starting the server.
  2. Require the https scheme for non-loopback destinations.
  3. Permit plaintext HTTP only for explicit loopback addresses such as 127.0.0.1, ::1, or localhost, preferably behind a separate opt-in flag.
  4. Reject URLs containing unexpected user-information components or unsupported schemes.
  5. Fail closed before accepting client traffic when the upstream configuration is unsafe.
  6. Clearly display the validated upstream scheme and host at startup without printing credentials.
  7. Document that bearer credentials are forwarded to the configured endpoint and that operators must trust that endpoint.
  8. Consider an optional hostname allowlist for managed deployments.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/proxy.py:393
Finding

Unbounded Request Size and Thread Creation Permit Resource-Exhaustion Attacks

Content
View full analysis

Vulnerability Details

File Location: scripts/proxy.py:393-395, 490
Vulnerability Type: Denial of service through unbounded request-body allocation and concurrency
Risk Level: Medium

Technical Analysis

The handler converts the client-provided Content-Length header directly to an integer and attempts to read that many bytes into memory. No maximum body size is enforced.

python
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length) if length else b"{}"

The service also uses ThreadingHTTPServer, which creates concurrent request-handling threads without an application-level concurrency limit:

python
server = ThreadingHTTPServer((LISTEN_HOST, LISTEN_PORT), Handler)

An oversized request can therefore cause substantial memory allocation, while many concurrent or deliberately slow uploads can retain numerous threads and sockets. Invalid non-numeric Content-Length values can also raise an uncaught ValueError in the request handler.

The default loopback binding limits exposure to local processes. However, PROXY_HOST is configurable, and the documentation allows changing it. If bound to a network-facing interface, the issue becomes remotely exploitable by any party that can reach the port. Even under the default binding, an untrusted local process can exploit it.

Attack Path

  1. The proxy is running locally, or PROXY_HOST has been configured to bind to a reachable network interface.
  2. An attacker opens one or more connections to the completion endpoint.
  3. The attacker supplies extremely large Content-Length values, sends large payloads, or sends request bodies very slowly.
  4. Each request occupies a handler thread, and completed large bodies are held in memory before JSON parsing.
  5. Repeated concurrent requests exhaust available memory, threads, file descriptors, or connection capacity.
  6. Legitimate requests fail, stall, or the proxy proce ...[truncated 500 chars]
Remediation
View remediation

Remediation Suggestions

  1. Define a conservative maximum request size appropriate for prompts and multimodal input.
  2. Validate Content-Length inside a guarded conversion and return HTTP 400 for malformed values.
  3. Return HTTP 411 when a required length is absent and HTTP 413 when it exceeds the configured limit.
  4. Read the body incrementally with an enforced byte counter rather than trusting the header alone.
  5. Configure socket read and write timeouts to mitigate slow-client attacks.
  6. Bound concurrent request handling with a worker pool or semaphore.
  7. Set limits on active connections and upstream request duration.
  8. Preserve the loopback-only default and emit a prominent warning when binding to a non-loopback address.
  9. If network exposure is required, place the proxy behind an authenticated reverse proxy that enforces body-size, connection, and rate limits.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tainted flow: 'upstream_req' from os.environ.get (line 426, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/proxy.py (reported line 434)May include surrounding context.

python
upstream_req.add_header("Authorization", "Bearer " + token)

        try:
            with urllib.request.urlopen(upstream_req, timeout=180) as resp:
                status = resp.getcode()
                ctype = resp.headers.get("Content-Type", "")
                if status != 200:

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The README makes a security claim that API keys are not written to disk, but the debugging section states that full inbound requests, upstream payloads, and errors are saved to local files. Full requests commonly contain prompts, attachments, tool arguments, and may include credentials or other secrets, so this is a contradictory and dangerous data-handling practice that can mislead users into exposing sensitive data.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The proxy performs outbound network calls to an upstream service, which is expected for functionality, but undeclared network capability conceals a key trust boundary. Because user prompts and bearer credentials are sent over the network, omission of this permission materially affects risk awareness.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The proxy performs outbound network calls to an upstream service, which is expected for functionality, but undeclared network capability conceals a key trust boundary. Because user prompts and bearer credentials are sent over the network, omission of this permission materially affects risk awareness.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The proxy performs outbound network calls to an upstream service, which is expected for functionality, but undeclared network capability conceals a key trust boundary. Because user prompts and bearer credentials are sent over the network, omission of this permission materially affects risk awareness.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The proxy performs outbound network calls to an upstream service, which is expected for functionality, but undeclared network capability conceals a key trust boundary. Because user prompts and bearer credentials are sent over the network, omission of this permission materially affects risk awareness.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The README makes a security assurance that API keys are never written to disk, but the debugging section also states that full inbound requests and upstream payloads are dumped to local files. Since inbound requests commonly contain sensitive prompts, attachments, tool arguments, and may include credentials or secrets in bodies or headers, this is a materially misleading claim and can result in sensitive data being stored on disk unexpectedly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README instructs users that the proxy logs requests and writes full request and upstream payloads to local files, but does not clearly warn that these artifacts may contain sensitive user prompts, images, tool-call arguments, or other confidential data. This creates a realistic risk of local data exposure through shared machines, backups, malware, or accidental disclosure of the generated files.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
>
> - 启动代理:`python3 proxy.py`(默认监听 `http://127.0.0.1:8787`)。
> - 把客户端的自定义模型 URL 指向 `http://127.0.0.1:8787/v1/chat/completions`。
> - 冒烟测试:`curl http://127.0.0.1:8787/v1/chat/completions -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"hi"}],"stream":false}'`

---

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.zh-CN.md (reported line 24)May include surrounding context.

md
>
> - 启动代理:`python3 proxy.py`(默认监听 `http://127.0.0.1:8787`)。
> - 把客户端的自定义模型 URL 指向 `http://127.0.0.1:8787/v1/chat/completions`。
> - 冒烟测试:`curl http://127.0.0.1:8787/v1/chat/completions -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"hi"}],"stream":false}'`

---

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation instructs users that requests and upstream payloads are written to local files but does not clearly warn that these files may contain sensitive prompts, personal data, proprietary content, or secrets. In the context of an AI proxy handling chat traffic and tool calls, this omission materially increases the risk of accidental local data exposure.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Stating that full inbound requests and upstream payloads are written to disk creates an explicit data leakage risk because this proxy processes user conversations and model interaction data that may be confidential. Plaintext debug artifacts on disk are vulnerable to local compromise, backup leakage, accidental sharing, and multi-user host access.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
version: 1.1.0
description: "Local stdlib-only proxy that adapts OpenAI Chat Completions to/from the Responses API so any OpenAI-compatible agent client (WorkBuddy, Cursor, Open WebUI, LobeChat, ...) can use Responses-API-only models such as OpenCode Go gpt-5.6-luna. Use when: setting up a Chat Completions to Responses API bridge, local proxy for responses-only models, fixing 'model only supports responses API', 'invalid_prompt' HTTP 400, 'custom model error 10000', or protocol transcoding for any Responses API endpoint (OPENCODE_UPSTREAM). 使用场景:协议转接/本地代理/把只支持 Responses API 的模型接入 OpenAI 兼容客户端/模型报 invalid_prompt 或自定义模型错误 10000。"
agent_created: true
allowed-tools: python3, curl
metadata:
  openclaw:
    requires:

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation states that API keys are not persisted, but the troubleshooting section says request logs and request/response snapshots are written to local files. Even if auth is intended to be redacted, these files can still capture sensitive prompts, tool outputs, model responses, metadata, and potentially secrets if redaction is incomplete or bypassed. The contradiction creates a realistic privacy and secret-handling risk for users running the proxy.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill documentation mentions local log and snapshot files for troubleshooting but does not provide a prominent privacy warning that request contents and metadata may be stored on disk. Because this proxy handles chat history, images, tool arguments, and possibly sensitive enterprise data, users may unknowingly expose confidential information to other local users, backups, or endpoint monitoring tools.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · examples/basic.md (reported line 1)May include surrounding context.

md
# 示例:curl 输入/输出对

以下请求直接打向本地代理 `http://127.0.0.1:8787/v1/chat/completions`,密钥放在
`Authorization: Bearer <你的上游key>` 头。返回均为标准 OpenAI Chat Completions 结构。

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The entire skill document is written in Chinese and does not indicate that other languages are supported or that Chinese is required for a region-specific purpose. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/proxy.py (reported line 30)May include surrounding context.

python
OPENCODE_UPSTREAM=https://... python proxy.py  # custom upstream

Smoke test:
  curl http://127.0.0.1:8787/v1/chat/completions \
    -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
    -d '{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"hi"}],"stream":false}'
"""

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The proxy writes debug information to local files beyond simple protocol bridging, including request and upstream payload dumps. This expands data exposure by persisting potentially sensitive prompts, tool arguments, and model interaction details on disk without clear necessity for normal operation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code logs substantial portions of inbound request bodies, which can include prompts, tool inputs, user content, and possibly secrets pasted into prompts. Persisting this data to disk broadens the blast radius of any local compromise and violates least-data principles for a proxy whose advertised role is protocol translation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Logging inbound request bodies to disk without a user-facing warning can silently capture sensitive prompts, tool outputs, and personal or proprietary data. Users of a local compatibility proxy are unlikely to expect persistent storage of their chat content, making this a genuine privacy issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Dumping the full upstream request payload to a file captures the transformed conversation content, tool schemas, and other request details without an explicit warning. Even if Authorization is not stored in that dump, the payload itself may contain confidential information and should not be persisted silently.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The code relays the bearer token from the incoming Authorization header to the configured upstream endpoint, which is a network transmission of credentials. While the module mentions that the API key is relayed, it does not provide a clear safety warning that client credentials are being forwarded to whatever OPENCODE_UPSTREAM is configured.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill describes a local proxy that transparently forwards prompts, tool outputs, images, and other model inputs to a configurable upstream Responses API, but it does not explicitly warn users that their data leaves the local client boundary. In a proxy/bridge context this omission is security-relevant because users may incorrectly assume the processing is purely local and may send sensitive data, credentials, or internal documents to an external endpoint.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

md
- **Reasoning**: upstream reasoning summaries → `reasoning_content` passthrough
- **Multimodal**: `image_url` (URL / base64 data URL) → `input_image`
- **Configurable upstream**: `OPENCODE_UPSTREAM` points to any Responses API endpoint,
  not limited to OpenCode Go
- **Single-point key management**: the proxy relays the key from the inbound `Authorization`
  header; it never writes keys to disk or code

Static analysis

No suspicious patterns detected.