Back to skill

Security audit

workspace-backup

Security checks for vulnerabilities and agentic risk

Overview

This is a user-directed workspace backup skill, but its own artifacts conflict about whether sensitive files may be copied into iCloud or other cloud-synced folders.

Review this before installing because it can read and copy substantial private workspace data, including secret files, and its cloud story is inconsistent. Only use destinations you intentionally want, avoid iCloud or other synced folders unless you accept cloud exposure, review ~/.workspace-backup/config.json before real runs, keep the default rsync binary, and supervise the first full backup.

Vulnerability Patterns
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def detect_copier(rsync_bin=RSYNC):
    try:
        p = subprocess.run([rsync_bin, "--version"], capture_output=True, text=True, timeout=20)
        out = (p.stdout or "") + (p.stderr or "")
    except Exception as e:
        return "absent", str(e)
Confidence
86% confidence
Finding
The script allows a caller-controlled --rsync-bin path to be executed directly via subprocess.run. Although it is not passed through a shell, this still permits arbitrary local program execution if an attacker can influence CLI arguments or the surrounding workflow, which is especially relevant in an automation skill that handles backups.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
os.makedirs(d, exist_ok=True)
        with open(os.path.join(s, "probe.txt"), "w") as f:
            f.write("probe\n")
        p = subprocess.run([rsync_bin, "-a", flag, "--dry-run", s + os.sep, d + os.sep],
                           capture_output=True, timeout=30)
        return p.returncode == 0
    except Exception:
Confidence
84% confidence
Finding
The probe routine executes the caller-supplied rsync_bin with real arguments against temporary directories. Because rsync_bin can come from --rsync-bin, an attacker who can supply that value can cause arbitrary local binary execution under the privileges of the backup process.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill clearly orchestrates local file reads, writes, and shell-script execution, but it declares no permissions or capability boundaries. That mismatch is dangerous because a backup skill inherently has broad filesystem reach; without explicit permission declarations, users and enforcement layers may not understand or constrain what the skill can access or modify.

Description-Behavior Mismatch

High
Confidence
94% confidence
Finding
The changelog explicitly describes an iCloud Drive destination and repeated runs verifying backups to iCloud, which directly contradicts the skill metadata claiming a PURE LOCAL backup limited to a fixed local folder and an external drive. This kind of documentation/behavior mismatch is security-relevant because users may expose sensitive workspace data to cloud storage when they were told the skill would remain local-only.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The changelog claims the skill documentation explicitly names cloud destinations as out of scope, yet other changelog entries discuss successful operation against an iCloud destination. That inconsistency can mislead operators about where their files may be copied, undermining informed consent and increasing the risk of unintended data disclosure.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The status tool is documented and primarily expected to report backup state, but the --record-rework path appends a journal event and therefore mutates state. This creates a hidden side effect in a read-oriented command surface, which can be abused by any caller with access to run status.py to tamper with audit/history data or create misleading operational records.

Unvalidated Output Injection

High
Category
Output Handling
Content
def xattr_names(path):
    try:
        p = subprocess.run(["/usr/bin/xattr", path], capture_output=True, text=True, timeout=20)
        return sorted(x for x in (p.stdout or "").split() if x)
    except Exception:
        return None
Confidence
68% confidence
Finding
The call to /usr/bin/xattr passes a filesystem path directly as a positional argument without a '--' terminator. On Unix tools, a filename beginning with '-' can be parsed as an option instead of a path, which can make verification misread metadata or fail unpredictably on attacker-controlled filenames in the workspace, undermining backup integrity claims.

Session Persistence

Medium
Category
Rogue Agent
Content
configured destination has no `.workspace-backup-dest.json` marker. Every other
run for the life of the skill skips this file entirely.

## 1. Write the config

Do not ask the user to compose one from nothing — start from the measured
defaults for this machine and show them for confirmation:
Confidence
72% confidence
Finding
This instruction establishes persistent state by writing configuration and destination markers under the user's home directory, which can affect future runs and backup routing. Although the skill is clearly intended to manage local backups and repeatedly emphasizes confirmations, persistence still matters because incorrect or stale config can silently influence later behavior, including what gets copied to removable media.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
references/destination-policy.md:157