·

Tiếng Việt: Hands-on: AI-assisted performance and security audit

Hands-on: AI-assisted performance and security audit

Đây là bài capstone (bài tổng hợp, vận dụng toàn bộ kỹ năng đã học) của module 10. Thay vì tách rời từng kỹ năng (viết load test, phân tích bottleneck, security scan, compliance check) như bốn bài trước, chúng ta sẽ ghép tất cả lại thành một quy trình audit (kiểm định) liên tục, chạy trên một hệ thống thật từ đầu đến cuối — giống như một senior QA sẽ làm khi được giao "audit lại toàn bộ platform trước khi go-live phase 2". Bối cảnh xuyên suốt bài này là một nền tảng e-commerce đang tái cấu trúc sang microservices, đúng loại dự án mà QA sẽ gặp trong thực tế.

Cách Xác Định Phạm Vi Audit Performance và Security Từ Tài Liệu Kiến Trúc và Code Changes

Bối cảnh của bài audit

Giả sử bạn là QA Lead của "ShopUnity" — một nền tảng e-commerce đã vận hành 4 năm trên monolith, hiện đang chia dần sang microservices. Sprint này, team engineering vừa ship xong 3 tính năng lớn cùng lúc:

  1. Real-time inventory reservation — giữ hàng tạm thời khi khách thêm vào cart, tránh oversell khi có flash sale.
  2. OAuth 2.0 social login — cho phép đăng nhập bằng Google/Facebook, thay thế một phần luồng đăng ký bằng email/password.
  3. ML recommendation engine — gợi ý sản phẩm dựa trên lịch sử mua hàng, gọi model inference real-time trên trang chủ và trang sản phẩm.

Ngoài ra, do ShopUnity mở rộng sang EU, legal team yêu cầu bổ sung GDPR data export (cho phép user tự export toàn bộ dữ liệu cá nhân dưới dạng file).

Bạn có 5 ngày làm việc, 1 QA engineer hỗ trợ, và phải bàn giao báo cáo audit performance + security trước ngày release. Đây chính xác là tình huống "audit dồn" thường gặp: nhiều thay đổi lớn, ít thời gian, không thể test lại từ đầu mọi thứ. AI ở đây không thay bạn ra quyết định, nhưng nó rút ngắn đáng kể thời gian đọc hiểu tài liệu thiết kế, tạo test case, và tổng hợp báo cáo.

Bước 1: Đưa tài liệu kiến trúc cho AI để trích xuất phạm vi audit

Bước đầu tiên không phải là mở JMeter hay Burp Suite, mà là đọc hiểu cái gì đã thay đổi. Với 3 tính năng lớn ship cùng sprint, đọc tay toàn bộ design doc sẽ tốn nửa ngày. Cách hiệu quả hơn: tóm tắt design doc thành các đoạn ngắn, đưa cho AI cùng traffic baseline, và yêu cầu AI trích xuất phạm vi audit (attack surface, performance risk, data risk) theo từng thay đổi.

Dưới đây là prompt thực tế bạn đưa cho AI, với context là bản tóm tắt kiến trúc mà bạn tự viết ra sau khi đọc design doc (đây không phải section độc lập, mà là dữ liệu đầu vào cho prompt):

## Architecture Changes (summaries from design docs)

### Microservices Migration
- Cart, Inventory, và Payment đã tách ra khỏi monolith thành 3 service riêng.
- Giao tiếp qua REST + Kafka (async event cho inventory update).
- Service mesh: Istio, chưa có mTLS bắt buộc giữa các service (đang optional).

### Real-Time Inventory Reservation
- Khi user thêm sản phẩm vào cart, Inventory Service tạo "reservation" (TTL 10 phút) trong Redis.
- Redis cluster: 3 node, chưa có replica cho reservation keyspace.
- Checkout API gọi Inventory Service đồng bộ để confirm reservation trước khi tạo order.
- Race condition risk: 2 request cùng lúc cho cùng SKU cuối cùng.

### OAuth 2.0 Social Login
- Tích hợp Google OAuth 2.0 và Facebook Login qua Authorization Code flow.
- Redirect URI whitelist cấu hình trong provider console, không validate lại ở backend.
- Token exchange endpoint: /api/auth/oauth/callback, chưa có state parameter validation rõ ràng trong code review.
- JWT session token issued sau khi OAuth thành công, TTL 24h, không có refresh token rotation.

### ML Recommendation Engine
- Model inference service (Python, FastAPI) gọi từ trang chủ và trang sản phẩm.
- Mỗi page load gọi 2 API riêng: /recommendations/home và /recommendations/product/{id}.
- Model load vào memory khi container start, cold start ~8s.
- Không có cache layer cho kết quả recommendation theo user.

### GDPR Data Export
- Endpoint mới: POST /api/user/data-export, tạo file JSON chứa order history, profile, payment methods (đã mask số thẻ).
- Job xử lý async, gửi email link download sau khi xong.
- Link download không có expiry, không giới hạn số lần download.

## Traffic Baseline
- Traffic trung bình: 850 RPS trên toàn hệ thống giờ cao điểm (19h-21h).
- Trang chủ: ~320 RPS, trang sản phẩm: ~280 RPS, checkout: ~40 RPS.
- Flash sale peak (dự kiến): 4200 RPS trong 10 phút đầu, tăng 5x baseline.
- SLA hiện tại: p95 response time < 800ms cho API checkout, < 1.2s cho trang sản phẩm.

Bạn là security + performance architect. Dựa trên các thay đổi kiến trúc và traffic baseline trên,
hãy:
1. Liệt kê các endpoint/luồng có rủi ro performance cao nhất khi traffic tăng 5x (flash sale),
   giải thích lý do dựa trên kiến trúc (không phải suy đoán chung).
2. Liệt kê các điểm có rủi ro security cao nhất, map theo OWASP Top 10 nếu phù hợp.
3. Với mỗi rủi ro, gợi ý loại test cần thực hiện (load test, race condition test,
   penetration test, v.v.) và mức độ ưu tiên (P0-P3).

Kết quả AI trả về thường sẽ chỉ ra đúng những điểm "nóng" nhất: race condition ở inventory reservation khi 2 request giành 1 SKU cuối, thiếu state parameter validation ở OAuth callback (CSRF risk), cold start (thời gian service khởi động lại từ đầu khi scale, gây chậm/timeout tức thời) của ML service gây timeout khi traffic tăng đột ngột, và link download GDPR export không expiry (data exposure risk nếu email bị forward hoặc log lộ link). Đây chính là 4 mũi audit bạn sẽ theo suốt bài này.

Mẹo: Đừng đưa nguyên design doc dài 20 trang vào prompt — AI sẽ pha loãng trọng tâm và bỏ lỡ chi tiết quan trọng. Tự tóm tắt thành các đoạn ngắn theo cấu trúc rõ ràng (mỗi tính năng một block, kèm số liệu cụ thể như TTL, số node Redis, traffic RPS) trước khi đưa cho AI. Việc tóm tắt buộc bạn đọc hiểu thật, và giúp AI trích xuất rủi ro chính xác hơn nhiều so với việc dán nguyên văn tài liệu gốc.

Bước 2: Ưu tiên phạm vi audit dựa trên ràng buộc thực tế của team

5 ngày, 2 người, 4 mũi rủi ro — không thể audit hết ở độ sâu tối đa. Bạn dùng AI để đối chiếu constraint thực tế và ra một scope quyết định, không phải để AI tự quyết mà để nó giúp bạn nhìn rõ trade-off. Hỏi thêm:

"Với 5 ngày làm việc, 2 QA engineer, và 4 rủi ro đã liệt kê ở trên, hãy đề xuất phân bổ effort theo ngày, giả định load test cần 1.5 ngày setup + run, security test cho OAuth cần 1 ngày, review GDPR export cần 0.5 ngày, và cần dành 1 ngày cuối để viết báo cáo."

AI sẽ đưa ra một lịch phân bổ hợp lý — ví dụ ngày 1-2 cho load test inventory reservation (rủi ro cao nhất về performance), ngày 2-3 cho OAuth security test (song song với 1 người), ngày 4 cho ML service + GDPR review, ngày 5 cho tổng hợp báo cáo. Bạn vẫn là người chốt scope cuối, nhưng AI giúp bạn nhìn thấy toàn cảnh nhanh hơn và tránh bỏ sót rủi ro nhỏ khi đang tập trung vào rủi ro lớn.

Cách Tạo Test Case Load Test và Security Trong Cùng Một AI Session

Một trong những lợi thế lớn nhất của AI trong audit là khả năng giữ context xuyên suốt một session. Bạn không cần mở 2 công cụ riêng cho load test và security test — bạn có thể cung cấp context một lần, rồi yêu cầu AI sinh cả 2 loại test case, với sự nhất quán về tech stack, test data, và ràng buộc non-destructive testing.

Thiết lập session: cung cấp context chung

Trước khi yêu cầu bất kỳ test case nào, thiết lập context chung cho toàn session:

## Application Under Test
- ShopUnity e-commerce platform, môi trường staging (staging.shopunity.internal).
- Các service liên quan: Cart Service, Inventory Service, Auth Service, Recommendation Service.

## Technology Stack
- Backend: Node.js (Cart, Auth), Go (Inventory), Python/FastAPI (Recommendation).
- Database: PostgreSQL (orders, users), Redis (inventory reservation, session cache).
- Message queue: Kafka.
- API Gateway: Kong, rate limit hiện tại 100 req/s/IP cho public endpoint.

## Test Data
- 50 test user account (test_user_001 đến test_user_050), đã seed sẵn trong staging.
- 20 SKU test với stock thấp (stock = 1 đến 5 units) để test race condition.
- Test OAuth account: dùng Google test app riêng, không đụng vào production OAuth client.

## Non-Destructive Testing Agreement
- Đã được Engineering Lead và DevOps xác nhận bằng văn bản (Slack #eng-approvals, 2026-07-01).
- Load test chỉ chạy trên môi trường staging, KHÔNG chạy trên production.
- Load test giới hạn tối đa 4500 RPS (không vượt quá 110% traffic baseline dự kiến của flash sale)
  để tránh crash staging cluster (staging chạy trên hạ tầng nhỏ hơn production 60%).
- Security test (OAuth flow) chỉ test trên test app riêng, không test trên OAuth client thật của
  Google/Facebook production.
- Test race condition trên inventory chỉ dùng 20 SKU test đã liệt kê trên, không đụng SKU thật.
- Mọi test phá hủy dữ liệu (data corruption, DoS) đều CẤM, trừ khi có phê duyệt riêng theo từng case.

Đây là bối cảnh dùng chung — bạn dán một lần ở đầu session, sau đó mọi câu hỏi tiếp theo trong cùng cuộc hội thoại AI đều "nhớ" các ràng buộc này, giúp test case sinh ra luôn tôn trọng giới hạn RPS, tôn trọng non-destructive agreement, và dùng đúng test data đã seed.

Tạo test case performance cho hệ thống inventory reservation

Với context đã có, bạn hỏi tiếp:

Dựa trên context đã cung cấp, hãy tạo bộ test case load test cho Real-Time Inventory Reservation.
Yêu cầu:
1. Test case 1: Load test bình thường — mô phỏng traffic baseline giờ cao điểm (850 RPS toàn hệ
   thống, trong đó ~15% liên quan tới cart/inventory). Đo p95, p99 latency của endpoint
   POST /api/cart/reserve, target p95 < 500ms.
2. Test case 2: Stress test flash sale — ramp traffic từ baseline lên 4200 RPS trong 2 phút,
   giữ 10 phút, sau đó ramp down. Đo latency, error rate, và Redis connection pool saturation.
3. Test case 3: Race condition test — 50 virtual user cùng gửi request reserve cho 1 SKU có
   stock = 1, trong cùng 1 giây, kiểm tra chỉ đúng 1 request thành công (không oversell).
4. Với mỗi test case, ghi rõ: VU (virtual user) count, ramp-up pattern, duration, pass/fail
   threshold, và công cụ đề xuất (k6 hoặc Locust vì team đã dùng k6 cho các service Node.js/Go).

AI trả về bộ test case cụ thể, ví dụ test case 2 có thể được diễn giải thành script k6 với ramp-up stages: 0→850 RPS trong 30s (baseline), 850→4200 RPS trong 90s (ramp lên flash sale), giữ 4200 RPS trong 10 phút, ramp down trong 60s. Threshold: error rate < 1%, p95 < 800ms theo đúng SLA đã có, và một điều kiện đặc thù — Redis reservation TTL không được hết hạn sớm hơn 10 phút dưới tải cao (kiểm tra qua log timestamp).

Test case 3 (race condition) là điểm mà kỹ năng đọc hiểu kiến trúc ở Bước 1 trả giá: vì bạn đã biết Redis reservation không có replica và checkout gọi đồng bộ, bạn biết chính xác cần test concurrency ở tầng nào, không chỉ ném tải chung.

AI sẽ diễn giải yêu cầu test case 3 thành một script k6 cụ thể, sẵn sàng chạy trên staging — đây chính là script reserve_race_test.js sẽ được dùng lại xuyên suốt phần audit và cả khi verify fix ở phần sau:

import http from 'k6/http';
import { check } from 'k6';
import { Counter } from 'k6/metrics';

const BASE_URL = __ENV.BASE_URL || 'https://staging.shopunity.internal';
const SKU = __ENV.SKU || 'test_sku_014'; // stock = 1, from the seeded 20-SKU test set
const ADMIN_TOKEN = __ENV.ADMIN_TOKEN;

const successfulReservations = new Counter('successful_reservations');
const outOfStockResponses = new Counter('out_of_stock_responses');
const unexpectedErrors = new Counter('unexpected_errors');

export function setup() {
  // Reset stock to exactly 1 before each run, via the internal admin API on staging
  const resetRes = http.put(
    `${BASE_URL}/api/admin/inventory/${SKU}`,
    JSON.stringify({ stock: 1 }),
    { headers: { Authorization: `Bearer ${ADMIN_TOKEN}`, 'Content-Type': 'application/json' } }
  );
  check(resetRes, { 'stock reset to 1 successfully': (r) => r.status === 200 });
}

export const options = {
  vus: 50,
  iterations: 50, // each VU runs exactly once, no ramp-up — simulates "within the same second"
  thresholds: {
    successful_reservations: ['count<=1'], // FAIL if more than 1 reservation succeeds for stock = 1
    unexpected_errors: ['count<5'],
  },
};

export default function () {
  const res = http.post(
    `${BASE_URL}/api/cart/reserve`,
    JSON.stringify({ sku: SKU, quantity: 1, userId: `test_user_${__VU.toString().padStart(3, '0')}` }),
    { headers: { 'Content-Type': 'application/json' } }
  );

  if (res.status === 200) {
    successfulReservations.add(1);
  } else if (res.status === 409) {
    outOfStockResponses.add(1);
  } else {
    unexpectedErrors.add(1);
    console.log(`Unexpected status ${res.status}: ${res.body}`);
  }
}

export function teardown() {
  const stockRes = http.get(`${BASE_URL}/api/admin/inventory/${SKU}`, {
    headers: { Authorization: `Bearer ${ADMIN_TOKEN}` },
  });
  const finalStock = stockRes.json('stock');
  console.log(`Final stock for ${SKU}: ${finalStock}`);
  if (finalStock < 0) {
    console.error(`OVERSELL DETECTED: stock went negative (${finalStock}) after the test run`);
  }
}

Chạy script này 20 lần liên tiếp (đổi seed/thời điểm mỗi lần để tránh cache warm-up ảnh hưởng kết quả) chính là cách bạn thu được số liệu "3/20 lần oversell" ở phần findings report bên dưới.

Tạo test case security cho luồng OAuth trong cùng session

Không cần mở session mới — trong cùng cuộc hội thoại, chuyển sang OAuth:

Vẫn dựa trên context đã cung cấp (Application Under Test, Technology Stack, Test Data,
Non-Destructive Testing Agreement), hãy tạo bộ test case security cho luồng OAuth 2.0 Social Login.
Tập trung vào các rủi ro đã xác định ở bước scoping: thiếu state parameter validation rõ ràng,
redirect URI whitelist chỉ cấu hình ở provider console, JWT session TTL 24h không có refresh
token rotation.

Yêu cầu test case cho:
1. CSRF qua OAuth callback (thiếu/không validate state parameter).
2. Open redirect qua redirect_uri parameter (thử redirect_uri trỏ ra domain ngoài whitelist).
3. Token replay: dùng lại authorization code sau khi đã exchange, kiểm tra có bị reject không.
4. JWT session token: kiểm tra token có thể bị dùng lại sau logout, kiểm tra token không bị
   accept nếu decode ra alg=none hoặc bị đổi signature.

Với mỗi test case, ghi rõ bước thực hiện, tool đề xuất (Burp Suite / OWASP ZAP / script thủ công),
và kết quả mong đợi (expected vs actual).

AI sinh ra các test case cụ thể — ví dụ test case 2 (open redirect) sẽ hướng dẫn: sửa giá trị redirect_uri trong request khởi tạo OAuth thành domain bên ngoài (như https://evil-test.example.com/callback), gửi request, kiểm tra xem Auth Service có redirect thẳng tới đó không hay reject vì không khớp whitelist server-side. Đây chính là điểm mà design doc ghi "whitelist chỉ cấu hình ở provider console, chưa validate lại ở backend" — nếu đúng vậy, test này rất có khả năng fail (tức phát hiện lỗ hổng thật).

Mẹo: Giữ nguyên một session AI xuyên suốt cho toàn bộ audit (performance + security), đừng tách ra thành nhiều cuộc hội thoại riêng biệt cho mỗi loại test. Khi AI vẫn "nhớ" tech stack, test data, và ràng buộc non-destructive testing đã cung cấp từ đầu, mọi test case tiếp theo — dù performance hay security — sẽ tự động nhất quán, không cần lặp lại context, và tiết kiệm rất nhiều token cũng như thời gian bạn ngồi copy-paste.

Cách Diễn Giải Kết Quả Audit và Tạo Findings Report Với AI

Sau 3 ngày chạy test theo scope đã định, bạn có trong tay: kết quả k6 cho load test inventory, kết quả Burp Suite cho OAuth, log cold start của recommendation service, và review thủ công cho GDPR export link. Giờ là lúc tổng hợp — đây là bước AI giúp tiết kiệm thời gian nhiều nhất, vì viết báo cáo audit rành mạch, có severity, có bằng chứng, luôn tốn nhiều giờ nếu làm tay.

Thu thập và cấu trúc kết quả để đưa vào phân tích AI

Trước khi đưa vào AI, bạn cần tổng hợp raw output (log k6, response Burp Suite, số liệu latency) thành các bullet ngắn có số liệu cụ thể — giống cách bạn đã làm ở Bước 1 với design doc. Đừng dán nguyên log JSON dài hàng nghìn dòng; trích ra các con số quan trọng (p95, error rate, status code, timestamp bất thường).

Tạo findings report bằng AI

Với dữ liệu đã tổng hợp, đưa cho AI theo cấu trúc:

## Audit Context
- Audit ShopUnity: microservices migration, inventory reservation, OAuth social login,
  ML recommendation engine, GDPR data export.
- Môi trường: staging. Thời gian audit: 3 ngày.

## Performance Findings
- Load test baseline (850 RPS): p95 = 420ms, p99 = 610ms — đạt SLA (target p95 < 500ms).
- Stress test flash sale (4200 RPS, 10 phút): p95 tăng lên 1350ms sau phút thứ 6, error rate
  tăng từ 0.3% lên 4.7%. Nguyên nhân xác định: Redis connection pool (max 50 connection) bị
  saturate, không có replica cho reservation keyspace.
- Race condition test: trong 20 lần chạy với 50 VU cạnh tranh 1 SKU (stock=1), có 3/20 lần
  ghi nhận 2 request cùng thành công (oversell). Root cause: thiếu atomic check-and-decrement,
  hiện dùng read-then-write pattern trong Inventory Service.
- Recommendation Service: cold start 8s khi container scale up dưới tải; trong lúc scale, 12%
  request tới /recommendations/home bị timeout (>3s) trong 90 giây đầu của flash sale simulation.

## Security Findings
- OAuth callback: KHÔNG có state parameter validation — xác nhận được bằng cách gửi request
  callback không kèm state, hệ thống vẫn xử lý và issue JWT thành công. CVSS ước tính: 8.1 (High) —
  CSRF cho phép attacker link tài khoản OAuth của mình vào session victim.
- Redirect URI: thử redirect_uri ra domain ngoài whitelist, hệ thống reject đúng như kỳ vọng
  (backend có validate) — not a finding, chỉ ghi nhận đã test.
- JWT session: token vẫn được accept 2 giờ sau khi user đã logout (không có token revocation
  list). CVSS ước tính: 6.5 (Medium).
- Token replay: authorization code dùng lại lần 2 bị reject đúng như kỳ vọng — not a finding.

## Accessibility Findings (included in audit scope)
- Trang checkout mới (phần hiển thị reservation countdown timer): không có aria-live attribute,
  screen reader không thông báo khi countdown gần hết (WCAG 2.1 SC 4.1.3, mức AA).
- Nút "Login with Google/Facebook": thiếu focus indicator rõ ràng khi tab bằng keyboard
  (WCAG 2.1 SC 2.4.7).

Bạn là security + performance audit lead. Dựa trên các finding trên, hãy viết báo cáo audit theo
cấu trúc: Executive Summary, danh sách finding (mỗi finding có: title, category, severity,
evidence, business impact, recommended fix), và Overall Risk Rating cho từng khu vực
(Inventory, Auth, Recommendation, GDPR export).

AI tổng hợp thành một báo cáo có cấu trúc rõ ràng, với executive summary khoảng 3-4 câu tóm tắt mức độ rủi ro tổng thể (ví dụ: "Hệ thống đạt SLA ở traffic baseline nhưng có 2 lỗ hổng High/Medium về OAuth cần fix trước go-live, và 1 vấn đề performance nghiêm trọng về oversell khi traffic cao"), theo sau là từng finding có severity rõ ràng. Đây là lúc bạn, với vai trò QA lead, đọc lại và điều chỉnh — AI có thể đánh giá severity hơi khác góc nhìn business của bạn (ví dụ oversell trong flash sale có thể còn nghiêm trọng hơn CVSS gợi ý, vì ảnh hưởng trực tiếp doanh thu và uy tín thương hiệu).

Mẹo: Luôn tách riêng "not a finding" (test đã chạy, kết quả đúng như kỳ vọng, hệ thống an toàn) ra khỏi danh sách finding thật. Nếu trộn lẫn, báo cáo sẽ trông như có nhiều vấn đề hơn thực tế, gây hoang mang không cần thiết cho stakeholder, và làm loãng sự chú ý dành cho những finding thật sự nghiêm trọng như race condition oversell hay thiếu state validation.

Tạo test case xác minh fix (fix validation) từ finding

Báo cáo chưa phải điểm kết — mỗi finding cần một test case xác minh sau khi dev fix xong, viết sẵn từ bây giờ để không phải soạn lại khi PR fix được mở. Ví dụ cho finding race condition:

Test case: Verify inventory reservation uses atomic decrement (fix validation)

Precondition: SKU test có stock = 1 (dùng lại test data đã seed, SKU trong danh sách 20 SKU test).

Steps:
1. Gửi đồng thời 50 request POST /api/cart/reserve cho cùng SKU (dùng k6, 50 VU, 0 ramp-up,
   chạy trong 1 giây duy nhất).
2. Đếm số request trả về HTTP 200 (reservation thành công).
3. Kiểm tra giá trị stock trong Redis sau khi test chạy xong.

Expected result:
- Đúng 1 request trả về HTTP 200, 49 request còn lại trả về HTTP 409 (Conflict) với message
  rõ ràng "SKU out of stock" hoặc tương đương.
- Giá trị stock trong Redis = 0 sau test, không âm.
- Lặp lại test 20 lần liên tiếp — không có lần nào ghi nhận 2 request cùng thành công.

Pass threshold: 20/20 lần chạy đều đúng expected result (0% oversell rate, so với 3/20 = 15%
oversell rate trước khi fix).

Việc chuẩn bị sẵn test case xác minh ngay khi viết finding — không phải đợi tới lúc dev báo "đã fix" — giúp bạn phản hồi nhanh và nhất quán về tiêu chí pass/fail, tránh tranh luận "fix rồi mà" khi thực ra chỉ giảm tỷ lệ lỗi chứ chưa triệt tiêu hoàn toàn.

Cách Ưu Tiên và Bàn Giao Finding Security/Performance Cho Dev Team

Có báo cáo tốt chưa đủ — audit chỉ tạo giá trị thật khi finding được fix. Bước cuối này thường bị QA junior bỏ qua: viết ticket mơ hồ, không có severity rõ ràng, khiến finding nằm im trong backlog hàng tháng.

Khung ưu tiên (prioritization framework)

Với 6 finding thu được (2 performance nghiêm trọng, 2 security, 2 accessibility), bạn không thể yêu cầu fix tất cả cùng lúc trước ngày release. Dùng khung ưu tiên kết hợp Impact × Likelihood, có điều chỉnh theo business impact cho tình huống e-commerce:

Finding Severity kỹ thuật Likelihood khai thác Business impact Priority cuối
Race condition oversell inventory High (CVSS-style 7.5) Cao (xảy ra tự nhiên khi traffic cao, không cần attacker) Rất cao — mất doanh thu, khách hàng khiếu nại khi order bị cancel sau thanh toán P0 — Blocker, phải fix trước release
OAuth thiếu state validation (CSRF) High (CVSS 8.1) Trung bình — cần attacker dụ victim click link Cao — chiếm session, ảnh hưởng uy tín P0 — Blocker
Redis pool saturation ở flash sale Medium-High Cao — chắc chắn xảy ra khi có flash sale thật Cao — toàn hệ thống chậm, không riêng 1 tính năng P1 — Fix trong sprint tới, có thể mitigate tạm bằng tăng connection pool trước release
JWT không revoke sau logout Medium (CVSS 6.5) Thấp — cần chiếm được token trước đó Trung bình P2 — Fix trong 2 sprint tới
Cold start recommendation timeout Medium Cao khi traffic tăng đột ngột Thấp — recommendation không phải luồng critical (checkout vẫn chạy được nếu recommendation fail) P2
Accessibility: thiếu aria-live, focus indicator Low-Medium N/A (luôn ảnh hưởng user dùng screen reader/keyboard) Trung bình — rủi ro compliance, ảnh hưởng trải nghiệm nhóm user cụ thể P1 — do liên quan compliance pháp lý, không chỉ UX

Khung này không cứng nhắc — điểm mấu chốt là bạn luôn giải thích được TẠI SAO một finding là P0 thay vì chỉ dán nhãn CVSS máy móc. Race condition có CVSS "chỉ" 7.5 nhưng lại đứng P0 vì likelihood gần như 100% khi có flash sale thật, và business impact trực tiếp vào doanh thu.

Tạo ticket sẵn sàng cho dev từ finding

Ticket mơ hồ là nguyên nhân số 1 khiến finding bị treo. Dùng AI để chuyển finding + evidence đã có thành ticket đầy đủ, dev có thể bắt tay vào việc ngay không cần hỏi lại QA:

[P0] Race condition allows inventory oversell during concurrent reservation requests

Severity: High (P0 - Blocker)
Component: Inventory Service (Go), Redis reservation keyspace
Found during: Performance audit, stress test flash sale simulation

Repro steps:
1. Set SKU test_sku_014 stock = 1 in staging.
2. Fire 50 concurrent POST /api/cart/reserve requests for test_sku_014 within the same second
   (k6 script: 50 VU, 0ms ramp-up, 1s duration — script attached: reserve_race_test.js).
3. Observe response codes and final stock value in Redis.

Evidence:
- 3 out of 20 test runs resulted in 2 concurrent requests both returning HTTP 200 (both
  reservations succeeded) for a SKU with stock = 1.
- Final Redis stock value went to -1 in the failing runs (see attached k6 output logs,
  run_07.log and run_13.log, timestamps 14:22:07 and 14:31:44).
- Root cause (from code review): Inventory Service reads current stock, checks > 0, then
  writes decrement — this read-check-write is not atomic, allowing a race window under
  concurrent load.

Suggested fix:
- Replace read-then-write pattern with an atomic Redis operation (e.g. Lua script using
  EVAL, or Redis WATCH/MULTI/EXEC transaction) so check-and-decrement happens as one
  atomic step.
- Add a Redis-level test with concurrent DECR to confirm no negative stock is possible
  even under 100+ concurrent requests.

Acceptance criteria:
- Running reserve_race_test.js (50 concurrent requests, stock = 1) 20 times in a row results
  in exactly 1 success (HTTP 200) and 49 HTTP 409 responses, every time — 0% oversell rate.
- Redis stock value never goes below 0 under any tested concurrency level up to 200 VU.
- Fix validated in staging before merging to release branch.

Attachments: reserve_race_test.js, run_07.log, run_13.log

Ticket kiểu này giảm hẳn số lần dev phải quay lại hỏi QA "làm sao để reproduce", vì mọi thứ — script, log, root cause, cả tiêu chí acceptance — đã có sẵn.

Truyền đạt trade-off rủi ro cho stakeholder

Dev team cần chi tiết kỹ thuật, nhưng Product Manager và stakeholder không quan tâm CVSS hay Redis atomic operation. Bạn cần một bản dịch sang ngôn ngữ business, súc tích, tập trung vào quyết định cần đưa ra:

Tóm tắt rủi ro cho stakeholder (finding: Race condition oversell)

Vấn đề: Khi nhiều khách hàng cùng mua sản phẩm cuối cùng trong lúc flash sale (traffic cao),
hệ thống có thể xác nhận đơn hàng cho CẢ HAI khách, dù chỉ còn 1 sản phẩm trong kho.

Rủi ro nếu không fix: Trong test giả lập, hiện tượng này xảy ra ở khoảng 15% các trường hợp
tranh chấp sản phẩm cuối — nghĩa là trong một flash sale thật với hàng trăm sản phẩm hot có
stock thấp, nhiều khách hàng sẽ nhận email xác nhận đơn hàng, thanh toán thành công, rồi sau đó
bị báo huỷ vì hết hàng. Đây là tình huống gây mất uy tín thương hiệu và có thể dẫn đến khiếu nại
hoàn tiền hàng loạt ngay trong ngày flash sale — thời điểm doanh thu và sự chú ý của khách hàng
cao nhất trong năm.

Đề xuất: Fix này cần khoảng 2 ngày effort của 1 backend engineer, đã có sẵn hướng giải pháp kỹ
thuật cụ thể. Khuyến nghị coi đây là điều kiện bắt buộc (go/no-go blocker) trước khi chạy flash
sale tiếp theo, không nên trì hoãn sang sau release.

Đoạn này không nói "race condition", "atomic operation", hay "CVSS" — nó nói bằng ngôn ngữ mà PM và stakeholder ra quyết định được ngay: hậu quả kinh doanh, xác suất xảy ra, chi phí fix, và khuyến nghị rõ ràng.

Sau bàn giao: theo dõi xác minh fix

Sau khi ticket được handoff, đừng để finding "biến mất" vào backlog rồi quên. Duy trì một bảng theo dõi đơn giản — mỗi finding gắn với: ticket ID, ngày handoff, người assign, ngày fix dự kiến, và trạng thái verification (Not started / Fix in review / Fix verified bằng test case đã viết ở phần trước / Regression sau fix). Với P0 như race condition oversell, bạn nên yêu cầu chạy lại đúng test case reserve_race_test.js 20 lần trên staging trước khi merge, và lưu log kết quả làm bằng chứng đóng ticket — không chỉ tin vào lời dev báo "đã fix". Vòng lặp audit → finding → ticket → fix → verify → close này chính là điều biến một báo cáo audit từ "tài liệu nằm im" thành cải thiện chất lượng thực sự đo được.

Mẹo: Khi viết ticket bàn giao cho dev, luôn đính kèm chính xác đoạn script hoặc lệnh dùng để reproduce lỗi (không chỉ mô tả bằng lời), và ghi rõ ngay trong ticket ai sẽ chạy lại test case đó để verify khi fix xong. Việc này biến QA từ vai trò "báo lỗi rồi chờ" thành vai trò chủ động đóng vòng lặp chất lượng — điều mà stakeholder và dev team đều đánh giá cao hơn nhiều so với một bản báo cáo audit dài nhưng không ai theo dõi tiếp.