T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate-digest.sh:9
- Finding
- Path Traversal Through Unvalidated Date Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-digest.sh`, lines 9-11, 16-19, and 61-64 **Vulnerability Type**: Path traversal resulting in unauthorized file reads and file overwrites **Risk Level**: Medium ### Vulnerable Code ```bash MEMORY_DIR="$HOME/clawd/memory" DATE="${1:-$(date -u +%Y-%m-%d)}" LOG_FILE="$MEMORY_DIR/$DATE.md" DIGEST_FILE="$MEMORY_DIR/digests/$DATE-digest.md" # Check if log exists if [ ! -f "$LOG_FILE" ]; then echo "Error: No log found at $LOG_FILE" exit 1 fi # Generate digest skeleton mkdir -p "$MEMORY_DIR/digests" cat > "$DIGEST_FILE" << EOF ``` ### Technical Analysis The command-line argument is documented as a date in `YYYY-MM-DD` format, but the script neither validates that format nor verifies that the resulting paths remain inside `$MEMORY_DIR`. The attacker-controlled `DATE` value is directly embedded in both `LOG_FILE` and `DIGEST_FILE`. Although the variables are correctly quoted and therefore do not permit shell command injection, quoting does not prevent filesystem traversal through `../` components. By supplying enough parent-directory components, a caller can make the source path resolve outside `$HOME/clawd/memory` and make the destination path resolve outside `$HOME/clawd/memory/digests`. The `.md` and `-digest.md` suffixes restrict which filenames can be targeted, but do not enforce the intended directory boundary. The script reads the selected source file through commands such as `wc`, `grep`, `sed`, and `awk`, then incorporates extracted content into a newly generated digest file. ### Attack Path 1. The attacker creates or identifies a readable file outside the memory directory whose name ends in `.md`. 2. The attacker supplies a traversal string instead of a valid date, for example: ```bash ./scripts/generate-digest.sh "../../../../../../tmp/source" ``` 3. After path normalization, `LOG_FILE` can resolve to `/tmp/source.md`, assuming a typical home-directory layout and ...[truncated 1364 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce the documented date format before constructing any path: ```bash if [[ ! "$DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then echo "Error: Date must use YYYY-MM-DD format" >&2 exit 1 fi ``` 2. Validate that the value represents a real calendar date: ```bash if ! parsed_date=$(date -u -d "$DATE" +%Y-%m-%d 2>/dev/null) || [[ "$parsed_date" != "$DATE" ]]; then echo "Error: Invalid calendar date" >&2 exit 1 fi ``` Use a platform-appropriate equivalent where GNU `date` is unavailable. 3. Resolve and verify the source and destination paths before accessing them. Their canonical paths must remain beneath the intended directories: ```bash MEMORY_DIR=$(realpath "$HOME/clawd/memory") DIGEST_DIR="$MEMORY_DIR/digests" mkdir -p "$DIGEST_DIR" LOG_FILE=$(realpath -m "$MEMORY_DIR/$DATE.md") DIGEST_FILE=$(realpath -m "$DIGEST_DIR/$DATE-digest.md") case "$LOG_FILE" in "$MEMORY_DIR"/*) ;; *) echo "Error: Source path escapes memory directory" >&2; exit 1 ;; esac case "$DIGEST_FILE" in "$DIGEST_DIR"/*) ;; *) echo "Error: Destination path escapes digest directory" >&2; exit 1 ;; esac ``` 4. Use restrictive creation permissions where digests may contain sensitive information: ```bash umask 077 ``` 5. Consider refusing to overwrite an existing digest unless the user explicitly supplies an overwrite option. If overwriting is required, write to a securely created temporary file in the digest directory and atomically rename it after successful generation. ]]>
