Back to skill

Security audit

backup-chain

Security checks for vulnerabilities and agentic risk

Overview

The skill is a legitimate local backup tool, but it needs Review because an optional ledger mode can run arbitrary shell commands saved in workspace configuration.

Install only if you are comfortable with a backup skill that can archive selected workspace files and restore them. Prefer the default file ledger or ledger none. If you use --ledger command, review .vault/config.json as executable code, keep it out of untrusted workspaces, and do not run backup-chain commands in a repository whose .vault/config.json you have not checked.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is tool parameter abuse: read_cmd is effectively a free-form shell payload supplied through tool configuration, then executed by the skill. In the context of an agent skill, untrusted project state or instructions can steer the tool into running attacker-chosen commands during seemingly harmless backup verification operations.

Content

Scanner excerpt · scripts/backup_chain.py (reported line 233)May include surrounding context.

python
return {"reachable": True, "max_gen": mx, "entries": entries,
                    "note": f"file ledger at {p}"}
        if adapter == "command":
            r = subprocess.run(led["read_cmd"], shell=True, capture_output=True,
                               text=True, timeout=120)
            if r.returncode != 0:
                note = f"ledger read command failed (rc={r.returncode}): {r.stderr.strip()[:200]}"

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

The append_cmd parameter is another arbitrary shell execution sink, this time on ledger registration. Because the skill is intended for backup workflows, users may not expect that enabling registration grants the project a generic ability to execute local commands, making misuse especially dangerous in agentized environments.

Content

Scanner excerpt · scripts/backup_chain.py (reported line 265)May include surrounding context.

python
os.fsync(f.fileno())
        return p
    if adapter == "command":
        r = subprocess.run(led["append_cmd"], shell=True, input=line.rstrip("\n") + "\n",
                           capture_output=True, text=True, timeout=120)
        if r.returncode != 0:
            raise GateError(f"ledger append command failed (rc={r.returncode}): "

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 201)May include surrounding context.

sh
print('YES' if 'skills/cache.txt' in m['members'] else 'NO')")"
[ "$INARCHIVE" = "NO" ] && ok "out-of-root link NOT in manifest (no external bytes)" || bad "out-of-root link reached the archive"
assert_has "drill still passes with boundary skip" "restore drill: PASS" "$OUT20"
rm -f skills/cache.txt

echo "== T16 broken symlink nested in a directory member (skipped, not a crash) =="
rm -f skills/linked.txt

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 204)May include surrounding context.

sh
rm -f skills/cache.txt

echo "== T16 broken symlink nested in a directory member (skipped, not a crash) =="
rm -f skills/linked.txt
ln -s "$WS_BASE/does-not-exist-15.txt" skills/broken.txt
OUT16="$($BC build --carries 'nested broken symlink' 2>&1)"; RC16=$?
check "build with nested broken symlink does not crash" 0 $RC16

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 209)May include surrounding context.

sh
OUT16="$($BC build --carries 'nested broken symlink' 2>&1)"; RC16=$?
check "build with nested broken symlink does not crash" 0 $RC16
assert_has "broken symlink skipped with a warning" "skipping broken symlink" "$OUT16"
rm -f skills/broken.txt

echo "== T17 'member add'/'member remove' with no path argument; bad recheck input =="
$BC member add >/dev/null 2>&1; check "member add w/o path is refused, not a crash" 2 $?

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 259)May include surrounding context.

sh
R21="$($BC restore "download/$T21A" --target "$TGT21" 2>&1)"
assert_has "refusal explains the fix" "remove or replace the symlink" "$R21"
[ ! -e "$WS_BASE/outside21/note.txt" ] && ok "refused restore wrote nothing outside target" || bad "restore escaped via symlinked dir"
rm -f "$TGT21/docs"

echo "== T22 final-component symlink: conflict, then --overwrite replaces the link itself =="
echo "outside22 bytes" > "$WS_BASE/outside22.txt"

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.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The invocation description is very broad and can trigger on generic terms like backups, snapshot, checkpoint, restore, or wipe recovery, including when the user may only want high-level advice or a simpler operation. Because this skill enables file writes and shell-adjacent behavior via a user-configured ledger command, over-invocation increases the chance of unnecessary exposure to destructive or sensitive filesystem operations in contexts where this heavyweight backup workflow is not appropriate.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The ledger read path executes led["read_cmd"] with shell=True, so reading backup metadata can trigger arbitrary shell commands. Because this skill operates on local workspaces and agent-controlled roots, a malicious project can plant or influence config that causes command execution when the agent merely checks backup state or restore fencing.

Content

Scanner excerpt · scripts/backup_chain.py (reported line 233)May include surrounding context.

python
return {"reachable": True, "max_gen": mx, "entries": entries,
                    "note": f"file ledger at {p}"}
        if adapter == "command":
            r = subprocess.run(led["read_cmd"], shell=True, capture_output=True,
                               text=True, timeout=120)
            if r.returncode != 0:
                note = f"ledger read command failed (rc={r.returncode}): {r.stderr.strip()[:200]}"

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

This is a true capability-expansion issue: the skill explicitly allows user-supplied ledger commands and executes them through the shell, giving the backup tool arbitrary command-execution behavior. In a security-sensitive agent environment, that crosses the boundary from backup logic into a generic shell runner, enabling malicious repos or prompts to abuse the skill for local code execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code executes shell commands from configuration without providing a runtime warning or confirmation at the point of execution. While the core issue is arbitrary command execution itself, the lack of strong user-facing warning increases the chance that operators will unknowingly enable a dangerous mode in an agent workflow.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The command-adapter path executes led["append_cmd"] with subprocess.run(..., shell=True), which turns a configuration value into arbitrary shell execution. In an agent skill context, that means a user prompt or workspace-controlled config can cause the skill to run unintended local commands with the agent's privileges, far beyond merely appending to a ledger.

Content

Scanner excerpt · scripts/backup_chain.py (reported line 265)May include surrounding context.

python
os.fsync(f.fileno())
        return p
    if adapter == "command":
        r = subprocess.run(led["append_cmd"], shell=True, input=line.rstrip("\n") + "\n",
                           capture_output=True, text=True, timeout=120)
        if r.returncode != 0:
            raise GateError(f"ledger append command failed (rc={r.returncode}): "

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 29)May include surrounding context.

sh
cd "$WS" || exit 1
echo "alpha" > worklog.md
mkdir -p skills/sk1 download
echo "skill body" > skills/sk1/SKILL.md
echo "deliverable" > download/report.md
$BC init --name demo --output-dir download >/dev/null 2>&1; check "init" 0 $?
$BC init >/dev/null 2>&1; check "re-init refused without --force" 2 $?

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · tests/test_backup_chain.sh (reported line 84)May include surrounding context.

sh
cd "$WS" || exit 1
echo "alpha" > worklog.md
mkdir -p skills/sk1 download
echo "skill body" > skills/sk1/SKILL.md
echo "deliverable" > download/report.md
$BC init --name demo --output-dir download >/dev/null 2>&1; check "init" 0 $?
$BC init >/dev/null 2>&1; check "re-init refused without --force" 2 $?

Static analysis

No suspicious patterns detected.