Back to skill

Security audit

Restic Home Backup

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its backup purpose, but its privileged bootstrap script can turn crafted setup values into root shell execution.

Review or patch the bootstrap before installing. Do not run it with --apply, --init-repo, or --enable-timers unless repository, password-file, username, and timezone inputs are strictly trusted and safely escaped; verify generated /etc/restic-home.env and systemd units before enabling timers.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bootstrap_restic_home.sh:97
Finding
Root Command Injection Through Shell-Sourced Configuration Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bootstrap_restic_home.sh:29-32, 97-102, 108-130, 136-198, 214-215` **Vulnerability Type**: Shell command injection through unsafe configuration generation **Risk Level**: High ### Vulnerable Code User-controlled arguments are accepted without validation: ```bash --user) USER_NAME="$2"; shift 2 ;; --repo) REPO="$2"; shift 2 ;; --password-file) PASS_FILE="$2"; shift 2 ;; --timezone) TIMEZONE="$2"; shift 2 ;; ``` The values are written verbatim into a file that is subsequently interpreted as shell code: ```bash cat >/etc/restic-home.env <<EOF RESTIC_REPOSITORY=${REPO} RESTIC_PASSWORD_FILE=${PASS_FILE} BACKUP_SOURCE=${HOME_DIR} EXCLUDES_FILE=/etc/restic-home/excludes.txt EOF chmod 600 /etc/restic-home.env ``` Every generated operational script sources that file: ```bash cat >/usr/local/bin/restic-home-backup.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic backup "$BACKUP_SOURCE" --exclude-file "$EXCLUDES_FILE" EOF chmod 755 /usr/local/bin/restic-home-backup.sh cat >/usr/local/bin/restic-home-prune.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune EOF chmod 755 /usr/local/bin/restic-home-prune.sh cat >/usr/local/bin/restic-home-check.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic check EOF chmod 755 /usr/local/bin/restic-home-check.sh ``` Repository initialization also directly sources the generated file: ```bash if [[ "$INIT_REPO" == "yes" ]]; then source /etc/restic-home.env if ! /usr/bin/restic snapshots >/dev/null 2>&1; then /usr/bin/restic init fi fi ``` ### Technical Analysis `REPO` and `PASS_FILE` are attacker-influenced command-line values. They are inserted into `/etc/restic-home.env` without shell escaping, quoting, newline rejection, or format validation. Becau ...[truncated 3160 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Do not source data files as shell code.** Replace `/etc/restic-home.env` with a configuration format parsed as data. For example, place fixed environment assignments directly in systemd unit definitions using properly escaped values, or use a dedicated parser that rejects shell syntax. 2. **Apply strict input validation.** - Reject newlines, carriage returns, NUL bytes, command substitutions, and shell control characters. - Validate repository values against explicitly supported restic repository formats and schemes. - Require `PASS_FILE` to be an absolute path under an approved directory such as `/etc/restic-home/`. - Validate `USER_NAME` using the operating system account database rather than constructing `/home/${USER_NAME}` directly. - Validate `TIMEZONE` against installed IANA timezone identifiers. 3. **If shell assignments cannot be avoided, serialize values safely.** Generate assignments with Bash-safe escaping, such as: ```bash { printf 'RESTIC_REPOSITORY=%q\n' "$REPO" printf 'RESTIC_PASSWORD_FILE=%q\n' "$PASS_FILE" printf 'BACKUP_SOURCE=%q\n' "$HOME_DIR" printf 'EXCLUDES_FILE=%q\n' '/etc/restic-home/excludes.txt' } > /etc/restic-home.env ``` Strict validation should still be performed before serialization. 4. **Remove duplicate shell interpretation.** The systemd units already specify `EnvironmentFile=/etc/restic-home.env`; generated scripts should not source the same file. Prefer fixed executables that consume environment variables supplied by systemd. 5. **Reduce service privileges.** Run backup operations under a dedicated restricted account where repository and source permissions permit. If root access is required to read the entire home directory, add systemd hardening controls appropriate for restic, including: - `NoNewPrivileges=true` - `PrivateTmp=true` - `ProtectSystem=strict` - Explicit `ReadOnlyPaths=` for backup sources - Explicit `R ...[truncated 526 chars]
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill clearly directs shell-based system administration actions such as writing files under /etc, installing scripts in /usr/local/bin, and managing systemd units, but it does not declare an explicit tool scope or allowed-tools boundary. This increases the chance that an agent may execute broader shell actions than intended, reducing auditability and containment for a workflow that touches privileged paths and backup credentials.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- Never print secrets or tokens in chat/log output.
  - Never delete snapshots/repositories without explicit user confirmation.
  - Never weaken permissions on credential files (`chmod 600` minimum).
  - Never claim backup success without checking command exit status and snapshot listing.
  - Never apply system changes implicitly: require explicit `--apply` (or explicit user confirmation) before writing to `/etc`, `/usr/local/bin`, or `/etc/systemd/system`.

## Workflow
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The publication workflow introduces packaging and publishing to ClawHub CLI, which is unrelated to configuring and validating encrypted home-directory backups. Adding an external distribution step broadens the skill's operational scope and can lead an agent to perform networked release actions, potentially exposing internal artifacts or publishing unreviewed content outside the user's original backup-management intent.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Wrong password
Symptoms: `wrong password or no key found`
- Confirm `/etc/restic-home.env` points to correct `RESTIC_PASSWORD_FILE`.
- Confirm password file permissions (`chmod 600`).

### Repo unreachable
Symptoms: timeout / connection refused
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Wrong password
Symptoms: `wrong password or no key found`
- Confirm `/etc/restic-home.env` points to correct `RESTIC_PASSWORD_FILE`.
- Confirm password file permissions (`chmod 600`).

### Repo unreachable
Symptoms: timeout / connection refused
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Wrong password
Symptoms: `wrong password or no key found`
- Confirm `/etc/restic-home.env` points to correct `RESTIC_PASSWORD_FILE`.
- Confirm password file permissions (`chmod 600`).

### Repo unreachable
Symptoms: timeout / connection refused
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Wrong password
Symptoms: `wrong password or no key found`
- Confirm `/etc/restic-home.env` points to correct `RESTIC_PASSWORD_FILE`.
- Confirm password file permissions (`chmod 600`).

### Repo unreachable
Symptoms: timeout / connection refused
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Wrong password
Symptoms: `wrong password or no key found`
- Confirm `/etc/restic-home.env` points to correct `RESTIC_PASSWORD_FILE`.
- Confirm password file permissions (`chmod 600`).

### Repo unreachable
Symptoms: timeout / connection refused
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
#   bash bootstrap_restic_home.sh --user pi --repo /mnt/backup/restic-home
#
# Apply changes:
#   sudo bash bootstrap_restic_home.sh --user pi --repo /mnt/backup/restic-home --apply
#
# Optional:
#   --password-file /etc/restic-home/password
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
#   bash bootstrap_restic_home.sh --user pi --repo /mnt/backup/restic-home
#
# Apply changes:
#   sudo bash bootstrap_restic_home.sh --user pi --repo /mnt/backup/restic-home --apply
#
# Optional:
#   --password-file /etc/restic-home/password
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
source /etc/restic-home.env
exec /usr/bin/restic backup "$BACKUP_SOURCE" --exclude-file "$EXCLUDES_FILE"
EOF
chmod 755 /usr/local/bin/restic-home-backup.sh

cat >/usr/local/bin/restic-home-prune.sh <<'EOF'
#!/usr/bin/env bash
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
source /etc/restic-home.env
exec /usr/bin/restic backup "$BACKUP_SOURCE" --exclude-file "$EXCLUDES_FILE"
EOF
chmod 755 /usr/local/bin/restic-home-backup.sh

cat >/usr/local/bin/restic-home-prune.sh <<'EOF'
#!/usr/bin/env bash
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
source /etc/restic-home.env
exec /usr/bin/restic backup "$BACKUP_SOURCE" --exclude-file "$EXCLUDES_FILE"
EOF
chmod 755 /usr/local/bin/restic-home-backup.sh

cat >/usr/local/bin/restic-home-prune.sh <<'EOF'
#!/usr/bin/env bash
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
systemctl daemon-reload

if [[ "$ENABLE_TIMERS" == "yes" ]]; then
  systemctl enable --now restic-home-backup.timer restic-home-prune.timer restic-home-check.timer
fi

if [[ "$INIT_REPO" == "yes" ]]; then
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.

Static analysis

No suspicious patterns detected.