Security: Fix XSS vulnerabilities in dashboard templates and improve mobile test UI safety
This commit is contained in:
19
data/self_modify_reports/20260226_070455_add_docstring.md
Normal file
19
data/self_modify_reports/20260226_070455_add_docstring.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Self-Modify Report: 20260226_070455
|
||||
|
||||
**Instruction:** Add docstring
|
||||
**Target files:** src/foo.py
|
||||
**Dry run:** True
|
||||
**Backend:** ollama
|
||||
**Branch:** N/A
|
||||
**Result:** SUCCESS
|
||||
**Error:** none
|
||||
**Commit:** none
|
||||
**Attempts:** 1
|
||||
**Autonomous cycles:** 0
|
||||
|
||||
## Attempt 1 -- dry_run
|
||||
|
||||
### LLM Response
|
||||
```
|
||||
llm raw
|
||||
```
|
||||
31
data/self_modify_reports/20260226_070455_break_it.md
Normal file
31
data/self_modify_reports/20260226_070455_break_it.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Self-Modify Report: 20260226_070455
|
||||
|
||||
**Instruction:** Break it
|
||||
**Target files:** src/foo.py
|
||||
**Dry run:** False
|
||||
**Backend:** ollama
|
||||
**Branch:** N/A
|
||||
**Result:** FAILED
|
||||
**Error:** Tests failed after 1 attempt(s).
|
||||
**Commit:** none
|
||||
**Attempts:** 1
|
||||
**Autonomous cycles:** 0
|
||||
|
||||
## Attempt 1 -- complete
|
||||
|
||||
### LLM Response
|
||||
```
|
||||
llm raw
|
||||
```
|
||||
|
||||
### Edits Written
|
||||
#### src/foo.py
|
||||
```python
|
||||
x = 1
|
||||
|
||||
```
|
||||
|
||||
### Test Result: FAILED
|
||||
```
|
||||
1 failed
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
# Self-Modify Report: 20260226_070455
|
||||
|
||||
**Instruction:** do something vague
|
||||
**Target files:** (auto-detected)
|
||||
**Dry run:** False
|
||||
**Backend:** ollama
|
||||
**Branch:** N/A
|
||||
**Result:** FAILED
|
||||
**Error:** No target files identified. Specify target_files or use more specific language.
|
||||
**Commit:** none
|
||||
**Attempts:** 0
|
||||
**Autonomous cycles:** 0
|
||||
31
data/self_modify_reports/20260226_070455_fix_foo.md
Normal file
31
data/self_modify_reports/20260226_070455_fix_foo.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Self-Modify Report: 20260226_070455
|
||||
|
||||
**Instruction:** Fix foo
|
||||
**Target files:** src/foo.py
|
||||
**Dry run:** False
|
||||
**Backend:** ollama
|
||||
**Branch:** N/A
|
||||
**Result:** FAILED
|
||||
**Error:** Tests failed after 1 attempt(s).
|
||||
**Commit:** none
|
||||
**Attempts:** 1
|
||||
**Autonomous cycles:** 0
|
||||
|
||||
## Attempt 1 -- complete
|
||||
|
||||
### LLM Response
|
||||
```
|
||||
llm raw
|
||||
```
|
||||
|
||||
### Edits Written
|
||||
#### src/foo.py
|
||||
```python
|
||||
x = 2
|
||||
|
||||
```
|
||||
|
||||
### Test Result: FAILED
|
||||
```
|
||||
FAILED
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
# Self-Modify Report: 20260226_070455
|
||||
|
||||
**Instruction:** Fix foo
|
||||
|
||||
IMPORTANT CORRECTION from previous failure:
|
||||
Fix: do X instead of Y
|
||||
**Target files:** src/foo.py
|
||||
**Dry run:** False
|
||||
**Backend:** ollama
|
||||
**Branch:** N/A
|
||||
**Result:** SUCCESS
|
||||
**Error:** none
|
||||
**Commit:** abc123
|
||||
**Attempts:** 1
|
||||
**Autonomous cycles:** 0
|
||||
|
||||
## Attempt 1 -- complete
|
||||
|
||||
### LLM Response
|
||||
```
|
||||
llm raw
|
||||
```
|
||||
|
||||
### Edits Written
|
||||
#### src/foo.py
|
||||
```python
|
||||
x = 2
|
||||
|
||||
```
|
||||
|
||||
### Test Result: PASSED
|
||||
```
|
||||
PASSED
|
||||
```
|
||||
@@ -343,15 +343,41 @@
|
||||
const pct = TOTAL ? Math.round((passed / TOTAL) * 100) : 0;
|
||||
const color = failed > 0 ? "var(--red)" : "var(--green)";
|
||||
|
||||
summaryBody.innerHTML = `
|
||||
<div class="mt-summary-score" style="color:${color}">${passed} / ${TOTAL}</div>
|
||||
<div class="mt-summary-pct">${pct}% pass rate</div>
|
||||
<div style="margin-top:16px;">
|
||||
<div class="mt-summary-row"><span>PASSED</span><span style="color:var(--green);font-weight:700;">${passed}</span></div>
|
||||
<div class="mt-summary-row"><span>FAILED</span><span style="color:var(--red);font-weight:700;">${failed}</span></div>
|
||||
<div class="mt-summary-row"><span>SKIPPED</span><span style="color:var(--amber);font-weight:700;">${skipped}</span></div>
|
||||
</div>
|
||||
`;
|
||||
// Safely build summary UI using DOM API to avoid XSS from potentially untrusted variables
|
||||
summaryBody.innerHTML = '';
|
||||
|
||||
const scoreDiv = document.createElement('div');
|
||||
scoreDiv.className = 'mt-summary-score';
|
||||
scoreDiv.style.color = color;
|
||||
scoreDiv.textContent = passed + ' / ' + TOTAL;
|
||||
summaryBody.appendChild(scoreDiv);
|
||||
|
||||
const pctDiv = document.createElement('div');
|
||||
pctDiv.className = 'mt-summary-pct';
|
||||
pctDiv.textContent = pct + '% pass rate';
|
||||
summaryBody.appendChild(pctDiv);
|
||||
|
||||
const statsContainer = document.createElement('div');
|
||||
statsContainer.style.marginTop = '16px';
|
||||
|
||||
const createRow = (label, value, colorVar) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'mt-summary-row';
|
||||
const labelSpan = document.createElement('span');
|
||||
labelSpan.textContent = label;
|
||||
const valSpan = document.createElement('span');
|
||||
valSpan.style.color = 'var(--' + colorVar + ')';
|
||||
valSpan.style.fontWeight = '700';
|
||||
valSpan.textContent = value;
|
||||
row.appendChild(labelSpan);
|
||||
row.appendChild(valSpan);
|
||||
return row;
|
||||
};
|
||||
|
||||
statsContainer.appendChild(createRow('PASSED', passed, 'green'));
|
||||
statsContainer.appendChild(createRow('FAILED', failed, 'red'));
|
||||
statsContainer.appendChild(createRow('SKIPPED', skipped, 'amber'));
|
||||
summaryBody.appendChild(statsContainer);
|
||||
|
||||
const statusMsg = document.createElement('p');
|
||||
statusMsg.style.marginTop = '12px';
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<div class="chat-message user">
|
||||
<div class="msg-meta" style="color:var(--orange);">YOU // {{ timestamp }}</div>
|
||||
<div class="msg-body" style="border-color:var(--border-glow);">{{ message }}</div>
|
||||
<div class="msg-body" style="border-color:var(--border-glow);">{{ message | e }}</div>
|
||||
</div>
|
||||
|
||||
{% if response %}
|
||||
<div class="chat-message agent">
|
||||
<div class="msg-meta" style="color:var(--purple);">{{ agent.name | upper }} // {{ timestamp }}</div>
|
||||
<div class="msg-body" style="border-left:3px solid var(--purple);">{{ response }}</div>
|
||||
<div class="msg-body" style="border-left:3px solid var(--purple);">{{ response | e }}</div>
|
||||
</div>
|
||||
|
||||
{% elif error %}
|
||||
<div class="chat-message error-msg">
|
||||
<div class="msg-meta">SYSTEM // {{ timestamp }}</div>
|
||||
<div class="msg-body" style="border-left:3px solid var(--red); color:var(--red);">{{ error }}</div>
|
||||
<div class="msg-body" style="border-left:3px solid var(--red); color:var(--red);">{{ error | e }}</div>
|
||||
</div>
|
||||
|
||||
{% elif task_id %}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<div class="card-header mc-panel-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<span class="status-dot {{ dot }}" style="margin-right:6px;"></span>
|
||||
// {{ agent.name | upper }}
|
||||
// {{ agent.name | upper | e }}
|
||||
<span style="font-size:10px; color:var(--text-dim); margin-left:10px; letter-spacing:.1em;">
|
||||
{{ agent.capabilities or "no capabilities listed" }}
|
||||
{{ agent.capabilities | e or "no capabilities listed" }}
|
||||
</span>
|
||||
</span>
|
||||
<button class="mc-btn-clear"
|
||||
@@ -31,9 +31,9 @@
|
||||
TASK · {{ task.status.value | upper }} · {{ task.created_at[:19].replace("T"," ") }}
|
||||
</div>
|
||||
<div class="msg-body" style="border-left: 3px solid var(--{% if task.status.value == 'completed' %}green{% elif task.status.value == 'failed' %}red{% else %}orange{% endif %});">
|
||||
<div style="color:var(--text-dim); font-size:11px; margin-bottom:4px;">{{ task.description }}</div>
|
||||
<div style="color:var(--text-dim); font-size:11px; margin-bottom:4px;">{{ task.description | e }}</div>
|
||||
{% if task.result %}
|
||||
<div style="color:var(--text-bright);">{{ task.result }}</div>
|
||||
<div style="color:var(--text-bright);">{{ task.result | e }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,7 +57,7 @@
|
||||
<input type="text"
|
||||
name="message"
|
||||
class="form-control mc-input"
|
||||
placeholder="send a message to {{ agent.name | lower }}..."
|
||||
placeholder="send a message to {{ agent.name | lower | e }}..."
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-1">
|
||||
<span class="status-dot {{ dot }}"></span>
|
||||
<span class="agent-name" style="font-size:13px;">{{ agent.name | upper }}</span>
|
||||
<span class="agent-name" style="font-size:13px;">{{ agent.name | upper | e }}</span>
|
||||
</div>
|
||||
|
||||
<div class="agent-meta" style="margin-bottom:8px;">
|
||||
@@ -21,7 +21,7 @@
|
||||
<span class="meta-val">{{ agent.status }}</span><br>
|
||||
{% if agent.capabilities %}
|
||||
<span class="meta-key">CAPS</span>
|
||||
<span class="meta-val" style="font-size:10px;">{{ agent.capabilities }}</span><br>
|
||||
<span class="meta-val" style="font-size:10px;">{{ agent.capabilities | e }}</span><br>
|
||||
{% endif %}
|
||||
<span class="meta-key">SEEN</span>
|
||||
<span class="meta-val" style="font-size:10px;">{{ agent.last_seen[:19].replace("T"," ") if agent.last_seen else "—" }}</span>
|
||||
|
||||
69
tests/test_security_fixes_xss.py
Normal file
69
tests/test_security_fixes_xss.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import pytest
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
def test_agent_chat_msg_xss_prevention():
|
||||
"""Verify XSS prevention in agent_chat_msg.html."""
|
||||
templates = Jinja2Templates(directory="src/dashboard/templates")
|
||||
payload = "<script>alert('xss')</script>"
|
||||
class MockAgent:
|
||||
def __init__(self):
|
||||
self.name = "TestAgent"
|
||||
self.id = "test-agent"
|
||||
|
||||
response = templates.get_template("partials/agent_chat_msg.html").render({
|
||||
"message": payload,
|
||||
"response": payload,
|
||||
"error": payload,
|
||||
"agent": MockAgent(),
|
||||
"timestamp": "12:00:00"
|
||||
})
|
||||
|
||||
# Check that payload is escaped
|
||||
assert "<script>alert('xss')</script>" in response
|
||||
assert payload not in response
|
||||
|
||||
def test_agent_panel_xss_prevention():
|
||||
"""Verify XSS prevention in agent_panel.html."""
|
||||
templates = Jinja2Templates(directory="src/dashboard/templates")
|
||||
payload = "<script>alert('xss')</script>"
|
||||
class MockAgent:
|
||||
def __init__(self):
|
||||
self.name = payload
|
||||
self.id = "test-agent"
|
||||
self.status = "idle"
|
||||
self.capabilities = payload
|
||||
|
||||
class MockTask:
|
||||
def __init__(self):
|
||||
self.id = "task-1"
|
||||
self.status = type('obj', (object,), {'value': 'completed'})
|
||||
self.created_at = "2026-02-26T12:00:00"
|
||||
self.description = payload
|
||||
self.result = payload
|
||||
|
||||
response = templates.get_template("partials/agent_panel.html").render({
|
||||
"agent": MockAgent(),
|
||||
"tasks": [MockTask()]
|
||||
})
|
||||
|
||||
assert "<script>alert('xss')</script>" in response
|
||||
assert payload not in response
|
||||
|
||||
def test_swarm_sidebar_xss_prevention():
|
||||
"""Verify XSS prevention in swarm_agents_sidebar.html."""
|
||||
templates = Jinja2Templates(directory="src/dashboard/templates")
|
||||
payload = "<script>alert('xss')</script>"
|
||||
class MockAgent:
|
||||
def __init__(self):
|
||||
self.name = payload
|
||||
self.id = "test-agent"
|
||||
self.status = "idle"
|
||||
self.capabilities = payload
|
||||
self.last_seen = "2026-02-26T12:00:00"
|
||||
|
||||
response = templates.get_template("partials/swarm_agents_sidebar.html").render({
|
||||
"agents": [MockAgent()]
|
||||
})
|
||||
|
||||
assert "<script>alert('xss')</script>" in response
|
||||
assert payload not in response
|
||||
Reference in New Issue
Block a user