T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/install-widget.sh:18
- Finding
- Arbitrary File Overwrite Through Target-Name Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install-widget.sh`, lines 18–41 **Vulnerability Type**: Path traversal leading to arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```bash SOURCE_FILE="$1" TARGET_NAME="${2:-$(basename "$SOURCE_FILE")}" WIDGET_DIR="$HOME/Library/Application Support/Übersicht/widgets" APP_PATH="$(find_uebersicht_app || true)" if [ ! -f "$SOURCE_FILE" ]; then echo "Source widget not found: $SOURCE_FILE" >&2 exit 1 fi if [ -z "$APP_PATH" ]; then echo "Übersicht.app not found. Run bash scripts/setup.sh first." >&2 exit 1 fi if [ ! -d "$WIDGET_DIR" ]; then echo "Widget directory not found: $WIDGET_DIR" >&2 echo "Run bash scripts/setup.sh first so Übersicht can create the widget directory." >&2 exit 1 fi cp "$SOURCE_FILE" "$WIDGET_DIR/$TARGET_NAME" ``` ### Technical Analysis The optional target filename is taken directly from the second command-line argument and appended to the widget directory without validation or canonicalization: ```bash TARGET_NAME="${2:-$(basename "$SOURCE_FILE")}" cp "$SOURCE_FILE" "$WIDGET_DIR/$TARGET_NAME" ``` Although the default value uses `basename`, a caller-supplied value can contain directory traversal sequences such as `../`. The operating system resolves those sequences before the copy is performed, allowing the destination to escape the intended Übersicht widget directory. The script does not reject: - Absolute or traversal-based paths - Directory separators - Non-`.jsx` filenames - Existing destination files - Destinations whose canonical parent is outside the widget directory Because `cp` overwrites existing files by default, exploitation can replace any file writable by the current user. ### Attack Path A concrete exploitation sequence is: 1. Create a file containing attacker-controlled shell commands: ```bash cat > /tmp/payload <<'EOF' echo "attacker-controlled command" > /tmp/widgetdesk-executed EOF ``` 2. Invoke the i ...[truncated 1331 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Treat the target name strictly as a filename rather than a path. 1. Enforce a conservative lowercase kebab-case `.jsx` filename: ```bash if [[ ! "$TARGET_NAME" =~ ^[a-z0-9][a-z0-9-]*\.jsx$ ]]; then echo "Invalid target name: use lowercase kebab-case ending in .jsx" >&2 exit 1 fi ``` 2. Explicitly reject absolute paths, separators, and parent-directory references: ```bash if [[ "$TARGET_NAME" == /* || "$TARGET_NAME" == *"/"* || "$TARGET_NAME" == *"\\"* || "$TARGET_NAME" == "." || "$TARGET_NAME" == ".." ]]; then echo "Target name must be a filename, not a path" >&2 exit 1 fi ``` 3. Construct the destination only after validation: ```bash DESTINATION="$WIDGET_DIR/$TARGET_NAME" ``` 4. Canonicalize and verify the destination parent where platform support permits: ```bash canonical_widget_dir="$(cd "$WIDGET_DIR" && pwd -P)" canonical_parent="$(cd "$(dirname "$DESTINATION")" && pwd -P)" if [ "$canonical_parent" != "$canonical_widget_dir" ]; then echo "Destination escapes the widget directory" >&2 exit 1 fi ``` 5. Avoid silently overwriting an existing widget. Either refuse replacement by default: ```bash cp -n -- "$SOURCE_FILE" "$DESTINATION" ``` or require a separate explicit `--force` option before overwriting. 6. Add regression tests covering `../`, absolute paths, embedded separators, hidden files, malformed extensions, and valid kebab-case widget names. ]]>
