npm을 통해 패키지를 설치하셨는데, npm audit 출력 결과는 문제가 없어 보였고, 여러분은 다음 단계로 넘어갔습니다.
6개월이 지났습니다. 최신 변경 사항을 가져오니 CI에서 경고가 떴고, 이제 한 가지 종속성에 대해 명확한 답이 필요합니다. 바로 tslib 가 이 브랜치에서 실제로 문제인가요? 그 한 가지 답을 찾기 위해 감사 보고서 전체를 샅샅이 뒤지는 것은 시간 낭비일 뿐입니다.
최신 버전의 npm은 이 질문에 더 직접적으로 답할 수 있습니다. 취약점 선택기를 사용하여 종속성 트리를 조회하거나, jq를 사용하여 구조화된 감사 데이터를 필터링할 수 있으며, 대략적인 확인만 필요한 경우에는 일반 터미널 도구를 사용할 수도 있습니다. 그러면 해당 패키지가 왜 설치되었는지 정확히 추적할 수 있습니다.
다음은 단일 npm 패키지를 확인하는 네 가지 방법으로, 우선순위 순으로 나열했습니다.
참고: 아래 명령어는 npm 10.9.7 및 npm 12.0.1에서 테스트되었습니다. npm의 주요 릴리스 간에는 선택자 및 출력 형식이 변경될 수 있으므로, npm -v 를 실행해 보시기 바랍니다.
1. 다음 명령어를 사용하여 패키지를 직접 조회합니다. npm query
npm query CSS 스타일의 종속성 선택자를 사용하여 설치된 종속성 트리를 탐색합니다. 여기서는 그중 두 가지가 역할을 수행합니다: #tslib 는 tslib인 패키지와 :vuln 는 결과를 알려진 보안 권고가 있는 인스턴스로만 좁혀줍니다.
npm query '#tslib:vuln'
빈 배열은 npm이 조회한 트리 및 레지스트리 데이터에서 해당 패키지와 일치하는 알려진 권고 사항을 찾지 못했음을 의미합니다. 값이 채워진 배열은 일치하는 항목을 찾았음을 의미하며, 각 항목에는 상세한 패키지 및 의존성 트리 메타데이터와 함께 위치 필드가 포함되어 있어 해당 사본이 node_modules하위 어디에 저장되어 있는지 나타내는location필드를 포함합니다.
그 메타데이터 덤프는 질문 하나 치고는 JSON 데이터가 너무 많네요. 다음 명령어로 불필요한 부분을 제거해 보세요. jq를 사용해 정리하세요:
npm query '#tslib:vuln' | jq '.[] | {location, version, advisories: [.queryContext.advisories[] | {severity, title}]}'
The :vuln 선택자는 일치하는 모든 권고 사항을 queryContext.advisories노드 아래에 매칭되는 모든 권고 사항을 연결하므로, 별도의 명령어를 실행하지 않아도 심각도, 제목, CVSS 점수, CWE 및 취약한 버전 범위를 확인할 수 있습니다. 또한 첫 번째 항목뿐만 아니라 모든 정보를 확인할 수 있습니다.
선택기 내에서 심각도별로 필터링할 수 있으며, 이는 트리 구조가 복잡할 때 유용합니다:
npm query ':vuln([severity=high])'
한 가지 주의할 점: 심각도 필터링은 해당 심각도와 정확히 일치하는 항목만 선택하며, 그보다 높거나 낮은 심각도의 항목은 포함되지 않습니다. [severity=high] 는 'critical' 등급의 발견 사항을 표시하지 않습니다. 두 가지 모두를 포함하려면 목록 형태로 전달하십시오:
npm query ':vuln([severity=critical],[severity=high])'
셀렉터는 결합되므로, 별도의 플래그를 사용할 필요 없이 프로덕션 트리에만 검사를 적용할 수 있습니다:
npm query '.prod#tslib:vuln' # 이 패키지, 프로덕션 트리만 npm query '.prod:vuln' # 프로덕션 트리 내의 모든 취약점
이 기능을 자동화 과정에서 신뢰하기 전에 알아두어야 할 두 가지 사항이 더 있습니다. :vuln 와 :outdated 모두 레지스트리에 접속해야 하므로 오프라인에서는 작동하지 않습니다. 또한 #name@version 형식은 구형 문서에서 여전히 볼 수 있지만 더 이상 구문 분석되지 않습니다. npm 12는 #[email protected] 를 EQUERYNODEPTYPE 오류를 반환합니다. 대신 semver 의사 선택자를 사용하세요:
npm query '#tslib:semver(2.4.0)'
The :vuln 선택자가 npm 10.5.0에 도입되었습니다. 현재 어떤 버전을 사용 중인지 확실하지 않다면, 추측하기보다는 직접 확인해 보십시오:
npm query ':vuln' >/dev/null 2>&1 && echo "지원됨" || echo "npm을 업그레이드하세요"
그보다 오래된 버전이라면, jq 메서드로 건너뛰세요.
2. 필터 npm audit JSON을 jq
npm audit 는 npm 6 버전부터 도입되었으며, :vuln 는 npm 10.5.0 이상이 필요합니다. 따라서 기여자들이 구버전의 npm을 사용 중일 수 있는 공유 저장소에서는 audit JSON이 더 안전한 선택입니다.
npm audit --json | jq '.vulnerabilities["tslib"]'
The –json 플래그는 정형화된 테이블 대신 기계가 읽을 수 있는 감사 데이터를 반환합니다. npm 7 이상에서는 해당 보고서에 vulnerabilities 객체를 포함하므로, 위의 필터는 tslib 에 대한 항목을 직접 추출합니다. 의사 결정에 실제로 영향을 미치는 필드를 가져오려면:
npm audit --json | jq '.vulnerabilities["tslib"] | {severity, range, fixAvailable}'
Read the top-level range as the aggregate vulnerable range for that entry. Do not reach into .via[0].range to get it. The via array mixes advisory objects with bare package-name strings for vulnerabilities inherited through a parent, and even when via[0] happens to be an object it often carries a narrower range than the entry as a whole – on a test tree, express reported <4.20.0 from via[0] against an aggregate range of <=4.21.2 || 5.0.0-alpha.1 – 5.0.0. Silently wrong is worse than empty.
직접 생성된 알림과 상속된 알림을 구분하고 싶다면, 다음을 요청하세요. via 를 통해 명시적으로 요청하고 직접 읽어보세요:
npm audit --json | jq '.vulnerabilities["tslib"] | {severity, range, via, fixAvailable}'
다음과 같은 구문 대신 대괄호 구문을 사용하십시오 .vulnerabilities.tslib.vulnerabilities.tslib 대신 중괄호 구문을 사용하십시오. 두 글자만 더 쓰면 되며, 이는 범위 지정된 패키지 이름에서도 정상적으로 작동하는 유일한 형식입니다. 반면 점 표기법은 @ 와 /에서 오류가 발생합니다:
npm audit --json | jq '.vulnerabilities["@babel/traverse"]'
패키지별로 확인하기 전에 표시된 항목을 모두 확인하려면:
npm audit --json | jq -r '.vulnerabilities | keys[]'
null 취약점이 없다는 뜻은 아닙니다
필터가 null을 반환한다면, 그 키 아래에 해당 리포트에 항목이 없다는 사실만 알 수 있을 뿐입니다. 네 가지 서로 다른 상황에서 동일한 null값이 발생합니다:
- 설치된 버전에는 npm이 조회한 데이터에 관련 권고 사항이 없습니다.
- 이 패키지는 감사 대상 트리에는 포함되지 않습니다.
- 해당 패키지는, 예를 들어 다음을 통해 감사 대상에서 제외되었습니다. –omit=dev와 같은 방식으로 감사 대상에서 제외되었습니다.
- 패키지 이름을 잘못 입력했습니다.
추측하는 대신 결과가 명확한 메시지를 전달하도록 하세요:
npm audit --json | jq '.vulnerabilities["tslib"] // "감사 보고서에 항목 없음"'
그런 다음 4절에 설명된 확인 방법을 사용하여 패키지가 실제로 설치되었는지 확인하십시오.
이 조건에 따라 빌드를 제한하는 경우
종료 코드가 오해를 불러일으킬 수 있습니다. npm audit 는 트리 내 어디에서든 설정된 감사 수준 이상에 해당하는 취약점을 발견할 때마다 1을 반환합니다. 이는 여러분이 문의하신 패키지에 대해서는 아무런 정보를 제공하지 않습니다. –audit-level=high 로 임계값을 높이면 명령어가 실패하는 시점이 달라질 뿐, 심각도가 낮은 발견 항목이 보고서에서 제거되지는 않으므로 보고서의 내용은 어느 쪽이든 동일하게 유지됩니다.
npm query 이 경우 더 안정적으로 동작합니다: 검색 결과를 찾았는지 여부와 관계없이 종료 코드가 0이므로, 결과 집합 자체를 기준으로 처리 여부를 결정할 수 있습니다.
만약 [ "$(npm query '#tslib:vuln' | jq length)" -gt 0 ]; 그 다음 echo "취약한 tslib가 존재합니다" fi
프로덕션에 배포되는 내용만 확인하고 싶다면 다음 명령어를 실행하세요. npm audit –omit=dev 를 실행하거나, .prod 선택자를 사용하십시오. 빌드 전용 의존성에서 발견된 중대한 문제는 런타임 트리에서 발견된 문제와는 별개의 사안입니다.
3. 다음 명령어를 사용하여 사람이 읽기 쉬운 출력 결과를 빠르게 검색하세요. grep을 사용하여
대략적인 내용을 확인하고 싶을 때, JSON에 대해 깊이 생각하고 싶지 않다면 표준 출력을 grep로 연결하세요.
npm audit | grep -A 5 -i "tslib"
그 | 연산자는 npm audit의 표준 출력을 grep로 전달하여, 이를 한 줄씩 스캔합니다. -A 5 옵션을 사용하면 각 일치 항목 뒤에 5줄의 후속 컨텍스트를 출력하므로, 주변의 권고 사항 세부 정보를 함께 포착할 수 있습니다. -i 옵션을 사용하면 대소문자를 구분하지 않고 일치 여부를 확인합니다.
이는 매우 취약한 부분이며, 그 이유를 구체적으로 설명할 필요가 있습니다. 줄 바꿈, npm 릴리스 간 레이아웃 변경, 컨텍스트 창보다 긴 조언 블록 등은 모두 사용자가 필요로 하는 부분을 가려버릴 수 있습니다. 더 심각한 문제는, 일치 항목이 없는 경우 아무런 경고도 표시되지 않아 다음과 같은 방식으로 모호해질 수 있다는 점입니다. null 와 똑같이 모호합니다. 즉, 문제가 없다는 뜻일 수도 있고, 패턴이 줄 바꿈된 줄을 놓쳤다는 뜻일 수도 있습니다.
대략적으로 확인하는 용도로는 괜찮습니다. 단, 해당 패키지에 알려진 취약점이 없다는 증거로 절대 사용해서는 안 됩니다.
4. 실제로 설치된 버전을 확인합니다.
감사 보고서는 npm이 사용자의 lockfile에서 해결한 종속성 트리를 설명합니다. 기본적으로 npm audit 명령어는 package-lock.json 이나 shrinkwrap을 요구하며, 해결된 패키지 이름과 버전을 레지스트리에 제출하고 이를 알려진 보안 권고 사항과 비교합니다. 심지어 –package-lock-only 옵션을 사용하여 node_modules 디렉터리가 전혀 없어도 실행됩니다.
npm ls 는 node_modules아래의 설치된 모듈 구조를 확인합니다: npm ls tslib
여기서 위치 필터는 사람들이 예상하는 것보다 더 많은 기능을 수행합니다. npm은 전체 트리를 순회하며, 중첩된 복사본을 포함해 일치하는 모든 인스턴스의 경로를 출력합니다. 따라서 4단계 깊이에 있는 패키지도 여전히 표시됩니다. –all 을 추가해도 이름으로 필터링할 때는 해당 출력이 변하지 않습니다(npm 10 및 npm 12에서 확인됨). –all 옵션을 사용하면 필터링되지 않은 npm ls명령어에 필요한 옵션으로, 기본 보기에서는 직접 의존성까지만 표시됩니다.
중복 항목이 있을 수 있습니다. 하나의 프로젝트에 동일한 패키지의 서로 다른 버전이 세 개나 포함될 수 있으며, 그중 일부만이 취약점이 있는 범위에 속합니다.
다음에 반하는 스크립트를 작성하지 마세요 npm ls 종료 코드
npm ls tslib 일치하는 패키지를 찾으면 0을, 찾지 못하면 1을 반환하는데, 이는 단순한 존재 여부 확인처럼 보입니다. 하지만 꼭 그렇지만은 않습니다. 일치한 패키지 자체가 유효하지 않거나, 불필요하거나, 누락된 것으로 표시된 경우, npm은 ELSPROBLEMS 오류를 발생시키고 1로 종료되지만, 발견된 패키지 정보는 여전히 출력합니다. 설치되어 있지만 일치하지 않는 의존성은 “not installed”로 표시됩니다:
`– [email protected]이(가) 유효하지 않습니다: 루트 프로젝트의 “0.0.8”
npm 오류 코드 ELSPROBLEMS
테스트 과정에서 트리 내 다른 곳의 관련 없는 문제들이 필터링된 종료 코드에 영향을 미치지 않았기 때문에, 이 문제는 겉보기에 비해 범위가 더 좁습니다. 하지만 버전 불일치 문제를 추적하는 과정에서 정확히 이런 상황에 직면할 가능성이 높습니다. 스크립트의 경우, npm query 를 사용해 일치하는 항목을 확인하세요. 이 명령어는 소스 트리의 상태와 관계없이 0을 반환하며, 중첩된 복사본도 찾아냅니다:
[ "$(npm query '#tslib' | jq length)" -gt 0 ] && echo "설치됨" || echo "설치되지 않음"
다음으로 이동 npm ls –json 에서 .dependencies[“tslib”] 를 확인하는 것은 유혹적인 대안이자 함정입니다. 전이적 패키지는 상위 패키지 아래에 중첩되어 있으므로, 최상위 수준에서 조회할 경우 두 단계 아래에 위치한 패키지가 없는 것으로 보고됩니다.
한 가지 더: lockfile과 node_modules 는 끊임없이 서로 달라집니다. 변경 사항을 가져온 후에는 npm ci 를 실행한 후에야 로컬 디렉터리를 신뢰할 수 있는 버전으로 간주해야 합니다.
보너스: 해당 패키지가 설치된 이유를 알아보세요
표시된 패키지가 본인의 package.json에 포함되어 있지 않다면, 다른 곳에서 해당 파일이 끌려 들어온 것입니다. 프로젝트 루트 디렉터리에서 다음 명령어를 실행하세요:
npm explain tslib
npm explain 는 일치하는 모든 설치 항목에 대한 의존성 체인을 출력하므로, 각 복사본마다 루트 프로젝트까지 거슬러 올라가는 고유한 추적 경로를 확인할 수 있습니다. 여러 복사본이 존재하지만 그중 하나만 확인하려는 경우, 해당 복사본의 경로를 전달하세요:
npm explain node_modules/some-package/node_modules/tslib
이렇게 하면 두 개의 서로 다른 상위 패키지가 모두 동일한 중간 패키지에 의존하는 경우를 포함하여, 해당 단일 인스턴스에 대한 의존성 체인을 출력합니다.
직접 의존성인 경우 → 직접 버전을 올립니다. 상위 패키지가 해당 의존성을 고정해 둔 경우 → 상위 패키지를 업그레이드하거나 오버라이드 항목을 package.json (npm 8.3+)에 임시 방편으로overrides항목을 추가하세요. 오버라이드는 상위 패키지에서 테스트된 적이 없는 버전을 강제로 적용하는 것이므로, 이를 단순한 한 줄 수정 사항이 아닌, 테스트 스위트를 통해 검증해야 하는 변경 사항으로 간주하십시오.
무엇 npm audit 알려주지 못하는 것
이 모든 내용을 애플리케이션 보안 프로그램이라기보다는 기본적인 선별 조치로 간주하십시오. 다음 명령어 중 어느 것도 다음과 같은 기능을 수행하지 않습니다:
- 애플리케이션이 취약한 코드 경로에 도달할 수 있는지 확인하십시오.
- 자신의 소스 코드 논리를 검토해 보세요.
- 패키지에 포함된 네이티브 바이너리를 심도 있게 분석합니다.
- 누군가가 신고하기 전에 악성 패키지나 의도적으로 난독화된 패키지를 탐지합니다.
가장 중요한 점은 첫 번째입니다. 감사 결과의 개수가 애플리케이션에 존재하는 악용 가능한 취약점의 수와 같지 않다는 것입니다. 또한 모든 결과는 npm이 해결한 트리, 사용자가 설정한 레지스트리, 그리고 명령어를 실행한 시점에 존재했던 모든 권고 사항 데이터에 따라 달라집니다. 다음 주에 동일한 감사를 실행하면 코드에 아무런 변경이 없더라도 다른 결과가 나올 수 있습니다.
마치며
간단히 말해서, 직접 해보려면 다음과 같은 순서로 진행하세요:
npm query '#tslib:vuln' # 가장 깔끔한 npm 네이티브 검사 npm audit --json | jq '.vulnerabilities["tslib"]' # 이식성이 뛰어나고 스크립트 사용에 편리함 npm ls tslib # 실제로 설치된 항목 npm explain tslib # 설치된 이유 터미널에서 한 번 확인하는 것은 순간적인 스냅샷에 불과하며, 의존성 스캔의 가치는 반복적인 수행에서 비롯됩니다. 지속적인 모니터링 도구(Dependabot, OSV-Scanner와 같은 오픈소스 스캐너, 또는 상용 소프트웨어 구성 분석 플랫폼)는 사용자가 손대지 않은 코드에 대해 새로 공개된 보안 권고 사항을 찾아내고, 여러 저장소 전반에 걸쳐 발견 사항을 가시화하며, 업그레이드 작업의 일부를 자동화해 줍니다. 하지만 이러한 도구들도 앞서 언급한 한계를 완전히 벗어나지는 못합니다. 여전히 대부분은 알려진 취약점 정보를 기반으로 작동하며, 보고되지 않은 악성 패키지는 이 도구들에도 감지되지 않습니다.
해당 취약점을 해결하려면 권고 사항이 발표되기 전에 검사를 실시해야 하는데, 이것이 바로 TuxCare SecureChain for OSS가 취하는 접근 방식입니다. 이 서비스는 소스에서 패키지를 재컴파일하고, 귀하의 node_modules폴더에 등록되기 전에 소스 코드에서 재컴파일되고 선별된, 엄선된 레지스트
터미널 명령어는 어쨌든 유용합니다. 특정 프로젝트의 특정 패키지에 대해 즉각적인 답변이 필요할 때, 대시보드를 여는 것보다 훨씬 빠릅니다.

