Back to skill

Security audit

interagent-queue

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed MIAB ledger observer, but its optional Discord notifier can resend full historical task details if its state file is corrupted or reset.

Install only if you are comfortable with MIAB ledger summaries being posted to the configured Discord target. Prefer the renamed miab-observer release if available, and do not run the notifier from cron until its closed_bottle_state.json recovery behavior is fixed or you have a clear operational plan for state corruption and ledger rotation.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/notify_closed_bottles.py:126
Finding
Corrupt Notifier State Silently Resets the Delivery Cursor## Vulnerability Details **File Location**: `scripts/notify_closed_bottles.py`, lines 126–134 and 392–393 **Vulnerability Type**: Improper state validation and fail-open cursor recovery **Risk Level**: Medium ### Vulnerable Code ```python def load_state() -> dict: p = state_file() p.parent.mkdir(parents=True, exist_ok=True) if p.exists(): try: return json.loads(p.read_text(encoding="utf-8")) except Exception: pass return {"enabled": True, "last_processed_line": 0} ``` The processing path also explicitly rewinds a cursor that exceeds the current ledger size: ```python cursor = state.get("last_processed_line", 0) if cursor > total: cursor = 0 ``` ### Technical Analysis The closed-bottle notifier treats every state read or JSON parsing failure as a fresh installation. It silently suppresses the exception, enables delivery, and resets `last_processed_line` to zero. It also fails to verify that the decoded value is a JSON object or that `enabled`, `last_processed_line`, and `delivered_ids` have valid types and values. Consequently, a truncated, malformed, unreadable, or deliberately replaced `closed_bottle_state.json` can erase the effective delivery cursor and deduplication history. The next `process` invocation scans the ledger from its beginning and reconstructs historical closed-bottle messages for external delivery. The separate `cursor > total` recovery has the same unsafe effect when the ledger is truncated or rotated: it automatically rewinds to zero instead of requiring explicit operator recovery. This behavior contrasts with `scripts/interagent_queue.py`, which rejects an existing but unusable state file rather than silently rewinding its cursor. ### Attack Path 1. The notifier processes closed bottles and records its cursor and delivered identifiers in `closed_bottle_state.json`. 2. An attacker with write access to that state pat ...[truncated 1742 chars]
Remediation
## Remediation Suggestions 1. Fail closed when an existing state file cannot be read or parsed. Emit a structured error and exit with a nonzero status without sending messages or modifying the cursor. 2. Validate the complete state schema before use: - The top-level value must be a JSON object. - `enabled` must be a Boolean. - `last_processed_line` must be a non-negative integer and must not be a Boolean. - `delivered_ids` must be a list containing only strings. 3. Distinguish a genuinely absent state file from an existing but unusable state file. Only the absent-file case should initialize a fresh cursor. 4. Do not automatically reset a cursor greater than the ledger length. Treat ledger truncation or rotation as an exceptional condition and require explicit operator confirmation or a documented recovery command. 5. Reuse the fail-closed validation approach already implemented by `scripts/interagent_queue.py`. 6. Preserve atomic state updates and consider flushing and synchronizing the temporary file before replacement where durability across abrupt shutdowns is required. 7. Add regression tests covering malformed JSON, non-object JSON, negative or incorrectly typed cursors, invalid `delivered_ids`, unreadable state, and a cursor greater than the ledger length. Each case should verify that no external delivery occurs.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Memory Manipulation

High
Category
Memory Poisoning
Content
### Fixed

- **A corrupt state file no longer replays the entire ledger.** `load_state()` swallowed every
  exception and fell through to the same `{"enabled": false, "last_processed_line": 0}` default
  it uses for a genuine fresh start. A truncated or malformed `queue_state.json` therefore
  rewound the cursor to 0 without a word, and the next `process` wrote every ledger record that
Confidence
90% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
### Fixed

- **A corrupt state file no longer replays the entire ledger.** `load_state()` swallowed every
  exception and fell through to the same `{"enabled": false, "last_processed_line": 0}` default
  it uses for a genuine fresh start. A truncated or malformed `queue_state.json` therefore
  rewound the cursor to 0 without a word, and the next `process` wrote every ledger record that
Confidence
90% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
PREREQUISITE: Requires the `miab-broker` skill to be installed and active.
It tails the append-only callback ledger (state/callbacks/ledger.jsonl) managed by miab-broker,
converts raw create / forward / return / resolve / cancel / fail / corrupt events into
human-readable log entries using the agent identity map, advances a once-only cursor, and — when the live
toggle is on — writes the formatted batch to the log file ($CLAW_HOME/logs/interagent-queue.log).
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.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script intentionally reconstructs a bottle's full history from the ledger and sends task, result, reasons, and step history to Discord. Because ledger contents may include sensitive prompts, operational data, identifiers, secrets, or user content, forwarding them to an external messaging platform can cause data exfiltration to a broader audience than the original system intended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if acct:
        cmd += ["--account", acct]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            print(f"delivery failed (exit {result.returncode}): {result.stderr.strip()}", file=sys.stderr)
            return False
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/notify_closed_dryrun.py:23