Đây là bài quan trọng nhất. False positive (báo lỗi sai) là kẻ giết chết mọi tool bảo mật: dev nhận 50 cảnh báo, 40 cái sai, họ mất niềm tin và tắt tool. Giải pháp: sau khi "agent finder" báo lỗi, ta cho một "agent verifier" đóng vai luật sư bào chữa, cố hết sức bác bỏ finding. Chỉ finding nào sống sót qua phản biện mới được giữ.
1. Nguyên lý: tách vai Tìm và Phản biện
Dùng cùng một prompt để "tìm rồi tự kiểm" không hiệu quả — mô hình thiên vị bảo vệ kết luận của chính nó (confirmation bias). Tách thành hai lời gọi độc lập với hai vai đối lập cho kết quả khách quan hơn nhiều:
- Finder: "tìm mọi lỗ hổng khả nghi" (recall cao, chấp nhận nhiễu).
- Verifier: "chứng minh finding này SAI" (precision cao, cắt nhiễu).
2. Prompt phản biện
<!-- prompts/verifier.md -->
Bạn là kỹ sư bảo mật hoài nghi. Nhiệm vụ: cố BÁC BỎ finding dưới đây.
Một finding chỉ HỢP LỆ nếu tồn tại đường đi khai thác THỰC SỰ.
Hãy kiểm tra từng lý do có thể khiến finding SAI:
1. Đầu vào có thực sự do người dùng kiểm soát? (không phải hằng số)
2. Có sanitizer đúng loại ở đâu đó trên đường đi không?
3. Có middleware/guard/ACL chặn trước khi tới sink không?
4. Sink có thực sự nguy hiểm, hay chỉ là log/biến nội bộ?
5. Framework có auto-bảo vệ (auto-escape, ORM binding) không?
Trả JSON: {
"verdict": "CONFIRMED|REJECTED|UNCERTAIN",
"reason": "<lập luận>",
"exploit_sketch": "<payload minh họa nếu CONFIRMED>",
"confidence": <0..1>
}
Nếu không dựng nổi payload khai thác cụ thể -> nghiêng về REJECTED/UNCERTAIN.
Yêu cầu exploit_sketch (phác thảo payload) là bộ lọc vàng: nếu verifier không nghĩ ra nổi một payload cụ thể, khả năng cao đó là false positive.
3. Vòng lặp xác thực
def verify(finding, code_ctx):
out = ask(VERIFIER, json.dumps(finding) + "\n\nCODE:\n" + code_ctx)
return out
def pipeline(findings, code_ctx):
kept = []
for f in findings:
v = verify(f, code_ctx)
if v["verdict"] == "CONFIRMED" and v["confidence"] >= 0.6:
f["exploit"] = v["exploit_sketch"]
f["final_confidence"] = (f["confidence"] + v["confidence"]) / 2
kept.append(f)
# REJECTED -> loại; UNCERTAIN -> đưa vào hàng chờ review người
return kept
4. Ví dụ verifier cứu bạn khỏi false positive
Finder báo SQLi ở:
$status = $_GET['status'];
$db->query("SELECT * FROM orders WHERE status = ?", [$status]);
Verifier phát hiện: có ? placeholder và tham số truyền qua mảng bind → prepared statement → REJECTED. Không có payload nào thoát được. Một finding sai bị chặn trước khi tới mắt dev.
5. Ba mức phán quyết và cách xử lý
- CONFIRMED: giữ, đưa vào báo cáo, xếp severity.
- REJECTED: loại bỏ khỏi báo cáo (nhưng log lại để audit).
- UNCERTAIN: đưa vào hàng chờ review người — không tự chặn CI, không tự bỏ.
Khung tư duy "phản biện đối kháng"
> Một finding chỉ đáng tin khi nó sống sót qua nỗ lực bác bỏ nghiêm túc. Nếu bạn không dựng được payload khai thác, đừng báo là lỗ hổng — hãy hạ xuống UNCERTAIN.
Các bước triển khai
- Tạo prompt verifier riêng, vai "hoài nghi".
- Bắt buộc verifier xuất
verdict+exploit_sketch. - Chỉ giữ CONFIRMED trên ngưỡng confidence.
- Định tuyến UNCERTAIN sang review người.
- Log REJECTED để đo tỷ lệ và tinh chỉnh finder.
- Đo lại false positive rate trước/sau khi thêm verifier.
Checklist
- [ ] Finder và Verifier là hai lời gọi tách biệt?
- [ ] Verifier được yêu cầu BÁC BỎ, không phải xác nhận?
- [ ] Có bắt buộc phác thảo payload khai thác?
- [ ] UNCERTAIN được định tuyến sang người, không tự bỏ?
- [ ] Có đo FP rate trước/sau?
Sai lầm thường gặp
- Gộp tìm và kiểm vào một prompt: confirmation bias, không giảm được FP.
- Verifier vai trung lập: phải là "luật sư bào chữa" mới cắt nhiễu mạnh.
- Vứt luôn UNCERTAIN: bỏ sót lỗi thật (false negative). Đưa cho người.
- Không log REJECTED: mất dữ liệu để cải thiện finder.