feat(engine): Phase 0.5 — engine-enforced capabilities + deploy_dir path validation #11

Merged
kavi merged 2 commits from feature/phase05-engine-caps into main 2026-07-15 04:57:03 +00:00
Owner

Closes the SOL-audit CRITICAL: the machine credential is full engine admin, so a compromised MCP can mutate the registry (≈ RCE by next deploy), unlock, or PATCH a shell-bearing deploy_dir. This adds the durable boundary the ADR 0008 threat model requires.

Additive & backward-compatible — the admin token keeps FULL access (capScope stays undefined); nothing changes until a scoped token is issued and used. Safe to merge now; behavior flips only when the rollout below runs.

What

  • Scoped tokens KUA_DEPLOY_SCOPED_TOKENS (mirrors kua-vault's KUA_VAULT_SCOPED_TOKENS): [{name, token(32+), caps:[read|deploy|rollback|registry:write|admin]}], digests stored not raw, matched in onRequestrequest.capScope.
  • requireCap() on every mutating endpoint: registry PUT/PATCH/DELETE + /registry/reloadregistry:write; /release /deploy /rebuilddeploy; /rollbackrollback; /unlock + /progress/resetadmin; /runtime-status → admin token or scoped read.
  • isSafeDeployPath(): repo_dir/deploy_dir must match ^/root/apps/[A-Za-z0-9._/-]+$ with no .. (they're interpolated into cd ${deployDir}). All 24 live registry entries already pass (verified).

Verification

  • node --check clean; capability matrix + path-injection cases unit-tested (all pass): a [deploy,rollback,read] token can deploy/rollback/read, cannot registry-write/unlock/reset; paths reject ; rm, $(...), .., and outside-/root/apps.

Rollout (follow-up, canaried — not in this PR)

  1. Issue a scoped {caps:[deploy,rollback,read]} token into kua-vault + set KUA_DEPLOY_SCOPED_TOKENS on the engine.
  2. Switch kua-mcp-core to that token instead of the admin bearer (getDeployAdminToken).
  3. Deploy engine (canary) + verify: scoped token deploys OK, registry PUT from the scoped token → 403 (the ADR Phase 2 acceptance test). Then the MCP no longer holds full admin → the CRITICAL is closed.

🤖 Generated with Claude Code

Closes the SOL-audit **CRITICAL**: the machine credential is full engine admin, so a compromised MCP can mutate the registry (≈ RCE by next deploy), unlock, or PATCH a shell-bearing `deploy_dir`. This adds the durable boundary the ADR 0008 threat model requires. **Additive & backward-compatible** — the admin token keeps FULL access (`capScope` stays undefined); nothing changes until a scoped token is issued *and used*. Safe to merge now; behavior flips only when the rollout below runs. ## What - **Scoped tokens** `KUA_DEPLOY_SCOPED_TOKENS` (mirrors kua-vault's `KUA_VAULT_SCOPED_TOKENS`): `[{name, token(32+), caps:[read|deploy|rollback|registry:write|admin]}]`, digests stored not raw, matched in `onRequest` → `request.capScope`. - **`requireCap()`** on every mutating endpoint: registry `PUT/PATCH/DELETE` + `/registry/reload` → `registry:write`; `/release /deploy /rebuild` → `deploy`; `/rollback` → `rollback`; `/unlock` + `/progress/reset` → `admin`; `/runtime-status` → admin token **or** scoped `read`. - **`isSafeDeployPath()`**: `repo_dir`/`deploy_dir` must match `^/root/apps/[A-Za-z0-9._/-]+$` with no `..` (they're interpolated into `cd ${deployDir}`). **All 24 live registry entries already pass** (verified). ## Verification - `node --check` clean; capability matrix + path-injection cases unit-tested (all pass): a `[deploy,rollback,read]` token **can** deploy/rollback/read, **cannot** registry-write/unlock/reset; paths reject `; rm`, `$(...)`, `..`, and outside-`/root/apps`. ## Rollout (follow-up, canaried — not in this PR) 1. Issue a scoped `{caps:[deploy,rollback,read]}` token into kua-vault + set `KUA_DEPLOY_SCOPED_TOKENS` on the engine. 2. Switch kua-mcp-core to that token instead of the admin bearer (`getDeployAdminToken`). 3. Deploy engine (canary) + verify: scoped token deploys OK, **registry PUT from the scoped token → 403** (the ADR Phase 2 acceptance test). Then the MCP no longer holds full admin → the CRITICAL is closed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes the SOL-audit CRITICAL: the machine credential was full engine admin, so a
compromised MCP could mutate the registry (≈ RCE by next deploy), unlock, or PATCH
a shell-bearing deploy_dir. This adds a scoped-token capability layer + path
validation. ADDITIVE and backward-compatible: the admin token keeps FULL access
(request.capScope stays undefined), so nothing changes until a scoped token is
issued and a caller uses it.

- Scoped tokens (KUA_DEPLOY_SCOPED_TOKENS, mirrors kua-vault's KUA_VAULT_SCOPED_TOKENS):
  JSON array of {name, token(32+), caps:[read|deploy|rollback|registry:write|admin]}.
  Digests stored, never raw. Matched in onRequest -> request.capScope.
- requireCap() gates every mutating endpoint:
  * registry PUT/PATCH/DELETE + /registry/reload -> registry:write
  * /release /deploy /rebuild -> deploy ; /rollback -> rollback ; PATCH /progress -> deploy
  * /unlock + /progress/reset -> admin
  * /runtime-status -> admin token OR scoped 'read' (keeps its hard-bearer intent)
- isSafeDeployPath(): repo_dir/deploy_dir must match ^/root/apps/[A-Za-z0-9._/-]+$
  and contain no "..". All 24 live registry entries already pass (verified).

Rollout (follow-up, canaried): (1) issue a scoped {caps:[deploy,rollback,read]}
token into kua-vault + set KUA_DEPLOY_SCOPED_TOKENS on the engine; (2) switch
kua-mcp-core to that token instead of the admin bearer; (3) deploy engine + verify
a scoped token deploys OK but registry PUT returns 403 (the ADR Phase 2 test). Then
the MCP no longer holds full admin.

Logic unit-tested (capability matrix + path-injection cases all pass); node --check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kavi merged commit 57bf881b23 into main 2026-07-15 04:57:03 +00:00
kavi deleted branch feature/phase05-engine-caps 2026-07-15 04:57:03 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kavi/kua-deploy!11
No description provided.