T09 · Insecure Skill Coding Practices
- Location
SKILL.md:54- Finding
Untrusted Query Interpolation Enables JavaScript and HTML Injection
- Content
View full analysis
request={ "kind": "evaluate", "fn": "(() => { const editor = document.querySelector('div.ql-editor[contenteditable=\"true\"]'); if (!editor) return 'editor not found'; editor.focus(); editor.innerHTML = 'YOUR_QUERY_HERE
'; editor.dispatchEvent(new Event('input', { bubbles: true })); return 'ok'; })()" } ``` The same unsafe pattern is also documented in the complete workflow: ```javascript browser action=act profile=chrome targetId= request={ "kind": "evaluate", "fn": "(() => { const editor = document.querySelector('div.ql-editor[contenteditable=\"true\"]'); if (!editor) return 'editor not found'; editor.focus(); editor.innerHTML = 'What is quantum computing?
'; editor.dispatchEvent(new Event('input', { bubbles: true })); return 'ok'; })()" } ``` ### Technical Analysis The documented workflow instructs the agent to replace `YOUR_QUERY_HERE` inside a JavaScript string passed to the CDP `evaluate` operation. The query is therefore inserted into two executable interpretation contexts without any documented validation or encoding: 1. A single-quoted JavaScript string literal. 2. An HTML parsing sink through `editor.innerHTML`. A query containing a single quote, JavaScript delimiters, or HTML markup can change the structure of the generated expression rather than being treated exclusively as text. For example, a value shaped like the following could terminate the intended string and introduce another JavaScript statement: ```text '; ATTACKER_CONTROLLED_JAVASCRIPT; // ``` After direct substitution, the browser would evaluate attacker-controlled syntax in the context of the attached Gemini page. Even when JavaScript string brea ...[truncated 2031 chars]- Remediation
View remediation
{ const editor = document.querySelector( 'div.ql-editor[contenteditable="true"]' ); if (!editor) { return 'editor not found'; } editor.focus(); editor.textContent = query; editor.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: query })); return 'ok'; })(SAFELY_BOUND_QUERY) ``` `SAFELY_BOUND_QUERY` must be supplied as data through the tool interface, not replaced with raw text inside the expression. If binding is unavailable, use a browser action that natively fills editable elements and keeps the input outside JavaScript source code. ]]>
