Back to skill

Security audit

cron-edit-safe

Security checks for vulnerabilities and agentic risk

Overview

This skill is meant to make OpenClaw cron edits safer, but its safety checks and rollback are unreliable around persistent cron changes.

Review this before installing if you use OpenClaw cron for important jobs. Treat dry-run as real local command execution, avoid passing sensitive environment variables, and do not rely on the advertised rollback as a complete restoration mechanism until the temp-file handling, verification, and rollback logic are fixed.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cron-edit-safe.sh:167
Finding

Predictable Shared Temporary Files Allow Local File Clobbering and Information Disclosure

Content
View full analysis
/tmp/cron-dry-run.out 2>&1 && DRY_RUN_OK=1 || DRY_RUN_OK=0 else timeout 60 bash -c "$DRY_RUN_CMD" >/tmp/cron-dry-run.out 2>&1 && DRY_RUN_OK=1 || DRY_RUN_OK=0 fi if [[ $DRY_RUN_OK -eq 1 ]]; then ok "Dry-run OK (exit 0)" log " 摘要: $(head -3 /tmp/cron-dry-run.out | tr '\n' ' ')" else err "Dry-run FAILED (exit non-zero)" log " 完整输出: /tmp/cron-dry-run.out" log " 前 5 行:" head -5 /tmp/cron-dry-run.out | sed 's/^/ /' fi ``` ```bash if openclaw cron edit "$CRON_ID" "${EDIT_ARGS[@]}" >/tmp/cron-edit.out 2>&1; then ok "Edit OK" else err "Edit FAILED" log " 输出: /tmp/cron-edit.out" EDIT_FAILED=1 fi ``` ```bash if openclaw cron get "$CRON_ID" >/tmp/cron-after.json 2>&1; then python3 -c " import json with open('/tmp/cron-after.json', 'r') as f: content = f.read() idx = content.find('{') if idx < 0: print('NO_JSON') else: d = json.loads(content[idx:]) print(f'payload.kind={d[\"payload\"][\"kind\"]}') argv = d['payload'].get('argv', []) if argv: print(f'argv=[\"{\" \".join(argv)}]') else: print('argv=[]') print(f'name={d[\"name\"]}') print(f'enabled={d.get(\"enabled\", \"?\")}') " > /tmp/cron-after-extract.txt 2>&1 fi ``` ```bash if openclaw cron edit "$CRON_ID" "${ROLLBACK_ARGS[@]}" >/tmp/cron-rollback.out 2>&1; then ok "Rollback OK: 已恢复 $CRON_ID 到原始状态" fi ``` ### Technical Analysis The script repeatedly writes potentially sensitive or privileged process output to fixed filenames in the globally writable `/tmp` directory. Shell redirection opens these paths without securely creating them, checking ownership, rejecting symbolic links, or applying explici ...[truncated 2144 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cron-edit-safe.sh:206
Finding

Verification Can Accept Incorrect Edits or Exit Before Rollback

Content
View full analysis
/tmp/cron-after.json 2>&1; then # openclaw cron get 输出顶部有 plugin warnings,跳过非 JSON 行 python3 -c " import json with open('/tmp/cron-after.json', 'r') as f: content = f.read() idx = content.find('{') if idx < 0: print('NO_JSON') else: d = json.loads(content[idx:]) print(f'payload.kind={d[\"payload\"][\"kind\"]}') argv = d['payload'].get('argv', []) if argv: print(f'argv=[\"{\" \".join(argv)}]') else: print('argv=[]') print(f'name={d[\"name\"]}') print(f'enabled={d.get(\"enabled\", \"?\")}') " > /tmp/cron-after-extract.txt 2>&1 # 读取提取结果 CHANGES_STR=$(cat /tmp/cron-after-extract.txt | grep -v "NO_JSON" | tr '\n' ' ') if grep -q "NO_JSON" /tmp/cron-after-extract.txt; then err "Verify FAILED: 输出无可用 JSON" else ok "Verify OK" log " 变更:" sed 's/^/ /' /tmp/cron-after-extract.txt VERIFY_OK=1 fi else err "Verify FAILED: 无法重新 get cron" fi fi ``` The script globally enables immediate termination: ```bash set -euo pipefail ``` ### Technical Analysis The verification stage extracts and displays a small set of fields, but it does not compare the resulting cron object against the changes requested through `EDIT_ARGS`. Any valid JSON that does not contain the literal `NO_JSON` marker is treated as successfully verified. Consequently, an edit that is ignored, partially applied, normalized incorrectly, or applied to the wrong fields may still produce `Verify OK`. Fields accepted by the command-line interface—including description, session, delivery destination, alerting settings, command working directory, an ...[truncated 2196 chars]
Remediation
View remediation
"$EXTRACT_OUTPUT"; then err "Verification parsing failed" VERIFY_OK=0 else # Perform explicit field comparisons here. verify_expected_state fi ``` 5. Route all failures through a single cleanup-and-rollback function. An `ERR` trap can provide defense in depth, but it should avoid recursively triggering rollback. 6. Use the already-required `jq` consistently, or declare Python as mandatory and check for it before any edit occurs. 7. Add tests for partial edits, unchanged values, malformed JSON, missing keys, unavailable parsers, and schema changes. 8. After rollback, retrieve the cron object again and verify that it matches the backup before reporting successful recovery. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cron-edit-safe.sh:253
Finding

Rollback Restores Only Selected Fields and Loses Command Argument Boundaries

Content
View full analysis
/tmp/cron-rollback.out 2>&1; then ok "Rollback OK: 已恢复 $CRON_ID 到原始状态" else err "Rollback FAILED" err " 手动恢复命令: openclaw cron edit $CRON_ID \\" err " --name \"\$ORIG_NAME\" --description \"\$ORIG_DESC\" ..." err " 或读 backup: $BACKUP_FILE" exit 2 fi ``` ### Technical Analysis The script describes the backup as a rollback source, but it does not restore the complete backed-up object. It reconstructs an edit command using only: - Name - Description - Payload kind - Command working directory - Command arguments or agent message Other mutable properties—including session mode, enabled state, delivery configuration, notification destination, failure-alert settings, cooldown values, and fields introduced by future OpenClaw versions—are not restored. A failed edit can therefore leave omitted properties in their modified state while the wrapper reports that rollback succeeded. The command rollback also converts `.payload.argv`, an array with explicit argument boundaries, into one space-delimited string: ```bash .payload.argv | ...[truncated 1752 chars]
Remediation
View remediation
"$TMP_DIR/original.canonical.json" openclaw cron get "$CRON_ID" | jq -S . >"$TMP_DIR/restored.canonical.json" if ! cmp -s "$TMP_DIR/original.canonical.json" \ "$TMP_DIR/restored.canonical.json"; then err "Rollback verification failed" exit 2 fi ``` 7. Account for non-object warning text before canonicalization, using a robust parser with explicit failure handling. 8. Report rollback success only after complete structural equivalence has been established. 9. Add regression tests covering spaces, quotes, empty arguments, shell metacharacters, delivery settings, session settings, enabled state, and failure-alert configuration. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (12)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script's 'dry-run' mode executes the user-supplied cron command locally via 'bash -c', which is arbitrary shell execution rather than a safe validation step. In a wrapper advertised as 'safe' cron editing, this significantly expands the trust boundary: any untrusted or mistaken command will run immediately on the operator's machine, potentially causing file changes, credential use, network calls, or destructive actions before the cron is even updated.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description and the entire user-facing README content are written in Chinese, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation unless the locale restriction is documented and justified.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 33)May include surrounding context.

md
if ! command -v jq >/dev/null 2>&1; then
  echo "❌ jq 未安装" >&2
  echo "   安装方法:" >&2
  echo "   - Debian/Ubuntu: sudo apt install jq" >&2
  echo "   - macOS: brew install jq" >&2
  echo "   - CentOS/RHEL: sudo yum install jq" >&2
  exit 1

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/cli.sh (reported line 18)May include surrounding context.

sh
if ! command -v jq >/dev/null 2>&1; then
  echo "❌ jq 未安装" >&2
  echo "   安装方法:" >&2
  echo "   - Debian/Ubuntu: sudo apt install jq" >&2
  echo "   - macOS: brew install jq" >&2
  echo "   - CentOS/RHEL: sudo yum install jq" >&2
  exit 1

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/cli.sh (reported line 20)May include surrounding context.

sh
if ! command -v jq >/dev/null 2>&1; then
  echo "❌ jq 未安装" >&2
  echo "   安装方法:" >&2
  echo "   - Debian/Ubuntu: sudo apt install jq" >&2
  echo "   - macOS: brew install jq" >&2
  echo "   - CentOS/RHEL: sudo yum install jq" >&2
  exit 1

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/cron-edit-safe.sh (reported line 106)May include surrounding context.

sh
if ! command -v jq >/dev/null 2>&1; then
  echo "❌ jq 未安装" >&2
  echo "   安装方法:" >&2
  echo "   - Debian/Ubuntu: sudo apt install jq" >&2
  echo "   - macOS: brew install jq" >&2
  echo "   - CentOS/RHEL: sudo yum install jq" >&2
  exit 1

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The wrapper exports EM_API_KEY and MINIMAX_API_KEY even though its stated purpose is only safe cron editing. Forwarding unrelated credentials increases the attack surface because any downstream script or subprocess invoked by the core script inherits secrets it may not need, enabling accidental disclosure, logging, or abuse if the called toolchain is compromised.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The comments throughout the file require Chinese comprehension, which can impose a language constraint on users or maintainers without any opt-in or justification. The policy allows locale constraints when explicitly justified, but this file does not state that the skill is intentionally Chinese-only or region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This shell script’s user-facing comments and usage/help text are written only in Chinese, and the file does not indicate any opt-in or alternative language support. Under the policy, forcing a specific language without user choice is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

This shell script’s header comments, usage text, and operational messages are written in Chinese, and there is no indication that users can select another language or that the skill is intended only for a Chinese-speaking or region-specific environment. That creates a natural-language locale constraint without opt-in, which matches the policy-violation category.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description and author fields are written in Chinese, which signals a fixed language choice in the skill's user-facing metadata. There is no indication that the skill offers a language option or that the Chinese-only presentation is a documented, justified locale constraint.

Content

No source excerpt is available for this finding.