Back to skill

Security audit

Nonblocking Agent Execution Enhanced

Security checks for vulnerabilities and agentic risk

Overview

This skill openly provides a background command runner, but its examples and implementation expose risky command execution, persistence, callbacks, and weak input controls that need review before use.

Install only in a trusted, local, single-user environment. Do not expose the REST wrapper as written, do not pass untrusted job IDs or commands, avoid callbacks for sensitive output, set restrictive permissions on ~/.nonblocking, clean up jobs regularly, and pin/verify the installed artifact before making scripts executable.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (6)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/jobctl.sh:100
Finding
Unvalidated Job Identifiers Enable Path Traversal and Unauthorized Process Control<![CDATA[ ## Vulnerability Details **File Location**: `scripts/jobctl.sh:100-117`, `scripts/jobctl.sh:533-550`, `scripts/jobctl.sh:645-656`, `scripts/jobctl.sh:873-890` **Vulnerability Type**: Path traversal, arbitrary file operations, and unsafe PID-file trust **Risk Level**: High ### Vulnerable Code ```bash get_state_file() { echo "${STATE_DIR}/${1}.json" } get_pid_file() { echo "${RUN_DIR}/${1}.pid" } get_log_file() { echo "${LOG_DIR}/${1}.log" } get_output_file() { echo "${STATE_DIR}/${1}.output" } get_feedback_file() { echo "${FEEDBACK_DIR}/${1}.feedback" } ``` The only validation performed by `cmd_start` is an empty-value check: ```bash # Validate job_id if [[ -z "$job_id" ]]; then log "ERROR" "Job ID is required" echo "Error: Job ID is required" >&2 exit 1 fi ``` The resulting paths are subsequently trusted for process management and file deletion: ```bash local pid_file=$(get_pid_file "$job_id") if [[ ! -f "$pid_file" ]]; then log "ERROR" "Job $job_id not found or already stopped" echo "Error: Job $job_id not found" >&2 exit 1 fi local pid=$(cat "$pid_file") if is_process_running "$pid"; then log "INFO" "Stopping job $job_id (PID: $pid)" kill -TERM "$pid" 2>/dev/null || true ``` ```bash rm -f "$(get_pid_file "$job_id")" \ "$(get_state_file "$job_id")" \ "$(get_log_file "$job_id")" \ "$(get_output_file "$job_id")" \ "$(get_feedback_file "$job_id")" \ "$RUN_DIR/${job_id}.wrapper.sh" ``` ### Technical Analysis The `job_id` value is directly concatenated into paths without restricting path separators, `..` components, control characters, absolute paths, or symlink behavior. Shell quoting prevents ordinary shell word splitting, but it does not prevent filesystem path traversal. Consequently, a value containing traversal components can cause state, PID, log, output, feedback, or wrapper operations to resolve outside their intended directories. The `stop` ope ...[truncated 1374 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate `job_id` centrally before any path construction. For example, require `^[A-Za-z0-9_-]{1,64}$`. - Reject path separators, `..`, absolute paths, newlines, and control characters. - Canonicalize each generated path and verify that it remains beneath its designated directory. - Reject symlinks and use secure, exclusive file creation where possible. - Store process ownership metadata and verify the actual process identity before sending signals. - Consider checking `/proc/<pid>` start time or a random per-job token to prevent stale or substituted PID-file attacks. - Add tests covering traversal, absolute paths, symlinks, malformed identifiers, and forged PID files. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
docs/INTEGRATION.md:463
Finding
Documented REST Wrapper Exposes Unauthenticated Remote Command Execution<![CDATA[ ## Vulnerability Details **File Location**: `docs/INTEGRATION.md:463-529` **Vulnerability Type**: Missing authentication and authorization on a remote command-execution interface **Risk Level**: Critical ### Vulnerable Code ```python @app.route('/jobs', methods=['POST']) def create_job(): data = request.json job_id = data.get('job_id') command = data.get('command') callback_url = data.get('callback_url') model = data.get('model') max_tokens = data.get('max_tokens') if not job_id or not command: return jsonify({'error': 'job_id and command are required'}), 400 cmd = [JOBCTL_PATH, 'start', job_id, command] if callback_url: cmd.append(callback_url) if model: cmd.append(model) if max_tokens: cmd.append(str(max_tokens)) try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) return jsonify(json.loads(result.stdout)), 202 except subprocess.CalledProcessError as e: return jsonify({'error': e.stderr}), 500 ``` The server is exposed on every network interface with debug mode enabled: ```python if __name__ == '__main__': app.run(host='0.0.0.0', port=8080, debug=True) ``` ### Technical Analysis The wrapper accepts an arbitrary `command` from an HTTP request and forwards it to `jobctl.sh start`. The underlying controller intentionally executes the command through a shell. No authentication, authorization, command allowlist, sandbox boundary, rate limit, or origin restriction is implemented. Using an argument array for `subprocess.run` prevents injection into the wrapper's immediate command line, but it does not make the supplied job command safe: `jobctl.sh` later executes that value as shell code. Binding to `0.0.0.0` makes the endpoint reachable from any network that can connect to port 8080. Enabling Flask debug mode is additionally inappropriate for a remotely reachable service. ### Attack Path 1. A user ...[truncated 1058 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not expose arbitrary command strings through an HTTP API. - Replace free-form commands with predefined, allowlisted job types and structured parameters. - Require strong authentication and per-operation authorization. - Bind to `127.0.0.1` by default and place any remote interface behind a properly configured authenticated gateway. - Disable Flask debug mode in all non-local examples. - Run the service as a dedicated unprivileged account inside a restricted container or VM. - Apply filesystem, network, CPU, memory, process-count, and execution-time restrictions. - Add request-size limits, rate limiting, audit logs, and anti-CSRF controls where browser clients are supported. - Explicitly warn that exposing the controller to untrusted clients creates a remote code-execution service. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
docs/INTEGRATION.md:246
Finding
Node.js Integration Performs Shell Injection Through String-Constructed Commands<![CDATA[ ## Vulnerability Details **File Location**: `docs/INTEGRATION.md:246-307` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```javascript const { execSync, exec } = require('child_process'); const path = require('path'); class NonBlockingExecutor { constructor(jobctlPath = path.join(__dirname, 'scripts', 'jobctl.sh')) { this.jobctlPath = jobctlPath; } startJob(jobId, command, callbackUrl = null, model = null, maxTokens = null) { let cmd = `${this.jobctlPath} start ${jobId} '${command}'`; if (callbackUrl) cmd += ` ${callbackUrl}`; if (model) cmd += ` ${model}`; if (maxTokens) cmd += ` ${maxTokens}`; try { const output = execSync(cmd, { encoding: 'utf8' }); return JSON.parse(output); } catch (error) { throw new Error(`Failed to start job: ${error.stderr}`); } } getStatus(jobId) { try { const output = execSync( `${this.jobctlPath} status ${jobId}`, { encoding: 'utf8' } ); return JSON.parse(output); } catch (error) { throw new Error(`Failed to get status: ${error.stderr}`); } } ``` The same unsafe interpolation is used by the process-control methods: ```javascript stopJob(jobId) { try { const output = execSync( `${this.jobctlPath} stop ${jobId}`, { encoding: 'utf8' } ); return JSON.parse(output); } catch (error) { throw new Error(`Failed to stop job: ${error.stderr}`); } } cleanupJob(jobId) { try { const output = execSync( `${this.jobctlPath} cleanup ${jobId}`, { encoding: 'utf8' } ); return JSON.parse(output); } catch (error) { throw new Error(`Failed to cleanup job: ${error.stderr}`); } } ``` ### Technical Analysis `execSync` executes ...[truncated 1351 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `execSync(commandString)` with `execFileSync` or `spawn` using an explicit argument array and `shell: false`. - Use an implementation such as: ```javascript const { execFileSync } = require('child_process'); const args = ['start', jobId, command]; if (callbackUrl) args.push(callbackUrl); if (model) args.push(model); if (maxTokens !== null) args.push(String(maxTokens)); const output = execFileSync(this.jobctlPath, args, { encoding: 'utf8', shell: false }); ``` - Apply strict job-ID validation even when argument arrays are used, because the underlying script uses the identifier in paths. - Validate callback URLs and numeric token values by type and range. - Add integration tests containing quotes, semicolons, substitutions, newlines, spaces, and option-like values. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/jobctl.sh:560
Finding
Caller-Controlled Values Are Unsafely Embedded in Generated Python and Shell Source<![CDATA[ ## Vulnerability Details **File Location**: `scripts/jobctl.sh:560-604` **Vulnerability Type**: Code injection through source-code generation and unsafe shell evaluation **Risk Level**: High ### Vulnerable Code ```bash local state_json=$(python3 -c " import json state = { 'job_id': '$job_id', 'command': '$command', 'status': 'queued', 'start_time': '$start_time', 'callback_url': '$callback_url', 'model': '$model', 'max_tokens': $max_tokens, 'tokens_used': 0, 'token_rate': 0.0, 'retry_count': 0, 'verified': False, 'verification_score': 0.0, 'self_improvement': {} } print(json.dumps(state, indent=2)) ") ``` The same values are embedded in a generated executable shell script: ```bash local wrapper="$RUN_DIR/${job_id}.wrapper.sh" cat > "$wrapper" <<WRAPPER #!/bin/bash set -euo pipefail JOB_ID="$job_id" COMMAND="$command" OUTPUT_FILE="$output_file" LOG_FILE="$log_file" # Execute the command if [ -n "\$COMMAND" ]; then echo "Starting command execution at \$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "\$LOG_FILE" # Execute and capture output eval "\$COMMAND" > "\$OUTPUT_FILE" 2>> "\$LOG_FILE" local exit_code=\$? echo "Command finished at \$(date -u +'%Y-%m-%dT%H:%M:%SZ') with exit code \$exit_code" >> "\$LOG_FILE" exit \$exit_code fi WRAPPER ``` ### Technical Analysis Values intended to be data are interpolated directly into Python source code and into a shell heredoc that becomes an executable script. Quotes, backslashes, newlines, substitutions, and other language-specific syntax can alter the generated program instead of remaining literal data. The `command` field is intentionally a shell command, but embedding it into a quoted variable assignment and then invoking it through `eval` creates an unnecessary second parsing layer. Non-command fields such as `job_id`, callback URL, model, and token count should never be able to modify Python or shell program structure. The un ...[truncated 1072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pass values to Python through `sys.argv`, environment variables, or JSON on standard input rather than interpolating them into source. - Parse and range-check `max_tokens` as an integer before use. - Do not generate executable shell source containing caller-controlled data. - Store job metadata in a securely created data file and read it without evaluating it. - If shell command strings are a required feature, invoke a fixed shell with the command as one explicit argument, for example `bash -c "$command"`, and clearly define that only the command field is executable. - Avoid `eval`, which introduces an additional expansion and parsing pass. - Generate wrapper files using fixed content only and pass dynamic values through positional parameters. - Add adversarial tests for quotes, backslashes, newlines, command substitutions, heredoc delimiters, and malformed numeric fields. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/jobctl.sh:49
Finding
Job State, Output, Logs, and Generated Wrappers Use Insecure Default Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/jobctl.sh:49-55`, `scripts/jobctl.sh:137-145`, `scripts/jobctl.sh:580-610` **Vulnerability Type**: Insecure storage permissions and unsafe temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```bash # Create directories if they don't exist mkdir -p "$RUN_DIR" "$LOG_DIR" "$STATE_DIR" "$CACHE_DIR" "$FEEDBACK_DIR" ``` State files are written without setting restrictive permissions: ```bash write_state() { local job_id="${1:-}" local json_data="${2:-}" local state_file=$(get_state_file "$job_id") local tmp_file="${state_file}.tmp" # Atomic write: write to temp file, then move echo "$json_data" > "$tmp_file" mv "$tmp_file" "$state_file" debug "State written for job $job_id" } ``` Generated wrappers and output paths similarly rely on the caller's current umask: ```bash local pid_file=$(get_pid_file "$job_id") local log_file=$(get_log_file "$job_id") local output_file=$(get_output_file "$job_id") # Create a wrapper script for better control local wrapper="$RUN_DIR/${job_id}.wrapper.sh" cat > "$wrapper" <<WRAPPER #!/bin/bash set -euo pipefail JOB_ID="$job_id" COMMAND="$command" OUTPUT_FILE="$output_file" LOG_FILE="$log_file" ... WRAPPER chmod +x "$wrapper" ``` ### Technical Analysis The script does not set `umask 077`, enforce directory mode `0700`, or enforce file mode `0600`. Under a common `022` umask, directories can be traversable and files can be readable by other local users. State files contain the complete command, callback URL, model configuration, and operational metadata. Output and logs can contain generated content, errors, tokens passed on command lines, filesystem paths, or other sensitive data. Wrapper scripts also contain the complete command. The predictable `${state_file}.tmp` name is not created using `mktemp` or an exclusive-open operation. In a directory accessible to another local process, this can permit race conditions ...[truncated 1197 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set `umask 077` before creating any job directories or files. - Create base and job directories with mode `0700`. - Create state, PID, log, output, and feedback files with mode `0600`. - Create executable wrappers with mode `0700` only if wrappers remain necessary. - Replace predictable `.tmp` files with securely created temporary files using `mktemp` inside the destination directory. - Verify file ownership and reject symbolic links before reading, writing, moving, or deleting files. - Avoid placing credentials in command strings, because command text is persisted and may also be visible through process inspection. - Add automated permission and symlink-race tests. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:739
Finding
Installation Instructions Use Mutable and Unverified Third-Party Sources<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:417-421`, `SKILL.md:739-745` **Vulnerability Type**: Unpinned dependency and mutable source installation **Risk Level**: Medium ### Vulnerable Code ```bash # Install the skill npx --yes clawhub@latest install @orionshaowswmw/nonblocking-agent-execution # Or clone this repository cd /path/to/skills git clone https://github.com/orionshaowswmw/nonblocking-agent-execution-enhanced.git ``` The later installation section repeats the same pattern: ```bash # Clone the repository git clone https://github.com/orionshaowswmw/nonblocking-agent-execution-enhanced.git # Or install via ClawHub (after publishing) npx --yes clawhub@latest install @orionshaowswmw/nonblocking-agent-execution # Make jobctl.sh executable chmod +x scripts/jobctl.sh ``` The documented verification value is not populated: ```text Artifact SHA-256 (TREE-SHA256-v1): Will be generated at publish time ``` ### Technical Analysis `clawhub@latest` resolves to a mutable package version at installation time. The Git command clones the repository's current default branch without pinning a commit or verifying a signed tag. The actual code installed later can therefore differ from the code reviewed in this audit. The instructions then make the downloaded script executable. Although no malicious upstream package was confirmed during this audit, the process lacks version pinning and effective integrity verification. The placeholder hash cannot establish artifact authenticity. ### Attack Path 1. A user follows the documented installation command. 2. The package registry resolves `clawhub@latest`, or Git resolves the repository's current default branch. 3. An upstream account, package, release, branch, or distribution channel has changed or is compromised after this audit. 4. The user downloads content different from the reviewed artifact. 5. The downloaded tooling installs or executes the changed Skill content under the user's privileges. ...[truncated 384 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin the installer to an exact reviewed package version instead of `@latest`. - Pin Git installations to an immutable commit hash or a cryptographically signed release tag. - Publish a real artifact digest and require users to verify it before execution. - Prefer signed release archives with documented provenance over cloning a mutable branch. - Record the exact dependency versions and source commits covered by each security review. - Add automated release checks that fail if the published digest remains a placeholder. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (21)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill clearly instructs users to execute shell commands, spawn background processes, manage subprocesses, and use network callbacks, yet it does not declare permissions accordingly. This creates a transparency and trust gap: users and platforms cannot accurately assess the capability surface before installation or invocation, increasing the chance of unsafe execution in sensitive environments.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The documentation explains what the skill does but not when it should or should not be invoked. For a skill that launches detached commands and persists state, unclear activation boundaries can cause accidental use in inappropriate contexts, leading to unintended command execution, lingering jobs, or sensitive data capture.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill emphasizes durable state and feedback persistence, including logs and command output, but does not strongly warn that these files may retain sensitive material long after task completion. In practice this can expose secrets, tokens, proprietary source, or personal data to later users, backups, or forensic inspection.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The callback feature can POST job results, including output content, to arbitrary external URLs, but the examples do not foreground the data-exfiltration risk. Because job output may contain secrets or internal artifacts, this optional integration materially expands the trust boundary and can leak sensitive data off-machine.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The callback API explicitly includes full job output in a POST body to an arbitrary callback URL, but the documentation does not warn that command output may contain sensitive data and will be exfiltrated off-host. In a job runner that executes arbitrary commands and persists outputs, this omission can lead users to unknowingly transmit secrets, credentials, logs, or proprietary data to external services.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The debug command returns state JSON, process info, logs, output, and feedback in one bundle, which may expose sensitive operational or user data, yet the documentation provides no warning about this exposure. In the context of a long-running job controller that captures command output and metadata, debug output can easily include secrets, tokens, prompts, internal paths, or personal data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The guide encourages use of debug mode, log viewing, and the debug command that exposes full job state, output contents, process details, and environment variables, but it does not warn that these artifacts may contain secrets, prompts, credentials, filesystem paths, or sensitive user data. In a production agent environment, such debugging guidance can lead operators to unintentionally disclose sensitive information in terminals, logs, screenshots, ticket systems, or shared monitoring pipelines.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The documentation repeatedly demonstrates callback URLs and manual callback testing without clearly warning that job outputs, metadata, or status information may be transmitted off-system to external services. In an agent-execution skill, users may run commands over sensitive repositories or data, so normalizing outbound callbacks without privacy guidance increases the risk of unintentional data exfiltration.

Missing User Warnings

High
Confidence
96% confidence
Finding
The integration guide explicitly instructs users to expose a REST wrapper around jobctl.sh that accepts job IDs and commands, then forwards them to a host-side execution primitive. Even though the sample binds to localhost in the curl example, the Flask server itself is later shown listening on 0.0.0.0 and exposes start/stop/cleanup/log actions with no authentication, authorization, or input restrictions, creating a straightforward remote command execution and host-control surface if deployed as written.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The webhook guidance normalizes posting job completion data, including output and metadata, to arbitrary callback URLs and even suggests third-party testing endpoints such as webhook.site and ngrok. That can leak sensitive command output, job identifiers, errors, and potentially secrets present in logs or outputs to external services if users follow the examples without understanding the disclosure risk.

External Transmission

Medium
Category
Data Exfiltration
Content
run: |
          cd nonblocking-agent-execution-enhanced
          ./scripts/jobctl.sh start ci-build 'npm install && npm run build' 
            https://api.github.com/repos/${{ github.repository }}/dispatches 
            gpt-4o-mini 4096
      
      - name: Poll for completion
Confidence
80% confidence
Finding
The GitHub Actions example sends job data to an external GitHub API callback endpoint without discussing what output or metadata may be transmitted when the job completes. In CI contexts, build output frequently contains internal repository details, dependency info, or secrets accidentally echoed during execution, so forwarding results externally increases data exposure risk.

External Transmission

Medium
Category
Data Exfiltration
Content
log "INFO" "Executing callback for job $job_id to $callback_url"
    
    # Try to POST the result
    if command -v curl >/dev/null 2>&1; then
        local response=$(curl -s -X POST \
            -H "Content-Type: application/json" \
            -H "Accept: application/json" \
Confidence
93% confidence
Finding
The script will POST job results to an arbitrary callback URL with no allowlist, scheme restriction, authentication policy, or redaction of potentially sensitive output. In this skill's context, jobs execute arbitrary commands and capture their output, so callback delivery can exfiltrate secrets, internal data, or command results to attacker-controlled endpoints.

Unvalidated Output Injection

High
Category
Output Handling
Content
print(f"Job still running... ({i+1}/120)")

# Get output
output = subprocess.run(
    ["cat", f"~/.nonblocking/state/{job_id}.output"],
    capture_output=True,
    text=True
Confidence
87% confidence
Finding
The example constructs a path from job_id and then reads job output without validating the identifier. If job_id is attacker-controlled, this pattern can enable path manipulation or unintended file access, and it also encourages consuming untrusted output directly, which can expose secrets or inject misleading terminal content into downstream workflows.

Unvalidated Output Injection

High
Category
Output Handling
Content
lines = request.args.get('lines', '50')
    cmd = [JOBCTL_PATH, 'log', job_id, lines]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        return result.stdout, 200, {'Content-Type': 'text/plain'}
    except subprocess.CalledProcessError as e:
        return jsonify({'error': e.stderr}), 404
Confidence
72% confidence
Finding
The log endpoint returns raw stdout from jobctl.sh directly as text/plain based on user-controlled job_id and line-count inputs, with no access control or sanitization discussed. In practice this can expose sensitive logs, terminal control sequences, secrets, or untrusted content to any caller of the API, and becomes more dangerous because the documentation encourages remote wrapping of host job control.

Session Persistence

Medium
Category
Rogue Agent
Content
|-------|---------|----------------------------|
| **Blocking tool call** — one `bash` call runs 20 min | UI shows nothing, user aborts | Detach with `setsid nohup ... &` + watchdog timer |
| **Interactive prompt with closed stdin** | Hangs forever, never times out | `--yes` / `--no-input` shims + always wrap in `timeout` |
| **Aborted turn kills the child process** | Work silently lost, half-done state | Detach with `setsid`, persist state to disk + atomic writes |

---
Confidence
90% confidence
Finding
Persisting job state to disk is a deliberate feature, but from a security perspective it extends the lifetime of potentially sensitive execution metadata and output beyond the agent session. That raises exposure risk if the host is shared, compromised later, or subject to broad backups/log collection.

Session Persistence

Medium
Category
Rogue Agent
Content
| Cause | Symptom | Fix (v2.0.0 Implementation) |
|-------|---------|----------------------------|
| **Blocking tool call** — one `bash` call runs 20 min | UI shows nothing, user aborts | Detach with `setsid nohup ... &` + watchdog timer |
| **Interactive prompt with closed stdin** | Hangs forever, never times out | `--yes` / `--no-input` shims + always wrap in `timeout` |
| **Aborted turn kills the child process** | Work silently lost, half-done state | Detach with `setsid`, persist state to disk + atomic writes |
Confidence
88% confidence
Finding
Using nohup is another persistence mechanism that intentionally keeps processes alive after hangup/session termination. While operationally useful, it increases the risk of lingering background tasks consuming resources, continuing network activity, or handling sensitive data outside the operator's immediate visibility.

Session Persistence

Medium
Category
Rogue Agent
Content
| Cause | Symptom | Fix (v2.0.0 Implementation) |
|-------|---------|----------------------------|
| **Blocking tool call** — one `bash` call runs 20 min | UI shows nothing, user aborts | Detach with `setsid nohup ... &` + watchdog timer |
| **Interactive prompt with closed stdin** | Hangs forever, never times out | `--yes` / `--no-input` shims + always wrap in `timeout` |
| **Aborted turn kills the child process** | Work silently lost, half-done state | Detach with `setsid`, persist state to disk + atomic writes |
Confidence
88% confidence
Finding
Using nohup is another persistence mechanism that intentionally keeps processes alive after hangup/session termination. While operationally useful, it increases the risk of lingering background tasks consuming resources, continuing network activity, or handling sensitive data outside the operator's immediate visibility.

Session Persistence

Medium
Category
Rogue Agent
Content
### Original Rules (Preserved)
1. **No tool call over ~60 s.** Long work is launched, not awaited.
2. **`setsid nohup … < /dev/null &`** — survives the turn being cancelled; plain `&` does not.
3. **Bounded wait only.** A poll helper that *always* returns within N seconds. Never `wait`.
4. **Every external command gets `timeout N`.** No exceptions for network CLIs.
5. **Non-interactive flags always** (`--yes`, `--no-input`, `-y`, `DEBIAN_FRONTEND=noninteractive`).
Confidence
89% confidence
Finding
The nohup guidance at this line reinforces persistence across session termination, which is operationally valuable but security-sensitive. In environments expecting tool invocations to end with the turn, this behavior can surprise operators and create stealthy long-lived execution.

Session Persistence

Medium
Category
Rogue Agent
Content
### Original Rules (Preserved)
1. **No tool call over ~60 s.** Long work is launched, not awaited.
2. **`setsid nohup … < /dev/null &`** — survives the turn being cancelled; plain `&` does not.
3. **Bounded wait only.** A poll helper that *always* returns within N seconds. Never `wait`.
4. **Every external command gets `timeout N`.** No exceptions for network CLIs.
5. **Non-interactive flags always** (`--yes`, `--no-input`, `-y`, `DEBIAN_FRONTEND=noninteractive`).
Confidence
89% confidence
Finding
The nohup guidance at this line reinforces persistence across session termination, which is operationally valuable but security-sensitive. In environments expecting tool invocations to end with the turn, this behavior can surprise operators and create stealthy long-lived execution.

Session Persistence

Medium
Category
Rogue Agent
Content
chmod +x "$wrapper"
    
    # Launch with setsid and nohup
    setsid nohup bash "$wrapper" > /dev/null 2>&1 &
    local pid=$!
    echo "$pid" > "$pid_file"
Confidence
74% confidence
Finding
Use of 'nohup' here is part of the same persistence mechanism for arbitrary commands, allowing them to survive hangups and continue beyond the initiating session. In a sandboxed agent setting, this can bypass expected turn-scoped execution assumptions and facilitate unnoticed long-running or abusive activity.

Session Persistence

Medium
Category
Rogue Agent
Content
chmod +x "$wrapper"
    
    # Launch with setsid and nohup
    setsid nohup bash "$wrapper" > /dev/null 2>&1 &
    local pid=$!
    echo "$pid" > "$pid_file"
Confidence
74% confidence
Finding
Use of 'nohup' here is part of the same persistence mechanism for arbitrary commands, allowing them to survive hangups and continue beyond the initiating session. In a sandboxed agent setting, this can bypass expected turn-scoped execution assumptions and facilitate unnoticed long-running or abusive activity.

Static analysis

No suspicious patterns detected.