T09 Β· Insecure Skill Coding Practices
- Location
open-app.sh:7- Finding
Directory Traversal Allows Directories Outside the App Root to Be Served Over the Network
- Content
View full analysis
/dev/null | jq -r '.nodes[0].displayName' 2>/dev/null)}" APPS_DIR="${CANVAS_APPS_DIR:-$HOME/.openclaw/workspace/apps}" if [ -z "$NODE" ] || [ "$NODE" = "null" ]; then echo "β No node found. Run: openclaw nodes status" exit 1 fi echo "π Opening $APP_NAME on port $PORT (node: $NODE)..." # Kill any existing server on this port lsof -ti:$PORT 2>/dev/null | xargs kill -9 2>/dev/null # Check app exists if [ ! -d "$APPS_DIR/$APP_NAME" ]; then echo "β App not found: $APPS_DIR/$APP_NAME" exit 1 fi # Start server cd "$APPS_DIR/$APP_NAME" python3 -m http.server $PORT > /dev/null 2>&1 & ``` ### Technical Analysis `APP_NAME` is accepted as an unrestricted command-line argument and appended directly to `APPS_DIR`. The directory existence check does not canonicalize the resulting path or verify that it remains beneath the configured application root. An input containing traversal components, such as `../../..`, can therefore cause the script to change into and serve a directory outside `~/.openclaw/workspace/apps`. In addition, `python3 -m http.server` binds to all available interfaces by default. Consequently, files in the escaped directory may be available not only to Canvas through localhost but also to other systems able to connect to the host. The port is also not validated as a numeric value in the permitted TCP port range. ### Attack Path 1. An attacker or untrusted caller invokes `open-app.sh` with an application name containing path traversal components. 2. The expression `"$APPS_DIR/$APP_NAME"` resolves to a directory outside the intended application root. 3. The `-d` check succeeds because the escaped target is an existing directory. 4. The script changes its wor ...[truncated 741 chars]- Remediation
View remediation
&2 exit 1 ;; esac ``` 2. Canonicalize both the app root and requested directory using `realpath`. 3. Verify that the canonical requested path starts with the canonical application root followed by a path separator. 4. Reject symlinks or resolve them before performing the containment check. 5. Bind the HTTP server explicitly to the loopback interface: ```bash python3 -m http.server "$PORT" --bind 127.0.0.1 ``` 6. Validate that `PORT` contains only digits and is between 1 and 65535. 7. Quote every use of the port argument. 8. Run the server under a minimally privileged account with access only to the intended app directory where feasible. ]]>
