Back to skill

Security audit

TeamClaw

Security checks across malware telemetry and agentic risk

Overview

TeamClaw’s multi-agent features are coherent, but it exposes broad agent, file, command, and public-web controls with weak credential handling that users should review before installing.

Install only if you are comfortable running a local agent platform with command execution, file access, network access, persistent memory, bot integrations, and optional public exposure. Avoid enabling the Cloudflare tunnel unless you add strong access controls, and do not reuse important passwords because this version passes and stores raw user passwords internally.

SkillSpector

By NVIDIA
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 (87)

Tainted flow: 'LOCAL_OPENAI_COMPLETIONS_URL' from os.getenv (line 31, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
# 直接透传请求体和 Authorization header 到后端
    auth_header = request.headers.get("Authorization", "")
    try:
        r = requests.post(
            LOCAL_OPENAI_COMPLETIONS_URL,
            json=request.get_json(silent=True),
            headers={
Confidence
97% confidence
Finding
r = requests.post( LOCAL_OPENAI_COMPLETIONS_URL, json=request.get_json(silent=True), headers={ "Authorization": auth_header,

Tainted flow: 'LOCAL_LOGIN_URL' from os.getenv (line 23, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
95% confidence
Finding
r = requests.post(LOCAL_LOGIN_URL, json={"user_id": user_id, "password": password}, timeout=10)

Tainted flow: 'LOCAL_AGENT_CANCEL_URL' from os.getenv (line 22, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
96% confidence
Finding
r = requests.post(LOCAL_AGENT_CANCEL_URL, json={"user_id": user_id, "password": password, "session_id": session_id}, timeout=5)

Tainted flow: 'LOCAL_TTS_URL' from os.getenv (line 28, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
96% confidence
Finding
r = requests.post(LOCAL_TTS_URL, json=payload, timeout=60)

Tainted flow: 'LOCAL_TOOLS_URL' from os.getenv (line 24, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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
Confidence
88% confidence
Finding
r = requests.get(LOCAL_TOOLS_URL, headers={"X-Internal-Token": INTERNAL_TOKEN}, timeout=10)

Tainted flow: 'LOCAL_SETTINGS_URL' from os.getenv (line 279, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
if not user_id or not password:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.get(LOCAL_SETTINGS_URL, params={"user_id": user_id, "password": password}, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500
Confidence
97% confidence
Finding
r = requests.get(LOCAL_SETTINGS_URL, params={"user_id": user_id, "password": password}, timeout=10)

Tainted flow: 'LOCAL_SETTINGS_URL' from os.getenv (line 279, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
data = request.get_json(force=True)
        data["user_id"] = user_id
        data["password"] = password
        r = requests.post(LOCAL_SETTINGS_URL, json=data, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500
Confidence
96% confidence
Finding
r = requests.post(LOCAL_SETTINGS_URL, json=data, timeout=10)

Tainted flow: 'LOCAL_SESSIONS_URL' from os.getenv (line 25, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
96% confidence
Finding
r = requests.post(LOCAL_SESSIONS_URL, json={"user_id": user_id, "password": password}, timeout=15)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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,
Confidence
96% confidence
Finding
r = requests.post( f"http://127.0.0.1:{PORT_AGENT}/sessions_status", json={"user_id": user_id, "password": password}, timeout=5, )

Tainted flow: 'LOCAL_SESSION_HISTORY_URL' from os.getenv (line 26, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
96% confidence
Finding
r = requests.post(LOCAL_SESSION_HISTORY_URL, json={ "user_id": user_id, "password": password, "session_id": sid }, timeout=15)

Tainted flow: 'LOCAL_SESSION_STATUS_URL' from os.getenv (line 29, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
96% confidence
Finding
r = requests.post(LOCAL_SESSION_STATUS_URL, json={ "user_id": user_id, "password": password, "session_id": sid }, timeout=5)

Tainted flow: 'LOCAL_DELETE_SESSION_URL' from os.getenv (line 27, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
97% confidence
Finding
r = requests.post(LOCAL_DELETE_SESSION_URL, json={ "user_id": user_id, "password": password, "session_id": sid }, timeout=15)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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
Confidence
97% confidence
Finding
r = requests.get(f"http://127.0.0.1:{PORT_AGENT}/groups", headers=headers, timeout=10)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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
Confidence
97% confidence
Finding
r = requests.post(f"http://127.0.0.1:{PORT_AGENT}/groups", json=request.get_json(silent=True), headers=headers, timeout=10)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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
Confidence
97% confidence
Finding
r = requests.get(f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}", headers=headers, timeout=10)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.put (network output)

Critical
Category
Data Flow
Content
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
Confidence
97% confidence
Finding
r = requests.put(f"http://127.0.0.1:{PORT_AGENT}/groups/{group_id}", json=request.get_json(silent=True), headers=headers, timeout=10)

Tainted flow: 'PORT_AGENT' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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,
Confidence
97% confidence
Finding
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 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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,
Confidence
97% confidence
Finding
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 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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,
        )
Confidence
97% confidence
Finding
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 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
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,
        )
Confidence
97% confidence
Finding
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 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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,
        )
Confidence
97% confidence
Finding
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 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
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,
        )
Confidence
97% confidence
Finding
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 36, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
if not user_id:
        return jsonify({"error": "未登录"}), 401
    try:
        r = requests.post(f"{OASIS_BASE_URL}/topics/{topic_id}/purge", params={"user_id": user_id}, timeout=10)
        return jsonify(r.json()), r.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500
Confidence
93% confidence
Finding
r = requests.post(f"{OASIS_BASE_URL}/topics/{topic_id}/purge", params={"user_id": user_id}, timeout=10)

Tainted flow: 'LOCAL_OPENAI_COMPLETIONS_URL' from os.getenv (line 31, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
"session_id": data.get("target_session_id") or "visual_orchestrator",
            "temperature": 0.3,
        }
        resp = requests.post(LOCAL_OPENAI_COMPLETIONS_URL, json=payload, headers=headers, timeout=60)
        if resp.status_code != 200:
            return jsonify({"prompt": prompt, "error": f"Agent returned HTTP {resp.status_code}: {resp.text[:500]}", "agent_yaml": None})
Confidence
97% confidence
Finding
resp = requests.post(LOCAL_OPENAI_COMPLETIONS_URL, json=payload, headers=headers, timeout=60)

Tainted flow: 'LOCAL_SESSIONS_URL' from os.getenv (line 25, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
if not user_id or not password:
        return jsonify([])
    try:
        r = requests.post(LOCAL_SESSIONS_URL, json={"user_id": user_id, "password": password}, timeout=10)
        if r.status_code != 200:
            return jsonify([])
        sessions_data = r.json()
Confidence
96% confidence
Finding
r = requests.post(LOCAL_SESSIONS_URL, json={"user_id": user_id, "password": password}, timeout=10)

VirusTotal

63/63 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.