diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bbe787b7..8c2886361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added OpenModel as a first-class Anthropic Messages provider, with config, CLI, provider picker, docs, and registry coverage. Harvested from #3585 by @noaft. +- Added WeCom Bridge deployment and security documentation, with shipped + runtime/bridge commands and approval-timeout environment guidance. Harvested + from #3640 by @pkeging. ### Changed diff --git a/SECURITY.md b/SECURITY.md index a7947ae3e..850a5d2f9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -62,6 +62,34 @@ You will receive status updates at each phase. If the timeline slips, we will co If you are unsure whether a bug is in scope, report it anyway. We will triage and respond. + +## WeCom Bridge Security + +The WeCom Bridge (`integrations/wecom-bridge/`) extends CodeWhale to WeCom +(企业微信) Smart Bot WebSocket sessions. It inherits all standard CodeWhale +security boundaries and adds bridge-specific controls. + +### Bridge-specific protections + +- **No public port**: The bridge communicates with `codewhale serve --http` on `127.0.0.1` only +- **Token gate**: All runtime API calls carry `CODEWHALE_RUNTIME_TOKEN` +- **Chat allowlist**: Only chats/users listed in `WECOM_CHAT_ALLOWLIST` can interact. First-pairing mode (`WECOM_ALLOW_UNLISTED=true`) is meant for onboarding only +- **Approval required**: Tool calls from WeCom sessions must be approved — either via explicit `/allow ` commands or natural-language keywords (`允许`, `yes`, `ok`, etc.) +- **No workspace exposure**: Only prompts, status summaries, and approval requests are sent to WeCom. Workspace contents, shell output, and runtime internals stay on the local machine + +### Reporting WeCom Bridge vulnerabilities + +Report bridge-specific security issues through the same channels listed above. +Include the bridge version (check `package.json`) and your WeCom deployment configuration +(sensitive values redacted). Bridge logs may be requested for reproduction. + +### Bridge environment safety + +- `WECOM_BOT_SECRET` and `CODEWHALE_RUNTIME_TOKEN` must never be committed to git +- The `.env` file is gitignored; use `.env.example` as the template +- Rotate secrets periodically, especially after sharing screen captures +- Use `CODEWHALE_APPROVAL_TIMEOUT_MS` (default 5 min) to limit the approval window + ## Hall of Fame We maintain a hall of fame for reporters who submit verified security vulnerabilities. To be credited, include your preferred name / handle in the report. diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 0b16841f1..7aa316c8f 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added OpenModel as a first-class Anthropic Messages provider, with config, CLI, provider picker, docs, and registry coverage. Harvested from #3585 by @noaft. +- Added WeCom Bridge deployment and security documentation, with shipped + runtime/bridge commands and approval-timeout environment guidance. Harvested + from #3640 by @pkeging. ### Changed diff --git a/docs/CONTRIBUTORS.md b/docs/CONTRIBUTORS.md index ee7e3084c..8ea0e358c 100644 --- a/docs/CONTRIBUTORS.md +++ b/docs/CONTRIBUTORS.md @@ -36,6 +36,9 @@ notes, and relevant issue/PR comments. - **[noaft](https://github.com/noaft)** — OpenModel provider support across config, CLI, TUI provider picker, docs, and registry checks (#3585, harvested) +- **[pkeging](https://github.com/pkeging)** — WeCom Bridge deployment and + security documentation, including the approval-timeout configuration surface + (#3640, harvested) diff --git a/integrations/wecom-bridge/.env.example b/integrations/wecom-bridge/.env.example index cb3d774ae..5b3b4741b 100644 --- a/integrations/wecom-bridge/.env.example +++ b/integrations/wecom-bridge/.env.example @@ -21,8 +21,8 @@ CODEWHALE_AUTO_APPROVE=false WECOM_BOT_ID=your-bot-id WECOM_BOT_SECRET=your-bot-secret -# 逗号分隔的允许用户 UserID(可在企业微信通讯录中查看) -# 首次配对时设为空并用 WECOM_ALLOW_UNLISTED=true +# 逗号分隔的允许用户 UserID 或 chat_id。 +# 首次配对:保持 WECOM_ALLOW_UNLISTED=false,向 bot 发送任意消息后会收到 chat_id/user_id。 WECOM_CHAT_ALLOWLIST= WECOM_ALLOW_UNLISTED=false @@ -33,6 +33,7 @@ WECOM_THREAD_MAP_PATH=/var/lib/codewhale-wecom-bridge/thread-map.json # 消息配置 WECOM_MAX_REPLY_CHARS=3500 CODEWHALE_TURN_TIMEOUT_MS=900000 +CODEWHALE_APPROVAL_TIMEOUT_MS=300000 # 企业微信 API 基础地址(一般不需要修改) WECOM_API_BASE_URL=https://qyapi.weixin.qq.com diff --git a/integrations/wecom-bridge/DEPLOYMENT.md b/integrations/wecom-bridge/DEPLOYMENT.md new file mode 100644 index 000000000..0853d6725 --- /dev/null +++ b/integrations/wecom-bridge/DEPLOYMENT.md @@ -0,0 +1,152 @@ +# WeCom Bridge — Deployment Guide + +## Overview + +The WeCom Bridge integrates CodeWhale with WeCom (企业微信) Smart Bot +WebSocket long-connection mode, enabling remote terminal agent interaction +without a public IP. + +## Prerequisites + +1. **WeCom admin access** to create a Smart Bot (智能机器人) +2. **CodeWhale runtime API** running at `http://127.0.0.1:7878` +3. **Node.js 18+** for the bridge runtime + +### Create a WeCom Smart Bot + +1. Open the [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps) +2. Navigate: 应用管理 → 智能机器人 → 创建机器人 +3. Choose **API mode** (not Webhook mode) +4. Copy the **BotID** and **Secret** — you will need these + +## Quick Start + +Use two terminals. In the first terminal, start the local runtime API: + +```bash +export CODEWHALE_RUNTIME_TOKEN="$(openssl rand -hex 32)" +codewhale serve --http --host 127.0.0.1 --port 7878 --auth-token "$CODEWHALE_RUNTIME_TOKEN" +``` + +In the second terminal, start the bridge: + +```bash +cd integrations/wecom-bridge +cp .env.example .env +# Edit .env with your WeCom credentials and the same CODEWHALE_RUNTIME_TOKEN. +npm install +npm run start +``` + +## Configuration + +Copy the environment template and edit: + +```bash +cp .env.example .env +# Edit .env with your credentials +``` + +### Required variables + +| Variable | Example | Description | +|----------|---------|-------------| +| `WECOM_BOT_ID` | `wb-xxxxxxxxxxxxxxxx` | Smart Bot BotID from WeCom Admin | +| `WECOM_BOT_SECRET` | `your-secret` | Smart Bot Secret from WeCom Admin | +| `CODEWHALE_RUNTIME_TOKEN` | `rand-xxxxxxxx` | Bearer token for Runtime API (generate a random string) | + +### Optional variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `CODEWHALE_RUNTIME_URL` | `http://127.0.0.1:7878` | Runtime API address | +| `CODEWHALE_WORKSPACE` | `(cwd)` | Workspace directory | +| `CODEWHALE_MODEL` | `auto` | Default model name | +| `WECOM_CHAT_ALLOWLIST` | `""` | Comma-separated allowed UserIDs | +| `WECOM_ALLOW_UNLISTED` | `false` | Enable first-pairing mode | +| `WECOM_MAX_REPLY_CHARS` | `3500` | Max characters per reply message | +| `CODEWHALE_TURN_TIMEOUT_MS` | `900000` | Turn timeout in ms (15 min) | +| `CODEWHALE_APPROVAL_TIMEOUT_MS` | `300000` | Approval timeout in ms (5 min) | + +## First Pairing + +1. Leave `WECOM_ALLOW_UNLISTED=false` and start the bridge. +2. Send any message to the bot in WeCom. +3. The bot will refuse the unlisted chat and reply with `chat_id=...` and, + when available, `user_id=...`. +4. Add one of those values to `WECOM_CHAT_ALLOWLIST`. +5. Restart the bridge. + +## Verify Installation + +```bash +# Check syntax +npm run check + +# Run bridge tests +npm test +``` + +Expected output: `ℹ tests 16 ℹ pass 16 ℹ fail 0` + +## Architecture + +``` +WeCom Client → Smart Bot WebSocket → WeCom Bridge ──HTTP──→ codewhale serve --http + ◀── aibot_respond_msg ◀── (127.0.0.1:7878) +``` + +The bridge: +1. Authenticates via BotID + Secret to obtain an `access_token` +2. Establishes a WebSocket long connection to the WeCom Smart Bot API +3. Receives `aibot_msg_callback` events, processes them through the Runtime API +4. Replies via `aibot_respond_msg` commands + +## Security Boundaries + +- **No public port exposure**: `codewhale serve --http` binds to `127.0.0.1` only +- **Token authentication**: all `/v1/*` runtime calls require `CODEWHALE_RUNTIME_TOKEN` +- **Chat allowlist**: only chats/users in `WECOM_CHAT_ALLOWLIST` are served +- **Approval gate**: tool calls from WeCom require explicit approval (`/allow` or natural-language keywords) +- **WeCom only sees**: prompts, status summaries, thread listings, and approval requests — workspace contents, shell output, and runtime internals stay on your local machine + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---------|-------------|-----| +| "not paired" warning | `WECOM_CHAT_ALLOWLIST` is empty | Add your user_id or enable `WECOM_ALLOW_UNLISTED=true` | +| 404 on `/allow` | Approval ID expired (5 min) | Respond faster, or increase `CODEWHALE_APPROVAL_TIMEOUT_MS` | +| Bridge exits immediately | Missing env vars | Run `node src/index.mjs` directly to see validation errors | +| Messages not received | Secret or BotID wrong | Verify credentials in WeCom Admin Console | +| WebSocket disconnect | Network flakiness | Bridge auto-reconnects; check the bridge stdout/stderr logs for details | + +## Production Deployment + +### Long-running service + +Run the runtime API and bridge under the process manager you already use +(systemd, launchd, Task Scheduler, pm2, or a terminal multiplexer). The two +commands to supervise are: + +```bash +codewhale serve --http --host 127.0.0.1 --port 7878 --auth-token "$CODEWHALE_RUNTIME_TOKEN" +npm run start --prefix integrations/wecom-bridge +``` + +### Logging + +The bridge logs to stdout/stderr. Configure your service manager to capture +those streams; for example, systemd captures them in `journalctl`, and launchd +can redirect them with `StandardOutPath` / `StandardErrorPath`. + +### Auto-restart + +Enable restart/recovery in the same process manager. The bridge reconnects to +WeCom after transient WebSocket disconnects, but the supervisor should restart +the process after crashes or host reboots. + +## Related Documentation + +- [WeCom Bridge README](README.md) +- [CodeWhale Security Policy](../../SECURITY.md) +- [CodeWhale Contributing Guide](../../CONTRIBUTING.md)