pypi package report

Is vibe-trading-ai safe?

3 known vulnerabilities, worst severity CRITICAL.

// reach

0 direct dependencies

none carry a known advisory

    0 packages depend on it

    an advisory here reaches each of them

      Create a free accountfor every dependency path, dependent and what to upgrade
      // ai model usage

      No published models are known to use this package.


      cvss
      0.0
      critical

      severity band, no base score published

      epss
      not scored

      chance of exploitation in 30 days

      xyz score
      0.0
      medium

      CyberXYZ composite out of 10

      fig. 01 — GHSA-v2f8-6655-7grj, the advisory selected below

      // 3 advisories

      GHSA-v2f8-6655-7grj

      CRITICAL
      // summary:

      5 findings — unauthenticated full-API exposure (F1, lead Critical), read-side authorization gap that persists even with APIAUTHKEY set (F2), unauthenticated file write of .py/.sh/.yaml to a server-returned path (F3), default-permissive CORS that combines with a loopback-only check to grant any browser page on whitelisted localhost ports credentialed cross-origin access (F-A4), and partial API-key disclosure via masksecret() (F-A5).

      ---

      // shared baseline (applies to all 5 findings)

      The shipped agent/.env.example line 112 ships # APIAUTHKEY= commented out. requireauth() at agent/apiserver.py line 303 executes if not apikey: return and returns None immediately when APIAUTHKEY is unset, so every endpoint decorated with dependencies=[Depends(requireauth)] operates as unauthenticated. The shipped Dockerfile does not contain a USER directive, so the FastAPI process runs as uid=0(root) inside the container (verified: docker exec id returns uid=0(root) gid=0(root)). The docker-compose.yml binds 0.0.0.0:8899 with no network restriction.

      The only operator action required beyond a clean install is supplying a working LLM API key so the agent loop can complete its tool-call round trip — this is the normal first step to make the agent functional, not an additional security opt-in. F-A4 and F-A5 do not require an LLM key (see per-finding notes); F1 and F3 do not require an LLM key for the unauth surface itself, only for the chained RCE demonstration in F1.

      // reproducer environment (common)
      git clone https://github.com/HKUDS/Vibe-Trading.git
      cd Vibe-Trading
      git checkout 7452610113a75529b5d55fd2217bb17f7bec66f7   # v0.1.6 + 1 frontend fix; same vuln state as v0.1.6
      cp agent/.env.example agent/.env
      # (For F1 chained demo only:) edit agent/.env to set OPENROUTER_API_KEY=
      docker compose up -d
      # port 8899 is now reachable; HOST below is the docker host's IP from the attacker's perspective

      > Note on the HOST placeholder used throughout the per-finding "Steps to observe" blocks below: replace HOST with the address you reach the docker host on — typically localhost (or 127.0.0.1) if you are running the reproducer on the same machine as the container. All curl commands below assume this substitution.

      ---

      // finding 1 — critical: unauthenticated network client reaches shell execution via post /sessions/{id}/messages
      • Severity: Critical
      • CVSS v3.1: 9.8 — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
      • CVSS v4.0: 10.0 — AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
      • CWE: CWE-306 (Missing Authentication for Critical Function); chains into CWE-78 (Group B, F6)

      Affected files:

      • agent/apiserver.py:303 — if not apikey: return early return in requireauth()
      • agent/apiserver.py:736 — @app.post("/sessions/{sessionid}/messages", dependencies=[Depends(requireauth)]) (the dependency is a no-op when APIAUTHKEY is unset)
      • agent/src/tools/bashtool.py:44-46 — subprocess.run(command, shell=True, cwd=cwd) with command read from kwargs['command'] (full bug detail tracked in GHSA-2 / Group B / F6)

      Intent vs actual: The session API is intended to serve authenticated users only. When APIAUTHKEY is unset, requireauth() returns None immediately and dependencies=[Depends(requireauth)] becomes a no-op. Any anonymous TCP client to port 8899 can therefore create a session, post a message, and receive the LLM agent's response. The LLM ReAct agent — given a natural-language request to run a command — selects BashTool from the auto-discovered registry, which calls subprocess.run(command, shell=True) with the LLM-emitted string. The container has no USER directive, so the resulting process runs as uid=0(root).

      Steps to observe:

      • Start the server per the shared reproducer above (with a working OPENROUTERAPIKEY set in agent/.env). Confirm curl -fs http://HOST:8899/health returns 200.
      • SID=$(curl -s -X POST http://HOST:8899/sessions -H 'Content-Type: application/json' -d '{}' | python3 -c "import json,sys;print(json.load(sys.stdin)['sessionid'])")
      • curl -s -X POST "http://HOST:8899/sessions/$SID/messages" -H 'Content-Type: application/json' -d '{"content":"Execute the shell command '\''id && uname -a'\'' and report the output verbatim."}'
      • Wait ~5–15 seconds, then curl -s "http://HOST:8899/sessions/$SID/messages" and observe the BashTool result message containing uid=0(root), the kernel version, and the container hostname — all returned with no Authorization header on any of the three requests.

      Impact: An unauthenticated caller with TCP access to port 8899 can execute arbitrary shell commands as root inside the container. This is the top-severity entry point of the RCE chain. Combined with the absent USER directive in the Dockerfile, the blast radius is full container takeover.

      ---

      // finding 2 — high: read endpoints return full session history with no authentication, even when apiauthkey is set
      • Severity: High
      • CVSS v3.1: 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
      • CVSS v4.0: 8.7 — AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
      • CWE: CWE-862 (Missing Authorization)

      Affected file: agent/apiserver.py — requireauth() docstring at line 289 states "Only write endpoints (POST/PUT/DELETE/PATCH) use this dependency." Read endpoints with no Depends(requireauth):

      • line 804 — @app.get("/runs", responsemodel=List[RunInfo])
      • line 788 — @app.get("/runs/{runid}", responsemodel=RunResponse)
      • line 748 — @app.get("/runs/{runid}/code")
      • line 769 — @app.get("/runs/{runid}/pine")
      • line 1153 — @app.get("/sessions", responsemodel=List[SessionResponse])
      • line 1173 — @app.get("/sessions/{sessionid}", responsemodel=SessionResponse)
      • line 1251 — @app.get("/sessions/{sessionid}/messages", responsemodel=List[MessageResponse])
      • line 1272 — @app.get("/sessions/{sessionid}/events")
      • line 1444 — @app.get("/swarm/runs")

      Intent vs actual: When APIAUTHKEY is configured, the implicit operator expectation is that all session data is protected. The actual design is documented in the docstring at line 289 — read endpoints have no Depends(requireauth), so they remain unauthenticated even with APIAUTHKEY set. A runtime probe with APIAUTHKEY=any-secret-value configured: a session was created with a valid Bearer token, a message containing BROKERTOKEN=ts-secret-deadbeef-real-private-data was posted, then GET /sessions/{id}/messages was issued with no Authorization header and returned HTTP 200 with the broker token string verbatim in the response — confirming the gap persists when authentication is enabled.

      Steps to observe:

      • Set APIAUTHKEY=any-secret-value in agent/.env. Under docker compose, add a bind-mount on the vibe-trading service so the container reads the change: volumes: - ./agent/.env:/app/agent/.env:ro. Then docker compose up -d --force-recreate (a plain restart reuses the existing process env and will not pick up the change).
      • SID=$(curl -s -X POST http://HOST:8899/sessions -H 'Authorization: Bearer any-secret-value' -H 'Content-Type: application/json' -d '{}' | python3 -c "import json,sys;print(json.load(sys.stdin)['sessionid'])")
      • curl -s -X POST "http://HOST:8899/sessions/$SID/messages" -H 'Authorization: Bearer any-secret-value' -H 'Content-Type: application/json' -d '{"content":"BROKERTOKEN=ts-secret-test-value"}' (this requires the Bearer token because POST is auth-protected)
      • No-auth read — curl -s "http://HOST:8899/sessions/$SID/messages" (no Authorization header). Observe HTTP 200 with the broker token visible.
      • Also: curl -s "http://HOST:8899/runs" returns all run records (including their prompt fields) with no auth.

      Impact: An unauthenticated caller can enumerate the full history of every agent session — including any broker tokens, LLM API keys, or trading account details the operator has pasted into prompts. The gap persists when the operator believes their write operations are protected, making it deceptive for operators who have followed the SECURITY.md spirit and turned auth on.

      // cvss v3.1 vector

      CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

      Attack vector
      Network
      Attack complexity
      Low
      Privileges required
      None
      User interaction
      None
      Scope
      Changed
      Confidentiality
      High
      Integrity
      High
      Availability
      High

      Checked 2026-10-04 at 01:16 UTC. The most recent advisory here was published 2026-10-02. Updated continuously from NVD, GHSA, OSV and CNA feeds.

      Think a verdict here is wrong? Tell us — we respond within 2 business days.