Skip to content

Daily security review — 2026-06-22 #49

Description

@nithiink

Automated daily security review (fan-out audit + adversarial verification). Coverage: command/template/path injection, unsafe exec/eval/deserialization, authN/access-control, CORS/Origin, SSRF, hardcoded secrets, XSS, shell scripts, and dependency risk.

Overall posture is strong. No Critical/High issues. No hardcoded secrets, no command injection (all subprocess calls are argv-based / shlex.quote'd, no shell=True/os.system/eval/pickle), no XSS sinks (no dangerouslySetInnerHTML), SSRF mint host is pinned, and all dependencies are exact-pinned with no known critical CVEs. The 3 verified findings below are Medium/Low.


Medium

M1 — No Host-header validation → DNS-rebinding can reach the command-executing API in the default tokenless mode

  • Files: backend/main.py:199-214 (_access_ok / require_auth), frontend/lib/proxyAuth.ts:49-69 (isCrossSiteRequest), backend/config.py:230-237 (broad default Origin regex). No TrustedHostMiddleware / Host allowlist exists anywhere in the backend.
  • Description: In the default yapcode up mode there is no token; the only protections on /api/tools/execute (which the backend turns into real command execution) are (a) the proxy's same-origin/CSRF check and (b) the backend trusting loopback clients. Neither pins the expected Host. A DNS-rebinding attacker lures the victim to http://evil.com, then rebinds evil.com → 127.0.0.1. The follow-up fetch("http://evil.com:3000/api/tools/execute") is now same-origin to the browser → Sec-Fetch-Site: same-origin and Origin.host === Host both pass isCrossSiteRequest. The Next proxy strips Origin (documented at proxyAuth.ts:11-16), so the backend sees a loopback client with no Origin and trusts it (_access_ok, main.py:199-201; the Origin check at main.py:212-214 only fires when an Origin is present). Full exploit chain → command execution. The broad default Origin regex (config.py:230-237, any localhost/private-LAN host on any port) similarly lets any co-resident localhost web app drive the proxy in tokenless mode.
  • Severity: Medium (requires victim to open an attacker page + a rebinding setup; only affects the tokenless default. Token/network mode is unaffected since the token gates it).
  • Fix (manual / needs design care): Add a Host-header allowlist — Starlette TrustedHostMiddleware on the backend (default localhost, 127.0.0.1, [::1], plus any configured LAN host) and/or a Host check inside blockCrossSite(). This is the standard DNS-rebinding mitigation and complements the existing CSRF logic. Care needed so legitimate LAN access in network mode (192.168.x.x / hostname) is still permitted — recommend a VC_ALLOWED_HOSTS env knob.

Low

L1 — tool_use_id used as a filesystem path component without validation (defense-in-depth gap)

  • Files: backend/tmux_hooks/_common.py:40-41 (decision_path), backend/tmux_hooks/hook_pretool.py:86,96 (read + os.remove), backend/tmux_runner.py:674-679 (os.replace write side).
  • Description: tool_use_id is interpolated directly into decisions/<id>.json and reaches open(), os.remove(), and os.replace() with no sanitization — unlike every other externally-derived path component in the codebase (session_id/handle are validated via validate_session_id/_SESSION_ID_RE). A traversal value (e.g. ../../...) would let the hook delete/write files outside decisions/. The value is currently CLI-minted (toolu_*), not directly attacker- or model-controlled, so practical exploitability is low — but it is the one path segment that skips the validation applied consistently elsewhere.
  • Severity: Low.
  • Fix: Validate tool_use_id as a safe single path component (reuse the existing regex, or os.path.basename + reject if it changes) in both decision_path() and the runner's write side.

L2 — --reload enabled in the network-exposed run script

  • File: backend/run-network.sh:47-52.
  • Description: run-network.sh binds 0.0.0.0 and runs uvicorn with --reload --reload-dir .. Running the autoreloader on a network-exposed service enlarges the attack surface (file-watching + child-process machinery; any writable-code scenario becomes RCE-on-write). Access is still gated by the mandatory VC_AUTH_TOKEN, so this is hardening, not an exploitable hole.
  • Severity: Low.
  • Fix: Drop --reload/--reload-dir from run-network.sh; keep them only in the localhost run.sh.

Generated by the automated daily security review. Verified findings only; speculative/low-signal items were discarded during the verification pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions