Back to skill

Security audit

Time Clawshine — OpenClaw Time Machine

Security checks for vulnerabilities and agentic risk

Overview

This is a clearly disclosed local backup and restore tool, but its privileged scheduler setup has a verified root cron injection risk that users should review before installing.

Review and fix the cron fallback before default installation, or install only with systemd-compatible schedules and trusted, root-controlled config files. Use setup.sh --dry-run first, keep privacy.local_only true unless you intentionally configure external alerts, back up the restic password file separately, and prefer restoring to a temporary target before overwriting live OpenClaw state.

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

T09 · Insecure Skill Coding Practices

Warning
Location
lib.sh:277
Finding
Unredacted Diagnostic Data Can Be Sent to a User-Configured Healthcheck Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `lib.sh:277-290`; related callers in `bin/backup.sh:75-79`, `bin/backup.sh:99-103`, and `bin/backup.sh:137-141` **Vulnerability Type**: Unredacted transmission of diagnostic information **Risk Level**: Medium ### Complete Code Snippet ```bash hc_send() { [[ "$PRIVACY_LOCAL_ONLY" == "true" ]] && return 0 [[ "$HC_ENABLED" != "true" ]] && return 0 [[ -z "$HC_URL" ]] && return 0 [[ "$HC_URL" == "null" ]] && return 0 local state="${1:-}" local body="${2:-}" local url="${HC_URL%/}${state}" [[ "$PRIVACY_SEND_ERROR_DETAILS" != "true" ]] && body="" if [[ -n "$body" ]]; then curl -fsS -m 10 --retry 2 --data-raw "$body" "$url" >/dev/null 2>&1 \ || log_warn "Healthcheck ping failed for state '${state:-success}'" else curl -fsS -m 10 --retry 2 "$url" >/dev/null 2>&1 \ || log_warn "Healthcheck ping failed for state '${state:-success}'" fi } ``` Related failure calls include: ```bash if [[ $BACKUP_EXIT -ne 0 ]]; then log_error "restic backup failed (exit $BACKUP_EXIT)" log_error "$BACKUP_OUTPUT" tg_failure "restic backup failed (exit $BACKUP_EXIT):\n\n$BACKUP_OUTPUT" hc_send /fail "restic backup failed (exit $BACKUP_EXIT)" exit 1 fi ``` ```bash if [[ $FORGET_EXIT -ne 0 ]]; then log_error "restic forget failed (exit $FORGET_EXIT)" log_error "$FORGET_OUTPUT" tg_failure "restic forget failed (exit $FORGET_EXIT):\n\n$FORGET_OUTPUT" hc_send /fail "restic forget failed (exit $FORGET_EXIT)" exit 1 fi ``` ```bash if [[ $CHECK_EXIT -ne 0 ]]; then log_error "restic check failed (exit $CHECK_EXIT)" log_error "$CHECK_OUTPUT" tg_failure "restic check failed (exit $CHECK_EXIT):\n\n$CHECK_OUTPUT" hc_send /fail "restic check failed (exit $CHECK_EXIT)" fi ``` ### Technical Analysis When both external healthchecks and `privacy.send_error_details` are enabled, `hc_send` tr ...[truncated 2242 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply the existing redaction function before transmitting any healthcheck body: ```bash if [[ -n "$body" ]]; then body=$(_redact_external_text "$body") body=$(head -c 600 <<< "$body") fi ``` 2. Prefer fixed, non-sensitive status values such as `backup_failed`, `prune_failed`, and `integrity_check_failed` rather than transmitting command output. 3. Introduce a separate, clearly named option such as `privacy.send_healthcheck_diagnostics`, disabled by default, instead of reusing a general error-detail setting. 4. Enforce a strict maximum request-body size and remove control characters before transmission. 5. Document that the healthcheck endpoint receives operational information and may be operated by a third party. 6. Add tests verifying that path-like strings, tokens, passwords, repository URLs, and oversized messages are redacted or rejected in healthcheck requests. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
bin/setup.sh:415
Finding
Root Cron Command Injection Through Insufficiently Validated Configuration Values<![CDATA[ ## Vulnerability Details **File Location**: `lib.sh:20-33`, `lib.sh:143-155`, and `bin/setup.sh:415-423` **Vulnerability Type**: Shell command injection in a generated root cron job **Risk Level**: High ### Complete Code Snippet Configuration values are loaded without path-specific safety validation: ```bash tc_load_config() { REPO=$(_cfg '.repository.path') PASS_FILE=$(_cfg '.repository.password_file') KEEP_LAST=$(_cfg '.retention.keep_last') LOG_FILE=$(_cfg '.logging.file') # shellcheck disable=SC2034 # Used by scripts sourcing lib.sh (e.g. backup.sh) VERBOSE=$(_cfg '.logging.verbose') CRON_EXPR=$(_cfg '.schedule.cron') ``` Cron validation only verifies the number of fields: ```bash # schedule.cron must look like a cron expression (5 fields) if [[ -n "$CRON_EXPR" && "$CRON_EXPR" != "null" ]]; then local field_count field_count=$(echo "$CRON_EXPR" | awk '{print NF}') if [[ "$field_count" -ne 5 ]]; then errors+=("schedule.cron must have 5 fields (got $field_count: '$CRON_EXPR')") fi fi ``` The values are then interpolated without shell quoting into a root cron entry: ```bash CRON_FILE="/etc/cron.d/time-clawshine" cat > "$CRON_FILE" <<EOF # Time Clawshine — scheduled backup # Generated by setup.sh on $(date) # Edit schedule in config.yaml, then re-run setup.sh $CRON_EXPR root TC_CONFIG=$CONFIG_FILE /usr/local/bin/time-clawshine >/dev/null 2>> $LOG_FILE EOF chmod 644 "$CRON_FILE" echo " Cron registered at: $CRON_FILE" ``` ### Technical Analysis The setup script runs as root and generates `/etc/cron.d/time-clawshine`. The generated command contains configuration-derived values, particularly `LOG_FILE`, without shell quoting or validation against shell metacharacters. A value such as the following is nonempty and therefore satisfies the existing required-field check: ```yaml logging: file: "/tmp/time-clawshine.log; /usr/bin/touch /root/cron-injection-success #" ``` When the setup path ...[truncated 2788 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Strictly validate every value used in generated system files. Require `logging.file`, the password file, repository path, and configuration path to be absolute paths without: - Newline or carriage-return characters - Shell metacharacters - Command substitutions - Control characters - Leading option characters where relevant 2. Validate cron expressions field by field rather than checking only the total field count. Reject comments, environment assignments, newlines, shell syntax, and malformed ranges. 3. Avoid constructing a shell command from configuration-derived text. Install a fixed wrapper and use a constant cron command, for example: ```cron 0 0 * * 1 root /usr/local/bin/time-clawshine-cron-wrapper ``` The wrapper should load the configuration internally without interpolating configuration values into shell source code. 4. If arguments must be emitted, use robust shell escaping such as `printf '%q'` and reject newlines before writing the cron file. Quoting alone should not replace strict validation. 5. Before privileged setup, verify that: - `config.yaml` is owned by root or the invoking administrator - It is not writable by group or other users - The Skill directory and scripts are not writable by untrusted users - A `TC_CONFIG` override does not refer to an untrusted file 6. Generate the cron file into a root-only temporary file, validate its complete contents, and atomically install it with controlled ownership and permissions. 7. Add regression tests using semicolons, command substitutions, backticks, spaces, quotes, and embedded newlines in every configuration value that reaches a systemd unit, cron file, or logrotate file. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (137)

External Script Fetching

High
Category
Supply Chain
Content
Auto-installed by `setup.sh`: `restic`, `yq` v4, `curl`, `jq`.
`yq` is downloaded from GitHub only when missing and is installed only after
SHA256 verification from the release checksum file. The setup script does not
use `curl | bash`.

## Platform support
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Self-Modification

High
Category
Rogue Agent
Content
Filesystem:
- Reads only the paths configured under `backup.paths` and `backup.extra_paths`.
- Writes encrypted snapshot data to `repository.path`.
- Restores snapshots to the chosen target; restoring to `/` can overwrite current files.
- Applies retention with `restic forget` and `restic prune`, which can remove old recovery points.
- `uninstall.sh --purge` can delete the repository, password file, and logs.
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description is for a broad encrypted backup/time-machine tool, but this specific code chunk implements a different feature: an interactive local analyzer/customizer for backup include/exclude settings. While it is related to the backup product, its actual behavior is not the described snapshot, restore, integrity-check, retention, alerting, prune/purge, or core setup functionality. It scans local directories, suggests whitelist/blacklist entries, writes to config.yaml after confirmation, and invokes setup.sh. Those are materially distinct capabilities not represented in the supplied description, so this chunk is mismatched to the declared purpose.

Self-Modification

High
Category
Rogue Agent
Content
**Time Clawshine gives OpenClaw a local encrypted time machine.** Every hour, restic takes an incremental snapshot of your agent's brain — memory, sessions, config, everything. Only changed bytes are stored, so backups stay fast and compact. When things break (and they will), you roll back by time, snapshot, or file to *exactly* the moment before it happened. Not yesterday. Not "the last backup." The exact hour.

Security note: this is a privileged backup/restore tool, not a narrow read-only helper. Setup can install packages and a scheduler with `sudo`; restore can overwrite current files; retention/prune can remove old recovery points; and optional external integrations can send minimal operational metadata only after explicit opt-in.

**One command to install. Zero maintenance. Just works.**
Confidence
94% confidence
Finding
The skill explicitly supports overwriting current files during restore, including agent memory, sessions, and config. This self-modification capability is contextually part of a backup tool, but it remains dangerous because it can erase current state, reintroduce compromised historical state, or materially alter future agent behavior.

Credential Access

High
Category
Privilege Escalation
Content
- **Telegram fires only on failure.** If the user has not configured `bot_token` and `chat_id`, failures are logged only.
- **This is the time machine layer.** It protects against "the agent broke something in the last 3 days." It is NOT a disaster recovery backup — that should be handled by an off-VM backup (e.g. restic to a remote server).
- **Password:** The restic repository is AES-256 encrypted. The password file location is configured in `config.yaml` (chmod 600). Losing it means losing access to all snapshots.
- **Never commit `secrets.env` or `.pass` files to git.** They are excluded via `.gitignore`.

---
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Chaining Abuse

High
Category
Tool Misuse
Content
# Remove cron if it exists (migrating to systemd)
        for legacy_cron in "/etc/cron.d/quick-backup-restore" "/etc/cron.d/time-clawshine"; do
            [[ -f "$legacy_cron" ]] && rm -f "$legacy_cron" && echo "    Removed legacy cron: $legacy_cron"
        done
    else
        echo "==> Registering cron job: [$CRON_EXPR]"
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Credential Access

High
Category
Privilege Escalation
Content
MSG_FILE=$(mktemp)
cp "$TC_ROOT/config.yaml" "$TMP_CONFIG"
yq e -i '.privacy.local_only = false | .privacy.send_error_details = false | .privacy.include_hostname = false | .notifications.telegram.enabled = true | .notifications.telegram.bot_token = "test-bot-token" | .notifications.telegram.chat_id = "test-chat-id"' "$TMP_CONFIG"
if TG_OUTPUT=$(MSG_FILE="$MSG_FILE" bash -c "export TC_CONFIG='$TMP_CONFIG' TC_SKIP_PASS_CHECK=true; source '$TC_ROOT/lib.sh'; tc_load_config; tg_send() { printf '%s' \"\$1\" > \"\$MSG_FILE\"; }; tg_failure 'secret path /root/.ssh/id_rsa token=abc123'; cat \"\$MSG_FILE\"" 2>&1); then
    if grep -Eq 'secret|/root/\.ssh|token=abc123|Host:' <<< "$TG_OUTPUT"; then
        _fail "message leaked raw detail: $TG_OUTPUT"
    else
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [[ " ${REMOVABLE[*]} " == *" systemd "* ]]; then
    systemctl stop time-clawshine.timer 2>/dev/null || true
    systemctl disable time-clawshine.timer 2>/dev/null || true
    rm -f /etc/systemd/system/time-clawshine.service /etc/systemd/system/time-clawshine.timer
    systemctl daemon-reload 2>/dev/null || true
    echo "    ✓ Systemd timer + service removed"
fi
Confidence
95% 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).

Session Persistence

Medium
Category
Rogue Agent
Content
- Refresh ClawHub positioning around the public name
  `Time Clawshine — OpenClaw Time Machine`.
- Rewrite the first-line description for discovery around restic, encrypted
  snapshots, time/snapshot/file restore, local-only privacy defaults, integrity,
  retention, and optional alerts.
- Split discovery tags into focused tags: backup, restore, restic, snapshots,
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.

Session Persistence

Medium
Category
Rogue Agent
Content
`FAILED (exit N)`; the script still exits non-zero on validation failure, but
  prints a short follow-up block pointing at the log and the retry command
  first. The field label `Cron` was renamed to `Scheduler` so it makes sense
  whether systemd timer or cron actually got installed.

- **Robust systemd detection + cleanup of the loser scheduler.** The
  systemd-presence check used `systemctl is-system-running`, which returns
Confidence
80% 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
94% confidence
Finding
The marketplace description promotes restore capabilities and mentions a safety gate for '/' but does not clearly warn that restore operations can overwrite existing local files, workspace state, agent memory, or configuration. In a backup/restore skill, omission of this warning can lead users to invoke destructive restores without understanding the consequences, increasing risk of data loss or corruption.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cd quick-backup-restore
bash bin/setup.sh --dry-run  # preview deps, files, scheduler, and privacy settings
nano config.yaml             # optional: review paths and opt in to integrations
sudo bin/setup.sh            # installs deps, initializes repo, registers scheduler
```

Or, repo-only setup (no apt-get, no cron, no /usr/local/bin changes):
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

Detected: suspicious.potential_exfiltration

Shell script base64-encodes a local file and sends it over the network.

Critical
Code
suspicious.potential_exfiltration
Location
bin/setup.sh:287