T09 · Insecure Skill Coding Practices
Warning
- Location
- switch.sh:10
- Finding
- Incorrect Base-Directory Resolution Causes Cross-Skill File Access and State Overwrites<![CDATA[ ## Vulnerability Details **File Location**: `switch.sh:10-13`, `switch.sh:65-73`, `switch.sh:88-112`, `restore.sh:10-14`, `restore.sh:39-64`, and `summarize.sh:10-12` **Vulnerability Type**: Improper path resolution and violation of the intended skill-directory boundary **Risk Level**: Medium ### Vulnerable Code `switch.sh`, lines 10-13: ```bash SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CONTEXT_FILE="$SKILL_DIR/current-context.json" SNAPSHOTS_DIR="$SKILL_DIR/snapshots" MODES_DIR="$SKILL_DIR/modes" ``` `switch.sh`, lines 65-73: ```bash # Create snapshots dir if needed mkdir -p "$SNAPSHOTS_DIR" # Save current state before switching TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") if [[ -f "$CONTEXT_FILE" ]]; then cp "$CONTEXT_FILE" "$SNAPSHOTS_DIR/pre-switch-state.json" fi ``` `switch.sh`, lines 88-112: ```bash # Write new context state cat > "$CONTEXT_FILE" <<EOF { "current_mode": "$MODE", "emoji": "$EMOJI", "activated_at": "$TIMESTAMP", "restore_at": "$RESTORE_AT", "restore_trigger": "$([ -n "$DURATION_MINUTES" ] && echo "timer_${DURATION_MINUTES}min" || echo "manual_or_calendar")", "duration_minutes": "$DURATION_MINUTES", "muted_channels": $([ "$MODE" = "dnd" ] && echo '["all"]' || ([ "$MODE" = "creative" ] && echo '["all"]' || ([ "$MODE" = "work" ] && echo '["personal","social","news"]' || echo '["work_slack","work_email","github"]'))), "session_notes": "" } EOF # Output confirmation for OpenClaw to relay to user echo "$EMOJI Switched to $MODE mode." if [[ -n "$DURATION_MINUTES" ]]; then echo "Auto-restore in ${DURATION_MINUTES} minutes (at ${RESTORE_AT})." fi if [[ -n "$MODE_FILE" && -f "$MODE_FILE" ]]; then echo "" echo "Loading your $MODE profile..." cat "$MODE_FILE" fi ``` `restore.sh`, lines 10-14: ```bash SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CONTEXT_FILE="$SKILL_DIR/current-context.json" SNAPSHOTS_DIR="$SKILL_DIR/snapshots" DND_LOG="$SNAPSHOTS_DIR/dnd-log.json" PRE_SWI ...[truncated 5160 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve paths relative to the actual checked-in layout. If scripts remain in the project root, use: ```bash SKILL_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" CONTEXT_FILE="$SKILL_DIR/current-context.json" SNAPSHOTS_DIR="$SKILL_DIR/snapshots" ``` 2. Update profile paths to match the supplied layout: ```bash MODE_FILE="$SKILL_DIR/work.md" ``` Alternatively, move the scripts into `scripts/` and profiles into `modes/` so the implementation matches the documented structure. 3. Apply the same corrected base-directory logic consistently in `switch.sh`, `restore.sh`, and `summarize.sh`. 4. Before reading or writing, verify that every resolved path remains inside the canonical package directory. Reject paths that resolve outside it, including through symbolic links. 5. Create state directories with restrictive permissions: ```bash umask 077 mkdir -p -- "$SNAPSHOTS_DIR" chmod 700 -- "$SNAPSHOTS_DIR" ``` 6. Use atomic writes for state files by creating a temporary file inside the package directory, validating its JSON, setting restrictive permissions, and renaming it over the destination. 7. Do not relay arbitrary profile or log contents as trusted agent instructions. Treat these files as untrusted data, delimit them clearly, and ensure the agent is instructed not to interpret embedded text as executable instructions. 8. Add an installation-layout test that invokes each script and asserts that all read and write targets are descendants of the installed `context-switcher` directory. 9. Update `README.md`, `SKILL.md`, and the security manifests to reflect the real package layout and actual files accessed. ]]>
