VassalOps is a local Windows desktop agent. Approved actions can click and type as your logged-in user. Read this before teaching duties or exposing the optional broker.
Security writing for personal/local agents is inspired by community practices (including documentation from projects such as OpenClaw on untrusted input and host-side tools). VassalOps focuses on Approve-gated desktop automation and workday duties.
| Version | Supported |
|---|---|
main branch |
Yes — please report issues against current main |
| Older commits / forks | Best-effort only |
Please do not open a public GitHub Issue for security vulnerabilities.
- Prefer GitHub private vulnerability reporting if enabled on the repository.
- Or email the maintainer associated with GitHub user AndyOliverR (see profile / Sponsors) with:
- Description of the issue
- Steps to reproduce
- Impact (e.g. bypass Approve, token leak, remote bind)
- Suggested fix if you have one
We will acknowledge reports as soon as practical and coordinate disclosure.
- Privilege: Automation runs with your Windows user rights. There is no OS jail or container. A subprocess “sandbox” is timeout-only, not isolation.
- Trust boundary: The human Approve / Reject gate (and playlist Confirm) is the primary control before mouse/keyboard macros.
- Local LLM: Prompts may include OCR/clipboard context; secrets are redacted where the redactor runs — still treat screen content as sensitive.
- Network: Optional WebSocket broker defaults to off. When used, it must bind to
127.0.0.1and requirebroker_auth_tokenfromconfig.json. Do not expose it to LAN/WAN without additional hardening you own. - Teach mode:
pynputrecords keystrokes and clicks. Passwords typed during teach can be stored in duty/macro JSON understorage/. Prefer click-only flows for logins; delete sensitive duty files if recorded by mistake. - Replay: Coordinate clicks are fragile; window-title / OCR landmarks help but can fail. Stuck detection pauses and asks the human to Continue — do not treat Continue as a security boundary for malicious plans you already Approved.
- Public contributions: Treat every PR as untrusted until reviewed. See docs/SUPPLY_CHAIN.md. Protect
mainwith required reviews / Code Owners. Do not run unreviewed contributor code on a machine with secrets. - Acceptable use: High-severity harmful goals are refused at runtime — see ACCEPTABLE_USE.md.
- Local PIN gate: Chat/Approve/duties require an unlocked local account (email + PIN + secret Q/A for reset). Profile lives under
storage/auth/(gitignored); PIN and secret answer are PBKDF2-hashed, never plaintext. This is a soft gate against casual use of an unlocked session — it is not OS-level access control (someone with disk access can still delete the profile or patch the app). - Installer email / GitHub notepad: Public GitHub clones do not provide downloader emails. Release zips inject a scoped Issues-write PAT into
config.local.jsonat publish time (Actions secretVASSALOPS_INSTALLS_PAT; never committed). Git clones still use gitignoredconfig.local.json(seeconfig.local.json.example). First signup creates a private-repo Issue (install: email+ JSON body). Bootstrap and unlock also retry once if signup was offline. Alternate: genericregistration_endpointHTTPS POST. Rotate the PAT if abused. - Lab-rat handshake: After the local PIN account exists, launch and close automatically use HTTPS to GitHub. Inbound: GitHub Releases zip (duties +
config.json+ existingconfig.local.jsonpreserved). Outbound: a sanitized skill distillate (duty step types, generic app families, redacted lessons). Never screenshots, OCR, clipboard, file contents, coordinates, typed keystrokes, or company inventory (Excel/Word/PDF/CSV understorage/internal_dataand Google Sheet grids). SetVASSALOPS_SKIP_HANDSHAKE=1to disable. Sponsor/star in the top bar are optional. - Internal catalog: Booking/availability lookup crawls files on this PC only. Optional Google Sheet read uses the employee’s already signed-in Chrome/Edge after Approve (open URL + copy grid). No Google API, no password capture. Sheet URLs may live in
config.json(internal_sheets); secrets do not.
Screen OCR, clipboard, and chat goals are untrusted input (prompt-injection surface). The local model may propose a plan; secrets are redacted where the redactor runs, but you still treat the plan as untrusted until you read it. Approve is the human judgement gate — AI can investigate and draft; you decide what runs. Same lesson as the GitHub Secure Open Source Fund Session 4 write-up (we are not a Fund participant).
- Silent full-day autopilot without Approve
- Auto-start the broker on install
- Ship unsigned EXE as the primary lay-user install path (prefer
bootstrap_and_run.bat)
Teach/replay and PyInstaller-packed launchers are commonly flagged as keylogger/RAT heuristics (e.g. K7, Windows Defender). See README. Restore only paths under your VassalOps install tree after verifying; add a folder exclusion rather than disabling AV globally.
- Read every proposed plan before Approve.
- Do not Approve teach/run while entering passwords you cannot rotate.
- Keep
config.jsonbroker tokens private; never commit real tokens to public forks. - Stop (UI) or pyautogui failsafe (mouse to top-left corner) if automation misbehaves.
Product limitations (UI drift, MFA, CAPTCHA) are expected; they are not vulnerabilities by themselves. Bypass of Approve, remote code without auth, or leakage of broker tokens are in scope.