설치할 수 있다는 사실이 알려 주는 범위

코드 변경 한 줄 때문에 의존성 그래프 전체가 바뀔 수 있습니다. 외부 패키지 하나가 가져오는 하위 패키지와 바이너리 다운로드, 개발 환경과 CI의 실행 권한도 함께 확인해야 합니다. 설치 후에만 오류가 나는 이름을 찾는 일과, 설치에 성공한 이름을 신뢰할 수 있는지 판단하는 일은 서로 다른 검사입니다.

AI가 작성한 코드에 패키지 이름이 등장하면 팀은 보통 레지스트리 검색부터 합니다. 검색 결과가 없으면 바로 설치할 수 없다는 점을 알 수 있습니다. 결과가 있으면 다음 질문이 시작됩니다. 제안된 API가 그 패키지에 실제로 있는지, 이름 철자가 같은 다른 생태계의 프로젝트인지, 현재 배포 버전이 공식 소스와 연결되는지 확인합니다. README와 레지스트리에서 서로의 주소를 가리키고 있어도 두 정보를 같은 배포자가 조작할 수 있으므로, 프로젝트가 이미 신뢰하는 공식 홈페이지나 승인된 내부 카탈로그 같은 별도의 기준점에 대조합니다.

Spracklen 등의 USENIX Security 2025 연구는 코드 생성 모델 16개와 프롬프트 데이터셋 2개를 사용해 Python과 JavaScript 코드 표본 576,000개에서 패키지 환각을 조사했습니다. 이 연구가 제공하는 근거의 범위는 당시의 모델, 프롬프트, 레지스트리 조건입니다. 운영팀에는 제안된 이름을 실제 배포 경로와 별도로 확인해야 할 이유가 됩니다. 우리 팀 PR에서 잘못된 패키지가 얼마나 자주 제안되는지는 자체 기록으로 구합니다. USENIX Security 2025 연구[1]

패키지 이름의 검수 단위를 다음처럼 고정합니다.

  • 직접 쓰려는 대상은 생태계, 정규화된 패키지 이름, 스코프, 레지스트리, 버전입니다. 내부 레지스트리와 공개 레지스트리에 같은 이름이 있을 수 있으므로 선택된 다운로드 위치를 남깁니다. 모노레포라면 어느 작업 공간의 설정과 잠금 파일이 적용되는지도 확인합니다.
  • 신뢰할 출처는 배포 주체, 정식 소스 주소, 해당 버전의 태그나 커밋, 최종 배포물의 식별값입니다. 레지스트리 설명란에 있는 저장소 주소 외에, 소스의 공식 릴리스 안내나 프로젝트 문서에서도 배포 이름을 확인합니다. 장기간 쓰던 이름에서 갑자기 배포 주체, 소스 또는 빌드 경로가 바뀌면 이전 버전의 승인 근거와 현재 후보를 다시 비교합니다.
  • 코드에서 쓸 기능은 import 이름, 호출할 API, 지원 런타임, 실패 시 동작입니다. 패키지 API를 실제 버전의 문서와 소스에서 찾아 제안된 예제가 호환되는지 확인합니다. 유사한 이름의 도구를 바꿔 달라는 수정도 신규 의존성으로 검토합니다.

저장소에 패키지 추가 시 지켜야 할 규칙이 여러 개라면 문서만 읽는 데서 멈추지 말고 개발자 PC, PR 빌드와 출시 빌드에서 같은 레지스트리 정책이 적용되는지 확인합니다. CI는 올바른 내역을 내려받지만 개발용 레지스트리 우선순위가 달라 별도의 아카이브를 설치할 수도 있습니다. 이 경우 실행 결과를 비교하기 전에 공급 경로의 차이부터 해소합니다.

검수 카드에 남길 다섯 묶음

아래 표를 PR의 의존성 추가 검수 카드로 사용할 수도 있습니다. 외부 코드의 영향을 확인할 수 있도록 애플리케이션 코드 변경과 잠금 파일 변경을 같은 검토 범위로 잡습니다. 보안팀 승인이나 예외 승인이 필요한 항목의 담당자는 작업 시작 전에 지정합니다.

묶음PR에 기록할 근거진행을 보류할 신호
필요성해결할 요구사항, 실제 호출 위치, 표준 라이브러리·기존 의존성·직접 구현으로 충족할 수 있는지 비교, 새 유지보수 담당자아직 호출하는 코드가 없거나 기능이 이미 팀의 공용 코드에 있음
배포 출처정확한 이름과 버전, 승인된 레지스트리, 배포 주체, 소스 저장소와 릴리스 커밋, 배포물 해시 또는 검증 가능한 출처 증명이름·스코프 불일치, 소스나 배포 주체 변경의 근거 없음, 설치된 산출물과 증명의 대상 불일치
그래프 변경의존성 선언 파일과 잠금 파일의 diff, 직접·간접 변경 목록, 새 레지스트리·라이선스·런타임 요구, 플랫폼별 차이요청과 무관한 대량 갱신, 커밋에 잠금 파일 누락, 설명되지 않은 다운로드 위치 변경
실행 행위설치·빌드·실행 단계별 스크립트 및 바이너리, 읽고 쓰는 경로, 네트워크 목적지와 권한, 격리 환경에서 관측한 결과운영 자격 증명을 상속하는 러너에서만 재현, 설치 때 미확인 실행 파일을 받음, 기록 없이 예상 밖 외부 통신 발생
유지보수실제 검토한 커밋과 스캔 범위, 미실행·권한 부족·타임아웃 목록, 업데이트 담당자, 삭제·대체 조건대시보드에 발견 수만 표시, 예외 만료일·담당자 없음, 재현 가능한 설치 근거 없음

조직에서 외부 구성 요소를 관리하는 기준의 출발점으로 NIST의 SSDF를 볼 수 있습니다. 확인 항목을 늘리기 전에 누가 무엇을 근거로 승인하는지 정하면 검토 시간이 짧아집니다. 유지보수 담당자가 제품 적합성을 판단하고 빌드 담당자가 잠금 파일과 실행 행위를 재현하며 보안 담당자는 출처와 권한 예외를 판단합니다. 한 사람이 여러 역할을 맡는 팀은 확인한 대상과 되풀이할 명령 또는 로그부터 남겨 다음 검토자가 결과를 재현할 수 있게 합니다. NIST SP 800-218 SSDF v1.1[2]

잠금 파일은 버전 차이와 전달 경로를 보여 줍니다

의존성 선언에는 버전 범위가 있고 잠금 파일에는 도구가 해석한 구체적인 의존성 그래프가 들어갑니다. 검토하려는 작업이 애플리케이션 하나에만 해당하더라도, 공유 잠금 파일이 다른 작업 공간의 해석 결과를 바꿀 수 있습니다. 직접 추가한 한 버전과 함께 새로 들어온 하위 패키지, 삭제된 버전, 같은 이름의 복수 버전을 확인합니다. 파일 전체가 대량 재정렬됐다면 도구 버전 차이인지 실제 그래프 변경인지 먼저 구분합니다.

검토 단위는 파일의 줄 수보다 공급 경로입니다. 잠금 파일에서 고정된 버전, 다운로드 대상과 무결성 정보를 확인합니다. Git URL, 로컬 경로, 미러, 별도 레지스트리에 대한 참조가 들어오면 이 경로를 빌드에서도 의도대로 사용했는지 대조합니다. 같은 식별값이 내려받아졌다는 확인은 검토한 바이트가 유지됐다는 근거입니다. 그 바이트의 동작과 신뢰 경로는 따로 확인합니다.

예를 들어 npm 프로젝트는 선언과 기존 잠금 파일의 의존성이 일치하지 않으면 npm ci가 오류를 내고, 잠금 파일을 갱신하지 않는다는 공식 동작을 사용해 재현 조건을 검사할 수도 있습니다. 잠금 파일을 생성할 때 그래프에 영향을 준 설정이 있었다면 CI에서도 같은 설정이 필요합니다. 다른 패키지 관리자를 쓰면 그 도구가 해당 버전에서 제공하는 잠금 파일 검사와 스크립트 정책을 확인합니다. 실제 검토에는 설치 도구 버전, 런타임 버전, 레지스트리 설정과 PR의 커밋을 함께 기록합니다. npm 공식 npm ci 문서[3]

하위 의존성이 업데이트된 이유도 남겨야 합니다. 예를 들어 새 패키지가 동일한 파서의 다른 버전을 요구해 두 버전이 공존하면 호출 경로를 확인합니다. 그래프에서 버전 하나가 제거됐더라도 배포에 들어가는 번들에 같은 코드 사본이 남을 수 있습니다. 패키지가 소스를 빌드 결과에 포함하거나 의존성을 번들 안에 넣는 구조라면 설치 목록, 최종 배포물, 실행 환경 가운데 어디를 검사했는지 표시합니다.

설치 전에 검토할 코드가 있습니다

패키지의 실행 시점에는 설치, 프로젝트 빌드, 테스트, 배포 및 실제 요청 처리가 포함됩니다. npm 문서에는 npm ci와 npm install에서 발생하는 라이프사이클 이벤트가 명시되어 있습니다. CI에서 테스트가 성공하더라도 앞 단계에서 어떤 설치 코드가 실행됐는지는 그 결과만으로 확인할 수 없습니다. 선택한 버전의 패키지에 선언된 스크립트와 실제 배포 아카이브에 포함된 파일을 먼저 확인합니다. npm 라이프사이클 스크립트[4]

필요한 스크립트라면 실행 이유를 구체적으로 적습니다. 예를 들어 지원 OS의 네이티브 모듈을 준비해야 한다면 어떤 바이너리를 어디에서 받고 어떤 값으로 확인하는지 기록합니다. 스크립트가 읽는 환경 변수, 쓰는 디렉터리, 생성 파일, 네트워크 요청을 정합니다. 파일을 내려받는 URL이 고정되어 있더라도 경로의 내용이 바뀔 수 있습니다. 무결성을 확인하는 방법과 빌드 환경에서 허용되는 도메인을 함께 검토합니다. 승인 후 최초 실행은 실제 고객 데이터나 운영 비밀이 없는 격리 환경에서 재현하고 필요 권한만 있는 빌드에서 동일하게 실행되는지 확인합니다.

초기 수집에서 스크립트 실행을 억제할 수 있는 도구라면 검토 단계에서 사용할 수도 있습니다. 이 단계에서 나온 실행 결과에는 억제한 범위가 남아야 합니다. 실제 배포에서 필요한 스크립트를 승인한 조건으로 다시 실행하고 결과 파일과 외부 통신을 확인합니다. 스크립트 설정을 일괄 완화하면 동시에 들어온 다른 패키지에도 실행 경로가 열리므로, 허용 대상과 예외 범위를 잠금 파일의 정확한 버전으로 좁힙니다.

출처 증명을 검증하고도 동작을 검토해야 하는 이유

소프트웨어 출처 증명인 provenance는 산출물이 어떤 소스와 과정에서 나왔는지 확인할 수 있게 연결하는 기록입니다. SLSA는 산출물 생산의 출처와 방법을 추적할 수 있는 검증 가능한 정보를 설명합니다. npm의 provenance는 등록된 패키지를 소스와 빌드 지침에 연결하는 근거를 제시합니다. 확인할 때는 서명이 유효한지, 그 서명이 이번에 설치할 버전과 아카이브를 대상으로 하는지, 제작 주체와 빌드 경로가 팀이 신뢰하기로 한 것인지 각각 확인합니다. SLSA v1.2 Provenance[5], npm provenance 문서[6]

유효한 증명으로 연결된 소스에도 위험한 변경이 들어갈 수 있습니다. 팀이 예상한 유지보수 주체의 저장소에서 같은 릴리스 태그를 가리키더라도, 최근 변경된 스크립트와 산출물에만 들어가는 코드의 검토는 필요합니다. 증명이 없는 패키지를 검토한다면 조직이 허용하는 대체 증거와 승인자를 먼저 확인합니다. 릴리스 산출물을 검토 가능한 소스와 대조하거나 내부 레지스트리에서 검토한 버전만 배포하는 방법을 고려할 수도 있습니다. 배포 주체조차 확인할 수 없다면 비슷한 이름의 패키지를 대체재로 바로 설치하는 대신 요구사항으로 돌아갑니다.

서비스 작동 방식

정상 흐름 너머의 예외까지 확인 — 구현된 동작에서 요구사항을 복원하고 입력과 권한, 외부 호출의 예외 경로를 실행해 확인합니다.

가상 사례에서 추가 승인 판단까지

가상의 배송 관리 앱에서는 외부 API에 넘길 짧은 조회 키의 입력값을 검증해야 했습니다. AI가 만든 PR에는 유틸리티 패키지가 추가됐고, 변경 설명에는 문자 변환이라고만 적혀 있었습니다. 팀은 요구사항을 다시 열어 입력의 허용 문자와 길이를 결정했습니다. 협력사 API가 대소문자를 구분하는지도 확인했습니다. 이 확인 없이 문자열을 일괄 소문자로 바꾸면 유효한 키가 다른 고객의 키로 해석될 수 있기 때문입니다.

비교 대상시험할 입력과 관측 결과의존성 결정 근거
저장소의 기존 검증 함수빈 문자열, 허용 범위에서 가장 긴 키, 금지 문자가 포함된 키, 같은 문자열의 대소문자행위가 일치하면 유지보수 경로를 재사용할 수 있음
표준 라이브러리와 간단한 검사최소·최대 길이, 줄바꿈, 앞뒤 공백, 혼용 문자, 협력사 의미를 보존할 대소문자규칙이 작고 고정적이며 기능 테스트에서 결과를 판정할 수 있음
검토 중인 외부 패키지정확한 버전에서 정상 입력과 입력 경계 동작, 간접 의존성, 라이프사이클 스크립트특수 문자 정규화 등 실제로 필요한 기능이 추가될 때 검수 비용과 함께 재평가

이 팀은 유효성 검사만 필요하다는 합의를 확인하고 기존 함수를 재사용했습니다. 의존성 선언과 잠금 파일에서 외부 패키지 변경을 제거한 커밋에 대해 테스트를 다시 실행했습니다. 정상 키가 종전과 같은 값으로 나가고 잘못된 키는 외부 요청을 만들기 전에 거부됐습니다. 실제 검수 카드에는 선택한 구현뿐 아니라 기존 테스트가 놓친 공백과 대소문자 경계 조건을 추가한 근거를 남겼습니다. 이후 요구가 다국어 정규화로 바뀌면 유틸리티의 필요성과 버전, 출처를 다시 평가할 수도 있습니다.

반대로 특정 파일 형식을 오랜 기간 관리하는 파서가 필요한 경우에는 표준 라이브러리나 팀 코드로 유지할 수 있는 비용도 평가해야 합니다. 새 의존성의 가치는 테스트로 입증할 수 있는 정상 처리 범위와 실패 격리에서 나옵니다. 도입 후 지원 종료, 보안 수정 부재, 배포 주체 변경이 발생하면 어떤 기능부터 끄거나 대체할지 정해 두면 무기한 버전 고정으로 흐르는 것을 막을 수 있습니다.

스캔 결과를 설치 승인에 사용하는 방법

검사 결과에는 대상 커밋, 해석한 잠금 파일, 확인한 생태계와 버전, 스캐너 출처가 있어야 합니다. 취약점 0건의 의미는 검사 범위와 함께 결정됩니다. 예를 들어 저장소 경보 기능이 비활성화되어도 별도의 잠금 파일 기반 검사는 동작할 수도 있습니다. 이 경우 하나는 미측정, 다른 하나는 검사한 대상과 데이터 기준으로 결과를 기록합니다. 두 결과를 합쳐 전체가 점검됐다고 표시하면 팀은 어느 검사를 다시 실행해야 할지 알 수 없습니다.

Qoretix 공개 상세 문서는 연결한 GitHub 저장소의 보안 경보와 잠금 파일 기반 공급망 조회 결과를 검토하는 범위를 안내합니다. 같은 문서는 저장소에서 비활성화된 검사를 미확인으로 표시하고 외부 스캐너 연계를 프리뷰로 구분합니다. 우리 프로젝트에서 어떤 도구와 계정 권한으로 어느 저장소의 결과를 읽을지는 도입 구성에 맞춰 확인합니다. 배포 증명의 판정, 설치 스크립트의 실행 승인과 네트워크 제한은 팀의 빌드·보안 정책으로 별도 확인합니다. IXC Qoretix 소개, Qoretix 보안 점검 상세[8]

미검증 상태가 남으면 영향받는 경계를 특정합니다. 운영 앱이 쓰는 직접 의존성이 목적지 검증 없이 설치 단계에서 실행된다면 보류 조건이 명확합니다. 개발 전용이라도 빌드 산출물을 만드는 러너에서 동작하면 운영 빌드에 간접 영향을 줄 수 있습니다. 코드를 배포했다면 이미 실행된 위치와 인증정보 노출 가능성을 조사합니다. 노출 가능성이 확인된 키의 조치와 검증은 저장소 비밀키 노출 대응의 절차를 참고할 수도 있습니다.

검수 결과를 병합 근거로 제출할 때는 승인한 잠금 파일과 스캔이 실행된 커밋이 맞는지 확인합니다. PR에 뒤늦게 패키지 버전 변경이 들어오면 이전 버전의 저위험 판정을 그대로 붙일 수 없습니다. 검사 도구 구성이나 레지스트리 정책이 바뀌어 설치 범위가 달라진 경우에도 새로 확인해야 합니다. 일반적인 보호 브랜치와 검사 기준의 관계는 AI 코딩 에이전트 PR의 병합 기준에서 이어서 볼 수 있습니다.