AI 생성 취약점 신고 품질 게이트

생성형 AI는 취약점 신고서를 빠르게 다듬지만, 매끄러운 문장은 취약점의 증거가 아니다. 반대로 AI를 썼다는 이유만으로 신고를 낮게 평가해서도 안 된다. 필요한 것은 작성 수단을 맞히는 탐지기가 아니라, 주장과 저장소 상태를 대조하고 같은 조건에서 재현할 수 있게 하는 접수 품질 게이트다.

GitHub는 2026년 10월 1일 private vulnerability report에 구조화된 양식과 신규 신고 rate limit을 도입했다.[1][2] 이 글의 기능 설명은 private vulnerability reporting을 활성화한 public repository에만 적용되며, 지원 플랜은 GitHub Free, Pro, Team, Enterprise Cloud다. private·internal repository나 GitHub Enterprise Server까지 일반화하지 않는다.[1][2][3]

구조화는 진위 판정이 아니라 접수 계약이다

기본 웹 양식은 summary, details, 150자 이상의 proof of concept, impact를 필수로 요구한다. 응답은 advisory description으로 합쳐지고 유지관리자는 이를 검토·편집한다.[1] 150자는 최소 서술량일 뿐 품질 점수나 실행 가능성 보증이 아니다.

입력 접수 시 확인할 최소 정보 아직 확정하지 않을 것
Summary 취약 동작, 영향 구성 요소, 공격 결과 심각도와 CVE 적격성
Details 영향 버전·커밋, 전제 권한, 입력 경로, 기대·실제 동작 모든 배포 환경의 영향
PoC 재현 단계, 입력, 관찰 지점, 성공·실패 기준 운영 환경에서의 악용 성공
Impact 공격자가 얻는 권한·데이터, 피해 경계, 사용자 상호작용 최대 피해가 실제 발생했다는 결론

형식 통과 뒤에는 참조 무결성을 본다. 함수와 파일 경로가 해당 revision에 존재하는지, 옵션이 신고 버전과 맞는지, 권한 전제가 서로 모순되지 않는지 확인한다. 구조화된 양식은 이 비교 비용을 낮추지만 허위 신고나 spam을 자동으로 제거하지 않는다.

저장소에 맞는 intake schema를 만든다

기본 브랜치의 .github/VULNERABILITY_REPORT.yml 또는 .yaml로 기본 양식을 교체할 수 있다. 조직·개인 계정의 .github 저장소에 공통 양식을 둘 수도 있으며 개별 저장소 양식이 우선한다. 양식은 issue form 문법을 사용하고 input과 textarea에 min_length를 지정할 수 있다. 유효하지 않은 custom form은 기본 양식으로 대체된다.[1][3]

질문 수보다 재현에 필요한 경계를 구조화해야 한다. 영향 버전과 commit SHA, 설치 방식, 최소 OS·runtime·dependency 조합, 공격자의 선행 권한, 정상 기대값과 실제 관찰값, 민감 정보를 제거한 요청·응답, 정리 절차를 분리해 받는다. SECURITY.md는 지원 버전, 공개하면 안 되는 정보, 응답 목표 시간과 대체 연락 수단을 설명한다. 양식은 사실을 수집하고 정책 문서는 절차를 안내하도록 역할을 나눈다.[1][3]

AI 사용 공개 항목은 provenance 정보다.[1] 체크 여부를 우선순위나 기각 조건으로 쓰지 말고, 인용한 코드의 존재, 단계의 결정성, 관찰 결과와 impact 사이의 논리를 평가한다.

CWE는 분류 보조 수단이다

저장소 설정에서 신규 신고에 하나 이상의 CWE를 요구할 수 있다. 이 요구는 웹과 REST API 제출에 적용되지만, 유지관리자가 advisory를 직접 만들거나 기존 신고를 편집할 때는 적용되지 않는다. 조직·enterprise owner는 정책으로 이를 강제할 수 있다.[1][3]

CWE는 라우팅과 통계에는 유용하지만 진위 판정기는 아니다. 분류가 부정확해도 재현 가능한 결함일 수 있고, 정확한 CWE를 선택한 문서가 실제 취약점을 증명하지도 않는다. 접수 단계에서는 약점 유형과 공격 경로의 일관성을 확인하고 triage 중 수정할 수 있게 한다.

웹 양식과 REST API를 하나의 계약으로 운영한다

REST 신고 endpoint는 POST /repos/{owner}/{repo}/security-advisories/reports이며 summary와 Markdown description이 필수다.[4] custom form이 적용되면 description에 필수 section과 응답을 포함해야 한다. 반면 built-in default form은 API 제출에 강제되지 않는다.[1][3][4]

자동화는 저장소의 raw YAML을 곧바로 enforced contract로 삼지 않는다. 먼저 현재 client가 만든 description을 검증하고, form mismatch 422가 반환되면 응답의 안내를 따라 GET /repos/{owner}/{repo}/security-advisories/reports/template을 호출한다. 이 endpoint의 200 응답은 { "template": "<YAML string>" } 형태이며, template 값이 GitHub가 해당 저장소에 실제 적용한 custom form이다.[1][3][4] 따라서 이 값을 parse해 필수 section의 label·순서, min_length, 필수 checkbox option을 반영한 뒤 payload를 다시 만든다. 422의 errors에는 field: "description"과 함께 missing_section, empty_section, unchecked_required_option, sections_out_of_order 같은 code가 올 수 있으므로, 단순 재시도 대신 payload 생성기의 어느 규칙이 어긋났는지 기록한다.[4]

Contents API로 읽은 .github/VULNERABILITY_REPORT.yml 또는 .yaml은 유지관리자가 작성한 authoring source를 사전 점검할 때만 보조적으로 쓴다.[3][5] 개별 저장소 파일, owner의 .github 저장소 fallback, invalid form의 기본 양식 대체가 개입하므로 raw file의 존재나 내용만으로 최종 적용 form을 증명할 수 없다.[1][3] API client의 계약 증거는 위 template endpoint 응답으로 고정하고, raw YAML과 다르면 enforced template을 우선한다.

Rollout에서는 현재 적용 양식을 fixture로 고정하고 다음 세 경로만 검증하면 된다.

  1. template endpoint가 반환한 YAML로 API description fixture를 생성하고, 웹 양식의 필수 section과 대조한다.
  2. 유효한 fixture는 접수되고 section 누락·빈 응답·필수 checkbox 미선택·순서 변경 fixture는 각각 422의 구조화된 errors로 거부된다.
  3. form 변경 뒤 template endpoint의 응답 변화를 감시하고 fixture를 갱신한다. raw Contents API 결과는 변경 원인을 추적하는 authoring 자료로만 남긴다.

실제 POST를 보내는 contract fixture는 전용 테스트 reporter가 지정 테스트 저장소에서만 제한적으로 실행한다. 생성된 private advisory의 ghsa_id를 기록해 검증 직후 state: "closed"로 전환하고, 종료 실패를 테스트 실패로 처리한다. 운영 저장소와 운영 reporter에서는 mock fixture만 실행해 triage queue와 신규 report rate limit을 오염시키지 않는다.[2][4]

양식 변경은 API client 변경과 같은 버전 관리 대상으로 취급해야 한다. UI만 확인하면 bot, bug bounty bridge, 사내 신고 도구가 validation error로 멈출 수 있다.

Rate limit은 큐 보호 장치다

새 rate limit은 계정 하나가 하루에 제출할 수 있는 신규 report 수를 제한한다. 특정 저장소 한도와 GitHub 전체 한도가 함께 적용되며, 기존 advisory의 comment에는 적용되지 않는다.[2] 열린 신고에서 추가 로그나 수정 PoC를 요청하는 협업을 신규 신고 제한과 혼동하면 안 된다.

저장소 관리자는 일일 전체 신고 한도를 조정하고 trusted reporter allow list를 둘 수 있다. allow list 사용자는 rate limit을 적용받지 않지만, 이는 검증 면제가 아니라 제출량 예외다.[2] trusted reporter의 PoC도 같은 재현·영향 검토를 거친다.

운영 지표는 차단 건수보다 신규 신고 수, 보완 요청률, 재현 성공률, 중복률, 최초 응답 시간, 예외 사용량을 함께 본다. 큐가 조용해졌어도 유효 신고가 다른 채널로 밀려났다면 개선이 아니다.

PoC는 격리하고, 관찰과 추정을 나눈다

신고서의 command나 archive는 untrusted workload다. 신고된 tag·commit과 lockfile을 고정하고, 실제 credential 없이 outbound network를 제한한 일회성 VM이나 sandbox에서 실행한다. 성공 기준과 음성 대조군을 먼저 정한 뒤 명령, image digest, sanitized output, exit code를 남긴다. 재현 실패는 누락된 전제·platform 차이·비결정성을 구분하고, 성공해도 관찰한 효과를 주장된 최대 impact로 확대하지 않는다.

세부적인 sandbox 증거 묶음과 상태 전이는 기존 글 Codex Security로 설계하는 취약점 검증 파이프라인의 Reproduction 절을 따른다. 여기서는 같은 6단계 절차를 반복하지 않는다.

결론

품질 게이트는 세 가지만 분리하면 된다. schema는 빠진 재현 정보를 드러내고, API contract test는 웹과 자동화 경로의 불일치를 막으며, 격리 재현은 문장을 관찰 가능한 증거로 바꾼다. rate limit과 trusted reporter는 큐의 양을 조절할 뿐 내용의 진위를 결정하지 않는다. 이 경계를 지켜야 AI를 사용한 유효한 연구를 받으면서도 검증 불가능한 신고가 triage를 독점하는 일을 줄일 수 있다.

Sources

[1] https://github.blog/changelog/2026-10-01-structured-forms-for-private-vulnerability-reports/ — Structured forms for private vulnerability reports [2] https://github.blog/changelog/2026-10-01-rate-limits-for-private-vulnerability-reports/ — Rate limits for private vulnerability reports [3] https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/configure-vulnerability-reporting/configure-for-a-repository — Configuring private vulnerability reporting for a repository [4] https://docs.github.com/en/rest/security-advisories/repository-advisories?apiVersion=2026-03-10 — REST API endpoints for repository security advisories [5] https://docs.github.com/en/rest/repos/contents?apiVersion=2026-03-10 — REST API endpoints for repository contents