QA Bug Playground

Answer Key

🇻🇳 Đáp án

Global / Layout (5)

🇻🇳 Toàn cục / Bố cục
  1. Nav bar has no wrap/scroll handling and overlaps the logo on narrow viewports
    🟢 Junior thường bỏ qua Chỉ test ở màn hình desktop rộng, không thu nhỏ cửa sổ hay test trên viewport điện thoại/tablet.
    🔵 Senior hay phát hiện Luôn resize/responsive-test qua nhiều breakpoint và thiết bị thật trước khi báo pass.
  2. The "QA Bug Playground" logo link has cursor:default instead of a pointer
    🟢 Junior thường bỏ qua Không để ý chi tiết nhỏ như con trỏ chuột — chỉ quan tâm click có hoạt động hay không.
    🔵 Senior hay phát hiện Đánh giá cả affordance/UX: người dùng có nhận ra đây là link không, chứ không chỉ chức năng có chạy.
  3. Fixed footer with no body bottom-padding can cover the end of long pages
    🟢 Junior thường bỏ qua Chỉ xem trang ngắn hoặc không cuộn xuống hẳn cuối trang dài.
    🔵 Senior hay phát hiện Luôn cuộn hết trang, đặc biệt các trang dài hoặc có nội dung động (danh sách, comment...).
  4. Footer text color fails WCAG contrast ratio against its background
    🟢 Junior thường bỏ qua Không kiểm tra accessibility/độ tương phản màu, chỉ nhìn qua bằng mắt thấy "đọc được" là đủ.
    🔵 Senior hay phát hiện Dùng công cụ (Lighthouse, axe, contrast checker) để verify accessibility theo chuẩn WCAG.
  5. No security headers set anywhere (CSP, X-Frame-Options, X-Content-Type-Options, HSTS)
    🟢 Junior thường bỏ qua Không mở tab Network/Headers trong DevTools, chỉ quan tâm giao diện hiển thị đúng.
    🔵 Senior hay phát hiện Luôn kiểm tra response headers — thiếu CSP/X-Frame-Options nghĩa là app có thể bị clickjacking hoặc XSS lan rộng hơn.

UI Bugs — /ui-bugs (17)

🇻🇳 Lỗi giao diện
  1. "Place Order" button looks disabled (CSS opacity) but has no disabled attribute and is fully clickable
    🟢 Junior thường bỏ qua Nhìn nút bị mờ đi là kết luận ngay "đã disable", không thử click thật vào nó.
    🔵 Senior hay phát hiện Luôn thử click vào phần tử "trông có vẻ disabled" để xác nhận nó thực sự chặn hành động.
  2. Modal close (X) button is wired to a non-existent element, so it can never close the modal
    🟢 Junior thường bỏ qua Test nhanh một lần, thấy modal mở được là coi như pass, không thử đóng lại.
    🔵 Senior hay phát hiện Test đủ vòng đời của component: mở → tương tác → đóng → mở lại.
  3. Dropdown menu renders behind the card's clipped edge (overflow:hidden on the parent)
    🟢 Junior thường bỏ qua Không kiểm tra dropdown/menu ở các vị trí hoặc kích thước container khác nhau.
    🔵 Senior hay phát hiện Kiểm tra các phần tử overlay (dropdown, tooltip, modal) trong nhiều ngữ cảnh layout khác nhau.
  4. Tabs always show Tab 1's content regardless of which tab is clicked
    🟢 Junior thường bỏ qua Chỉ click 1 tab rồi kết luận tính năng tab hoạt động bình thường.
    🔵 Senior hay phát hiện Test lần lượt tất cả các tab/trạng thái, không suy luận kết quả từ một mẫu duy nhất.
  5. Star rating is off by one: clicking the 3rd star fills 4 stars
    🟢 Junior thường bỏ qua Không đối chiếu số sao hiển thị với đúng vị trí đã click.
    🔵 Senior hay phát hiện Test các giá trị biên (sao đầu tiên, giữa, cuối cùng) và so khớp chính xác kết quả mong đợi.
  6. "+1" / "-1" counter buttons are swapped: plus decrements, minus increments
    🟢 Junior thường bỏ qua Chỉ click 1 nút, thấy số thay đổi là pass, không đọc kỹ số tăng hay giảm.
    🔵 Senior hay phát hiện Luôn đối chiếu hành vi thực tế với nhãn nút — verify giá trị trước/sau mỗi thao tác.
  7. Accordion expands to a fixed max-height shorter than its content, clipping the text
    🟢 Junior thường bỏ qua Test với nội dung mẫu ngắn, không thử nội dung dài như thực tế sẽ gặp.
    🔵 Senior hay phát hiện Test với dữ liệu dài/thực tế (long-content testing), không chỉ dữ liệu mẫu ngắn gọn.
  8. Product image points at a non-existent file and has no alt text
    🟢 Junior thường bỏ qua Không để ý ảnh vỡ nếu nó nhỏ, hoặc không nghĩ tới việc kiểm tra alt text.
    🔵 Senior hay phát hiện Kiểm tra alt text cho accessibility và trải nghiệm khi ảnh không tải được.
  9. Password visibility toggle changes its own icon but never flips the input's type
    🟢 Junior thường bỏ qua Chỉ nhìn icon con mắt đổi trạng thái, không kiểm tra input có thực sự hiển thị giá trị hay không.
    🔵 Senior hay phát hiện Verify trạng thái thực sự thay đổi (type=text), chứ không chỉ verify icon trên UI.
  10. Toast notification auto-dismisses after 250ms and can render behind the header (z-index)
    🟢 Junior thường bỏ qua Không đo thời gian hiển thị thực tế của thông báo, thấy "có xuất hiện" là pass.
    🔵 Senior hay phát hiện Đo thời gian hiển thị, kiểm tra z-index/stacking context ở nhiều vị trí cuộn trang.
  11. Long unbroken order-reference text overflows its container, causing horizontal page scroll
    🟢 Junior thường bỏ qua Test với text ngắn mẫu, không thử input dài bất thường.
    🔵 Senior hay phát hiện Luôn test với input cực dài (long string) để phát hiện lỗi tràn layout.
  12. Pagination "Next" never clamps to the last page ("Page 7 of 3")
    🟢 Junior thường bỏ qua Chỉ test lướt qua một hoặc hai trang đầu.
    🔵 Senior hay phát hiện Test giá trị biên: trang đầu, trang cuối, và vượt quá giới hạn (boundary testing).
  13. Feedback submit button has no debounce/disable-on-click guard, so a fast double-click submits twice
    🟢 Junior thường bỏ qua Chỉ click một lần chậm rãi, không test double-click hoặc mạng chậm.
    🔵 Senior hay phát hiện Test double-submit/race condition bằng cách click nhanh liên tục để tìm lỗi trùng lặp.
  14. Explicit, inconsistent tabindex values scramble keyboard tab order
    🟢 Junior thường bỏ qua Chỉ test bằng chuột, không bao giờ dùng bàn phím để điều hướng qua trang.
    🔵 Senior hay phát hiện Test đầy đủ bằng bàn phím (Tab/Shift+Tab) và trải nghiệm screen reader cho accessibility.
  15. Clicking the table's "Price" header computes a sorted array but never re-renders it into the DOM
    🟢 Junior thường bỏ qua Thấy icon mũi tên đổi hướng là kết luận sort hoạt động, không nhìn kỹ dữ liệu trong bảng.
    🔵 Senior hay phát hiện Luôn đối chiếu dữ liệu thực tế hiển thị, không chỉ tin vào tín hiệu UI (icon/label).
  16. An input placed mid-page has autofocus, causing a disorienting scroll-jump on load
    🟢 Junior thường bỏ qua Không để ý hành vi cuộn trang tự động khi trang vừa tải xong.
    🔵 Senior hay phát hiện Đánh giá trải nghiệm tải trang đầu tiên (first-load UX), đặc biệt ảnh hưởng người dùng screen reader.
  17. "Load more" always appends the same hardcoded items instead of tracking an offset/cursor
    🟢 Junior thường bỏ qua Click "Load more" một lần thấy có thêm item là pass, không click nhiều lần liên tục.
    🔵 Senior hay phát hiện Test lặp lại hành động nhiều lần để phát hiện lỗi trùng lặp hoặc thiếu cơ chế phân trang.

Checkout / Cart — /checkout (6)

🇻🇳 Thanh toán / Giỏ hàng
  1. Quantity field has no lower bound — 0 or negative values are accepted
    🟢 Junior thường bỏ qua Chỉ test số lượng dương hợp lệ (1, 2, 3...).
    🔵 Senior hay phát hiện Luôn test giá trị biên và giá trị phi lý: 0, số âm, số thập phân, ký tự chữ.
  2. An item marked "out of stock" is still fully purchasable — nothing disables the form
    🟢 Junior thường bỏ qua Không đối chiếu trạng thái tồn kho hiển thị với hành vi thực tế của nút mua.
    🔵 Senior hay phát hiện Kiểm tra tính nhất quán giữa dữ liệu hiển thị (business state) và hành vi thực tế cho phép của UI.
  3. Coupon discount stacks another 10% every time "Apply" is clicked, with no cap
    🟢 Junior thường bỏ qua Áp mã giảm giá một lần, thấy giảm đúng % là pass.
    🔵 Senior hay phát hiện Test click lặp lại nhiều lần trên cùng một hành động để tìm lỗi tích lũy (stacking bug).
  4. Totals are computed in plain floating-point with no rounding, producing artifacts like 59.970000000000006
    🟢 Junior thường bỏ qua Không tự tính lại số tiền bằng máy tính để đối chiếu.
    🔵 Senior hay phát hiện Luôn verify phép tính tiền bằng công cụ độc lập, đặc biệt với số thập phân/tiền tệ.
  5. Free-shipping banner uses total <= 50 instead of < 50, an off-by-one at exactly $50.00
    🟢 Junior thường bỏ qua Test với số tiền rõ ràng trên hoặc dưới ngưỡng, không test đúng ngay tại ngưỡng.
    🔵 Senior hay phát hiện Test chính xác tại giá trị biên (ví dụ đúng $50.00), không chỉ trên/dưới ngưỡng.
  6. The server accepts the client-submitted unit_price/total hidden fields instead of re-pricing from the catalog
    🟢 Junior thường bỏ qua Không mở DevTools để xem/sửa các trường ẩn (hidden field) trước khi submit.
    🔵 Senior hay phát hiện Luôn nghĩ theo góc nhìn kẻ tấn công: sửa hidden field, replay request bằng curl/Burp để kiểm tra server có tính lại giá không.

Form Validation — /validation (11)

🇻🇳 Kiểm tra dữ liệu nhập
  1. Username is never required — empty string is accepted client- and server-side
    🟢 Junior thường bỏ qua Chỉ test với dữ liệu hợp lệ đầy đủ, không thử để trống trường bắt buộc.
    🔵 Senior hay phát hiện Luôn test bỏ trống từng trường required để xác nhận validation thực sự chặn được.
  2. Email regex only checks for "@", so "abc@d" passes
    🟢 Junior thường bỏ qua Chỉ test một email hợp lệ dạng chuẩn (name@domain.com).
    🔵 Senior hay phát hiện Test nhiều biến thể input gần-đúng định dạng để dò tìm regex yếu.
  3. Password label says "at least 8 characters" but only 4 is enforced (and no number required)
    🟢 Junior thường bỏ qua Không đối chiếu chính xác giữa message/label hiển thị và hành vi thực tế được chấp nhận.
    🔵 Senior hay phát hiện Đối chiếu từng câu chữ trong UI với hành vi backend thực tế — rule hiển thị phải khớp rule thực thi.
  4. Confirm-password is collected but never actually compared to the password
    🟢 Junior thường bỏ qua Nhập hai mật khẩu giống nhau để test, không thử cố tình nhập khác nhau.
    🔵 Senior hay phát hiện Test negative case: cố tình nhập sai lệch để xác nhận validation có thực sự so sánh hay không.
  5. Age field accepts negative numbers, decimals, or non-numeric junk — only "empty" is rejected
    🟢 Junior thường bỏ qua Chỉ test tuổi hợp lý (18, 25...), không thử giá trị phi logic.
    🔵 Senior hay phát hiện Test toàn bộ input biên: âm, thập phân, chữ, ký tự đặc biệt, giá trị cực lớn.
  6. Phone field claims "digits only" but nothing enforces it — letters are accepted
    🟢 Junior thường bỏ qua Tin vào label "digits only" mà không thử nhập chữ để kiểm chứng.
    🔵 Senior hay phát hiện Không bao giờ tin label/placeholder — luôn tự kiểm chứng bằng input thực tế.
  7. "I agree to terms" checkbox condition is inverted — submitting unchecked still succeeds
    🟢 Junior thường bỏ qua Luôn tick checkbox trước khi submit theo thói quen, không thử để trống.
    🔵 Senior hay phát hiện Test rõ từng nhánh boolean (checked/unchecked) — cả hai chiều đều phải được verify.
  8. Client-side validation never actually blocks anything (errors don't clear, and preventDefault is never called)
    🟢 Junior thường bỏ qua Coi client validation và server validation là một, không phân biệt hai lớp kiểm tra riêng biệt.
    🔵 Senior hay phát hiện Luôn test cả hai lớp riêng biệt: tắt JS hoặc gửi thẳng request để xác nhận server tự bảo vệ được.
  9. Leading/trailing whitespace on username is never trimmed (" admin" looks like a different account)
    🟢 Junior thường bỏ qua Không thử input có khoảng trắng ở đầu hoặc cuối chuỗi.
    🔵 Senior hay phát hiện Test input có whitespace/ký tự ẩn để phát hiện lỗi trùng lặp dữ liệu (" admin" khác "admin").
  10. No length limit or Unicode normalization on username (accepts huge pastes, emoji, RTL text)
    🟢 Junior thường bỏ qua Không thử input cực dài hoặc emoji/ký tự đặc biệt.
    🔵 Senior hay phát hiện Test input Unicode, emoji, RTL text và input rất dài để tìm lỗi hiển thị/lưu trữ.
  11. The success banner echoes the submitted username back unescaped (reflected XSS)
    🟢 Junior thường bỏ qua Chỉ test dữ liệu text thông thường, không thử payload HTML/JS.
    🔵 Senior hay phát hiện Luôn thử payload XSS cơ bản (<script>, <img onerror>) ở mọi trường input được hiển thị lại.

SQL Injection & Data — /sqli (4)

🇻🇳 Chèn lệnh SQL & Dữ liệu
  1. Login query is built with raw string concatenation (bypass: ' OR '1'='1' -- ); passwords are also stored in plaintext
    🟢 Junior thường bỏ qua Chỉ test đăng nhập với tài khoản hợp lệ/sai theo cách thông thường.
    🔵 Senior hay phát hiện Luôn thử payload SQL injection kinh điển trong các trường đăng nhập, và kiểm tra cách mật khẩu được lưu trữ.
  2. Product search is vulnerable to UNION-based extraction of the users table
    🟢 Junior thường bỏ qua Chỉ test tìm kiếm với từ khóa sản phẩm thông thường.
    🔵 Senior hay phát hiện Thử payload UNION SELECT để kiểm tra khả năng rò rỉ dữ liệu qua chức năng tìm kiếm.
  3. The username-availability check is a textbook blind boolean-based injection point
    🟢 Junior thường bỏ qua Bỏ qua các API/endpoint "phụ" không hiển thị dữ liệu trực tiếp, cho là an toàn vì "không thấy gì".
    🔵 Senior hay phát hiện Hiểu rằng một response chỉ trả true/false vẫn có thể bị khai thác qua blind injection.
  4. The last search term is echoed unescaped into the search box's value attribute (reflected XSS / attribute breakout)
    🟢 Junior thường bỏ qua Chỉ kiểm tra input có hiển thị lại đúng giá trị đã gõ, không thử payload phá vỡ thuộc tính HTML.
    🔵 Senior hay phát hiện Thử payload dạng "><script> để kiểm tra khả năng thoát khỏi thuộc tính HTML (attribute breakout).

XSS Guestbook — /xss (2)

🇻🇳 Sổ lưu bút dính XSS
  1. Comment body is stored and re-rendered without any HTML-escaping (stored XSS)
    🟢 Junior thường bỏ qua Test comment với nội dung text thường, không thử tag HTML/script.
    🔵 Senior hay phát hiện Luôn thử payload XSS lưu trữ (stored XSS) ở mọi trường nội dung do người dùng nhập và hiển thị lại cho người khác.
  2. "Reset demo comments" is a destructive action exposed via a plain GET link with no CSRF protection
    🟢 Junior thường bỏ qua Không để ý phương thức HTTP (GET/POST) được dùng cho một hành động xóa dữ liệu.
    🔵 Senior hay phát hiện Luôn kiểm tra các hành động thay đổi dữ liệu phải dùng POST/DELETE có bảo vệ CSRF, không phải GET.

Broken Access Control / IDOR — /idor (2)

🇻🇳 Lỗi phân quyền / IDOR
  1. Order lookup never verifies the order's user_id matches the logged-in user — sequential IDs are enumerable
    🟢 Junior thường bỏ qua Chỉ test xem đúng đơn hàng của tài khoản mình đang đăng nhập.
    🔵 Senior hay phát hiện Chủ động thử truy cập ID của người dùng khác (đổi ID trên URL) để kiểm tra authorization boundary.
  2. "Login" is just a spoofable ?as= URL query parameter — no real session or credential check
    🟢 Junior thường bỏ qua Coi việc chuyển đổi user qua URL là tính năng bình thường của app demo, không đặt câu hỏi về cơ chế xác thực.
    🔵 Senior hay phát hiện Luôn đặt câu hỏi: cơ chế xác thực có thực sự an toàn không, hay chỉ là "ảo giác" ở lớp giao diện.

Undocumented API — /api/* (3)

🇻🇳 API không tài liệu hóa
  1. GET /api/orders serves every user's order history (incl. shipping addresses) with wide-open CORS and zero auth
    🟢 Junior thường bỏ qua Không đoán/tìm các API endpoint không được liên kết từ giao diện.
    🔵 Senior hay phát hiện Chủ động dò tìm endpoint ẩn (network tab, đoán theo REST convention) và kiểm tra CORS/auth trên từng API.
  2. GET /api/debug returns the raw error message and full stack trace to the client
    🟢 Junior thường bỏ qua Không cố tình gửi input gây lỗi để xem phản hồi lỗi trông như thế nào.
    🔵 Senior hay phát hiện Chủ động kích hoạt lỗi (invalid input, edge case) để kiểm tra thông tin nhạy cảm có bị rò rỉ qua error response không.
  3. No rate limiting anywhere — login, the username-availability check, etc. can all be brute-forced freely
    🟢 Junior thường bỏ qua Không nghĩ đến việc test số lượng request lớn hoặc lặp lại liên tục.
    🔵 Senior hay phát hiện Luôn đánh giá khả năng chống brute-force/rate limiting cho các endpoint nhạy cảm như đăng nhập.