Back to skill

Security audit

Little7 Bcdr

Security checks for vulnerabilities and agentic risk

Overview

This backup skill is coherent, but it handles identity, memory, local skills, and optional secrets in ways that need careful review before use.

Review this before installing or automating it. Use it only if you understand that selected secrets may be archived in plaintext and synced to Google Drive. Prefer adding encryption, restrictive umask/permissions, secure temporary-directory creation, and explicit review of the secret allowlist before enabling cron-driven backups.

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/little7_backup.sh:18
Finding
Predictable and Insecure Temporary Directory Used for Sensitive Backup Staging<![CDATA[ ## Vulnerability Details **File Location**: `scripts/little7_backup.sh`, lines 18–34 **Vulnerability Type**: Predictable temporary directory with unenforced access permissions **Risk Level**: High ### Vulnerable Code ```bash DATE="$(date +%F)" TMP_BASE="${TMPDIR:-/tmp}/little7-backup-${MODE}-${DATE}-$$" STAGE_STATE="$TMP_BASE/state" STAGE_SECRETS="$TMP_BASE/secrets" OUT_DIR="$DRIVE_BASE/$MODE" LATEST_DIR="$DRIVE_BASE/latest" RESTORE_DIR="$DRIVE_BASE/restore-notes" STATE_NAME="little7-${MODE}-${DATE}.tar.gz" SECRETS_NAME="little7-${MODE}-secrets-${DATE}.tar.gz" STATE_OUT="$OUT_DIR/$STATE_NAME" SECRETS_OUT="$OUT_DIR/$SECRETS_NAME" LATEST_STATE="$LATEST_DIR/little7-${MODE}-latest.tar.gz" LATEST_SECRETS="$LATEST_DIR/little7-${MODE}-secrets-latest.tar.gz" MANIFEST="$TMP_BASE/manifest.txt" mkdir -p "$STAGE_STATE" "$STAGE_SECRETS" "$OUT_DIR" "$LATEST_DIR" "$RESTORE_DIR" cleanup() { rm -rf "$TMP_BASE"; } trap cleanup EXIT ``` ### Technical Analysis The staging path is constructed from a fixed prefix, backup mode, current date, and process ID. These values are predictable or observable on a multi-user system. The directory is created using `mkdir -p` instead of an atomic temporary-directory facility such as `mktemp -d`. The script also does not establish a restrictive `umask` or explicitly set the staging directory to mode `0700`. Consequently, the effective permissions depend on the invoking process's environment and existing filesystem objects. Because `mkdir -p` accepts existing directory components, a local attacker may pre-create a predicted staging path or manipulate its components before the backup process uses it. The staging area subsequently receives identity files, memory, scripts, and explicitly allowlisted secrets. ### Attack Path 1. A local attacker determines the backup schedule and predicts or observes candidate process IDs. 2. The attacker pre-creates a matching path under `/tmp`, or manipulates a directory component used by the backu ...[truncated 1183 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Create the temporary directory atomically and enforce private permissions before copying any data: ```bash umask 077 TMP_ROOT="${TMPDIR:-/tmp}" TMP_BASE="$(mktemp -d "$TMP_ROOT/little7-backup-${MODE}-${DATE}-XXXXXXXX")" chmod 700 "$TMP_BASE" STAGE_STATE="$TMP_BASE/state" STAGE_SECRETS="$TMP_BASE/secrets" mkdir -m 700 "$STAGE_STATE" "$STAGE_SECRETS" ``` Additional hardening should include: 1. Validate that `TMPDIR` is an absolute path to a trusted directory. 2. Reject a `TMPDIR` that is unexpectedly writable or controlled by an untrusted party. 3. Store the exact path returned by `mktemp` and only remove that path during cleanup. 4. Verify ownership and permissions before staging sensitive content. 5. Consider staging secret material on an encrypted filesystem or avoiding plaintext secret staging entirely. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/little7_backup.sh:101
Finding
Unencrypted Secret Archives Created Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/little7_backup.sh`, lines 101–111 and 152–155 **Vulnerability Type**: Plaintext sensitive-data storage and insufficient permission enforcement **Risk Level**: High ### Vulnerable Code ```bash if [ -n "$SECRET_PATHS_FILE" ] && [ -f "$SECRET_PATHS_FILE" ]; then while IFS= read -r line; do line="${line#${line%%[![:space:]]*}}" line="${line%${line##*[![:space:]]}}" [ -n "$line" ] || continue case "$line" in \#*) continue ;; esac copy_abs_secret "$line" done < "$SECRET_PATHS_FILE" fi ``` ```bash ( cd "$STAGE_STATE" && tar czf "$STATE_OUT" . ) ( cd "$STAGE_SECRETS" && tar czf "$SECRETS_OUT" . ) cp -f "$STATE_OUT" "$LATEST_STATE" cp -f "$SECRETS_OUT" "$LATEST_SECRETS" ``` ### Technical Analysis Files listed in the secret-path allowlist are copied into a staging directory and packaged using `tar czf`. Gzip compression does not provide encryption, authentication, or access control. Anyone who obtains the resulting archive can extract its original contents. The script does not set `umask 077`, explicitly create the archive with mode `0600`, or verify destination permissions. It also creates an additional plaintext “latest” copy. The default destination is a Google Drive synchronization directory, so the archive may be uploaded to cloud storage and replicated to other systems. Although the documentation recommends keeping secret recovery material tightly permissioned, the implementation does not enforce that requirement. Security therefore depends on ambient umask, local directory permissions, cloud-account security, and sharing configuration. ### Attack Path 1. The operator configures `LITTLE7_SECRET_PATHS_FILE`, or uses the default allowlist, to include credentials, private keys, tokens, or other sensitive recovery data. 2. The backup copies those files into the plaintext staging hierarchy. 3. The script creates a gzip-compressed tar archive without encryption. 4. Th ...[truncated 1066 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Secret backups should be encrypted with an authenticated encryption mechanism before being written to synchronized storage. For example, use `age` with a dedicated recovery recipient: ```bash umask 077 SECRET_TAR="$TMP_BASE/secrets.tar" ( cd "$STAGE_SECRETS" tar cf "$SECRET_TAR" . ) age \ --recipient "$LITTLE7_AGE_RECIPIENT" \ --output "$SECRETS_OUT.age" \ "$SECRET_TAR" chmod 600 "$SECRETS_OUT.age" rm -f "$SECRET_TAR" ``` A complete hardening plan should: 1. Set `umask 077` at the beginning of the script. 2. Require encryption whenever a secret allowlist is configured. 3. Fail closed if the encryption recipient or key configuration is missing. 4. Create secret output directories with mode `0700`. 5. Create encrypted archives with mode `0600`. 6. Use atomic temporary output files and rename them into place only after successful encryption and validation. 7. Store secrets separately from ordinary state archives and apply a distinct retention policy. 8. Avoid creating an additional plaintext “latest” copy. 9. Verify cloud-sharing and synchronization settings. 10. Document and test key recovery so encryption does not undermine disaster-recovery availability. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/little7_backup.sh:87
Finding
Unsafe Shell Word Splitting During Backup File Enumeration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/little7_backup.sh`, lines 87–90 **Vulnerability Type**: Unsafe filename handling through command substitution **Risk Level**: Medium ### Vulnerable Code ```bash for path in $(find "$ROOT/scripts" "$ROOT/skills" -type f 2>/dev/null | sort); do should_skip_state_path "$path" && continue copy_rel "${path#$ROOT/}" done ``` ### Technical Analysis The output of `find` is consumed through unquoted command substitution. Bash performs word splitting and pathname expansion on the resulting text before assigning values to `path`. As a result: - Filenames containing spaces or tabs are split into multiple loop iterations. - Filenames containing newlines cannot be represented safely. - Filename components containing shell glob characters may undergo pathname expansion. - Required files can be silently omitted or incorrect paths can be passed to `copy_rel`. The script uses `set -e`, but `copy_rel` deliberately returns successfully when a reconstructed path does not exist. Therefore, malformed path splitting can result in silent data loss rather than a backup failure. ### Attack Path 1. A required file under `scripts` or `skills` has a filename containing whitespace, a newline, or shell glob metacharacters. 2. `find` emits the filename as ordinary newline-delimited text. 3. Command substitution removes trailing newlines and Bash performs word splitting and pathname expansion. 4. The loop processes fragments or expanded names instead of the original filename. 5. `copy_rel` skips nonexistent fragments without reporting an error, or copies unintended matching files. 6. The backup completes and may pass its limited member checks even though required content is absent. 7. During disaster recovery, the omitted script or skill is unavailable. A user who can create files in the backed-up `scripts` or `skills` directories can deliberately trigger the behavior. It can also occur accidentally with legitimate file ...[truncated 631 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use null-delimited filenames throughout enumeration and avoid command substitution: ```bash while IFS= read -r -d '' path; do should_skip_state_path "$path" && continue copy_rel "${path#$ROOT/}" done < <( find "$ROOT/scripts" "$ROOT/skills" -type f -print0 2>/dev/null ) ``` If deterministic ordering is required and the platform supports it, use null-safe sorting: ```bash while IFS= read -r -d '' path; do should_skip_state_path "$path" && continue copy_rel "${path#$ROOT/}" done < <( find "$ROOT/scripts" "$ROOT/skills" -type f -print0 2>/dev/null | sort -z ) ``` Additional controls should include: 1. Record every selected source path in the manifest. 2. Treat a failed copy as an error instead of silently continuing. 3. Compare selected source files with archived members after archive creation. 4. Add tests covering spaces, tabs, newlines, wildcard characters, and leading hyphens in filenames. 5. Expand the health check beyond three fixed files so that backup completeness can be verified against the generated manifest. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep

Static analysis

No suspicious patterns detected.