콘텐츠로 이동

보고서 읽는 법

이 문서는 스캔 뒤 BomLens 보고서를 어떻게 읽고 해석하는지를 다룹니다. 생성 방법은 보고서 생성을 참고하세요.

고지문이 함께 처리하는 것

오픈소스 고지문(NOTICE)은 라이선스별로 컴포넌트를 묶습니다. 그 묶음 외에 고지문은 다음을 함께 처리합니다.

  • 라이선스 이름을 SPDX 식별자로 정규화합니다. 예를 들어 "Apache License, version 2.0"은 Apache-2.0으로 모읍니다. 같은 라이선스의 표기가 갈려 중복되던 항목이 하나로 합쳐집니다.
  • 스캔 도구가 라이선스 이름을 정하지 못해 CUSTOM으로 남기고 라이선스 파일 전문만 붙여 둔 경우(저작권 표기 줄이 바뀐 LICENSE 파일을 쓰는 Go 모듈에서 흔합니다), 그 전문을 주요 라이선스(MIT, BSD-2-Clause, BSD-3-Clause, Apache-2.0, ISC)의 조항 문구와 대조해 SPDX 식별자를 복구합니다. 대조해도 애매한 전문은 추측하지 않고 CUSTOM으로 둡니다.
  • SBOM에 저작권(copyright) 값이 있으면 컴포넌트별로 표시합니다.
  • 주요 오픈소스 라이선스 21종(Apache-2.0, MIT, BSD-3-Clause, GPL/LGPL 계열 등)의 SPDX 표준 전문을 고지문 끝에 함께 묶습니다. 전문 동봉을 요구하는 라이선스의 의무를 별도 수집 없이 충족합니다. 번들 원본은 docker/lib/licenses/*.txt에 있습니다.

우선순위 신호 (CVSS, EPSS, CISA KEV)

심각도(Severity)만으로는 무엇을 먼저 고칠지 정하기 어렵습니다. 이를 보완하기 위해 보안 보고서는 심각도 외에 세 가지 신호를 함께 보여 줍니다. Markdown과 HTML 표의 열 구성은 Severity | KEV | CVSS | EPSS | CVE | Package | Installed | Fixed입니다.

  • CVSS — 취약점의 기술적 심각도 점수(0~10). V3 점수를 우선 쓰고 없으면 V2로 대체합니다.
  • EPSS — 향후 30일 내 실제 악용 가능성(0~1)입니다. FIRST.org에서 조회하며, 점수가 높을수록 공격에 쓰일 확률이 큽니다.
  • CISA KEV — 미국 CISA가 관리하는 "실제 악용된 취약점" 목록에 포함됐는지 여부입니다. 포함되면 HTML 보고서에 ⚠️ 배지로 표시합니다.

표는 KEV 포함 항목을 맨 위에 두고, 그다음 심각도, 마지막으로 EPSS 내림차순으로 정렬합니다. 위에서부터 처리하면 자연히 위험이 큰 것부터 대응하게 됩니다.

EPSS와 KEV는 외부 API 조회가 필요합니다. 폐쇄망에서는 SECURITY_ENRICH=false로 두면 두 열을 생략하고 나머지 보고서는 그대로 생성합니다.

배포 라이선스 충돌

위의 라이선스 분류는 각 컴포넌트의 카피레프트 강도를 그 자체로만 표시합니다. 그 강도가 실제로 문제인지는 내가 배포하는 라이선스에 따라 달라지는데, 소스 스캔으로는 이 값을 알아낼 수 없습니다. 그래서 --license <spdx-id>(웹 UI에서는 배포 라이선스 칸)로 선언하면 그 기준으로 각 의존성을 판정합니다.

  • 충족 불가 — 선언한 라이선스로 배포하면서 그 의존성의 조건을 지킬 수 없습니다.
  • 배포 방식에 따라 달라짐 — 배포나 링크 방식에 따라 의무가 달라집니다. 사람이 확인해야 합니다.
  • 판정 불가 — 라이선스를 인식하지 못했거나 배포 라이선스를 선언하지 않았습니다.

예외 조항이 붙은 라이선스(classpath 예외가 대표적)는 첫 번째로 판정하지 않습니다. 그 조항이 바로 그런 결합을 허용하려고 존재하기 때문입니다.

선언하지 않으면 검사하지 않으며, 보고서에서 해당 절이 아예 빠집니다. 아무도 확인하지 않은 결과를 문제없음으로 보여 주지 않기 위해서입니다. 법적 판단이 아니라 문서 준비를 돕는 자료입니다. 플래그는 CLI 레퍼런스, 판정이 표시되는 위치는 웹 UI를 참고하세요.

악성 패키지

취약점은 정상적인 패키지에 결함이 있다는 뜻입니다. 악성 패키지는 성격이 다릅니다. 설치하는 쪽을 공격하려고 배포된 것으로, 흔한 이름을 노린 오타 도용이나 탈취된 관리자 계정으로 올라온 배포본, 설치 과정에서 실행되는 악성 코드가 여기 해당합니다. BomLens는 이 항목을 취약점 표와 분리해 표시합니다. 대응 방법이 다르기 때문입니다. 올릴 수 있는 버전이 있는 것이 아니라 패키지를 제거해야 하고, 빌드 과정에서 접근할 수 있었던 자격 증명도 교체해야 합니다.

  • 이름이 아니라 PURL로 대조합니다. 악성 패키지는 진짜 패키지와 비슷하게 이름을 짓기 때문에, 이름 대조는 오히려 위험합니다.
  • 데이터는 OSV가 공개하는 악성 패키지 권고(MAL- 식별자를 가진 항목)를 이미지에 담아 둔 스냅샷입니다. 그래서 EOL 표시와 마찬가지로 네트워크 없이 동작합니다. 건너뛰려면 ENRICH_MALICIOUS=false로 설정합니다.
  • 대부분의 권고는 버전을 특정하지 않는데, 그 패키지의 모든 배포 버전이 악성이라는 뜻입니다. 버전을 특정한 권고는 해당 버전만 표시합니다.
  • 표시된 컴포넌트에는 권고 식별자와 스냅샷 날짜가 함께 기록됩니다(bomlens:malicious:id, bomlens:malicious:source). 날짜가 중요합니다. 이 분야는 변화가 빨라서, 아무것도 나오지 않았다는 것은 "이 스냅샷에 없다"는 뜻이지 "지금 안전하다"는 뜻이 아닙니다.

컴포넌트 지원 종료(EOL)

BomLens는 각 컴포넌트의 릴리스 주기가 상위(upstream) 지원 종료(End-of-Life, EOL)에 이르렀는지도 함께 표시합니다. 이는 취약점(CVE)과는 별개의 공급망 위험입니다. 지원 기한이 지난 런타임이나 프레임워크는 상위 보안 패치가 더 나오지 않으므로, 이후 Critical이나 High가 보고돼도 적용할 패치가 없습니다.

  • 날짜는 스캐너 이미지에 번들한 endoflife.date 스냅샷에서 가져옵니다. 그래서 이 점검은 네트워크 호출 없이 오프라인으로 동작하며 폐쇄망에서도 쓸 수 있습니다. 출처와 스냅샷 날짜는 표시된 각 컴포넌트에 기록됩니다(bomlens:eol:source).
  • 커버리지는 endoflife.date를 따릅니다. endoflife.date는 런타임, 주요 프레임워크, 운영체제, 데이터베이스를 다룹니다(spring-boot, express, django, nodejs, python, php, nginx, openssl, ubuntu, debian 등). 규모가 작은 라이브러리 다수는 대상이 아니며, 매핑이 없는 컴포넌트는 추측하지 않고 미표기(unknown)로 둡니다.
  • 웹 UI에서는 개요(Overview)에 "지원 종료" 개수 타일이 나오고, 그중 취약점도 있는 컴포넌트는 위험색으로 강조됩니다. 지원 종료 컴포넌트는 자신의 CVE에 대한 상위 패치가 없으므로 실제로 대응해야 할 대상입니다. 컴포넌트 표에는 "지원 종료" 배지(가능하면 종료 날짜 포함)와 "지원 종료" 필터가 더해집니다.
  • 오프라인이라 지연이 없어 기본으로 켜져 있습니다. 끄려면 ENRICH_EOL=false로 설정합니다. AI/ML 모델 스캔은 런타임이나 프레임워크 컴포넌트가 없어 이 단계를 건너뜁니다.

버전 최신성

지원되는 릴리스 주기 안에 있다고 해서 최신 버전을 쓰는 것은 아닙니다. 그래서 BomLens는 컴포넌트가 뒤처졌는지도 함께 표시합니다. 이 판정은 두 층으로 나뉩니다.

  • 오프라인 층은 EOL 점검과 함께 기본으로 켜져 있습니다. 같은 endoflife.date 스냅샷에 각 릴리스 주기의 최신 패치가 담겨 있어, 설치된 버전이 자기 주기 안에서 최신 패치보다 뒤처졌는지를 오프라인으로 판정합니다. EOL 점검과 똑같이 네트워크 호출 없이 찾아내는, 안전한 주기 내 업그레이드 신호입니다. 뒤처진 컴포넌트에는 bomlens:currency:outdated=true가 붙고, 목표 패치는 bomlens:currency:latestPatch에 담깁니다. 이 판정은 EOL 단계 안에서 돌기 때문에 ENRICH_EOL=false로 두면 함께 꺼지고, AI/ML 모델 스캔은 건너뜁니다.
  • deps.dev 층은 옵트인입니다. STALENESS_ENRICH=true로 켜면 각 컴포넌트를 deps.dev(구글의 공개 패키지 메타데이터)에서 조회해 절대 최신 버전(bomlens:staleness:latest), 설치 버전이 몇 릴리스 뒤인지(bomlens:staleness:releasesBehind), 최신 버전이 언제 나왔는지(bomlens:staleness:lastReleased)를 기록합니다. 이는 컴포넌트마다 네트워크 호출을 하므로 스캔의 오프라인 결정성을 최신성과 맞바꾸며, 폐쇄망에는 맞지 않아 기본으로 꺼져 있습니다. 최선 노력이자 시간 제한이 있어, 조회에 실패해도 스캔을 멈추지 않습니다. 지원 생태계는 npm, PyPI, Maven, Go, Cargo, NuGet, RubyGems입니다. 프로젝트가 지금도 활발히 유지보수되는지는 이번 릴리스에 포함되지 않으며, 이후 확장할 부분입니다.
  • 웹 UI에서는 개요(Overview)에 최신 버전보다 뒤처진 컴포넌트 수 타일이 더해지고, 컴포넌트 표는 최신 버전이 아닌 컴포넌트를 표시하며 "Outdated" 필터를 더합니다. deps.dev 층을 켜면 그런 컴포넌트마다 몇 릴리스 뒤인지와 마지막 릴리스 날짜도 함께 보여줍니다.

결과 해석과 후속 조치

Severity 의미 권장 조치
Critical 즉시 악용 가능, 심각 최우선 패치 — Fixed 버전으로 즉시 업그레이드
High 위험도 높음 단기 내 패치 계획 수립
Medium / Low 영향 제한적 정기 점검 시 처리
Unknown 심각도 미평가 해당 CVE를 직접 확인 후 분류
  • 보고서의 Fixed 열에 버전이 있으면, 그 버전 이상으로 의존성을 올리면 해결됩니다. 가장 빠른 1차 대응입니다.
  • CI 게이트 예시. Critical이 1건이라도 있으면 빌드 실패:
    crit=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length' *_security.json)
    [ "$crit" -gt 0 ] && { echo "Critical 취약점 ${crit}건"; exit 1; }
    
  • 오탐(실제 영향 없음) 판단, 예외 승인, 이력 관리 같은 취약점 분류 업무는 BomLens의 범위를 넘습니다. 취약점 관리 시스템(Dependency-Track, TRUSCA 등)에 SBOM을 업로드해 처리하세요.

오픈소스위험분석보고서

오픈소스위험분석보고서는 취약점을 심각도별로 집계하고 권고 대응 기한(Critical 7일, High 30일)을 명시합니다. 라이선스 요약도 담고 있으며, 공급사 SBOM을 분석한 경우에는 포맷 적합성 결과가 더해집니다.

라이선스 요약에는 카피레프트 강도에 따른 컴포넌트 분류도 함께 담기며, 웹 UI가 보여 주는 분류와 같은 기준을 사용합니다. SBOM의 각 컴포넌트에는 bomlens:licenseClass 속성이 network-copyleft, strong-copyleft, weak-copyleft, permissive, uncategorized 중 하나로 기록되고, 보고서에는 분류별 개수와 카피레프트 노출을 만드는 컴포넌트 목록이 더해집니다. 인식되지 않은 라이선스는 permissive로 간주하지 않고 uncategorized로 남겨 사람이 확인하도록 합니다.

관련 문서