T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/publish_note.sh:234
- Finding
- JavaScript Injection Through Topic Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/publish_note.sh:234-245` **Vulnerability Type**: Browser-context JavaScript injection **Risk Level**: High ### Vulnerable Code ```javascript const topicResult = await js(`((topic) => { const container = document.getElementById('creator-editor-topic-container') if (!container) { return { error: 'topic container not found' } } const items = [...container.querySelectorAll('.item')] if (!items.length) { return { error: 'no topic items', containerText: container.innerText.slice(0, 200) } } let item = items.find(el => { const nameEl = el.querySelector('.name') if (!nameEl) return false const text = nameEl.innerText return text === '#' + topic || text === topic || text.includes(topic) }) if (!item) item = items[0] item.click() return { clicked: true, text: item.querySelector('.name')?.innerText || item.innerText } })('${topic.replace(/'/g, "\\'")}')`) ``` ### Technical Analysis The topic value is interpolated directly into JavaScript source executed by the `js()` browser-automation API. The code escapes apostrophes but does not safely serialize backslashes, line terminators, or other JavaScript syntax. Escaping only `'` is insufficient because an attacker can place a backslash before an apostrophe. The transformation adds another backslash, potentially leaving the quote effectively unescaped in the resulting JavaScript source. The attacker can then terminate the string literal and append arbitrary JavaScript. The shell-level validation does not restrict these characters. Passing the value through JSON before this point does not provide protection because the parsed value is subsequently inserted into executable source code. ### Attack Path 1. An attacker influences a topic passed through the `TOPICS` environment variable or generated publishing parameters. 2. The shell script stores the topic in the JSON parameter file. 3. The Node.js publishing code pa ...[truncated 992 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not construct executable JavaScript by concatenating topic values. 1. Pass the topic through a structured argument mechanism provided by the browser automation API. 2. If the API cannot accept arguments, serialize the complete JavaScript literal with `JSON.stringify`: ```javascript const topicLiteral = JSON.stringify(topic) const topicResult = await js(`((topic) => { // Existing lookup logic })(${topicLiteral})`) ``` 3. Prefer DOM APIs that accept structured selector or text arguments without evaluating generated source. 4. Validate topics with a conservative allowlist and enforce platform-compatible length limits. 5. Add tests covering apostrophes, backslashes, newlines, Unicode separators, template-literal characters, and attempted source termination. ]]>
