Skip to content

Hands declare shell_exec for a Phase 0 command the core unconditionally blocks — grants unrestricted host shell for no working feature #103

Description

@nevgenov

Symptom

15 of the 17 bundled hands declare shell_exec in their tools list. For a large subset of them, the only shell usage documented anywhere in the system prompt is a Phase 0 platform-detection one-liner:

## Phase 0 — Platform Detection & Context (ALWAYS DO THIS FIRST)

Detect the operating system:

python -c "import platform; print(platform.system())"

That command can never succeed. crates/librefang-runtime/src/dangerous_command.rs:123-126 in the core blocks it unconditionally:

dp!(
    "script execution via -e/-c flag",
    r"\b(python[23]?|perl|ruby|node)\s+-[ec]\s+"
),

python -c "import platform; ..." matches that regex. We observed it firing live in production — the agent dutifully ran the Phase 0 instruction on each activation and got shell_exec blocked: dangerous command detected (script execution via -e/-c flag) every time, then improvised alternatives.

Scope

Hands declaring shell_exec: analytics, apitester, clip, collector, devops, devteam, lead, linkedin, predictor, reddit, researcher, strategist, trader, twitter, wiki (15/17 — only browser and creator do not).

Hands whose prompt contains the blocked python -c line: analytics, apitester, clip, collector, devops, lead, linkedin, predictor, reddit, researcher, strategist, trader, twitter (13).

Of those, 9 carry the identical "Platform Detection" section verbatim (collector, lead, predictor, linkedin, clip, researcher, trader, reddit, twitter) — it reads as a copy-pasted prompt template rather than a per-hand requirement.

Why this matters beyond a dead instruction

Declaring shell_exec is not free. In the core, crates/librefang-kernel/src/kernel/hands_lifecycle.rs:357-365 keys off exactly this list:

if manifest.exec_policy.is_none() && def.tools.iter().any(|t| t == "shell_exec") {
    manifest.exec_policy = Some(librefang_types::config::ExecPolicy {
        mode: librefang_types::config::ExecSecurityMode::Full,
        ...

So a hand that names shell_exec in tools and does not declare an explicit [exec_policy] is granted ExecSecurityMode::Full — no allowlist filtering, and (per tool_runner/dispatch.rs:1708-1712) no approval prompt either — regardless of the operator's global exec_policy.mode. I filed that core behaviour separately as librefang/librefang#6594; none of the bundled hands declare [exec_policy], so every one of the 15 inherits it.

The result for a research/social/analytics hand is unrestricted host shell as a side effect of a one-line OS check that cannot run.

Suggested change

  1. Drop the Phase 0 platform-detection block from the prompts where it appears. It never executes, and the hands using it (research, social posting, analytics) do not branch on OS anywhere else in their prompt.
  2. Remove shell_exec from tools for hands that have no other shell usage. For the ones that genuinely need it (devops, devteam, apitester plausibly do), keep it.
  3. Add an explicit [exec_policy] to any hand that keeps shell_exec, so the grant is intentional and visible rather than inherited from the core default:
    [exec_policy]
    mode = "allowlist"   # or "deny"
    The core only injects Full when the field is absent; an explicit value is respected. This also makes the hand's shell posture self-documenting for operators reviewing it before activation.
  4. If platform detection is actually wanted, uname -s is not caught by the guard — but it is worth asking whether these hands need it at all.

Environment

  • Registry at ff69767 (2026-07-16)
  • Core v2026.7.21; the guard and lifecycle code cited above are byte-identical at v2026.7.27 (latest tag)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions