Back to skill

Security audit

Claude Code Custom Model Proxy

Security checks for vulnerabilities and agentic risk

Overview

This skill is a Claude Code model proxy, but it ships with a hardcoded third-party endpoint and credential, logs prompt bodies, and persistently redirects Claude Code traffic without enough user control or disclosure.

Review carefully before installing. Do not use this skill with confidential projects unless you replace the hardcoded upstream host, model, and API key with your own trusted configuration, remove prompt-body logging, restrict local access, and prefer temporary environment variables over persistent Claude Code settings. Revoke the embedded upstream key if you control it.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/claude_code_proxy.py:11
Finding

Hardcoded Upstream API Credential

Content
View full analysis

Vulnerability Details

File Location: scripts/claude_code_proxy.py:11-16
Vulnerability Type: Hardcoded bearer credential
Risk Level: High

Vulnerable Code

python
UPSTREAM_HOST = "api.53hk.cn"
UPSTREAM_PATH = "/v1/chat/completions"
API_KEY = "sk-4194e63b46b29886573888adcb69b5b10fcd41e8b9801f1ae66487c19d57a75a"
LISTEN_PORT = 4002
MAX_RETRIES = 3
BASE_WAIT_SECONDS = 10

The credential is subsequently transmitted to the upstream provider:

python
upstream_url = f"https://{UPSTREAM_HOST}{UPSTREAM_PATH}"
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {API_KEY}",
    "Accept": "text/event-stream"
}

Technical Analysis

A live-format API key is embedded directly in distributed source code. Anyone with read access to the Skill package can recover the credential without executing the proxy. The proxy uses this value as a bearer token when submitting requests to https://api.53hk.cn/v1/chat/completions.

Hardcoded credentials cannot be safely rotated per installation, are likely to propagate into archives and source-control history, and make it impossible to distinguish authorized users of the credential. This exceeds least privilege because every recipient of the Skill receives access to the same upstream identity.

Attack Path

  1. An attacker downloads or otherwise obtains the Skill package.
  2. The attacker reads scripts/claude_code_proxy.py.
  3. The attacker extracts the value assigned to API_KEY.
  4. The attacker sends requests directly to the configured upstream endpoint with the extracted bearer token.
  5. Requests are charged to, rate-limited against, or attributed to the credential owner until the credential is revoked.

Impact Assessment

The exposed credential may permit unauthorized model requests under the upstream account's privileges. Potential impact includes quota theft, financial charges, service disruption thr ...[truncated 145 chars]

Remediation
View remediation

Remediation Suggestions

  1. Revoke the exposed credential immediately and review upstream usage records for unauthorized activity.
  2. Remove all credentials from the distributed source and repository history.
  3. Read the key from a protected environment variable or operating-system secret store:
    python
    import os
    
    API_KEY = os.environ.get("UPSTREAM_API_KEY")
    if not API_KEY:
        raise RuntimeError("UPSTREAM_API_KEY is required")
    
  4. Give each deployment a separate, narrowly scoped credential with spending and rate limits.
  5. Add secret scanning to CI and pre-commit checks.
  6. Ensure exceptions and diagnostic output never print authorization headers.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/claude_code_proxy.py:173
Finding

Sensitive Claude Code Requests Written to Plaintext Logs

Content
View full analysis

Vulnerability Details

File Location: scripts/claude_code_proxy.py:173-178; related usage at SKILL.md:51-53
Vulnerability Type: Plaintext logging of potentially sensitive request data
Risk Level: Medium

Vulnerable Code

python
def do_POST(self):
    parsed_path = urlparse(self.path)
    path = parsed_path.path
    log(f"POST {self.path} (path: {path})")
    content_length = int(self.headers.get("Content-Length", 0))
    body = json.loads(self.rfile.read(content_length))
    log(f"Body: {json.dumps(body)[:300]}")

The documented background command redirects this output to a predictable temporary file:

bash
nohup python3 ~/.workbuddy/skills/claude-code-custom-model-proxy/scripts/claude_code_proxy.py > /tmp/claude_proxy.log 2>&1 &

Technical Analysis

Every POST request has its first 300 serialized characters logged. Claude Code requests can contain user prompts, source code, file contents, proprietary instructions, access tokens, passwords, or other secrets. Truncation does not provide meaningful protection because credentials and sensitive instructions frequently appear near the beginning of a request.

The documented background execution command stores this output in /tmp/claude_proxy.log without creating the file with explicitly restrictive permissions. The predictable name also creates exposure to local users, automated log collectors, backups, and potential temporary-file link attacks, depending on operating-system configuration and existing filesystem permissions.

Request-body logging is not necessary for the proxy's declared conversion and forwarding functionality.

Attack Path

  1. A user starts the proxy using the documented background command.
  2. Claude Code submits a message containing source code, prompts, credentials, or other sensitive material.
  3. The proxy serializes and logs the first 300 characters of the request.
  4. Shell redirection writes th ...[truncated 489 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove request-body logging entirely from normal operation.
  2. Log only non-sensitive metadata such as a random request ID, path, timing, status, and payload size.
  3. If diagnostic payload logging is indispensable, require an explicit opt-in debug flag and recursively redact credentials and sensitive fields.
  4. Never document a shared, predictable log path in /tmp.
  5. Use a user-private logging directory and create files atomically with mode 0600.
  6. Configure retention limits and warn users clearly before any payload logging is enabled.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/claude_code_proxy.py:287
Finding

Unauthenticated Local Proxy Permits Requests from Arbitrary Browser Origins

Content
View full analysis

Vulnerability Details

File Location: scripts/claude_code_proxy.py:287-292; related headers at lines 161 and 194 and binding at line 313
Vulnerability Type: Missing client authentication and overly permissive CORS policy
Risk Level: High

Vulnerable Code

python
def do_OPTIONS(self):
    self.send_response(204)
    self.send_header("Access-Control-Allow-Origin", "*")
    self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
    self.send_header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-api-key")
    self.end_headers()

Successful GET and POST responses also permit every origin:

python
self.send_header("Access-Control-Allow-Origin", "*")

The service is restricted to loopback, but no application-level client authentication is enforced:

python
server = ThreadedHTTPServer(("127.0.0.1", LISTEN_PORT), ProxyHandler)
server.serve_forever()

Technical Analysis

Loopback binding prevents ordinary remote TCP connections, but it does not establish which local application is authorized to use the service. The proxy accepts POST requests without validating an authorization token and explicitly returns Access-Control-Allow-Origin: *.

Consequently, an attacker-controlled website opened by a user while the proxy is running may attempt to communicate with the loopback endpoint. Where browser Private Network Access policy or related controls do not block the request, the website can use the proxy as an authenticated gateway to the upstream provider. The proxy supplies its embedded upstream bearer credential automatically.

CORS is unnecessary for the documented Claude Code client workflow and expands access beyond the minimum privilege needed. Browser security behavior differs by browser and deployment context, so browser-side protections should not replace service authentication.

Attack Path

  1. The victim starts the proxy on `127.0. ...[truncated 873 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove CORS headers unless browser access is an explicit functional requirement.
  2. If browser access is required, use a strict allowlist of trusted origins rather than *.
  3. Generate a high-entropy local bearer token for each installation or process start and require it on every endpoint.
  4. Validate the Origin and Host headers and reject unexpected values.
  5. Apply request-size, concurrency, and rate limits.
  6. Continue binding to loopback, but do not treat loopback binding as authentication.
  7. Consider a Unix-domain socket with restrictive filesystem permissions when supported by the client.

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/claude_code_proxy.py:28
Finding

Advertised Claude Model Identities Are Silently Replaced with a Fixed Third-Party Model

Content
View full analysis

Vulnerability Details

File Location: scripts/claude_code_proxy.py:28-48; related model advertisement at lines 142-159 and forwarding at lines 205-213
Vulnerability Type: API and model identity spoofing
Risk Level: High

Vulnerable Code

The conversion function ignores the model selected by the client:

python
def anthropic_to_openai(body):
    """Convert Anthropic API format to OpenAI Chat Completions format"""
    messages = body.get("messages", [])

    openai_messages = []
    for msg in messages:
        role = msg.get("role")
        content = msg.get("content", "")

        if isinstance(content, list):
            text_content = ""
            for block in content:
                if block.get("type") == "text":
                    text_content += block.get("text", "")
            content = text_content

        openai_messages.append({"role": role, "content": content})

    openai_body = {
        "model": "MiniMax-M2.7-highspeed",
        "messages": openai_messages,
        "stream": True,
        "max_tokens": body.get("max_tokens", 4096),
        "temperature": body.get("temperature", 0.7)
    }
    return openai_body

The local models endpoint advertises Claude model identities:

python
models = {
    "data": [
        {
            "type": "model",
            "id": "claude-sonnet-4-6",
            "display_name": "Claude Sonnet 4.6"
        },
        {
            "type": "model",
            "id": "claude-opus-4-5",
            "display_name": "Claude Opus 4.5"
        },
        {
            "type": "model",
            "id": "claude-haiku-4-5",
            "display_name": "Claude Haiku 4.5"
        },
        {
            "type": "model",
            "id": "MiniMax-M2.7-highspeed",
            "display_name": "MiniMax M2.7 Highspeed"
        }
    ]
}

All converted message text is sent to a fix ...[truncated 2360 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not advertise model identities that the upstream service does not actually provide.
  2. Preserve the client's requested model only when an explicit, documented mapping exists; otherwise reject the request.
  3. Make the upstream URL and model user-supplied configuration rather than fixed source constants.
  4. Display the actual upstream provider, endpoint hostname, and model prominently at startup.
  5. Require affirmative user confirmation before sending prompts or repository data to a newly configured provider.
  6. Document the provider's data-handling implications and advise users not to submit confidential data without authorization.
  7. Return the actual model identity in converted response metadata.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented behavior materially differs from the declared purpose: it acts as an active proxy, fabricates model identities, forces requests to a hardcoded model, and references a hardcoded third-party upstream host and API key. This mismatch prevents informed consent and can misroute sensitive prompts or credentials to an unexpected service under misleading Anthropic-branded model names.

Content

No source excerpt is available for this finding.

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

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

1. Configure Claude Code

Create or edit ~/.claude/settings.json:

json
{

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

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

bash
# Test models endpoint
curl -s http://127.0.0.1:4002/v1/models | python3 -m json.tool

# Test messages endpoint
curl -X POST http://127.0.0.1:4002/v1/messages \

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

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

bash
ls -lt ~/.claude/debug/*.txt | head -1
tail -50 ~/.claude/debug/<latest>.txt

File Structure

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill describes and relies on network-capable behavior but does not declare any explicit tool scope or permissions boundary. That omission weakens user awareness and reviewability, making it easier for a skill to perform request forwarding or other network actions without clear consent expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explains forwarding Claude Code traffic through a local proxy to an upstream provider but does not prominently warn that prompts, outputs, and potentially sensitive project data will be transmitted to a third party. It also omits credential-handling cautions, increasing the risk of accidental disclosure of API keys or confidential content.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
83% confidence
Finding

The skill instructs users to persistently modify ~/.claude/settings.json to redirect future Claude Code traffic through the proxy. Persistent redirection can continue affecting later sessions and projects, causing unintended data routing long after the immediate task is complete.

Content

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

1. Configure Claude Code

Create or edit ~/.claude/settings.json:

json
{

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Running the proxy with nohup in the background creates a persistent local service that may continue intercepting and forwarding requests beyond the user's awareness. This raises the chance of accidental ongoing data transmission, stale insecure configuration, or unnoticed misuse if the process remains active.

Content

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

Or run in background:

bash
nohup python3 ~/.workbuddy/skills/claude-code-custom-model-proxy/scripts/claude_code_proxy.py > /tmp/claude_proxy.log 2>&1 &

3. Start Claude Code

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 172)May include surrounding context.

curl -s http://127.0.0.1:4002/v1/models | python3 -m json.tool

Test messages endpoint

curl -X POST http://127.0.0.1:4002/v1/messages
-H "Content-Type: application/json"
-d '{"model":"claude-sonnet-4-6","messages":[{"role":"user","content":"Hello"}],"max_tokens":100}'

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

A live API credential is hardcoded in source, making it recoverable by anyone with file or repository access and likely reusable against the upstream service. Embedded secrets are especially dangerous in distributed skills because they are commonly copied, committed, logged, or published unintentionally.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/claude_code_proxy.py (reported line 125)May include surrounding context.

python
"""调用上游 API,支持重试(处理 429 错误)"""
    for attempt in range(max_retries + 1):
        try:
            r = requests.post(url, json=data, headers=headers, stream=True, timeout=120)
            
            if r.status_code == 429:
                if attempt < max_retries:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The proxy forwards the full user conversation to a third-party upstream service, which changes the trust boundary and can expose sensitive prompts, files, or embedded secrets to an external provider. In a model-proxy skill this behavior is expected functionally, but the lack of user-facing disclosure or consent is still risky because users may assume local-only handling.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The proxy logs request bodies that contain user prompts and message content, which may include secrets, proprietary code, or personal data. Logging this sensitive payload is not necessary for normal proxy operation and creates an avoidable disclosure channel via console logs, log files, or monitoring systems.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The module's natural-language description is entirely in Chinese and presents the tool behavior only in that language. Under the stated policy, forcing a specific language without opt-in can be a locale/language policy issue when no alternative or justification is provided.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The class docstring says the server 'supports multithreading,' but the code inherits from http.server.HTTPServer rather than a threading-capable server class or mixin. Setting daemon_threads alone does not make HTTPServer multithreaded, so the inline documentation contradicts actual behavior.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/claude_code_proxy.py:15