Skip to content

claude-ops: fleet-state.sh's FLEET_STATE_HOOK_UTILS test override is not gated to test contexts #797

Description

@kyle-sexton

Problem

plugins/claude-ops/skills/plugins/scripts/fleet-state.sh lines ~62-69:

HOOK_UTILS="${FLEET_STATE_HOOK_UTILS:-${CLAUDE_PLUGIN_ROOT:-$PLUGIN_ROOT_DEFAULT}/hooks/hook-utils.sh}"
if [[ -f "$HOOK_UTILS" ]]; then
  source "$HOOK_UTILS"

FLEET_STATE_HOOK_UTILS is documented as "testing only" but is not restricted to test contexts at runtime — any process able to set this env var (a malicious project .claude/settings.json env block, a hook that exports it, an inherited shell environment) can point it at an arbitrary file and have it sourced with the script's ambient permissions. The [[ -f "$HOOK_UTILS" ]] guard only checks the path exists, not that it's the trusted default.

Observed vs expected

Observed: the override is unconditional — any caller-set env var wins over the trusted CLAUDE_PLUGIN_ROOT-derived default, in production as well as tests.
Expected: the override should only be honored in a recognized test context (e.g. gated behind an existing test-mode flag this script or its harness already uses), or the sourced path should be validated against an allowlist/expected location before sourcing.

Category

security (hardening — low severity, requires the attacker to already have process-level env-var control, at which point broader compromise paths likely already exist)

Confidence

PLAUSIBLE, per the reviewing pass — not confirmed exploitable in the current harness, flagged for awareness/hardening.

Lane / source

work lane, 2026-07-21. Surfaced by a security-review pass on PR #795 (fix/254-fleet-state-review-gaps) — explicitly called out as pre-existing (unchanged by that PR) and out of that PR's scope to fix. Filed standalone per the review-deferral exemption to the intake throttle (a citable issue number is needed for the deferral).

Metadata

Metadata

Assignees

Labels

area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.priority: mediumReal value, no hard deadline; normal backlog flow.status: readyTriaged, unblocked, and fully specified; eligible to pick up.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions