Back to skill

Security audit

teamclawtestv101

Security checks for vulnerabilities and agentic risk

Overview

This is a powerful local agent service, but its setup and runtime grant broad machine, credential, and public-exposure authority without enough safeguards.

Review this carefully before installing. Use it only in an isolated local environment, avoid the public tunnel unless you add strong authentication and CSRF/origin protections, disable or strictly gate command and Python execution tools, and pin/verify all remote installers, binaries, and dependencies before running setup or startup.

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
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
Findings (7)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/setup_env.sh:13
Finding

Mutable Remote Installer Is Downloaded and Executed Without Verification

Content
View full analysis
/dev/null; then echo "✅ uv is installed: $(uv --version)" else echo "📦 uv was not detected; installing..." curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Technical Analysis The documented setup process retrieves a shell script from an external URL and pipes the response directly into `sh`. The retrieved content is not pinned to a reviewed version and is not verified using a cryptographic checksum or digital signature. HTTPS provides transport protection but does not guarantee that the remote source, account, release process, DNS resolution, or delivered content will remain trustworthy after the Skill has been reviewed. The effective code executed by this project can therefore change independently of the audited repository. This operation is part of the declared setup workflow because `selfskill/scripts/run.sh setup` invokes `scripts/setup_env.sh`. ### Attack Path 1. A user follows the documented setup procedure and runs `bash selfskill/scripts/run.sh setup`. 2. `run.sh` invokes `scripts/setup_env.sh`. 3. If `uv` is absent, the script requests the current contents of `https://astral.sh/uv/install.sh`. 4. The HTTP response is passed directly to `sh` without being saved or verified. 5. A compromised or malicious response executes with the privileges of the user performing installation. ### Impact Assessment Successful exploitation provides arbitrary command execution under the installing user's account. The payload could access user files, environment variables, API credentials, SSH material available to that user, or install additional persistence. If installation is performed with elevated privileges, the impact may extend to full system compromise. ]]>
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/launcher.py:72
Finding

Unverified Mutable Bark Server Binary Is Downloaded and Executed

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/mcp_commander.py:37
Finding

Shell Command Allowlist Does Not Provide an Effective Security Boundary

Content
View full analysis
0: continue cmd_name = os.path.basename(token) break if cmd_name and cmd_name not in ALLOWED_COMMANDS: return f"Command '{cmd_name}' is not in the allowlist" ``` Validated input is passed to a shell: ```python proc = await asyncio.create_subprocess_shell( command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=workspace, env=_sandbox_env(workspace, username), ) ``` ### Technical Analysis The implementation describes the working directory as a sandbox, but changing `cwd` and limiting environment variables do not create an operating-system security boundary. Commands still execute as the host service account and can reference absolute paths or use allowed programs to perform unrestricted actions. The allowlist includes `python`, `python3`, `node`, `npm`, ...[truncated 1428 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/mcp_commander.py:253
Finding

Agent Tool Executes Arbitrary Python Without a Real Sandbox

Content
View full analysis
str: """ Execute a Python code snippet in the user's isolated working directory. """ 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), ) try: stdout, stderr = await asyncio.wait_for( proc.communicate(), timeout=EXEC_TIMEOUT ) ``` ### Technical Analysis The tool writes model- or user-supplied source code to disk and executes it using the same Python interpreter as the service. No container, mandatory access-control profile, syscall filter, restricted Python runtime, filesystem namespace, or network namespace is applied. The restricted environment dictionary and workspace current directory do not prevent Python from: - Reading or modifying absolute filesystem paths. - Importing `os`, `subprocess`, `socket`, or other powerful standard-library modules. - Opening outbound network connections. - Connecting to localhost services. - Spawning child processes. - Reading project databases and configuration files available to the service account. The timeout only limits duration. It does not limit privileges or prevent immediate data theft and modification. ### Attack Path 1. An attacker provides content that causes the LLM to call `run_python_code`. 2. The tool writes the supplied code to `.tmp_exec.py`. 3. The host Python int ...[truncated 553 chars]
Remediation
View remediation

T02 · Agent Memory Poisoning

Error
Location
src/agent.py:207
Finding

Persistent User Profile Content Is Reintroduced as Trusted System-Prompt Text

Content
View full analysis
str: """Read 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 "" ``` It is then concatenated into the system prompt: ```python 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" ``` ### Technical Analysis The profile is durable state that may be written as a consequence of user conversation. Its contents are later concatenated directly into `base_prompt`, which gives the text system-prompt proximity and no explicit untrusted-data boundary. There is no structured schema, instruction-content filtering, provenance metadata, approval step, or delimiter that clearly tells the model never to execute instructions found in the profile. An attacker can attempt to cause instruction-like text to be stored in `user_profile.txt`, producing a persistent prompt injection that survives across sessions. The prompt also instructs the Agent to infer and proactively persist us ...[truncated 995 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/mcp_filemanager.py:10
Finding

User File Path Validation Can Be Bypassed by Prefix Collisions and Symlinks

Content
View full analysis
str: """Get and create the user directory.""" safe_name = os.path.basename(username) user_path = os.path.join(BASE_DIR, safe_name) os.makedirs(user_path, exist_ok=True) return user_path def _safe_path(username: str, filename: str) -> str: """Build a safe path and prevent path traversal.""" user_path = _user_dir(username) full_path = os.path.normpath(os.path.join(user_path, filename)) # Ensure that the path is inside the user directory if not full_path.startswith(user_path): raise ValueError(f"Illegal path: {filename}") return full_path ``` The returned path is later passed to ordinary `open`, `os.remove`, and related filesystem operations. ### Technical Analysis A string-prefix comparison is not a valid filesystem containment check. For example, if the approved directory is: ```text .../data/user_files/foo ``` then a normalized target under: ```text .../data/user_files/foobar ``` still begins with the string for the `foo` directory. A filename such as `../foobar/target` may therefore pass the check. The code also does not resolve symbolic links. A symlink placed inside the user workspace may point outside it, while the lexical path still begins with `user_path`. Subsequent file operations follow the symlink. This affects read, write, append, and delete operations because they all rely on `_safe_path`. ### Attack Path **Sibling-prefix traversal:** 1. An authenticated user has a workspace named `foo`. 2. A sibling directory named `foobar` exists. 3. The attacker requests an operation on `../foobar/target`. 4. `normpath` resolves the path into the sibling directory. 5. `startswith(user_path)` returns true because `foobar` shares the `foo` prefix. 6. The requested file oper ...[truncated 768 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 (456)

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The /v1/chat/completions route blindly forwards the client-supplied Authorization header and request body to an internal service. This exposes a powerful authenticated proxy to the browser and allows direct user control over requests sent to the local agent backend, increasing abuse impact if the frontend is exposed cross-origin or the backend trusts this proxy boundary.

Content

Scanner excerpt · src/front.py (reported line 5508)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 5547)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
92% confidence
Finding

The login proxy collects raw user credentials and stores both user_id and password in the Flask session for reuse on later backend calls. Persisting backend credentials in session state raises credential exposure risk through session compromise, logging mistakes, or weak cookie protections.

Content

Scanner excerpt · src/front.py (reported line 5560)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

This deprecated proxy forwards user prompts and images to the internal model service using a constructed Bearer credential of user_id:password. Even though it is local, it transmits sensitive content and reusable credentials to another service boundary and depends on that downstream service for safe handling.

Content

Scanner excerpt · src/front.py (reported line 5606)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

The streaming proxy forwards multimodal user input, session IDs, and a password-derived bearer credential to the backend model endpoint. This broadens exposure of secrets and sensitive uploaded content and makes the frontend a trusted relay for potentially dangerous backend operations.

Content

Scanner excerpt · src/front.py (reported line 5697)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 5753)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

The TTS proxy forwards user text plus stored credentials to another backend service. Sensitive chat text may be transmitted for speech generation without clear minimization, and the same credential-reuse pattern increases blast radius if the backend or session is compromised.

Content

Scanner excerpt · src/front.py (reported line 5775)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
93% confidence
Finding

The /proxy_tools endpoint exposes an internal token-authenticated backend capability to any web client that can reach this Flask app, with no user authentication check. That effectively turns the frontend into an unauthenticated proxy for privileged internal metadata retrieval.

Content

Scanner excerpt · src/front.py (reported line 5791)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

This endpoint forwards stored credentials to retrieve session lists from the backend. While common for a proxy, it perpetuates the insecure pattern of password reuse across internal calls and enlarges the exposure surface for user metadata.

Content

Scanner excerpt · src/front.py (reported line 5810)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 5824)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: 'LOCAL_SESSION_HISTORY_URL' from os.getenv (line 23, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Session history retrieval forwards user credentials and session identifiers to the backend and returns potentially sensitive conversation data. If the Flask session or route protections fail, this becomes a convenient exfiltration path for historical prompts and outputs.

Content

Scanner excerpt · src/front.py (reported line 5843)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 5860)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

This route deletes session state on the backend based on a browser request and stored credentials, but there is no visible CSRF protection. A victim visiting an attacker-controlled page could potentially be induced to trigger destructive actions if cookie-based auth is accepted.

Content

Scanner excerpt · src/front.py (reported line 5877)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 5903)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

Creating groups is a state-changing backend action proxied through Flask with cookie-based session auth and no visible CSRF control. That makes unauthorized cross-site state mutation plausible if the app is reachable from a browser session.

Content

Scanner excerpt · src/front.py (reported line 5917)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 5930)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

Updating group membership is a sensitive state-changing operation proxied without visible CSRF defenses. An attacker could abuse a logged-in browser to add or remove agent sessions from groups, altering multi-agent behavior and data access patterns.

Content

Scanner excerpt · src/front.py (reported line 5944)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 5971)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

Posting group messages can trigger downstream agent behavior and persistent state changes, yet the proxy accepts browser requests with no visible CSRF protection. In this application context, message posting is more dangerous than ordinary chat because it can influence orchestration and multi-agent execution.

Content

Scanner excerpt · src/front.py (reported line 5989)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

Muting a group is a privileged state change exposed as a simple POST proxy with no visible CSRF control. An attacker could disrupt workflows or safety monitoring by toggling group state from a victim's browser session.

Content

Scanner excerpt · src/front.py (reported line 6006)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

Unmuting a group is another state-changing action vulnerable to the same browser-forgery pattern. In an orchestration tool, unintended resume of agent activity can have operational and data-handling consequences.

Content

Scanner excerpt · src/front.py (reported line 6022)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 6038)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 6054)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

This proxy leaks the logged-in user_id to the OASIS service as a query parameter and logs it server-side. Query-string identifiers are more likely to be retained in logs, intermediaries, and diagnostics, exposing user metadata beyond what is necessary.

Content

Scanner excerpt · src/front.py (reported line 6073)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:

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Topic-detail retrieval similarly sends user_id in the query string and logs the full request target. This creates unnecessary metadata exposure and can disclose which discussions a user is accessing.

Content

Scanner excerpt · src/front.py (reported line 6090)May include surrounding context.

python
try:
        url = f"{OASIS_BASE_URL}/topics/{topic_id}"
        print(f"[OASIS Proxy] Fetching topic detail from {url} for user={user_id}")
        r = requests.get(url, params={"user_id": user_id}, timeout=10)
        print(f"[OASIS Proxy] Detail response status: {r.status_code}")
        return jsonify(r.json()), r.status_code
    except Exception as e: