경험 많은 리뷰어 두 명에게 같은 풀 리퀘스트를 읽히면 서로 다른 두 개의 목록이 돌아옵니다. 한 사람은 URL의 식별자가 세션과 한 번도 대조되지 않는다는 점을 발견하고, 다른 사람은 재시도 루프가 실패할 때마다 데이터베이스 연결을 열어 둔다는 점을 발견합니다. 둘 다 틀리지 않았고, 둘 다 완전하지 않습니다. 언어 모델도 같은 이유로 똑같이 행동합니다. 리뷰어가 무엇을 알아채는지는 그동안 무엇을 봐 왔는지에 달려 있습니다.
일치는 신호다
모델 하나는 자신의 확신도를 알려 줍니다. 그 숫자에도 의미는 있지만 자기 보고입니다. 모델이 자기 답안을 채점하는 셈이죠. 서로 다른 연구소에서, 서로 다른 데이터로, 서로 다른 약점을 안고 학습한 두 모델이 독립적으로 같은 파일의 같은 취약점에 도달했다면, 그 일치는 모델 바깥에서 온 증거입니다. 자동화된 리뷰가 세컨드 오피니언에 가장 가까워지는 지점입니다.
반대의 경우도 똑같이 중요합니다. 한 엔진만 보고한 발견 사항이 그 이유만으로 틀린 것은 아니며, 그것을 버리면 단독 리뷰어가 잘 잡아내는 버그를 그대로 버리는 셈입니다. 그래서 보고하고, 어떤 엔진이 찾았는지 함께 표시합니다. 판단은 사용자의 몫입니다.
한 엔진이 조용해지는 지점
실제로 두 엔진이 가장 크게 갈리는 지점은 패턴 매칭이 아니라 추론이 필요한 버그 유형입니다.
- 로직 결함: 두 번 적용되는 할인, 환불 뒤에 환불을 한 번 더 받아들이는 상태 머신, 잘못된 객체에 대해 도는 소유권 검사.
- 프레임워크 고유의 인가: 라우트 그룹은 보호하면서 그 옆에 붙은 API 핸들러는 보호하지 않는 미들웨어.
- 리소스와 메모리 누수: 요청마다 열리고 정상 경로에서만 해제되는 리스너, 타이머, 커넥션.
- 주석이나 픽스처에 숨은 프롬프트 인젝션. 찾고 있지 않으면 무해한 문장으로 읽힙니다.
두 리포트가 하나가 되는 방식
병합은 두 번째 엔진이 값을 하느냐, 소음을 두 배로 만드느냐가 갈리는 지점입니다. 우리가 정한 규칙은 이렇습니다.
- 발견 사항은 표현이 아니라 버그를 식별하는 것, 즉 CWE 분류와 해당 파일을 기준으로 맞춥니다.
- 일치한 항목은 한 번만, 두 심각도 중 더 엄격한 쪽으로 보고합니다. 행동의 기준은 안전한 해석이기 때문입니다.
- 개발자가 실제로 쓸 수 있는 서술을 남깁니다. 더 긴 근거, 구체적인 패치, 순서가 있는 수정 단계.
- 교차 확인된 항목은 신뢰도를 높음으로 올리고 두 엔진 이름을 함께 붙입니다.
- 한 엔진만 본 항목도 찾아낸 엔진 이름과 함께 남깁니다.
두 엔진은 같은 소스를 동시에 읽기 때문에 감사 시간이 두 배가 되지는 않습니다. 두 배가 되는 것은 실행 비용이고, Pro 플랜에 속한 이유도 그것입니다. 두 번째 엔진을 쓸 수 없거나 쓸 수 없는 응답이 오면, 감사는 실패하지 않고 첫 번째 엔진만으로 완료됩니다.
이 가운데 무엇도 리포트를 참으로 만들지는 않습니다. 두 엔진이 같은 결론에 이르고 둘 다 틀릴 수 있습니다. 모든 발견 사항 아래 인용된 근거는 믿는 대신 확인하라고 있는 것입니다. 교차 확인이 주는 것은 우선순위입니다. 발견 사항 백 건이 도착했고 시간은 반나절뿐이라면, 두 독립적인 리뷰어가 함께 본 것부터 시작하십시오.