Checkout / Cart
🇻🇳 Thanh toán / Giỏ hàng
1. Quantity has no lower bound
🇻🇳 Số lượng không có giới hạn dưới
💡 Hint: The quantity field is type=number but has no min attribute and nothing rejects 0 or negative values client- or server-side, so you can "order" -3 mice.
🇻🇳 Ô số lượng là type=number nhưng không có thuộc tính min và không gì chặn giá trị 0 hoặc âm ở cả client lẫn server, nên bạn có thể "đặt" -3 con chuột.
🇻🇳 Ô số lượng là type=number nhưng không có thuộc tính min và không gì chặn giá trị 0 hoặc âm ở cả client lẫn server, nên bạn có thể "đặt" -3 con chuột.
2. Out-of-stock item is still purchasable
🇻🇳 Hàng hết kho vẫn mua được
💡 Hint: Stock shows 0, but neither the quantity input nor the Place Order button is disabled for it — a classic case of the UI displaying the right data without acting on it.
🇻🇳 Tồn kho hiển thị 0, nhưng cả ô số lượng lẫn nút Place Order đều không bị vô hiệu hóa — một ví dụ kinh điển của việc UI hiển thị đúng dữ liệu nhưng không hành xử theo dữ liệu đó.
🇻🇳 Tồn kho hiển thị 0, nhưng cả ô số lượng lẫn nút Place Order đều không bị vô hiệu hóa — một ví dụ kinh điển của việc UI hiển thị đúng dữ liệu nhưng không hành xử theo dữ liệu đó.
3. Coupon discount stacks indefinitely
🇻🇳 Mã giảm giá cộng dồn vô hạn
💡 Hint: "Apply" subtracts another 10% off the current total every time it's clicked, instead of validating the code once and locking the field — click it five times and the mouse is free.
🇻🇳 "Apply" trừ thêm 10% khỏi tổng hiện tại mỗi lần bấm, thay vì chỉ validate mã một lần rồi khóa ô lại — bấm 5 lần là con chuột thành miễn phí.
🇻🇳 "Apply" trừ thêm 10% khỏi tổng hiện tại mỗi lần bấm, thay vì chỉ validate mã một lần rồi khóa ô lại — bấm 5 lần là con chuột thành miễn phí.
4. Floating-point total
🇻🇳 Tổng tiền bị lỗi số thực (floating-point)
💡 Hint: The subtotal is computed as quantity * unitPrice in plain JS floating point without rounding to cents, so some quantities (try 3) display totals like 59.970000000000006 instead of 59.97.
🇻🇳 Tổng phụ được tính bằng quantity * unitPrice ở dạng số thực JS thuần, không làm tròn về đơn vị cent, nên với một số số lượng (thử 3) tổng hiển thị ra như 59.970000000000006 thay vì 59.97.
🇻🇳 Tổng phụ được tính bằng quantity * unitPrice ở dạng số thực JS thuần, không làm tròn về đơn vị cent, nên với một số số lượng (thử 3) tổng hiển thị ra như 59.970000000000006 thay vì 59.97.
5. Off-by-one free-shipping banner
🇻🇳 Thông báo freeship lệch 1 đơn vị
💡 Hint: The free-shipping message checks total <= 50 instead of total < 50, so it still tells you you're "$0.00 away from free shipping" even once you've already reached exactly $50.00.
🇻🇳 Thông báo freeship kiểm tra total <= 50 thay vì total < 50, nên nó vẫn báo "còn $0.00 nữa là được freeship" ngay cả khi bạn đã đạt đúng $50.00.
🇻🇳 Thông báo freeship kiểm tra total <= 50 thay vì total < 50, nên nó vẫn báo "còn $0.00 nữa là được freeship" ngay cả khi bạn đã đạt đúng $50.00.
6. Server trusts the client-submitted price
🇻🇳 Server tin tưởng giá do client gửi lên
💡 Hint: The real bug: unit_price and total are plain hidden form fields. Open devtools, edit unit_price to 0.01, resubmit — the server records the order at whatever price you sent instead of re-pricing it from the product catalog. This is the kind of finding a price-skimming attacker (or a careless refactor) would exploit; it's easy for a quick manual pass to miss because the visible UI never shows anything wrong.
🇻🇳 Lỗi thật sự: unit_price và total chỉ là các trường ẩn (hidden field) bình thường. Mở devtools, sửa unit_price thành 0.01, submit lại — server sẽ ghi nhận đơn hàng theo bất kỳ giá nào bạn gửi thay vì tự tính lại từ catalog sản phẩm. Đây là kiểu lỗi mà kẻ tấn công (hoặc một lần refactor cẩu thả) sẽ khai thác; rất dễ bị một lượt test thủ công qua loa bỏ sót vì giao diện nhìn thấy không hề báo gì sai.
🇻🇳 Lỗi thật sự: unit_price và total chỉ là các trường ẩn (hidden field) bình thường. Mở devtools, sửa unit_price thành 0.01, submit lại — server sẽ ghi nhận đơn hàng theo bất kỳ giá nào bạn gửi thay vì tự tính lại từ catalog sản phẩm. Đây là kiểu lỗi mà kẻ tấn công (hoặc một lần refactor cẩu thả) sẽ khai thác; rất dễ bị một lượt test thủ công qua loa bỏ sót vì giao diện nhìn thấy không hề báo gì sai.