Back to skill

Security audit

TeamClaw

Security checks for vulnerabilities and agentic risk

Overview

TeamClaw is a disclosed multi-agent service, but it grants broad local execution, credential, session, and network authority with several under-scoped security controls.

Install only if you intend to run a broad local agent platform and can isolate it. Keep it on loopback unless you add strong authentication, avoid the public tunnel on untrusted networks, do not expose OASIS directly, rotate any configured OpenClaw/API/internal tokens after testing, and treat the command/code execution tools as full local code execution rather than a sandbox.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
Findings (7)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
oasis/server.py:122
Finding

Administrator Token Exfiltration Through an Attacker-Controlled Callback

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
oasis/server.py:659
Finding

Unauthenticated OASIS Administration and OpenClaw API-Key Disclosure

Content
View full analysis
session) and list formats sessions = [] if isinstance(data, dict): for k, v in data.items(): if isinstance(v, dict): v.setdefault("key", k) sessions.append(v) elif isinstance(data, list): sessions = data # Keyword filter if filter: sessions = [s for s in sessions if filter.lower() in s.get("key", "").lower()] # Sort by updatedAt descending sessions.sort(key=lambda s: s.get("updatedAt", 0), reverse=True) result = [ { "key": s.get("key"), "sessionId" ...[truncated 2277 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/mcp_commander.py:194
Finding

Command Sandbox Permits Unrestricted Native Code Execution

Content
View full analysis
str: workspace = _user_workspace(username) tmp_script = os.path.join(workspace, ".tmp_exec.py") try: with open(tmp_script, "w", encoding="utf-8") as f: f.write(code) proc = await asyncio.create_subprocess_exec( _python_cmd(), tmp_script, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=workspace, env=_sandbox_env(workspace, username), ) ``` ```python enabled_names = state.get("enabled_tools") if enabled_names is not None: enabled_set = set(enabled_names) else: enabled_set = None # None = all allowed ``` ```python if enabled_names is not None: filtered_tools = [t for t in all_tools if t.name in enabled_names] else: filter ...[truncated 2023 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/setup_env.sh:13
Finding

Mutable Remote Scripts and Binaries Are Downloaded and Executed Without Integrity Verification

Content
View full analysis
/dev/null; then echo "✅ uv is installed: $(uv --version)" else echo "Installing uv..." curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" ``` ```python def download_bark_server(): """Download bark-server binary to bin/ directory.""" os_name, arch = detect_bark_platform() if not os_name: return False url = f"https://github.com/Finb/bark-server/releases/latest/download/bark-server_{os_name}_{arch}" bin_dir = os.path.join(PROJECT_ROOT, "bin") os.makedirs(bin_dir, exist_ok=True) try: urllib.request.urlretrieve(url, BARK_SERVER_PATH) os.chmod( BARK_SERVER_PATH, os.stat(BARK_SERVER_PATH).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH, ) return True ``` ```python bark_proc = subprocess.Popen( [BARK_SERVER_PATH, "-addr", f"127.0.0.1:{PORT_BARK}", "-data", bark_data_dir], cwd=PROJECT_ROOT, stdout=None, stderr=None, ) ``` ```bash setup) echo "=== Environment setup ===" bash scripts/setup_env.sh echo "=== Environment setup complete ===" ;; ``` ### Technical Analysis The documented setup path pipes a remote HTTP response directly into a shell. The payload can change after the Skill has been reviewed. Normal startup separately downloads a Bark executable from a mutable `latest` release URL, marks it executable, and launches it. The code does not pin an immutable version or verify a checksum, signature, expected size, or trusted release manifest. HTTPS protects transport under normal conditions but does not ensure that the publisher account, mutable release, bui ...[truncated 1031 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
oasis/server.py:522
Finding

Unauthenticated Path Traversal in Workflow and Discussion Persistence

Content
View full analysis
str: user_dir = os.path.join(DISCUSSIONS_DIR, self.user_id) os.makedirs(user_dir, exist_ok=True) return os.path.join(user_dir, f"{self.topic_id}.json") def save(self): """Persist current state to disk.""" path = self._storage_path() with open(path, "w", encoding="utf-8") as f: json.dump(self.to_dict(), f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The OASIS server joins caller-controlled `user_id` and workflow `name` values directly into filesystem paths. It does not reject absolute paths or traversal components, reso ...[truncated 1438 chars]
Remediation
View remediation

T02 · Agent Memory Poisoning

Error
Location
src/agent.py:449
Finding

Persistent User Profile Is Injected Into the System Prompt Without Trust Separation

Content
View full analysis
str: """Read user profile from data/user_files/{user_id}/user_profile.txt.""" user_files_dir = self._prompts.get("_user_files_dir", "") fpath = os.path.join(user_files_dir, user_id, "user_profile.txt") try: with open(fpath, "r", encoding="utf-8") as f: return f.read().strip() except FileNotFoundError: return "" ``` ```python base_prompt = ( self._prompts["base_system"] + "\n\n" f"【Default available tools】\n{all_tool_list_str}\n" "All tools above are enabled by default.\n" ) if not is_subagent: user_profile = self._get_user_profile(user_id) if user_profile: base_prompt += f"\n{user_profile}\n" base_prompt += self._get_user_skills(user_id) + "\n" ``` ```python input_messages = [SystemMessage(content=base_prompt)] + history_messages ``` The base prompt also instructs the agent to proactively update `user_profile.txt` when it infers valuable user characteristics. ### Technical Analysis Profile data can be influenced by user conversation and written by the agent. On later requests, the profile file is concatenated directly into `base_prompt`, which is then sent as a privileged `SystemMessage`. There is no structured schema, provenance annotation, quoting, escaping, instruction filtering, or separation between trusted policy and untrusted remembered data. Consequently, instruction-like text stored in the profile receives system-message placement in future sessions. This changes a transient prompt-injection attempt into a persistent influence channel. The risk is amplified because all tools, including code execution, are enabled when no explicit tool list is supplied. ### Attack Path 1 ...[truncated 1131 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
config/requirements.txt:1
Finding

All Python Dependencies Are Installed Without Version or Hash Pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (449)

Tainted flow: 'LOCAL_OPENAI_COMPLETIONS_URL' from os.getenv (line 28, credential/environment) → requests.post (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 · src/front.py (reported line 5959)May include surrounding context.

python
# 直接透传请求体和 Authorization header 到后端
    auth_header = request.headers.get("Authorization", "")
    try:
        r = requests.post(
            LOCAL_OPENAI_COMPLETIONS_URL,
            json=request.get_json(silent=True),
            headers={

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 5998)May include surrounding context.

python
def proxy_openai_models():
    """透传 /v1/models"""
    try:
        r = requests.get(f"http://127.0.0.1:{PORT_AGENT}/v1/models", timeout=10)
        return Response(r.content, status=r.status_code, content_type=r.headers.get("content-type", "application/json"))
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'LOCAL_LOGIN_URL' from os.getenv (line 20, credential/environment) → requests.post (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 · src/front.py (reported line 6011)May include surrounding context.

python
password = request.json.get("password", "")

    try:
        r = requests.post(LOCAL_LOGIN_URL, json={"user_id": user_id, "password": password}, timeout=10)
        if r.status_code == 200:
            # 登录成功,在 Flask session 中记录
            session["user_id"] = user_id

Tainted flow: 'LOCAL_OPENAI_COMPLETIONS_URL' from os.getenv (line 28, credential/environment) → requests.post (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 · src/front.py (reported line 6057)May include surrounding context.

python
}

    try:
        r = requests.post(
            LOCAL_OPENAI_COMPLETIONS_URL,
            json=openai_payload,
            headers={"Authorization": f"Bearer {user_id}:{password}"},

Tainted flow: 'LOCAL_OPENAI_COMPLETIONS_URL' from os.getenv (line 28, credential/environment) → requests.post (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 · src/front.py (reported line 6148)May include surrounding context.

python
}

    try:
        r = requests.post(
            LOCAL_OPENAI_COMPLETIONS_URL,
            json=openai_payload,
            headers={"Authorization": f"Bearer {user_id}:{password}"},

Tainted flow: 'LOCAL_AGENT_CANCEL_URL' from os.getenv (line 19, credential/environment) → requests.post (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 · src/front.py (reported line 6204)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    session_id = request.json.get("session_id", "default") if request.is_json else "default"
    try:
        r = requests.post(LOCAL_AGENT_CANCEL_URL, json={"user_id": user_id, "password": password, "session_id": session_id}, timeout=5)
        return jsonify(r.json())
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'LOCAL_TTS_URL' from os.getenv (line 25, credential/environment) → requests.post (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 · src/front.py (reported line 6226)May include surrounding context.

python
payload = {"user_id": user_id, "password": password, "text": text}
        if voice:
            payload["voice"] = voice
        r = requests.post(LOCAL_TTS_URL, json=payload, timeout=60)
        if r.status_code != 200:
            return jsonify({"error": f"TTS 服务错误: {r.status_code}"}), r.status_code

Tainted flow: 'LOCAL_TOOLS_URL' from os.getenv (line 21, credential/environment) → requests.get (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 · src/front.py (reported line 6242)May include surrounding context.

python
def proxy_tools():
    """代理获取工具列表请求到后端 Agent"""
    try:
        r = requests.get(LOCAL_TOOLS_URL, headers={"X-Internal-Token": INTERNAL_TOKEN}, timeout=10)
        return jsonify(r.json())
    except Exception as e:
        return jsonify({"error": str(e), "tools": []}), 500

Tainted flow: 'LOCAL_SESSIONS_URL' from os.getenv (line 22, credential/environment) → requests.post (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 · src/front.py (reported line 6261)May include surrounding context.

python
if not user_id or not password:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.post(LOCAL_SESSIONS_URL, json={"user_id": user_id, "password": password}, timeout=15)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.post (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 · src/front.py (reported line 6275)May include surrounding context.

python
if not user_id or not password:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.post(
            f"http://127.0.0.1:{PORT_AGENT}/sessions_status",
            json={"user_id": user_id, "password": password},
            timeout=5,

Tainted flow: 'OASIS_BASE_URL' from os.getenv (line 33, credential/environment) → requests.get (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 · src/front.py (reported line 6290)May include surrounding context.

python
"""Proxy to fetch OpenClaw session list from OASIS server."""
    filter_kw = request.args.get("filter", "")
    try:
        r = requests.get(
            f"{OASIS_BASE_URL}/sessions/openclaw",
            params={"filter": filter_kw},
            timeout=10,

Tainted flow: 'LOCAL_SESSION_HISTORY_URL' from os.getenv (line 23, credential/environment) → requests.post (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 · src/front.py (reported line 6309)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    sid = request.json.get("session_id", "")
    try:
        r = requests.post(LOCAL_SESSION_HISTORY_URL, json={
            "user_id": user_id, "password": password, "session_id": sid
        }, timeout=15)
        return jsonify(r.json()), r.status_code

Tainted flow: 'LOCAL_SESSION_STATUS_URL' from os.getenv (line 26, credential/environment) → requests.post (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 · src/front.py (reported line 6326)May include surrounding context.

python
return jsonify({"has_new_messages": False}), 200
    sid = request.json.get("session_id", "") if request.is_json else ""
    try:
        r = requests.post(LOCAL_SESSION_STATUS_URL, json={
            "user_id": user_id, "password": password, "session_id": sid
        }, timeout=5)
        return jsonify(r.json()), r.status_code

Tainted flow: 'LOCAL_DELETE_SESSION_URL' from os.getenv (line 24, credential/environment) → requests.post (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 · src/front.py (reported line 6343)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    sid = request.json.get("session_id", "") if request.is_json else ""
    try:
        r = requests.post(LOCAL_DELETE_SESSION_URL, json={
            "user_id": user_id, "password": password, "session_id": sid
        }, timeout=15)
        return jsonify(r.json()), r.status_code

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 6369)May include surrounding context.

python
if not uid:
        return jsonify([]), 200
    try:
        r = requests.get(f"http://127.0.0.1:{PORT_AGENT}/groups", headers=headers, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.post (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 · src/front.py (reported line 6383)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    try:
        headers["Content-Type"] = "application/json"
        r = requests.post(f"http://127.0.0.1:{PORT_AGENT}/groups", json=request.get_json(silent=True), headers=headers, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 6396)May include surrounding context.

python
if not uid:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.get(f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}", headers=headers, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.put (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 · src/front.py (reported line 6410)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    try:
        headers["Content-Type"] = "application/json"
        r = requests.put(f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}", json=request.get_json(silent=True), headers=headers, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 6437)May include surrounding context.

python
return jsonify({"messages": []}), 200
    try:
        after_id = request.args.get("after_id", "0")
        r = requests.get(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/messages",
            params={"after_id": after_id},
            headers=headers, timeout=10,

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.post (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 · src/front.py (reported line 6455)May include surrounding context.

python
return jsonify({"error": "未登录"}), 401
    try:
        headers["Content-Type"] = "application/json"
        r = requests.post(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/messages",
            json=request.get_json(silent=True),
            headers=headers, timeout=10,

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.post (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 · src/front.py (reported line 6472)May include surrounding context.

python
if not uid:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.post(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/mute",
            headers=headers, timeout=10,
        )

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.post (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 · src/front.py (reported line 6488)May include surrounding context.

python
if not uid:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.post(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/unmute",
            headers=headers, timeout=10,
        )

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 6504)May include surrounding context.

python
if not uid:
        return jsonify({"muted": False}), 200
    try:
        r = requests.get(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/mute_status",
            headers=headers, timeout=10,
        )

Tainted flow: 'PORT_AGENT' from os.getenv (line 16, credential/environment) → requests.get (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 · src/front.py (reported line 6520)May include surrounding context.

python
if not uid:
        return jsonify({"sessions": []}), 200
    try:
        r = requests.get(
            f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}/sessions",
            headers=headers, timeout=15,
        )

Tainted flow: 'OASIS_BASE_URL' from os.getenv (line 33, credential/environment) → requests.get (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 · src/front.py (reported line 6539)May include surrounding context.

python
return jsonify([]), 200
    try:
        print(f"[OASIS Proxy] Fetching topics from {OASIS_BASE_URL}/topics for user={user_id}")
        r = requests.get(f"{OASIS_BASE_URL}/topics", params={"user_id": user_id}, timeout=10)
        print(f"[OASIS Proxy] Response status: {r.status_code}, count: {len(r.json()) if r.text else 0}")
        return jsonify(r.json()), r.status_code
    except Exception as e: