Back to skill

Security audit

oauth-coder-bridge

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says, but it exposes an OAuth-backed Claude bridge with weak access controls and persistent/local configuration changes that users should review before installing.

Install only if you intend to run a local Claude OAuth bridge. Keep it bound to 127.0.0.1, do not expose port 8787 through a proxy or container port mapping, review changes to ~/.openclaw/openclaw.json, and avoid enabling auto-start unless you understand how to stop and remove the service.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/oauth-coder-bridge.py:21
Finding

Unauthenticated OAuth-backed bridge can be exposed on a non-loopback interface

Content
View full analysis
MAX_REQUEST_SIZE: self._send_error_response( 413, "request_too_large", f"Request body exceeds {MAX_REQUEST_SIZE} bytes" ) return body = self.rfile.read(content_length).decode("u ...[truncated 2772 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/oauth-coder-bridge.py:143
Finding

Untrusted X-Forwarded-For values allow rate-limit bypass

Content
View full analysis
str: """Extract client IP, handling proxies.""" # Check X-Forwarded-For header (common with reverse proxies) xff = self.headers.get("X-Forwarded-For", "").split(",")[0].strip() if xff: return xff return self.client_address[0] def do_POST(self): """Handle POST requests to /v1/messages""" with self._stats_lock: self._stats["requests_total"] += 1 # Check rate limit client_ip = self._get_client_ip() if not check_rate_limit(client_ip): self._send_error_response(429, "rate_limit_error", "Too many requests") with self._stats_lock: self._stats["requests_rate_limited"] += 1 return ``` ### Technical Analysis The bridge unconditionally trusts the first value in the `X-Forwarded-For` header as the client identity used by the rate limiter. HTTP clients can set this header directly unless a trusted reverse proxy removes and reconstructs it. The rate-limit state is indexed by the returned string. An attacker can therefore submit a different fabricated `X-Forwarded-For` value with each request, causing every request or group of requests to use a fresh rate-limit bucket. No validation establishes that the direct peer is a trusted proxy. This weakness is exploitable even when the attacker connects through an unconfigured proxy that preserves user-supplied forwarding headers. On a loopback-only deployment, malicious local processes can also bypass the limit. ### Attack Path 1. The attacker obtains network access to the bridge, or runs as a local process able to connect to `127.0.0.1:8787`. 2. The attacker sends up to 30 requests with `X-Forwarded-For: 198.51.100.1`. 3. After reaching that bucket's limit, the attac ...[truncated 779 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/oauth-coder-bridge.py:342
Finding

Model-generated calls to undeclared tools are forwarded as valid tool requests

Content
View full analysis
UNDECLARED_TOOL{...}`. 3. The model returns the requested XML. 4. `_parse_respo ...[truncated 865 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (10)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/oauth-coder-bridge.py (reported line 86)May include surrounding context.

python
# Remove null bytes and control characters
    prompt = re.sub(r'[\x00-\x08\x0b-\x0c\x0e-\x1f]', '', prompt)
    
    return prompt


def validate_json_schema(obj: Any, max_depth: int = 10, current_depth: int = 0) -> bool:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documents installation and operation of scripts that can read environment variables, modify local files/configuration, invoke shell commands, and expose a local network service, but it declares no explicit tool scope or permissions. This is dangerous because consumers and agent frameworks cannot accurately constrain or review the capability boundary, increasing the risk of over-privileged execution and unsafe deployment of the bridge and installer scripts.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

The skill suggests enabling the bridge as a persistent user service with systemd, causing it to start automatically and continue running beyond the initial session. Persistence is security-relevant because it increases attack surface over time, may keep OAuth-backed model access continuously available on localhost, and can survive user forgetfulness or changes in trust in the skill.

Content

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

bash
bash scripts/setup.sh              # copies bridge, adds claude-cli provider to openclaw.json
python3 ~/.openclaw/scripts/oauth-coder-bridge.py &
# Or: systemctl --user enable --now oauth-coder-bridge

Verify

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This service forwards user-supplied HTTP request content to an external local CLI process, which may transmit prompts, tool definitions, and embedded data to another component or remote model backend. Without explicit disclosure, callers may unknowingly send sensitive data outside the immediate HTTP service boundary, creating confidentiality and compliance risks.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/oauth-coder-bridge.py (reported line 266)May include surrounding context.

python
# Execute with timeout
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,

Tainted flow: 'cmd' from os.environ.get (line 252, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The executable path comes from OAUTH_CODER_BIN, an environment variable, and is then invoked as a subprocess without integrity checks beyond file existence. If an attacker can influence the service environment, startup wrapper, or deployment config, they can redirect execution to an arbitrary local binary/script and gain code execution in the bridge process context.

Content

Scanner excerpt · scripts/oauth-coder-bridge.py (reported line 266)May include surrounding context.

python
# Execute with timeout
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,

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 · scripts/setup.sh (reported line 11)May include surrounding context.

sh
SKILL_DIR="$(dirname "$SCRIPT_DIR")"

# Copy bridge script to ~/.openclaw/scripts
mkdir -p ~/.openclaw/scripts
cp "$SCRIPT_DIR/oauth-coder-bridge.py" ~/.openclaw/scripts/
chmod +x ~/.openclaw/scripts/oauth-coder-bridge.py

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 · scripts/setup.sh (reported line 25)May include surrounding context.

sh
echo "  python3 ~/.openclaw/scripts/oauth-coder-bridge.py &"
echo ""
echo "Or enable systemd auto-start:"
echo "  systemctl --user enable --now oauth-coder-bridge"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script writes directly to the user's ~/.openclaw/openclaw.json in place without prompting, creating a backup, or performing any validation before replacement. In a skill that modifies a user-specific configuration file, this can unintentionally destroy existing settings or leave the application misconfigured if the write is interrupted or the generated structure is incompatible.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/oauth-coder-bridge.py (reported line 35)May include surrounding context.

python
# Setup logging
log_format = "%(asctime)s | %(levelname)s | %(message)s"
logging.basicConfig(
    level=getattr(logging, LOG_LEVEL.upper()),
    format=log_format,
    handlers=[
        logging.StreamHandler(sys.stderr),