2026-08-18 18:33:49 -07:00
|
|
|
# Phase 4 — Surface C: terminal
|
|
|
|
|
|
|
|
|
|
~2 days. First surface accepting user input — injection (A03) is the primary risk here.
|
|
|
|
|
|
|
|
|
|
## RED
|
|
|
|
|
|
|
|
|
|
- [ ] `exec_parse_test.go` — fuzz corpus: every non-allowlisted input rejects and performs zero upstream calls
|
|
|
|
|
- [ ] `exec_test.go` — unknown namespace rejects on snapshot membership, not regex
|
2026-08-18 19:10:51 -07:00
|
|
|
- [ ] `terminal.e2e.ts` (Playwright, hard requirement) — load terminal UI, submit `get nodes`, assert rendered output; submit an injection payload, assert rejection surfaces in UI, zero upstream call
|
2026-08-18 18:33:49 -07:00
|
|
|
|
|
|
|
|
## GREEN
|
|
|
|
|
|
|
|
|
|
- [ ] `POST /api/exec` — session cookie, 20/min/session; input parses to a closed command enum, anything unmatched rejected before any lookup
|
|
|
|
|
- [ ] Command set: `get nodes`, `get pods <ns>`, `get apps`, `top nodes`, `describe pod <ns> <name>`, `help`
|
|
|
|
|
- [ ] Namespace and resource-name arguments validated by **set membership against current snapshot**, not regex/escaping
|
|
|
|
|
- [ ] No shell, no `exec`, no `kubectl` binary in the container image
|
|
|
|
|
- [ ] Frontend: reuse [components/InteractiveTerminal.tsx](../components/InteractiveTerminal.tsx), wire to `/api/exec`
|
|
|
|
|
- [ ] Structured logging: every rejected `/api/exec` input logged
|
|
|
|
|
|
|
|
|
|
## REFACTOR
|
|
|
|
|
|
|
|
|
|
- [ ] `simplify` pass on the enum parser
|
|
|
|
|
|
|
|
|
|
## Verify
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# attempt injection payloads against /api/exec; all rejected, all logged
|
|
|
|
|
curl -X POST https://riotpiao.com/api/exec -d '{"cmd":"get pods; rm -rf /"}'
|
2026-08-18 19:10:51 -07:00
|
|
|
pnpm test:e2e terminal.e2e.ts # required, must pass before merge
|
2026-08-18 18:33:49 -07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Next: [06-phase5-chat.md](06-phase5-chat.md)
|