T09 · Insecure Skill Coding Practices
- Location
SKILL.md:61- Finding
Predictable Shared Temporary File Enables Blocklist Tampering and File Races
- Content
View full analysis
/tmp/blocklist.json && mv /tmp/blocklist.json ~/.pilot/blocklists/default.json ``` ```bash jq --arg agent "$AGENT" '.entries = [.entries[] | select(.hostname != $agent)]' \ ~/.pilot/blocklists/default.json > /tmp/blocklist.json && mv /tmp/blocklist.json ~/.pilot/blocklists/default.json ``` ```bash jq --arg agent "$AGENT" --arg node "$NODE_ID" --arg reason "$REASON" \ '.entries += [{hostname: $agent, node_id: $node, reason: $reason, blocked_at: (now | strftime("%Y-%m-%dT%H:%M:%SZ"))}] | .entries |= unique_by(.hostname)' \ "$BLOCKLIST" > /tmp/blocklist.json && mv /tmp/blocklist.json "$BLOCKLIST" ``` ### Technical Analysis Every blocklist update uses the same globally predictable path, `/tmp/blocklist.json`. The command does not create the file exclusively, verify its ownership or type, restrict its permissions, or isolate concurrent operations. A local process able to manipulate the temporary path may race the update or pre-create the path. Depending on operating-system protections and the relationship between the attacking and executing accounts, symbolic-link attacks may cause shell redirection to follow an unintended target. Even where protected-symlink and sticky-directory controls prevent cross-user exploitation, concurrent skill invocations under the same account can overwrite each other's temporary data or move stale content into the persistent blocklist. Because the temporary file is moved into a security-sensitive configuration path, interference can affect both the integrity and availability of d ...[truncated 1421 chars]- Remediation
View remediation
"$tmp" && jq -e . "$tmp" >/dev/null && mv -- "$tmp" "$BLOCKLIST"; then trap - EXIT HUP INT TERM else exit 1 fi ``` Additional hardening measures: - Keep the temporary file in the same directory as the destination so the final rename is atomic and does not cross filesystems. - Ensure `~/.pilot/blocklists` is owned by the expected account and is not writable by other users. - Set an appropriate restrictive `umask`, such as `umask 077`. - Use a lock, such as `flock`, if concurrent blocklist updates are possible. - Validate the generated JSON before replacing the active blocklist. ]]>
