Back to skill

Security audit

foreman

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly transparent about dispatching code to worker agents, but it needs review because it can run autonomous workers with repository access and a live API key without enforcing network containment.

Install only if you are comfortable delegating repository code to configured worker agents and exposing a dedicated, spend-limited DeepSeek key to the optional caged worker. Review backend configuration, use isolated worktrees, avoid global credential exports, consider network egress controls for the Docker worker, and require explicit approval before using confirmation-bypass modes.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
foreman-cworker.sh:75
Finding
Credential-Bearing Autonomous Worker Has Unrestricted Network Egress<![CDATA[ ## Vulnerability Details **File Location**: `foreman-cworker.sh:75-85` **Vulnerability Type**: Credential exposure through insufficient container egress isolation **Risk Level**: High ### Vulnerable Code ```bash exec docker ${ctx[@]+"${ctx[@]}"} run --rm \ --memory 4g --cpus 2 --pids-limit 512 \ --cap-drop=ALL --security-opt no-new-privileges \ --read-only --tmpfs /tmp:rw,size=512m --tmpfs /home/worker:rw,mode=1777,size=256m \ --mount "type=bind,source=$WT,target=/work" \ --mount "type=bind,source=$PROMPT,target=/prompt.md,readonly" \ -e ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \ -e ANTHROPIC_AUTH_TOKEN="$DEEPSEEK_API_KEY" \ -e ANTHROPIC_MODEL="$MODEL" \ "$IMAGE" \ -p "$(cat "$PROMPT")" --dangerously-skip-permissions --output-format text ``` ### Technical Analysis The container receives the live `DEEPSEEK_API_KEY` through the `ANTHROPIC_AUTH_TOKEN` environment variable. It also runs an autonomous agent with `--dangerously-skip-permissions`, has write access to the mounted task worktree, and retains Docker's default unrestricted outbound network access. The container hardening options—dropping capabilities, enabling `no-new-privileges`, imposing resource limits, and using a read-only root filesystem—reduce host compromise risk but do not constrain network destinations. Setting `ANTHROPIC_BASE_URL` controls the normal agent API endpoint; it is not an enforceable egress policy. Any code executed inside the container can read the credential from its environment and independently open a connection to another destination. Consequently, the statement in `foreman-cworker.sh:22-24` that the credential authenticates against “exactly one endpoint” is not technically enforced. ### Attack Path 1. An attacker introduces hostile repository content, prompt instructions, executable tooling, or a dependency lifecycle hook into a delegated worktree. 2. The caged worker is launched with permission confirmations disabled. 3. The wo ...[truncated 1141 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Place the worker behind an egress-filtering proxy or firewall that permits only the required DeepSeek API hostname and necessary DNS resolution. 2. Do not treat `ANTHROPIC_BASE_URL` as a security boundary; revise the documentation until endpoint restrictions are technically enforced. 3. Prefer an API broker outside the container so the worker never receives the raw provider credential. 4. If direct credential injection is unavoidable, use a dedicated, short-lived, narrowly scoped key with a strict spending limit and routine rotation. 5. Prevent unnecessary child processes from inheriting the credential. Where supported, pass credentials through a protected broker or descriptor rather than a general environment variable. 6. Disable package lifecycle scripts and arbitrary dependency installation during worker execution where practical. 7. Monitor the dedicated key for unexpected destinations, usage patterns, and spending, and revoke it automatically after each task. 8. Consider a default-deny network namespace or sandbox policy, with explicit opt-in for the provider endpoint. ]]>

T08 · Insecure Dependencies

Warning
Location
dispatch.md:38
Finding
Executable Third-Party Dependencies Are Not Cryptographically Pinned<![CDATA[ ## Vulnerability Details **File Locations**: - `dispatch.md:38` - `dispatch.md:77-84` - `fleet-check.sh:10-12` - `ci-gate.yml:10` **Vulnerability Type**: Insufficient software supply-chain integrity controls **Risk Level**: Medium ### Vulnerable Code `dispatch.md:38`: ```bash `handoff` is a public CLI (`uv tool install handoff-cli==4.0.2 && handoff init` — pin the version you reviewed; upgrade deliberately, not implicitly); configure your backends in `~/.handoff/config.yaml`. ``` `dispatch.md:77-84`: ```dockerfile docker build -t foreman-worker - <<'DOCKERFILE' FROM node:22.19-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90 RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates ripgrep \ && rm -rf /var/lib/apt/lists/* \ && npm i -g @anthropic-ai/claude-code@2.1.270 \ && useradd -m worker USER worker WORKDIR /work ENTRYPOINT ["claude"] DOCKERFILE ``` `fleet-check.sh:10-12`: ```bash command -v handoff >/dev/null 2>&1 && ok "handoff CLI" \ || bad "handoff not installed" "uv tool install handoff-cli==4.0.2 && handoff init" ``` `ci-gate.yml:10`: ```yaml - uses: actions/checkout@v4 ``` ### Technical Analysis The Skill uses exact versions for `handoff-cli` and `@anthropic-ai/claude-code`, which helps prevent accidental upgrades. However, the installation instructions do not verify package artifacts with cryptographic hashes or a reviewed lockfile. Exact version constraints alone do not verify that the downloaded artifact and its transitive dependencies match the artifacts reviewed by the Skill author. The Docker base image is correctly pinned by digest, but the packages installed through `apt-get` and npm are resolved at build time. The npm installation does not use a committed lockfile and `npm ci`, so transitive dependency resolution is not fully reproducible. The CI template uses the mutable `actions/checkout@v4` major-version tag rather than a full commit SHA. Although ...[truncated 2068 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Download and verify Python package distributions against reviewed SHA-256 hashes, or install them through a hash-locked dependency file. 2. Build the worker package from a committed, reviewed npm lockfile and use `npm ci` rather than a global registry resolution during the image build. 3. Record and verify package provenance or registry signatures where supported. 4. Pin `actions/checkout` and all other GitHub Actions to full reviewed commit SHAs. 5. Pin operating-system package snapshots or use a prebuilt worker image whose final digest is generated by a controlled build pipeline. 6. Generate and retain an SBOM for the worker image, including transitive npm and operating-system packages. 7. Scan the resulting image and dependencies for known vulnerabilities before publishing the accepted image digest. 8. Restrict package installation to trusted registries and ensure local package-manager configuration cannot silently redirect dependencies to an untrusted index. 9. Rebuild and update dependencies only through an explicit review process, then record the resulting immutable image digest. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Ae1

High
Category
analysis-evasion
Content
- **Probes backends before dispatch** (`backend-health.sh`, all probes listed in its header): `claude auth status` and `cursor-agent status` read local login st
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill explicitly handles sensitive capabilities: it reads local auth/config state, writes repository and state files, performs backend network probes, sends repository content to configured worker backends, and can launch a Dockerized worker with an API key. Despite this, the skill declares no explicit tool scope such as permissions or allowed-tools, so an execution framework or reviewer has no machine-readable guardrails for those capabilities. In this context that gap is more dangerous than usual because the skill is a dispatcher/orchestrator that intentionally crosses trust boundaries and handles credentials and code exfiltration paths by design.

External Transmission

Medium
Category
Data Exfiltration
Content
# Built-in probes, chosen by backend name, and what each one costs:
#   claude | opus | sonnet   `claude auth status` — local read, no request, no tokens
#   cursor                   `cursor-agent status` — local login check, no conversation request
#   deepseek                 GET https://api.deepseek.com/user/balance with $DEEPSEEK_API_KEY — no tokens.
#                            The key is sent only to that endpoint. If the variable isn't set, the result is `unchecked`.
#   codex                    one minimal `codex exec` call — this one spends a request, which is why its failures always cool down
# A backend with neither a built-in nor a FOREMAN_PROBE_<NAME> probe reports `unchecked`: pick still uses it, but says so.
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Built-in probes, chosen by backend name, and what each one costs:
#   claude | opus | sonnet   `claude auth status` — local read, no request, no tokens
#   cursor                   `cursor-agent status` — local login check, no conversation request
#   deepseek                 GET https://api.deepseek.com/user/balance with $DEEPSEEK_API_KEY — no tokens.
#                            The key is sent only to that endpoint. If the variable isn't set, the result is `unchecked`.
#   codex                    one minimal `codex exec` call — this one spends a request, which is why its failures always cool down
# A backend with neither a built-in nor a FOREMAN_PROBE_<NAME> probe reports `unchecked`: pick still uses it, but says so.
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Built-in probes, chosen by backend name, and what each one costs:
#   claude | opus | sonnet   `claude auth status` — local read, no request, no tokens
#   cursor                   `cursor-agent status` — local login check, no conversation request
#   deepseek                 GET https://api.deepseek.com/user/balance with $DEEPSEEK_API_KEY — no tokens.
#                            The key is sent only to that endpoint. If the variable isn't set, the result is `unchecked`.
#   codex                    one minimal `codex exec` call — this one spends a request, which is why its failures always cool down
# A backend with neither a built-in nor a FOREMAN_PROBE_<NAME> probe reports `unchecked`: pick still uses it, but says so.
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
BE=$(bash <skill-dir>/backend-health.sh pick) || { echo "no usable backend in the pool — stop and report"; exit 1; }
WT=$(bash <skill-dir>/worktree-setup.sh "$(pwd)" <slug>) || { echo "worktree setup failed — see the error above"; exit 1; }

handoff new --backend "$BE" --slug <slug> --write <<'__HF_EOF__'
[full work order]
__HF_EOF__
handoff run --backend "$BE" --cwd "$WT" ~/.handoff/tasks/<RUN_ID>.prompt.md   # run in background
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Cursor through handoff

No handoff code change is needed. handoff deep-merges per-backend fields over the type defaults (lists are replaced wholesale), and `command`, `session_flags`, `continue_id_flags` and `resume_flags` are all overridable — so a backend of `type: claude` with `command: cursor-agent` works. `cursor-agent` has no `--dangerously-skip-permissions` or `--append-system-prompt`: use `--force` to skip confirmations, and put the system prompt into the prompt body itself. Unverified: whether the model weighs an inlined system prompt the same as a separate system layer — only that it is sent and followed.

### Caged dispatch (optional; for sensitive repos or when you want a hard blast radius)
Confidence
92% confidence
Finding
This section instructs operators to bypass confirmations for `cursor-agent`, enabling the worker to act autonomously without approval prompts. Because the overall skill is a dispatch system for background agents performing coding tasks, the autonomy is not incidental: it directly increases the risk of unauthorized edits, destructive commands, or policy-violating actions if prompts or task inputs are malformed or adversarial.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly recommends using `cursor-agent --force` to skip confirmations, which removes an interactive safety barrier for an autonomous coding agent. In this skill's context, that is materially risky because the worker is given repository content and can modify code in a background run, so disabling confirmations increases the chance of unintended or unsafe actions without contemporaneous user review.

Session Persistence

Medium
Category
Rogue Agent
Content
- Task state at `.foreman/<task_id>.json`:
  `{task_id, attempt_id, handoff_run_id, backend, worktree, branch, dispatched_at, deadline_at, rework_count, repair_rounds, error_fingerprint, backend_fallback, status}`
  `handoff_run_id` is the basename of the `.prompt.md` path that `handoff new` prints (that command pre-allocates the run and echoes the path, not a bare id). Write it at dispatch time and update it with `attempt_id` on every redispatch — the batch → task → delivery audit chain closes through that field.
  `worktree` / `branch` are what `worktree-setup.sh` returned and created; intake runs against those two fields instead of guessing paths.
- Batch state at `.foreman/batch.json`: `{batch_id, members[], integration_sha: null, total_rounds: 0}`
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

External Transmission

Medium
Category
Data Exfiltration
Content
ok "no ANTHROPIC_API_KEY in the shell"
fi
case "${ANTHROPIC_BASE_URL:-}" in
  ""|https://api.anthropic.com|https://api.anthropic.com/) ok "no ANTHROPIC_BASE_URL leak" ;;
  *) bad "ANTHROPIC_BASE_URL is set to a non-default value (not printed here — it can carry a token)" \
        "remove it from your shell rc — it silently reroutes every agent session on this machine to another backend" ;;
esac
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
#!/bin/bash
# foreman dispatch helper: create an isolated git worktree for one task and print its absolute path on stdout.
#
# Why a script instead of "remember to run git worktree add": for a long time "one git worktree per task" was an
# invariant written in prose, while the copy-paste dispatch example never showed how to create one or pass --cwd to
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
docker build -t foreman-worker - <<'DOCKERFILE'
FROM node:22.19-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates ripgrep \
    && rm -rf /var/lib/apt/lists/* \
    && npm i -g @anthropic-ai/claude-code@2.1.270 \
    && useradd -m worker
USER worker
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
docker build -t foreman-worker - <<'DOCKERFILE'
FROM node:22.19-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates ripgrep \
    && rm -rf /var/lib/apt/lists/* \
    && npm i -g @anthropic-ai/claude-code@2.1.270 \
    && useradd -m worker
USER worker
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
docker build -t foreman-worker - <<'DOCKERFILE'
FROM node:22.19-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates ripgrep \
    && rm -rf /var/lib/apt/lists/* \
    && npm i -g @anthropic-ai/claude-code@2.1.270 \
    && useradd -m worker
USER worker
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Chaining Abuse

Low
Category
Tool Misuse
Content
docker build -t foreman-worker - <<'DOCKERFILE'
FROM node:22.19-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates ripgrep \
    && rm -rf /var/lib/apt/lists/* \
    && npm i -g @anthropic-ai/claude-code@2.1.270 \
    && useradd -m worker
USER worker
Confidence
15% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Missing User Warnings

Low
Confidence
78% confidence
Finding
This markdown file instructs the operator to copy `ci-gate.yml` into `.github/workflows/` and add `.foreman/` to `.gitignore`, which are write operations affecting repository behavior. The surrounding text does not explicitly warn that this step modifies tracked project files and CI execution.

Static analysis

No suspicious patterns detected.