T09 · Insecure Skill Coding Practices
- Location
helpers.js:73- Finding
JavaScript Injection Through Dynamically Generated Browser Script
- Content
View full analysis
{ const input = document.querySelector('[contenteditable="true"]'); if (!input) return 'not found'; input.focus(); input.textContent = '${message.replace(/'/g, "\\'")}'; input.dispatchEvent(new Event('input', {bubbles: true})); return 'ok'; }`; } ``` ### Technical Analysis `generateInputScript()` embeds `message` directly into executable JavaScript source. It attempts to secure the value by escaping apostrophes, but this is not a complete JavaScript string-literal encoding mechanism. In particular, the function does not safely encode backslashes, Unicode line terminators, newlines, or other JavaScript syntax. An attacker can place a backslash immediately before an apostrophe so that the newly inserted escape character does not prevent the apostrophe from terminating the generated string. Once the string is terminated, attacker-supplied JavaScript can become part of the generated function. The vulnerable value can originate from an influencer's public profile, biography, title, or content when that information is passed into a personalized message template. The resulting script is intended to execute in the context of an authenticated Douyin browser session. ### Attack Path 1. An attacker creates a Douyin profile or post containing a specially crafted title, biography, product name, or similar value. 2. The Skill retrieves this attacker-controlled content while inspecting the profile or latest post. 3. The value is passed into `generateMessage()` or another personalization step. 4. The resulting message is supplied to `generateInputScript()`. 5. `generateInputScript()` escapes only apostrophes and concatenates the message into executable JavaScript. 6. A crafted backslash and apostrophe sequence te ...[truncated 751 chars]- Remediation
View remediation
{ const input = document.querySelector('[contenteditable="true"]'); if (!input) return 'not found'; input.focus(); input.textContent = ${encodedMessage}; input.dispatchEvent(new Event('input', { bubbles: true })); return 'ok'; }`; } ``` - Treat all profile metadata, post titles, comments, and extracted product names as untrusted input. - Add tests covering apostrophes, backslashes, newlines, Unicode line separators, template-literal characters, and attempted script injection. - Prefer eliminating `generateInputScript()` entirely to ensure future callers cannot reintroduce source-code interpolation. ]]>
