T09 · Insecure Skill Coding Practices
- Location
demo/taskflow-app.html:394- Finding
Persistent DOM-based cross-site scripting in the TaskFlow demo
- Content
View full analysis
{ const origIndex = tasks.indexOf(task); const li = document.createElement('li'); li.className = 'task-item'; li.innerHTML = ` ${task.text} ${task.priority} × `; list.appendChild(li); }); ``` ```javascript function addTask() { const input = document.getElementById('taskInput'); const text = input.value.trim(); if (!text) { showToast('Please enter a task!'); return; } tasks.push({ text, priority: document.getElementById('taskPriority').value, done: false, createdAt: Date.now() }); ``` ### Technical Analysis The application accepts arbitrary task text and stores it without validation or output encoding. During rendering, `task.text` is interpolated into an HTML template assigned to `li.innerHTML`. Because `innerHTML` parses its input as markup, an attacker can supply HTML containing executable event handlers. For example: ```html``` The task is also serialized into `localStorage ...[truncated 2164 chars]
- Remediation
View remediation
toggleTask(origIndex)); const textElement = document.createElement('span'); textElement.className = `task-text${task.done ? ' completed' : ''}`; textElement.textContent = task.text; const priorityElement = document.createElement('span'); priorityElement.className = `task-priority priority-${task.priority}`; priorityElement.textContent = task.priority; const deleteButton = document.createElement('button'); deleteButton.className = 'task-delete'; deleteButton.type = 'button'; deleteButton.textContent = '×'; deleteButton.addEventListener('click', () => deleteTask(origIndex)); li.append(checkbox, textElement, priorityElement, deleteButton); list.appendChild(li); ``` Additional hardening should include: 1. Validate records loaded from `localStorage` before using them. 2. Require `task.text` to be a string and apply an appropriate maximum length. 3. Restrict `priority` to the explicit values `low`, `medium`, or `high`. 4. Replace all inline event handlers with `addEventListener`. 5. Add a restrictive Content Security Policy when the demo is served over HTTP. 6. If rich HTML is intentionally supported in the future, sanitize it using a maintained, security-reviewed HTML sanitizer rather than custom escaping. 7. Add regression tests using common XSS payloads and verify that they appear only as literal text. ]]>
