Skip to content

fix(host-agent): use new hooks schema + sandboxed env for install hooks#905

Merged
Lightheartdevs merged 1 commit intoLight-Heart-Labs:mainfrom
yasinBursali:fix/run-install-hook-resolver-env
Apr 18, 2026
Merged

fix(host-agent): use new hooks schema + sandboxed env for install hooks#905
Lightheartdevs merged 1 commit intoLight-Heart-Labs:mainfrom
yasinBursali:fix/run-install-hook-resolver-env

Conversation

@yasinBursali
Copy link
Copy Markdown
Contributor

@yasinBursali yasinBursali commented Apr 11, 2026

Merge order: Merge after #906, before #900 and #908 — all modify dream-host-agent.py.

What

_handle_install's inner _run_install worker now uses the new hooks.post_install manifest schema and passes a minimal allowlist environment to the subprocess, matching the pattern already used by _execute_hook elsewhere in the host agent.

Why

Two bugs, one function:

  1. Legacy resolver_run_install was the last call site still using _resolve_setup_hook(), which only reads the old service.setup_hook manifest field. Any extension declaring its setup via hooks.post_install was silently skipped during install.
  2. Secret leak via subprocess env inheritancesubprocess.run(...) had no env= kwarg, so the extension's setup.sh inherited the full host-agent environment including AGENT_API_KEY, DREAM_AGENT_KEY, DASHBOARD_API_KEY. A malicious extension could exfiltrate host-agent credentials during install.

How

  • Replace _resolve_setup_hook(ext_dir) with _resolve_hook(ext_dir, \"post_install\") (the schema-aware resolver that _handle_setup_hook and _handle_hook already use).
  • Build the same minimal hook_env dict _execute_hook uses (8 keys: PATH, HOME, SERVICE_ID, SERVICE_PORT, SERVICE_DATA_DIR, DREAM_VERSION, GPU_BACKEND, HOOK_NAME). Pass via env=hook_env.
  • Delete _resolve_setup_hook (now unreferenced repo-wide).
  • Add TestInstallHookEnvAllowlist — 4 source-level assertions that fail loudly if anyone reverts the kwarg, re-adds a secret, or removes a required allowlist key.

Testing

  • `pytest dashboard-api/tests/test_host_agent.py` → 26 passed (22 pre-existing + 4 new)
  • `python3 -m py_compile dream-server/bin/dream-host-agent.py` → clean
  • Repo-wide grep for `_resolve_setup_hook` → empty (dead code removed)
  • Manual test needed: install an extension whose `manifest.yaml` declares `hooks.post_install: setup.sh` where setup.sh writes `env > /tmp/leak.txt`. Verify leak.txt does NOT contain any of `AGENT_API_KEY`, `DREAM_AGENT_KEY`, `DASHBOARD_API_KEY`.

Platform Impact

  • macOS: affected — extensions using `hooks.post_install` now run correctly, host-agent secrets no longer leak to hook scripts.
  • Linux: affected — same.
  • Windows/WSL2: affected — same.

Review notes

  • No orphaned imports.
  • No DB or config-file migrations.
  • Independent PR — can merge before or after any other C-series or B-series fix.

_handle_install's inner _run_install worker used the legacy
_resolve_setup_hook() which only read the old service.setup_hook field,
silently skipping any extension that defines its setup via the new
hooks.post_install schema. It also invoked subprocess.run without an
env= kwarg, inheriting the full host-agent environment (AGENT_API_KEY,
DREAM_AGENT_KEY, DASHBOARD_API_KEY and other secrets) into extension
setup scripts — a credential exfiltration vector.

Switch to _resolve_hook(ext_dir, "post_install") to honour both schemas,
and build the same minimal env allowlist that _execute_hook already
uses. Delete the now-unreferenced _resolve_setup_hook function and add a
source-level regression test that fails if the kwarg, allowlist, or
resolver call is ever reverted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@Lightheartdevs Lightheartdevs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong security fix — this closes two real bugs that were masking each other.

Bug 1 (legacy hook resolver): _run_install was the only call site still using _resolve_setup_hook() (reads old service.setup_hook manifest field). Any extension using the new hooks.post_install schema was silently skipped during install. Switching to _resolve_hook(ext_dir, 'post_install') matches the pattern already used by _handle_setup_hook and _handle_hook — consistent with the rest of the agent.

Bug 2 (subprocess env inheritance): more serious — subprocess.run(...) without env= let extension setup scripts inherit AGENT_API_KEY, DREAM_AGENT_KEY, DASHBOARD_API_KEY. A malicious or compromised extension could exfiltrate host-agent credentials during install. The 8-key allowlist (hook_env) is the right shape — matches _execute_hook exactly.

Test coverage: TestInstallHookEnvAllowlist as source-level assertions is clever — will fail loudly if anyone reverts the env= kwarg or re-adds a secret key. This is the right way to lock in a security invariant.

Dead code removal: _resolve_setup_hook now unreferenced repo-wide per author's grep — good.

Author's merge-order note: after #906, before #900 and #908. That matches what I've recommended elsewhere. Ship.

Copy link
Copy Markdown
Collaborator

@Lightheartdevs Lightheartdevs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the current diff and live checks again. This still cleanly delivers the new hooks schema + sandboxed env handoff without widening scope. Approved.

@Lightheartdevs Lightheartdevs merged commit bb7e37f into Light-Heart-Labs:main Apr 18, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants