T09 · Insecure Skill Coding Practices
Warning
- Location
- PUBLISH-API.md:68
- Finding
- Predictable Shared Temporary Files Permit Symlink Attacks and Cross-Session Data Corruption<![CDATA[ ## Vulnerability Details **File Location**: `PUBLISH-API.md:68-80, 100-113, 134-153`; `DRAFTING.md:80-83`; `PHOTO-INGESTION.md:66-72` **Vulnerability Type**: Predictable temporary files, symlink following, and unsafe concurrent access **Risk Level**: Medium ### Vulnerable Code `PUBLISH-API.md:68-80`: ```sh xurl media upload "$MEDIA_PATH" > "${TMPDIR:-/tmp}/upload.out" MEDIA_ID="$(jq -Rrs '[split("\n")[] | fromjson? | .data.id? // empty] | last // empty' \ < "${TMPDIR:-/tmp}/upload.out")" ``` Fallback processing uses the same predictable file: ```sh MEDIA_ID="$(grep -oE '"id"[[:space:]]*:[[:space:]]*"[0-9]+"' "${TMPDIR:-/tmp}/upload.out" \ | head -1 | grep -oE '[0-9]+')" ``` `PUBLISH-API.md:100-113`: ```sh cat > "${TMPDIR:-/tmp}/draft.txt" <<'XPOSTER_EOF_3f9c1a' <tweet text verbatim> XPOSTER_EOF_3f9c1a BODY="$(jq -Rsc '{text: rtrimstr("\n")}' < "${TMPDIR:-/tmp}/draft.txt")" xurl -X POST /2/tweets -d "$BODY" ``` The media variant also reads from the same fixed path: ```sh BODY="$(jq -Rsc --arg mid "$MEDIA_ID" \ '{text: rtrimstr("\n"), media: {media_ids: [$mid]}}' < "${TMPDIR:-/tmp}/draft.txt")" xurl -X POST /2/tweets -d "$BODY" ``` `PUBLISH-API.md:134-153`: ```sh cat > "${TMPDIR:-/tmp}/reply.txt" <<'XPOSTER_EOF_3f9c1a' <reply text verbatim> XPOSTER_EOF_3f9c1a RBODY="$(jq -Rsc --arg tid "$TWEET_ID" \ '{text: rtrimstr("\n"), reply: {in_reply_to_tweet_id: $tid}}' < "${TMPDIR:-/tmp}/reply.txt")" xurl -X POST /2/tweets -d "$RBODY" ``` ```text Return the permalink(s) and delete the temp files (`draft.txt`, `reply.txt`, `upload.out`). ``` `DRAFTING.md:80-83`: ```sh cat > "${TMPDIR:-/tmp}/draft.txt" <<'XPOSTER_EOF_3f9c1a' <paste the draft text here, verbatim> XPOSTER_EOF_3f9c1a ``` `PHOTO-INGESTION.md:66-72`: ```sh {baseDir}/ingest-photo.sh --dir "$PWD/postflight-state/<dir>" \ --note-file "${TMPDIR:-/tmp}/note.txt" \ --location-file "${TMPDIR:-/tmp}/loc.txt" \ --name <slug-you-composed> --taken <date-if-override> \ "<staged path> ...[truncated 2853 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create a unique private temporary directory for every invocation: ```sh umask 077 tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/postflight.XXXXXXXX")" || exit 1 trap 'rm -rf -- "$tmpdir"' EXIT HUP INT TERM ``` 2. Store every temporary artifact inside that directory: ```sh draft_file="$tmpdir/draft.txt" reply_file="$tmpdir/reply.txt" upload_file="$tmpdir/upload.out" note_file="$tmpdir/note.txt" location_file="$tmpdir/loc.txt" ``` 3. Replace every fixed `${TMPDIR:-/tmp}/<name>` reference with the corresponding unique path. 4. Keep `umask 077` active before creating files containing draft text, locations, notes, API responses, or media identifiers. 5. Ensure cleanup runs on both success and failure through an `EXIT` trap rather than relying on the final workflow step. 6. Do not reuse one temporary directory between concurrent drafting, ingestion, or publishing turns. 7. Where practical, create outputs atomically and reject symbolic links. For script implementations, use safe file descriptors or no-follow/exclusive creation semantics supported by the host platform. 8. Keep the existing quoted heredoc and JSON construction safeguards; they address a different command-injection risk and should remain in place. ]]>
