bytelyst-devops-tools/dashboard/backend/vitest.config.ts
Hermes VM 74a8ee0993 feat(dashboard): close 3 of 5 Phase 5 P2 mitigation items (allow-list, projectPath, audit-log)
Closes the three Phase 5 P2 follow-ups from the DEPLOYMENT.md
mitigation roadmap that don't need infra changes. Two P2 items remain
(non-root container, docker-proxy daemon) — both genuinely need
container/orchestration work and stay queued.

1. Allow-list shell wrapper (P1)
   New `lib/shell.ts`:
     - `execAllowed(cmd, args, opts)` — `execFile`-only, no shell, no
       interpolation. Single escape hatch for ad-hoc invocations.
     - `dockerRestart(name)` — name validated against
       `[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}`; throws InvalidShellArgError
       on anything else (including non-strings, shell metacharacters,
       command-substitution attempts). Tests cover all of these.
     - `dockerPrune(kind, {all?})` — kind constrained to
       {container,image,volume,builder}; `--all` only valid for image.
     - `runBashScript(path, args, {allowedRoots})` — script path AND
       cwd both checked against allowed roots; rejects `..` escapes
       and prefix-matching siblings (`/opt/projects-evil` vs
       `/opt/projects`).
     - `runNpmScript(script, {cwd, allowedRoots})` — script ∈
       {typecheck,lint,build,test,test:run,start}; cwd inside roots.
   17 unit tests cover every rejection path. Module added to the
   coverage gate (≥95% lines).

   Migrated highest-risk callers off template-literal `exec`:
     - `vm/repository.ts:restartContainer` → `dockerRestart`. Was
       previously `await execAsync(\`docker restart "${name}"\`)`
       with only a regex check; now goes through the wrapper.
     - `system/repository.ts:dockerCleanup` → `dockerPrune` per kind
       + `execAllowed` for `docker system df`. Drops the array of
       template-literal command strings entirely.
     - `code-quality/repository.ts` → `runNpmScript` for every
       lifecycle invocation. cwd is now the resolved (normalised,
       `..`-collapsed) path, not the raw input.

2. projectPath validation for /code-quality/check (P1)
   `runCodeQualityCheck` now calls
   `assertPathInAllowedRoots(projectPath, getAllowedRoots())` before
   any subprocess spawns. `getAllowedRoots()` reads
   `CODE_QUALITY_ALLOWED_ROOTS` (colon-separated env, defaults to
   `/opt/bytelyst`). Rejection happens with a clear error message
   listing the configured roots so operators know what to allow.

3. Audit-log every privileged shell-out (P2)
   `audit/types.ts` extended: `action` now includes `'shell-exec'`,
   `entityType` includes `'host'`. The migration is additive — old
   audit rows still validate.

   Three privileged routes now write a `shell-exec` audit row with
   actor (authUserId / authRole), entity id, and a sanitized details
   payload before responding:
     - `POST /docker/cleanup` — `entityId: docker-cleanup:<type>`,
       details include {type, force, freedSpace}.
     - `POST /vm/cleanup` — `entityId: vm-cleanup:<mode>`.
     - `POST /vm/containers/:name/restart` — `entityId:
       container-restart:<name>`, details include {success, message}.
       Audited even on failure so attempted privileged actions are
       still recorded.
   Audit writes are best-effort — a Cosmos hiccup logs a warn but
   never fails the request the operator was running.

Verified: backend typecheck , 74/74 unit tests  (17 new for
shell.ts + audit changes), 7/7 E2E , lint 0 errors, coverage gate
≥95% lines on every gated file (which now includes shell.ts).

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-30 08:18:50 +00:00

37 lines
1.2 KiB
TypeScript

import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
passWithNoTests: true,
coverage: {
provider: 'v8',
reporter: ['text', 'text-summary'],
// Only the modules we've explicitly committed to keeping covered are
// subject to the threshold; the rest of the codebase is reported but
// not gated. Add files here as they gain a real test suite (ratchet,
// do not relax).
include: [
'src/lib/auth.ts',
'src/lib/csrf.ts',
'src/lib/shell.ts',
'src/modules/health/repository.ts',
'src/modules/hermes-ops/repository.ts',
'src/modules/hermes-telemetry/repository.ts',
'src/modules/deployments/orchestrator.ts',
'src/modules/services/repository.ts',
],
thresholds: {
// Module-wide minimums — actual numbers today are well above these
// (≥95% lines for every gated file). Floor is set conservatively so
// routine refactors don't trip the gate.
lines: 85,
functions: 85,
statements: 85,
branches: 65,
},
},
},
});