2026년 하루 평균 보고된 CVE가 전년 대비 131% 증가했습니다.

SBOM

오픈 소스 SBOM 생성 도구가 놓치는 것

Syft, Trivy, cdxgen은 유능하고 무료이며 널리 쓰입니다. 세 도구 모두 잠금 파일, 매니페스트, 설치된 패키지 등 지정한 파일로부터 SBOM을 만듭니다. 그래서 같은 리포지토리에서도 결과가 서로 다르고, 그 파일 밖에서 빌드가 가져온 것은 어느 도구도 보지 못합니다.

업데이트

What a scanner reads versus what the build pulled in A scanner reads the lockfile, which lists direct packages A, B and C and transitive packages D and E, so its SBOM contains only those. CRACI sees the network traffic into the build job, so its recorded SBOM also contains the hidden dependencies no lockfile lists: code downloaded by an install hook, a download by a build script, and the base image layers. A scanner reads the lockfile package-lock.json direct A B C transitive D E Scanned SBOM A, B, C, D, E only what the lockfile lists CRACI sees the traffic into the build A B C D E install hook download build script download base image layers build job on CRACI Recorded SBOM every package it fetched, hidden too Dashed: hidden dependencies, listed in no lockfile

각 도구로 SBOM을 생성하는 방법

각 도구는 소스 디렉터리나 컨테이너 이미지에 대해 명령 하나로 CycloneDX 또는 SPDX 출력을 만듭니다.

# Syft: a source directory or a container image
syft ./my-project -o cyclonedx-json=sbom.cdx.json
syft my-image:latest -o spdx-json=sbom.spdx.json

# Trivy: a filesystem or a container image
trivy fs --format cyclonedx --output sbom.cdx.json .
trivy image --format spdx-json --output sbom.spdx.json my-image:latest

# cdxgen: the current directory, optionally with a project type
cdxgen -o bom.json .
cdxgen -t js -o bom.json .

명령은 쉬운 부분입니다. 파일에 무엇이 담기는지는 각 도구가 무엇을 읽는지에 달려 있으며, 도구마다 읽는 대상이 같지 않습니다.

각 도구가 읽는 대상

도구 소스 디렉터리 컨테이너 이미지 알아 둘 점
Syft 선언된 종속성을 읽는 디렉터리용 기본 카탈로거 세트 설치된 패키지를 읽는 이미지용 기본 카탈로거 세트 카탈로거는 스캔 유형에 따라 선택됩니다. syft cataloger list로 어떤 카탈로거가 실행되는지 확인하고, --select-catalogers로 추가하거나 제거합니다.
Trivy 잠금 파일. Node.js의 경우 package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock Node.js의 경우 package.json 파일(node_modules 내부) --include-dev-deps를 지정하지 않으면 개발 종속성을 제외합니다.
cdxgen 개발 종속성을 포함하는 잠금 파일을 우선 읽으며, Node.js의 경우 코드의 import도 파싱 지원 -t로 프로젝트 유형을 지정해 탐색 범위를 제한합니다.

같은 리포지토리에서 결과가 다른 이유

세 도구를 하나의 커밋에 실행하면 세 가지 다른 목록이 나옵니다. 어느 도구도 고장 난 것은 아닙니다. 각자 문서화된 자체 규칙을 따를 뿐입니다.

  • 잠금 파일이 없으면 종속성도 없습니다. Express 5.2.1은 잠금 파일을 커밋하지 않습니다. 깨끗하게 체크아웃한 상태에서 잠금 파일이나 설치된 패키지로 종속성을 해석하는 도구는 둘 다 찾지 못하므로, package.json에 선언된 npm 종속성이 SBOM에 들어가지 않습니다. 그래도 도구는 인식한 나머지 항목으로 유효한 문서를 작성합니다.
  • 설치된 것인가, 선언된 것인가. 스캔 전에 설치를 실행했는지 여부가 어떤 도구를 쓰는지보다 더 중요할 수 있습니다. 같은 리포지토리의 설치된 사본에서는 Syft 카탈로거 설정 하나가 설치된 npm 패키지가 나타나는지 여부를 결정합니다.
  • 개발 종속성. Trivy는 기본적으로 개발 종속성을 제외하고 cdxgen은 포함하므로, 같은 잠금 파일이 "이 프로젝트에 무엇이 들어 있는가"에 대해 두 가지 답을 냅니다.
  • 이름과 버전. 도구에 따라 일부 구성 요소를 서로 다른 패키지 URL 유형으로 식별하거나, 한 도구는 GitHub Action을 태그로 보고하는데 다른 도구는 커밋 SHA로 고정하기도 합니다. 잘못된 식별자를 기준으로 한 취약점 조회는 확신에 찬 오답을 냅니다.
  • 매니페스트가 아예 없는 경우. anthropics/claude-code 리포지토리에는 종속성 매니페스트가 없으며, 그 npm 패키지는 사전 빌드된 바이너리를 감쌉니다. 각 도구가 반환하는 결과는 프로젝트보다 도구에 대해 더 많은 것을 말해 줍니다.

모두가 놓치는 것: 숨은 종속성

더 근본적인 한계는 모든 도구에 공통됩니다. 스캐너는 잠금 파일이나 빌드 결과물을 읽고, 무엇이 들어갔는지를 사후에 추론합니다. 빌드가 그 파일 밖에서 가져온 것은 SBOM에 들어가지 않습니다. 이것이 숨은 종속성입니다.

  • 설치 훅. npm preinstall 및 postinstall 스크립트는 설치 중에 실행되며 추가 코드를 내려받을 수 있습니다. Shai-Hulud 웜이 이렇게 실행되었습니다.
  • 빌드 시점에 필요한 것을 내려받는 Rust 빌드 스크립트 같은 빌드 스크립트.
  • 어떤 언어의 잠금 파일에도 나열되지 않는, 컨테이너 빌드의 베이스 이미지와 그 운영체제 계층.
  • 현재 잠금 파일과 일치하지 않을 수 있는 CI 캐시에서 복원된 패키지. GitHub Actions 캐시가 숨기는 것을 참고하세요.
  • 설정 단계에서 설치되며 자체 종속성 트리를 가진 빌드 도구.
  • 잠금 파일이 없거나, 오래되었거나, 고정 설치(frozen install)에서 사용되지 않을 때 빌드 시점에 해석되는 전이적 종속성 버전. 전이적 종속성을 참고하세요.

결과물을 사후에 스캔해도 이 간극은 메워지지 않습니다. 완성된 바이너리, 특히 Rust 같은 컴파일된 코드는 무엇이 들어갔는지를 스캐너가 추측하게 만듭니다. 상용 SCA 스캐너도 같은 종류의 파일을 바탕으로 작동하며 같은 사각지대를 공유합니다. CRACI는 Snyk와 Aikido가 보고하지 않은 취약한 패키지를 찾아냈습니다. 해당 구성 요소가 그 도구들이 스캔한 대상에 애초에 없었기 때문입니다.

Yocto 빌드에도 같은 문제가 있습니다. SBOM 스캐너가 Yocto 빌드의 내용을 놓치는 이유를 참고하세요.

How a scanned SBOM and a recorded SBOM are made Left, a scanner: it reads the lockfile after the build, which lists packages A to E, so its SBOM contains A to E. The build also fetched code through an install hook, a build script download and a base image, which are in no lockfile, so the scanned SBOM is missing them. Right, CRACI: it sees the traffic coming into the build, so all eight packages, including the three hidden dependencies, land in the recorded SBOM. Scanner reads files after the build package-lock.json A B C D E the build also fetched install hook download build script download base image layers in no lockfile, so the scan never sees them Scanned SBOM A B C D E missing missing missing CRACI sees the traffic into the build the network build job on CRACI every package that comes in Recorded SBOM A B C D E install hook build script base image hidden ones too
A scanner can only list what the lockfile or the output declares. CRACI sees what actually came into the build, including the hidden dependencies.

도구도 공급망의 일부입니다

스캐너는 코드와, 흔히 시크릿에도 접근할 수 있는 상태로 CI에서 실행됩니다. 2026년 3월 Aqua Security의 보안 권고(CVE-2026-33634)는 악성 Trivy v0.69.4 릴리스와, CI 러너에서 자격 증명을 훔친 오염된 trivy-action 및 setup-trivy 태그를 설명했습니다. 스캐너 액션은 전체 커밋 SHA로 고정하고, 사용 중인 버전이 권고에 해당하는지 확인하세요.

자체 SBOM을 점검하는 방법

  1. 같은 커밋에 두 가지 생성 도구를 실행하고, 각각이 보고하는 패키지 URL을 비교합니다.
  2. 리포지토리가 잠금 파일을 커밋하는지, CI가 고정 설치로 그 잠금 파일에서 설치하는지 확인합니다.
  3. 설치 단계 전과 후에 각각 스캔하고 비교합니다.
  4. 설정 단계와 캐시 복원을 포함해, 빌드 로그에서 내려받은 것으로 나타나는 항목과 목록을 비교합니다.

목록이 다르다면, 게시하는 SBOM은 실제로 출시한 것이 아니라 우연히 실행한 도구에 따라 달라지고 있는 것입니다.

대신 빌드를 기록하세요

CRACI는 트래픽을 보지만 스캐너는 보지 못합니다. CRACI는 각 빌드로 들어오는 네트워크 트래픽을 보는 GitHub Actions 러너이므로, 그 SBOM은 숨은 종속성과 CI 캐시에서 복원된 패키지를 포함해 트리의 모든 깊이에서 작업이 가져온 모든 패키지를 기록합니다. 지정할 디렉터리도, 선택할 카탈로거도 없습니다. 예를 들어 작업이 모니터링되지 않는 트래픽을 허용해 기록이 모든 것을 볼 수 없었다면, SBOM이 완전성 상태로 이를 알려 줍니다.

빌드가 재현 가능한 경우가 드물다는 점에서도 기록이 중요합니다. 같은 커밋을 연달아 두 번 빌드해도 패키지 관리자가 다른 순서로 해석하고 설치할 수 있어, 두 번째 빌드에는 다른 패키지가 들어갈 수 있습니다. 릴리스를 만든 빌드 중에 기록된 SBOM만이 그 릴리스에 무엇이 들어 있는지 입증합니다.

직접 빌드하지 않은 서드파티 이미지와 바이너리에는 Syft나 Trivy를 계속 사용하세요. 자체 릴리스에 대해서는 빌드 시점 SBOM을 확인하거나, 데모를 예약해 리포지토리 하나에서 기록된 SBOM과 현재 도구의 SBOM을 비교해 보세요.

오픈 소스 SBOM 생성 도구: 자주 묻는 질문

SBOM에는 Syft와 Trivy 중 무엇이 더 좋나요?

두 도구는 서로 다른 용도로 만들어졌습니다. Syft는 많은 카탈로거와 서명된 증명을 갖춘 전용 SBOM 생성 도구이고, Trivy는 SBOM을 출력 중 하나로 작성하는 보안 스캐너입니다. 둘 다 눈앞의 파일을 읽으므로, 같은 리포지토리에서는 대개 잠금 파일이 커밋되어 있는지, 스캔 전에 종속성을 설치했는지가 더 큰 차이를 만듭니다.

SBOM에 종속성이 하나도 나오지 않는 이유는 무엇인가요?

대부분 리포지토리가 잠금 파일을 커밋하지 않았고 스캔 전에 아무것도 설치하지 않았기 때문입니다. 잠금 파일이나 설치된 패키지를 읽는 생성 도구는 읽을 것이 없지만, 그래도 유효한 SBOM을 작성합니다. 잠금 파일을 커밋하거나, 설치 단계 이후에 스캔하세요.

Trivy는 SBOM에 개발 종속성을 포함하나요?

기본적으로는 포함하지 않습니다. Trivy의 Node.js 문서에 따르면 --include-dev-deps를 지정하지 않으면 개발 종속성을 보고하지 않습니다. 반면 cdxgen은 잠금 파일을 파싱하고 개발 종속성을 포함합니다. 이 이유만으로도 두 도구는 같은 프로젝트에서 서로 다른 결과를 낼 수 있습니다.

오픈 소스 생성 도구의 SBOM을 신뢰할 수 있나요?

읽은 것에 대해서는 신뢰할 수 있습니다. 문제는 읽지 않은 것입니다. 캐시, 빌드 도구, 다운로드, 설치 스크립트, 패키지 메타데이터가 없는 바이너리가 그렇습니다. 스캔한 SBOM은 릴리스를 만든 빌드가 아니라 도구에 지정한 파일에 대한 설명으로 다루세요.

가장 좋은 SBOM 도구는 무엇인가요?

직접 빌드하는 소프트웨어의 경우, 가장 완전한 SBOM은 파일을 스캔하는 것이 아니라 빌드를 기록하는 데서 나옵니다. CRACI는 캐시를 포함해 각 GitHub Actions 작업이 가져오는 모든 패키지를 기록하며, Snyk와 Aikido가 보고하지 않은 취약한 패키지를 찾아냈습니다. 직접 빌드하지 않은 서드파티 이미지와 바이너리에는 여전히 Syft나 Trivy 같은 스캐너가 실용적인 선택입니다.

기록된 SBOM을 스캐너의 SBOM과 나란히 놓아 보세요

GitHub Actions 워크플로 하나를 CRACI에서 실행하고, 가져온 패키지의 SBOM을 Syft, Trivy 또는 cdxgen이 보고한 내용과 비교해 보세요.

데모 예약