|
1 | 1 | # ChannelGate — Features |
2 | 2 |
|
| 3 | +## Web and Slack surface hardening (security audit batch 1) |
| 4 | + |
| 5 | +- **Admin mode is admin-only on every surface.** The Slack Settings mode control refuses to switch |
| 6 | + a channel into Admin mode unless the clicker is a current organization admin, the same rule |
| 7 | + `/mode admin` and the `set_channel_admin_mode` tool already enforce. A channel manager keeps |
| 8 | + every other control in a channel that is already in Admin mode (changing Auto, Lean or the |
| 9 | + network switch leaves the mode alone) and may step the channel down to Worker or Read-only; the |
| 10 | + "Admin" option is listed only for someone who may enable it or when it is the current mode. |
| 11 | +- **Case-variant URLs never reach the admin router.** Express case-sensitive routing is on, and the |
| 12 | + Host/Origin guard, the no-password lockdown, the 401 branch and the run-API key gate all compare |
| 13 | + lowercased paths, so `/API/settings` is refused exactly like `/api/settings` instead of slipping |
| 14 | + past them into the router. |
| 15 | +- **Security headers.** Every response carries `X-Content-Type-Options: nosniff`, |
| 16 | + `X-Frame-Options: DENY` and `Referrer-Policy: same-origin`. The admin shell and the login page are |
| 17 | + served with a Content-Security-Policy that admits same-origin scripts only plus the import map |
| 18 | + by content hash (no inline script anywhere; the login handler lives in `public/login.js`), |
| 19 | + same-origin styles with inline attributes, same-origin fonts, images and connections, |
| 20 | + `frame-ancestors 'none'`, `object-src 'none'`. |
| 21 | +- **No stack traces.** One Express error handler answers unexpected failures with a bare 500 (logged |
| 22 | + server-side) and keeps exposable client errors such as a malformed JSON body as their 4xx with |
| 23 | + the message only; the systemd unit also sets `NODE_ENV=production`. `/resume` requires the admin |
| 24 | + session; the `/internal/*` IPC secret is compared in constant time. |
| 25 | +- **Background-job status button.** "Check status" on a background job card runs through the same |
| 26 | + authorization gate as `/status` (admin, approved, or an explicit guest of that channel) and only |
| 27 | + reports a job that belongs to that channel; the log-tail fallback reads the channel's own log. |
| 28 | +- **File explorer exports re-prove the file.** Share, Send to me, Preview and Edit open the resolved |
| 29 | + file `O_NOFOLLOW` and re-resolve the kernel descriptor through `/proc/self/fd`, refusing a file |
| 30 | + that no longer sits under the channel root, which closes the symlink-swap race the agent-side |
| 31 | + export tools already closed. |
| 32 | +- **License card.** The organization, tier and limit values from the licensing platform are |
| 33 | + HTML-escaped before rendering. |
| 34 | +- **Dependencies.** `npm audit fix` applied: undici, zod, hono, fast-uri, body-parser and |
| 35 | + ip-address moved to their patched releases; `npm audit --omit=dev` reports no vulnerabilities. |
| 36 | + |
3 | 37 | ## Admin access to Slack controls |
4 | 38 |
|
5 | 39 | - Current organization admins can open Files, Variables, Settings and legacy menu Resume controls |
|
0 commit comments