安全决策报告

LLM Proxy

The skill acts as an unauthenticated local proxy forwarding user API credentials to external LLM providers, with a content-security layer that declares blocking but only logs critical alerts (including credential theft and reverse shell patterns), creating a deceptive security illusion.

安装决策优先 来源: 手动上传 扫描时间: 2026/4/4
文件 7
IOC 25
越权项 4
发现 8
最直接的威胁证据
严重 凭证窃取
Critical content-blocking disabled — credential exfiltration not prevented

content-filter-rules.json defines CRED-001 (sk-, AKIA-, ghp_, api_key patterns) as severity=critical, but response_actions.critical.block=false. The proxy will log credential-theft attempts but forward the actual request to the upstream LLM provider, meaning stolen API keys go through the proxy.

scripts/content-filter-rules.json:217

为什么得出这个结论

2/4 个维度触发
阻止
声明与实际能力

发现 4 项声明之外的能力或越权行为。

复核
隐藏执行与外联

提取到 25 个一般风险产物,需要结合上下文判断。

阻止
攻击链与高危发现

报告包含 5 步攻击链,另有 5 项高危或严重发现。

通过
依赖与供应链卫生

依赖结构存在,但暂未看到明显高危告警。

攻击链

01
User invokes skill to '启动llm-proxy'

初始入口 · SKILL.md:17

02
Skill runs llm-proxy-ctl.sh which starts daemon via background process (&), kill/kill -9 port cleanup — shell:WRITE capability used without declaration

权限提升 · scripts/llm-proxy-ctl.sh:34

03
Proxy binds to TCP port 18888 on 127.0.0.1 — network:WRITE not declared in SKILL.md

权限提升 · scripts/llm-proxy.py:482

04
Attacker sends POST with stolen API key through the proxy (no auth required since block=false)

权限提升 · scripts/llm-proxy.py:259

05
CRED-001 fires but block=false; credential exfiltration is only logged, not prevented. Stolen key is forwarded to upstream provider.

最终危害 · scripts/content-filter-rules.json:217

风险分是怎么被拉高的

Undeclared shell execution +15

SKILL.md does not mention subprocess, shell, or process management; llm-proxy-ctl.sh uses kill/kill_by_port

Undeclared network binding +12

SKILL.md does not declare opening a TCP port or running a persistent background service

Auth bypass - no credential verification +18

Proxy forwards user Authorization/API-Key headers to external providers with no auth check; any local process can use the proxy

Critical content rules have block=false +20

response_actions block=false for critical severity — credential exfiltration and reverse shell patterns only logged, not blocked

Doc-to-code behavior mismatch +10

SKILL.md states '严重违规时阻断响应' but code sets block=false for all severity levels including critical

最关键的证据

严重 凭证窃取

Critical content-blocking disabled — credential exfiltration not prevented

content-filter-rules.json defines CRED-001 (sk-, AKIA-, ghp_, api_key patterns) as severity=critical, but response_actions.critical.block=false. The proxy will log credential-theft attempts but forward the actual request to the upstream LLM provider, meaning stolen API keys go through the proxy.

scripts/content-filter-rules.json:217
Set block: true for critical severity in response_actions, or add a hard check in llm-proxy.py before _forward_request() is called.
严重 凭证窃取

API key forwarding without authorization

The proxy blindly forwards the Authorization and X-Api-Key headers from incoming requests directly to upstream LLM providers. Any local process can send requests with arbitrary credentials through this proxy. Combined with block=false on credential detection, a stolen API key can be routed through this proxy.

scripts/llm-proxy.py:259
Add local authentication (e.g., a shared secret header) to validate requests before forwarding credentials.
高危 文档欺骗

Documentation claims blocking, code does not block

SKILL.md states: '严重违规时阻断响应并返回错误' (blocks on serious violations) and README states '内容安全检测仅记录和提醒,不自动拦截(可配置)' — both contradictory. The code sets block=false for all severity levels. This is a doc-to-code mismatch.

SKILL.md:99
Align documentation with actual behavior or change block=false to block=true in response_actions.
高危 敏感访问

Undeclared persistent background service

SKILL.md does not mention that the skill runs a persistent background daemon, opens a TCP port, or manages processes. The llm-proxy-ctl.sh starts a daemon via backgrounding (&) and writes to /tmp/llm-proxy.pid. This is not declared.

scripts/llm-proxy-ctl.sh:34
Declare in SKILL.md that the skill starts a background daemon and manages processes.
高危 文档欺骗

Undeclared shell and process management capabilities

SKILL.md declares no shell execution, but llm-proxy-ctl.sh uses kill, kill -9, lsof, curl, mkdir, and background process management. SKILL.md also declares no filesystem WRITE, but scripts write to ~/.openclaw/logs/ and /tmp/.

scripts/llm-proxy-ctl.sh:1
Declare shell:WRITE in the capability manifest and document process/service management in SKILL.md.
中危 代码执行

SIGUSR1 debug handler exposes full thread stacks

llm-proxy.py registers signal.SIGUSR1 which, when triggered, prints full thread stacks including any sensitive data in stack frames. This could leak internal state, credentials in variables, or request content.

scripts/llm-proxy.py:471
Remove the SIGUSR1 debug handler in production code, or restrict it to trusted users only.
中危 数据外泄

Verbose request/response logging to user-writable directory

All requests and responses (including message content, provider, request_id, status) are written to ~/.openclaw/logs/llm-proxy/proxy-YYYY-MM-DD.jsonl. While Authorization headers are redacted, the response content and full request metadata are logged. This creates a local data trail.

scripts/llm-proxy.py:361
Log only anonymized metadata, not full request/response content. Add .gitignore and warn users about the log directory.
低危 供应链

No dependencies declared — no requirements.txt or package.json

The skill uses only Python standard library (json, re, time, os, sys, signal, traceback, uuid, threading, datetime, http.server, socketserver, urllib). No third-party dependencies, which reduces supply chain risk. However, subprocess is not declared as a dependency since it's used in shell scripts.

scripts/llm-proxy.py:1
Document that only Python standard library is required and declare shell access in SKILL.md.

声明能力 vs 实际能力

网络访问 阻止
声明 NONE
推断 WRITE
SKILL.md:1 — SKILL.md declares no network access, but the proxy opens TCP port 18888 and makes outbound HTTP requests to 22+ external LLM provider APIs
命令执行 阻止
声明 NONE
推断 WRITE
llm-proxy-ctl.sh:34,47 — Uses lsof, kill, kill -9, backgrounding python3; SKILL.md makes no mention of subprocess or shell usage
文件系统 阻止
声明 NONE
推断 WRITE
llm-proxy-ctl.sh:35 — mkdir -p for log dirs; llm-proxy.py:89 — writes to ~/.openclaw/logs/
环境变量 阻止
声明 NONE
推断 READ
llm-proxy.py:34-37 — reads LLMPROXY_CONFIG, LLM_PROXY_PORT, RULES_FILE from os.environ

可疑产物与外联

中危 外部 URL
http://127.0.0.1:18888/health

README.md:116

中危 外部 URL
https://api.your-provider.com/v1

README.md:147

中危 外部 URL
http://127.0.0.1:18888/your-provider/chat/completions

README.md:156

中危 外部 URL
https://api.your-provider.com/v1/chat/completions

README.md:157

中危 外部 URL
http://127.0.0.1:18888/openai/chat/completions

README.md:259

中危 外部 URL
http://127.0.0.1:18888/bailian/chat/completions

README.md:272

中危 外部 URL
https://api.groq.com/openai/v1

scripts/llm-proxy-config.json:49

中危 外部 URL
https://api.cloudflare.com/client/v4/accounts

scripts/llm-proxy-config.json:55

中危 外部 URL
https://api.deepseek.com/v1

scripts/llm-proxy-config.json:61

中危 外部 URL
https://api.moonshot.cn/v1

scripts/llm-proxy-config.json:67

中危 外部 URL
https://open.bigmodel.cn/api/paas/v4

scripts/llm-proxy-config.json:73

中危 外部 URL
https://api.siliconflow.cn/v1

scripts/llm-proxy-config.json:79

依赖与供应链

包名版本来源漏洞备注
Python standard library only N/A stdlib Uses only json, re, time, os, sys, signal, threading, http.server, socketserver, urllib — no pip packages needed

文件构成

7 个文件 · 1748 行
Python 1 个文件 · 608 行Markdown 2 个文件 · 539 行JSON 2 个文件 · 412 行Shell 2 个文件 · 189 行
需关注文件 · 6
scripts/llm-proxy.py Python · 608 行
API key forwarding without authorization · SIGUSR1 debug handler exposes full thread stacks · Verbose request/response logging to user-writable directory · No dependencies declared — no requirements.txt or package.json · [email protected]
scripts/content-filter-rules.json JSON · 248 行
Critical content-blocking disabled — credential exfiltration not prevented · [email protected]
README.md Markdown · 347 行
http://127.0.0.1:18888/health · https://api.your-provider.com/v1 · http://127.0.0.1:18888/your-provider/chat/completions · https://api.your-provider.com/v1/chat/completions · http://127.0.0.1:18888/openai/chat/completions · http://127.0.0.1:18888/bailian/chat/completions
scripts/llm-proxy-config.json JSON · 164 行
https://api.groq.com/openai/v1 · https://api.cloudflare.com/client/v4/accounts · https://api.deepseek.com/v1 · https://api.moonshot.cn/v1 · https://open.bigmodel.cn/api/paas/v4 · https://api.siliconflow.cn/v1 · https://openrouter.ai/api/v1 · https://integrate.api.nvidia.com/v1 · https://coding.dashscope.aliyuncs.com/v1 · https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxin_workshop · https://spark-api.xf-yun.com/v3.5/chat · https://api.minimax.chat/v1 · https://api.lingyiwanwu.com/v1 · https://api.baichuan-ai.com/v1 · https://api.together.xyz/v1 · https://api.fireworks.ai/inference/v1 · https://api.replicate.com/v1
scripts/llm-proxy-ctl.sh Shell · 134 行
Undeclared persistent background service · Undeclared shell and process management capabilities
SKILL.md Markdown · 192 行
Documentation claims blocking, code does not block
其他文件 · llm-proxy-common.sh

安全亮点

Content filter rules are comprehensive and well-structured with L1 (malicious command), L2 (sensitive content), and L3 (LLM review) layers
Credential patterns (sk-, AKIA-, ghp_) are detected via regex in the filter rules
Authorization headers are redacted in log entries (***REDACTED***)
API keys in response previews are masked with regex substitution
Proxy binds only to 127.0.0.1 (not exposed to the internet)
Request body size is limited (10MB) to prevent DoS
Uses only Python standard library — no third-party dependencies to compromise
Response data field removed from logs (blocked responses only log alert metadata, not content)
Thread-safe logging with locks prevents log injection
Config keys prefixed with '_' are ignored during loading