Back to skill

Security audit

AgentOps Guardrails

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its ops-monitoring purpose, but its router can place untrusted alert text into downstream investigator instructions.

Install only if you will run it on trusted detector inputs or add trigger allowlisting before any agent consumes router task text. Review cleanup targets before running clean-generated.sh, and pin the ClawHub publisher command if republishing.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T01 · Skill Instruction Hijacking

Error
Location
scripts/ops-incident-router.sh:63
Finding
Untrusted detector trigger is embedded in downstream agent instructions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ops-incident-router.sh:63-73, 90-105` **Vulnerability Type**: Prompt injection through untrusted detector data **Risk Level**: High ### Vulnerable Code ```bash map_trigger_to_check_id() { case "$1" in cron_failure) echo "cron_failure" ;; heartbeat_gap|paymaster_gap) echo "heartbeat_gap" ;; context_crit|context_100pct) echo "context_crit" ;; context_high|context_90pct) echo "context_high" ;; context_warn|context_80pct) echo "context_warn" ;; dangling_sessions) echo "dangling_sessions" ;; token_spike) echo "token_spike" ;; *) echo "unknown_${1}" ;; esac } ``` ```bash while IFS= read -r alert; do trigger="$(jq -r '.trigger // "unknown"' <<<"$alert")" severity="$(jq -r '.sev // "Sev-3"' <<<"$alert")" check_id="$(map_trigger_to_check_id "$trigger")" guard_raw="$(bash "$SCRIPT_DIR/incident-guard-check.sh" --check-id "$check_id" --severity "$severity" --state-file "$STATE_FILE")" allowed="$(jq -r '.allowed // false' <<<"$guard_raw")" reason="$(jq -r '.reason // "unknown"' <<<"$guard_raw")" if [[ "$allowed" == "true" ]]; then action_json="$(jq -cn \ --arg action "spawn" \ --arg check_id "$check_id" \ --arg severity "$severity" \ --arg mode "$([[ "$LIVE" == "true" ]] && echo live || echo dry-run)" \ --arg task "Investigate incident: ${check_id}. Gather evidence, classify severity, propose low-risk remediations with rollback." \ '{action:$action,check_id:$check_id,severity:$severity,mode:$mode,task:$task}')" ``` ### Technical Analysis The router accepts detector JSON from standard input or a caller-selected file. Known trigger names are mapped to fixed identifiers, but an unknown trigger is copied into `check_id` through the default `unknown_${1}` branch. That value is subsequently interpolated into the natural-language `task` field intended for an investigator agent. The use of `jq --arg` safely encodes the value ...[truncated 1651 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject unknown trigger identifiers instead of reflecting them: ```bash map_trigger_to_check_id() { case "$1" in cron_failure) echo "cron_failure" ;; heartbeat_gap|paymaster_gap) echo "heartbeat_gap" ;; context_crit|context_100pct) echo "context_crit" ;; context_high|context_90pct) echo "context_high" ;; context_warn|context_80pct) echo "context_warn" ;; dangling_sessions) echo "dangling_sessions" ;; token_spike) echo "token_spike" ;; *) return 1 ;; esac } ``` 2. Generate task text exclusively from fixed templates selected by an allowlisted identifier. 3. Validate the complete detector schema, including allowed trigger and severity values, before processing alerts. 4. Keep source-provided descriptions in a separate structured data field clearly labeled as untrusted evidence; never interpolate them into agent instructions. 5. Configure downstream agents to treat detector fields as data rather than instructions and enforce least-privilege tool access. 6. Add tests using newlines, control characters, long strings, and instruction-like trigger values to verify that unknown triggers are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ops-incident-router.sh:94
Finding
Non-atomic guard acquisition permits duplicate incident workflows<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ops-incident-router.sh:94-112` **Vulnerability Type**: Time-of-check to time-of-use race condition **Risk Level**: Medium ### Vulnerable Code ```bash guard_raw="$(bash "$SCRIPT_DIR/incident-guard-check.sh" --check-id "$check_id" --severity "$severity" --state-file "$STATE_FILE")" allowed="$(jq -r '.allowed // false' <<<"$guard_raw")" reason="$(jq -r '.reason // "unknown"' <<<"$guard_raw")" if [[ "$allowed" == "true" ]]; then action_json="$(jq -cn \ --arg action "spawn" \ --arg check_id "$check_id" \ --arg severity "$severity" \ --arg mode "$([[ "$LIVE" == "true" ]] && echo live || echo dry-run)" \ --arg task "Investigate incident: ${check_id}. Gather evidence, classify severity, propose low-risk remediations with rollback." \ '{action:$action,check_id:$check_id,severity:$severity,mode:$mode,task:$task}')" echo "$action_json" [[ -n "$ROUTER_OUT" ]] && echo "$action_json" >> "$ROUTER_OUT" if [[ "$LIVE" == "true" ]]; then bash "$SCRIPT_DIR/incident-state-update.sh" --action start --check-id "$check_id" --severity "$severity" --state-file "$STATE_FILE" >/dev/null fi ``` The state writer also uses a shared predictable temporary path in `scripts/incident-state-update.sh:77-83`: ```python def write_state(path: str, state: dict): os.makedirs(os.path.dirname(path) or ".", exist_ok=True) tmp = f"{path}.tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump(state, f, indent=2, ensure_ascii=True) f.write("\n") os.replace(tmp, path) ``` ### Technical Analysis Guard checking and state acquisition occur in separate processes without a shared lock or atomic compare-and-set operation. The router also emits and records the `spawn` action before marking the incident as in flight. Two concurrent live router processes can consequently both read the same state, both receive `allowed=true`, and both emit a spawn action. Their later updates ...[truncated 1347 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the separate check and update operations with one atomic guard-acquisition operation. 2. Hold an exclusive `flock` on a dedicated state lock file while loading, validating, modifying, and replacing state. 3. Under that lock, perform a compare-and-set operation: - Reject acquisition if `in_flight` is true. - Reject acquisition if cooldown is active. - Otherwise set `in_flight=true` and persist the state. 4. Emit the `spawn` action only after successful acquisition. 5. Use a uniquely created temporary file in the state file's directory, apply restrictive permissions, flush it, and atomically replace the destination while still holding the lock. 6. Lock router output appends or write each action through a single synchronized writer. 7. Add a concurrency test that launches many live routers for the same alert and verifies that exactly one receives the lock and emits `spawn`. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:7
Finding
Publishing instructions execute an unpinned mutable npm package<![CDATA[ ## Vulnerability Details **File Location**: `README.md:7-11` **Vulnerability Type**: Unpinned executable dependency **Risk Level**: Medium ### Vulnerable Code ```bash npx clawhub@latest publish . \ --slug ops-detection-incident-routing \ --name "Ops Detection + Incident Routing" \ --version 1.0.0 \ --changelog "Initial public release" ``` ### Technical Analysis The documented publishing command uses `npx` to resolve and execute `clawhub@latest`. The `latest` tag is mutable and does not identify a specific reviewed package artifact. No exact version, lockfile, integrity value, or provenance verification is included. Because npm packages execute with the invoking user's privileges, a compromised maintainer account, malicious newly tagged release, registry compromise, or unexpected upstream change could alter the code executed by a publisher after this project was audited. The command publishes the current directory and is likely to run in an environment containing registry authentication. This increases the consequences of executing an unverified package version. ### Attack Path 1. A publisher follows the README instructions. 2. `npx` resolves the package currently referenced by the mutable `latest` tag. 3. npm downloads and executes that package locally. 4. If the resolved package has been compromised, its code runs with the publisher's user privileges. 5. The malicious dependency could access files available to that user, inspect publishing credentials exposed to the process, alter the package contents, or publish a modified artifact. ### Impact Assessment Successful exploitation can provide code execution with the publisher's local user privileges. The reachable scope can include the project directory, files readable by that account, environment-provided credentials, npm configuration, and the authority associated with the publishing token. No evidence shows that the current `clawhub` package is malicious; the vulnerability is the m ...[truncated 58 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin a reviewed exact package version rather than using `@latest`. 2. Record the dependency in a lockfile with integrity metadata and install it through a controlled dependency workflow. 3. Verify package ownership, provenance, signatures, and release metadata before execution. 4. Run publishing in an isolated, least-privilege environment containing only the files required for release. 5. Use a short-lived, narrowly scoped publishing token and prevent unrelated credentials from entering the process environment. 6. Generate and inspect the publication manifest before publishing so unexpected files are not uploaded. 7. Consider requiring manual approval for dependency updates and publishing workflow changes. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/clean-generated.sh:8
Finding
Cleanup script recursively deletes files using broad filename patterns<![CDATA[ ## Vulnerability Details **File Location**: `scripts/clean-generated.sh:8-15` **Vulnerability Type**: Overly broad destructive file operation **Risk Level**: Low ### Vulnerable Code ```bash # Remove generated line-delimited logs and lock files. find "$ROOT_DIR" -type f \( -name '*.jsonl' -o -name '*.lock' \) -exec unlink {} \; # Remove generated workspace tree if present. if [[ -d "$WORKSPACE_DIR" ]]; then find "$WORKSPACE_DIR" -type f -exec unlink {} \; find "$WORKSPACE_DIR" -type l -exec unlink {} \; find "$WORKSPACE_DIR" -depth -type d -exec rmdir {} \; 2>/dev/null || true fi ``` ### Technical Analysis The cleanup command recursively removes every regular file ending in `.jsonl` or `.lock` anywhere below the project root. It then deletes every file and symbolic link under the example workspace, regardless of whether each entry was generated by this Skill. The script has no generated-artifact manifest, ownership validation, confirmation prompt, or dry-run mode. As the project evolves, legitimate fixtures, diagnostic evidence, or manually created files matching these broad criteria can be deleted. The fixed root is derived from the script location, which limits deletion to the project tree under normal execution. The issue is therefore destructive overreach within that tree rather than arbitrary path injection. ### Attack Path 1. A user or another process stores a legitimate `.jsonl` or `.lock` file under the project root, or stores any important file under `examples/workspace`. 2. The user follows the documented republishing procedure and executes `bash scripts/clean-generated.sh`. 3. `find` matches the legitimate file. 4. `unlink` removes it without confirmation or recovery support. ### Impact Assessment The script does not grant elevated privileges. It can delete any matching file within the project tree that is writable by the invoking user. Potential effects include loss of incident evidence, test fixtures, local runtime records, ...[truncated 152 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict deletion to explicit generated output paths rather than searching the entire project. 2. Maintain a manifest of files generated by setup and runtime commands. 3. Resolve and validate canonical paths before deletion, and refuse any path outside a dedicated generated-artifacts directory. 4. Add a `--dry-run` mode that lists all prospective deletions. 5. Require explicit confirmation for recursive cleanup unless a deliberate noninteractive flag is supplied. 6. Preserve committed fixtures and operational evidence by default. 7. Add tests demonstrating that unrelated `.jsonl` and `.lock` files remain untouched. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill description presents itself as a production-safe anomaly detection and incident routing workflow, but the referenced behavior includes cleanup and possible workspace artifact removal that is outside that stated purpose. This kind of capability mismatch can mislead operators into granting trust or execution in environments where deletion side effects are unexpected, increasing the likelihood of accidental data loss or unsafe automation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill description presents itself as a production-safe anomaly detection and incident routing workflow, but the referenced behavior includes cleanup and possible workspace artifact removal that is outside that stated purpose. This kind of capability mismatch can mislead operators into granting trust or execution in environments where deletion side effects are unexpected, increasing the likelihood of accidental data loss or unsafe automation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill description presents itself as a production-safe anomaly detection and incident routing workflow, but the referenced behavior includes cleanup and possible workspace artifact removal that is outside that stated purpose. This kind of capability mismatch can mislead operators into granting trust or execution in environments where deletion side effects are unexpected, increasing the likelihood of accidental data loss or unsafe automation.

Ae1

High
Category
analysis-evasion
Content
- `scripts/ops-threshold-detector.sh`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- `scripts/ops-threshold-detector.sh`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The README instructs users to run `npx clawhub@latest publish`, which fetches and executes the latest package version at runtime rather than a pinned, reviewed release. That creates a supply-chain risk: if the package is compromised or a breaking/malicious version is published later, anyone following the instructions could execute untrusted code on their system.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises and invokes shell scripts that read and write local files, but the manifest does not declare any explicit tool scope or permissions boundary. That makes the operational capability opaque to reviewers and increases the chance the skill is executed with broader file and shell access than intended, which is risky for a skill that manipulates state and generated artifacts.