T09 · Insecure Skill Coding Practices
- Location
scripts/push_draft.py:83- Finding
Unconditional deletion of existing WeChat drafts without confirmation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/push_draft.py, lines 83–109 and 202–203
Vulnerability Type: Unconfirmed destructive operation
Risk Level: HighVulnerable Code
python def delete_all_drafts(token): """Delete all existing drafts.""" r = requests.post( "https://api.weixin.qq.com/cgi-bin/draft/batchget", json={"offset": 0, "count": 20, "no_content": 1}, params={"access_token": token}, timeout=30 ) data = r.json() items = data.get("item", []) print(f"[INFO] Existing drafts: {len(items)}") for item in items: mid = item["media_id"] title = "" try: title = item["content"]["news_item"][0]["title"] except Exception: pass try: requests.post( f"https://api.weixin.qq.com/cgi-bin/draft/delete", json={"media_id": mid}, params={"access_token": token}, timeout=30 ) print(f" [DELETED] {mid} ({title})") except Exception as e: print(f" [SKIP] {mid}: {e}")The deletion is invoked unconditionally from the main workflow:
python # Delete old drafts delete_all_drafts(token)Technical Analysis
Every successful invocation of
push_draft.pywith valid WeChat credentials callsdelete_all_drafts()before uploading the requested cover image and creating the new draft. The function retrieves up to 20 existing drafts and sends a deletion request for every returned media ID.There is no opt-in flag, dry-run mode, interactive confirmation, draft allowlist, or selection of drafts belonging to the current operation. The Skill documentation describes adding an article to the draft box and reserving final publication for user confirmation, but it does not disclose that unrelated existing drafts will be deleted.
T ...[truncated 1440 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove automatic deletion from the normal draft-upload path.
- If cleanup is required, expose it through a separate explicit option such as
--delete-draft ID; do not use a broad--delete-alldefault. - List the exact draft IDs and titles that would be affected and require explicit user confirmation before deletion.
- Add a dry-run mode that performs enumeration without mutation.
- Restrict deletion to draft IDs specifically selected by the user or demonstrably created by the same operation.
- Create and verify the new draft before offering cleanup of old drafts.
- Check the HTTP status and API response for each deletion rather than treating every completed request as successful.
- Consider preserving metadata or backups when the service supports recovery or export.
