facts
npm이 악성코드로 인해 삭제하고 '0.0.2-security'라는 표식만 남겨둔 보안 플레이스홀더 패키지 35개가 매주 지속적으로 설치되고 있다. 242개의 유명 패키지를 시드로 설정해 LLM의 이름 생성 패턴을 분석한 결과, 후보군 772개 중 262개가 실제로 npm에 존재했으며 이 중 35개가 보안 삭제된 패키지였다.
주간 설치 수가 높은 상위 패키지 명단과 실제 대응 패키지는 다음과 같다.
- crossenv: 1,437 /주 (cross-env)
- eslint-js: 612 /주 (@eslint/js)
- supabase-js: 397 /주 (@supabase/supabase-js)
- mysqljs: 231 /주 (mysql)
- unused-imports: 209 /주 (eslint-plugin-unused-imports)
- nodemailer-js: 174 /주 (nodemailer)
해당 패키지들은 사람이 직접 입력하기보다 LLM이 패키지명을 생성하는 과정에서 발생하는 환각(Hallucination)으로 인해 호출되는 경향이 크다. 특히 LLM의 패키지명 환각은 43%의 확률로 매번 동일한 오답을 반복하는 특성을 보인다.
how-it-works
LLM이 패키지명을 잘못 생성하는 방식은 크게 5가지 패턴으로 분류된다. 첫째는 `@types/node`를 `types-node`로 바꾸는 스코프 제거, 둘째는 `socket.io`를 `socket-io`로 바꾸는 점과 하이픈의 치환, 셋째는 `cross-env`에서 하이픈을 뺀 `crossenv` 형태의 생성, 넷째는 `eslint-plugin-unused-imports`에서 접두어를 뺀 `unused-imports` 형태, 다섯째는 `nodemailer` 뒤에 `-js`를 붙이는 방식이다. 공격자가 이러한 환각 패턴을 예측해 미리 유사한 이름의 패키지를 등록해두는 수법을 '슬롭스쿼팅(Slopsquatting)'이라 한다.
이를 탐지하기 위해 개발된 `pkgtruth`는 에이전트가 `package.json`을 작성하기 전 스스로 확인하는 MCP(Model Context Protocol) 서버이자 CI(지속적 통합) 단계에서 빌드를 차단하는 CLI 도구다. 이 도구는 다음 네 가지 축을 기준으로 패키지의 위험성을 판단한다.
1. 패키지가 npm 레지스트리에 실제로 존재하는가
2. npm이 악성코드로 인해 삭제한 이름인가
3. 설치 스크립트(install script)가 포함되어 있는가
4. 유사한 이름의 다른 패키지가 수만 배 더 많이 사용되고 있는가
검증을 위한 실행 명령어는 다음과 같다.
npx pkgtruth check crossenv supabase-js types-node전체 탐지 목록은 SLOPSQUATS.md 저장소에서 확인할 수 있으며, 매주 월요일 자동으로 갱신된다. 명단에는 npm이 직접 삭제한 이름과 패키지 자체 폐기 안내에서 실제 이름을 명시한 경우만 포함해 오탐을 줄였다.
implementation-impact
`pkgtruth`는 소스코드를 직접 분석하지 않으므로, 정상적으로 보이는 패키지 내부에 심어진 페이로드는 탐지하지 못한다. 따라서 `npm audit`이나 Snyk 같은 취약점 스캔 도구의 대체재가 아니라, '이 패키지가 여기에 포함되는 것이 맞는가'라는 입력 단계의 적절성을 검증하는 전처리 도구로 작동한다. 현재는 npm 레지스트리만 지원한다.
LLM 에이전트를 활용해 의존성 파일을 자동 생성하거나 라이브러리를 추천받는 환경의 개발자는, 생성된 패키지명이 실제 공식 명칭과 일치하는지 `pkgtruth`와 같은 검증 도구를 CI 파이프라인에 통합해 자동 확인하는 절차를 추가해야 한다.




