T09 · Insecure Skill Coding Practices
- Location
- scripts/publish_asr_report.py:71
- Finding
- Public disclosure of sensitive recording excerpts through persistent report URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_asr_insight_html.py:278-286`, `scripts/publish_asr_report.py:16-18, 71-86, 105-111`, and `SKILL.md:442-456` **Vulnerability Type**: Sensitive information exposure through publicly accessible report delivery **Risk Level**: High ### Vulnerable Code ```python DEFAULT_UPLOAD_URL = "https://legion.tongfudun.com/version/upload" DEFAULT_BUCKET = "legionclaw" DEFAULT_DOWNLOAD_BASE = "https://chat-minio.tongfudun.com/legionclaw" ``` ```python body, boundary = _multipart_body( { "bucket": bkt, "objectName": obj, "contentType": HTML_CONTENT_TYPE, "contentDisposition": "inline", }, "file", html_path, ) req = Request( up, data=body, method="POST", headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) ``` ```python report_url = f"{base}/{obj}" return { "ok": True, "reportUrl": report_url, "objectName": obj, "bucket": bkt, } ``` The uploaded HTML includes excerpts taken directly from recording transcripts: ```python for tag, snippet, file_alias in stat["excerpts"]: alias_display = html.escape(file_alias) if file_alias else "" alias_html = f'<span class="record-name">[{alias_display}]</span> ' if alias_display else "" items.append( f'<li>{alias_html}<span class="tag">{html.escape(tag)}</span>' f"{html.escape(snippet)}</li>" ) ``` The Skill explicitly requires public-link delivery: ```markdown This Skill's generated HTML must be delivered to the user through a publicly accessible download link. https://chat-minio.tongfudun.com/legionclaw/{objectName} ``` ### Technical Analysis The report contains verbatim excerpts from private recordings and may include customer names, financial discussions, personal circumstances, and confidential business information. The publisher uploads this report to a shared bucket with `Content-Disposition: inline`, and then constructs a stabl ...[truncated 2054 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store reports in a private, per-user or per-tenant bucket. 2. Return short-lived, server-generated signed URLs rather than constructing public URLs locally. 3. Bind download authorization to the requesting user and current session. 4. Generate object names with at least 128 bits of cryptographic randomness. 5. Configure explicit expiration and automatic deletion for generated reports. 6. Redact or pseudonymize names, account details, and other sensitive entities before upload. 7. Allow users to choose whether verbatim excerpts are included. 8. Obtain explicit confirmation before uploading recording-derived content to an externally reachable service. 9. Do not assume the upload endpoint and download origin have equivalent access policies; validate the server response. 10. Prevent referrer leakage with `Referrer-Policy: no-referrer` and serve reports from an isolated, credential-free origin. ]]>
