T09 · Insecure Skill Coding Practices
Warning
- Location
- safe-change.sh:61
- Finding
- Edit-script failure bypasses automatic configuration rollback<![CDATA[ ## Vulnerability Details **File Location**: `safe-change.sh`, lines 2 and 61–78 **Vulnerability Type**: Improper error handling resulting in rollback bypass **Risk Level**: Medium ### Vulnerable Code ```bash set -euo pipefail echo "[2/5] apply main change via script" bash "$MAIN_SCRIPT" if [[ -n "$SECONDARY_SCRIPT" ]]; then echo "[2b/5] apply secondary change via script" bash "$SECONDARY_SCRIPT" fi rollback() { echo "[rollback] restoring backups..." cp "$MAIN_BAK" "$MAIN_CFG" || true if [[ -f "$SECONDARY_BAK" ]]; then cp "$SECONDARY_BAK" "$SECONDARY_CFG" || true; fi openclaw gateway restart >/dev/null 2>&1 || true if command -v launchctl >/dev/null 2>&1; then launchctl kickstart -k "gui/$(id -u)/ai.openclaw.gateway.secondary" >/dev/null 2>&1 || true fi echo "[rollback] done" } ``` ### Technical Analysis The script enables Bash's immediate-exit behavior through `set -e`. It then executes the main and optional secondary edit scripts directly. If either delegated script exits with a nonzero status, `safe-change.sh` terminates immediately. The `rollback` function is defined only after the edit scripts are executed, and no `ERR`, `EXIT`, `INT`, or `TERM` trap is installed to restore the backups when execution terminates unexpectedly. Rollback is invoked only for explicit validation failures later in the script. Consequently, an edit script can partially modify an OpenClaw configuration file and then fail before validation. The modified configuration remains on disk even though a backup was successfully created. This contradicts the documented guarantee in `SKILL.md` that failures trigger automatic rollback. ### Attack Path 1. A user or automation process invokes `safe-change.sh` with a faulty or attacker-controlled edit script. 2. The runner creates a backup of the existing OpenClaw configuration. 3. The delegated edit script partially overwrites or corrupts `openclaw.json`. 4. The edit script exits with a nonzero status, ...[truncated 1209 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Define the rollback function before executing either delegated edit script. 2. Track whether backups were created and whether configuration changes have started. 3. Install guarded `ERR`, `INT`, and `TERM` traps immediately after successful backup creation. 4. Disable the traps only after all edits and validation checks complete successfully. 5. Preserve and return the original failure status after rollback. 6. Prevent recursive rollback by disabling traps at the beginning of the rollback handler. 7. Consider writing edited configurations to temporary files, validating them, and replacing the active files atomically only after validation succeeds. 8. Add automated tests covering: - Main edit-script failure after a partial write. - Secondary edit-script failure after the main edit succeeds. - Validation failure. - Interruption by `SIGINT` or `SIGTERM`. - Rollback failure and reporting. Example hardening pattern: ```bash rollback_required=0 rollback() { local status="${1:-1}" trap - ERR INT TERM if [[ "$rollback_required" -eq 1 ]]; then echo "[rollback] restoring backups..." cp -- "$MAIN_BAK" "$MAIN_CFG" || true if [[ -f "$SECONDARY_BAK" ]]; then cp -- "$SECONDARY_BAK" "$SECONDARY_CFG" || true fi openclaw gateway restart >/dev/null 2>&1 || true fi exit "$status" } trap 'rollback $?' ERR trap 'rollback 130' INT trap 'rollback 143' TERM rollback_required=1 bash "$MAIN_SCRIPT" if [[ -n "$SECONDARY_SCRIPT" ]]; then bash "$SECONDARY_SCRIPT" fi # Perform all validation here. rollback_required=0 trap - ERR INT TERM ``` The production implementation should also distinguish failures that occur before any configuration modification from failures requiring a service restart, avoiding unnecessary restarts where possible. ]]>
