본문으로 건너뛰기

스캔

스캔은 프로젝트의 컴포넌트·라이선스·취약점을 탐지하는 end-to-end 실행입니다. 소스 스캔 파이프라인은 cdxgen(CycloneDX generator), scancode(first-party 라이선스 탐지), Trivy(trivy sbom으로 로컬 Trivy DB 매칭)를 체이닝하며, 컨테이너 스캔 파이프라인은 Trivy(Aqua Security 컨테이너 스캐너)를 사용해 CVE(Common Vulnerabilities and Exposures)와 라이선스 이슈를 탐지합니다. 스캔은 Celery 워커에서 실행되며(API 인라인 절대 금지), 일반적으로 5분(작은 npm 프로젝트)에서 60분(큰 멀티 모듈 Java 레포)까지 소요됩니다.

대상 독자

프로젝트 소속 팀의 developer 이상 권한 보유 엔지니어. 사설 저장소 스캔은 프로젝트의 git_url에 자격증명을 포함해야 합니다 — 프로젝트 → 사설 저장소.

스캔 종류​

종류파이프라인탐지 대상
sourcecdxgen → scancode(first-party 라이선스 탐지) → Trivy(trivy sbom)컴포넌트와 그 declared 라이선스(의존성 메타데이터에서), detected 라이선스(scancode가 직접 스캔한 first-party 소스), 로컬 Trivy DB가 NVD + OSV + GHSA + EPSS + KEV로 매칭한 CVE(Common Vulnerabilities and Exposures).
containerTrivy컨테이너 이미지의 전체 패키지 목록과 선언된 라이선스, OS 패키지 취약점(언어 패키지 CVE는 제한적).
sbom적합성 채점 → 컴포넌트 적재 → Trivy(trivy sbom)고객 도구가 이미 생성한 SBOM(CycloneDX-JSON 또는 SPDX). TRUSCA는 소스를 복제·빌드하지 않고 SBOM의 품질을 채점하고 컴포넌트를 적재하며 CVE를 매칭합니다. 아래 SBOM 업로드 참고.

Source와 Container는 UI 스캔 다이얼로그에서 선택합니다 — 스캔을 트리거할 때 하나를 고르세요(스캔 트리거 → UI에서 참고). sbom 스캔은 방식이 다릅니다. 다이얼로그에서 고르는 대신 기존 SBOM을 인제스트 엔드포인트로 업로드합니다(아래 SBOM 업로드 참고). API는 세 종류를 모두 수용합니다.

스캔 트리거​

UI에서​

  1. 사이드바에서 Projects 열기.
  2. 프로젝트 행을 찾고, 행 끝의 Scan 버튼 클릭.
  3. 스캔 다이얼로그가 열립니다. 상단에서 스캔 종류를 선택합니다.
    • Source — 프로젝트 소스에 cdxgen + scancode + Trivy 실행. 기본값입니다.
    • Container — 지정한 컨테이너 이미지에 Trivy 실행. 컨테이너 이미지 스캔 참고.
  4. Source 스캔이면 소스 제공 방식(Git URL, 업로드한 .zip, 브라우저에서 압축할 폴더)을 고른 뒤 Start scan을 클릭합니다.
상세 로그 (디버그)

스캔 다이얼로그에는 Verbose logs (debug)(상세 로그) 토글이 있습니다(기본 꺼짐). 표준 진행 로그만 보려면 꺼 두세요. 특정 스캔에서 켜면 cdxgen / scancode / Trivy의 전체 진단 출력이 단계별 로그 패널로 스트리밍됩니다 — cdxgen은 디버그 모드로, scancode는 파일별 라인을, Trivy는 --debug로 전환됩니다. 컴포넌트가 너무 적게 잡히거나, 라이선스를 놓치거나, 예상치 못한 CVE가 매칭된 원인을 디버깅할 때 사용하세요. 상세 출력은 양이 많을 수 있으며, 스캔당 라인 예산(SCAN_LOG_MAX_LINES_PER_SCAN, 기본 20000)이 여전히 상한을 둡니다. 자격 증명은 로그로 나갈 때 마스킹되며, 상세 source 스캔은 스캔된 각 파일 경로도 함께 기록합니다 — 해당 스캔을 열 수 있는 팀원 누구에게나 보이므로, 로그를 폭넓게 공유할 때는 내부/신뢰된 프로젝트에서 사용하는 것을 권장합니다. API 에서는 metadata.verbosity를 "verbose"로 설정합니다(없거나 "normal" 이면 조용한 로그 유지).

프로젝트 목록 페이지에서 우측 슬라이드 드로어가 열리며 WebSocket 기반의 실시간 진행 뷰가 표시됩니다. 탭을 닫아도 스캔은 워커에서 계속됩니다. 프로젝트를 다시 열면 언제든 재연결됩니다. 스캔이 queued 또는 running 인 동안 드로어에는 Cancel scan(스캔 취소) 동작이 함께 표시됩니다 — 스캔 취소 참고.

스캔 진행 드로어 — bootstrap → fetch → cdxgen → scancode → vuln_match → finalize 단계, WebSocket 실시간 표시

브랜치당 동시 스캔은 하나

한 프로젝트에서 여러 브랜치를 동시에 스캔할 수 있습니다. main과 release/1.x에 함께 push해도 한쪽 CI가 다른 쪽 스캔을 기다리지 않습니다. 두 브랜치는 서로 다른 스냅샷에 쓰기 때문입니다. 여전히 직렬화되는 것은 같은 브랜치입니다. 이미 queued 또는 running 스캔이 있는 브랜치에 두 번째 스캔을 트리거하면 409 Conflict와 RFC 7807 확장 필드 scan_already_in_progress: true를 반환하고, detail에 어느 브랜치가 사용 중인지 밝힙니다.

ref 없이 트리거한 스캔은 — Scan 버튼과 metadata.ref를 생략한 API 호출 — 모두 한 묶음으로 셉니다. 그래서 그중 둘은 여전히 서로 충돌합니다. 버튼이 비활성화되는 것도 다른 ref 없는 스캔이 실행 중일 때뿐이고, main의 CI 스캔이 도는 중에는 버튼을 쓸 수 있습니다. 진행중 칩은 어느 경우에도 그대로 보이며, 클릭하면 해당 스캔의 드로어가 열립니다.

충돌한 스캔이 종료 상태에 도달하거나 Cancel 한 뒤 다시 트리거하세요. 동일한 가드가 UI·API·CI 클라이언트에 적용됩니다.

소스 스캔의 브랜치 선택

소스 스캔은 프로젝트의 default_branch(보통 main)에 대해 실행됩니다. 현재 릴리스에서는 UI와 API 모두 브랜치 오버라이드를 노출하지 않습니다. develop 이나 feature 브랜치를 스캔하려면 트리거 전에 Project Settings에서 default_branch를 임시로 변경한 뒤 되돌리세요. 트리거에 정식 branch 필드를 추가하는 작업은 로드맵 항목입니다.

컨테이너 이미지 스캔​

스캔 다이얼로그에서 Container를 선택하면 소스 대신 빌드된 이미지를 스캔합니다. Trivy(Aqua Security 컨테이너 스캐너)가 이미지의 OS 패키지에서 알려진 취약점을 검사합니다 — 애플리케이션 의존성 트리를 다루는 소스 스캔과 상호 보완적입니다.

스캔은 CVE가 있는 패키지만이 아니라 Trivy가 이미지에서 찾은 모든 패키지를 저장합니다. 각 패키지는 선언된 라이선스(라이선스마다 소견 하나)와 패키지 사이의 의존 관계를 갖춘 컴포넌트로 나타나므로, 프로젝트의 SBOM 내보내기와 라이선스 화면이 이미지 전체를 다룹니다. 라이선스를 여러 개 적은 패키지도 전부 남습니다. 이 변경 전에 기록된 스캔에는 취약한 패키지만 들어 있으니, 전체 목록이 필요하면 이미지를 다시 스캔하십시오.

  1. 프로젝트 행의 Scan 버튼에서 스캔 다이얼로그를 엽니다.
  2. 다이얼로그 상단에서 Container를 선택합니다.
  3. 컨테이너 이미지 참조를 name:tag 형식으로 입력합니다. 예: alpine:3.19 또는 ghcr.io/org/app:1.2.3. 워커가 풀할 수 있는 이미지여야 합니다. 공개 레지스트리는 그대로 동작하고, 사설 레지스트리는 관리자가 그 레지스트리의 자격증명을 저장해 두어야 합니다(아래 사설 레지스트리 참고). 운영자가 허용 레지스트리를 제한해 둘 수도 있으며(허용 레지스트리 참고), 그 경우 목록에 없는 이미지는 스캔을 시작할 때 거부됩니다.
  4. Start scan을 클릭합니다.

동일한 진행 드로어가 열립니다. 스캔이 succeeded에 도달하면 OS 패키지 취약점이 프로젝트의 Vulnerabilities 탭에 나타납니다.

BomLens 엔진으로 실행하는 컨테이너 스캔(미리 보기)

관리자는 컨테이너 스캔을 BomLens 스캔 엔진으로 실행하도록 설정할 수 있습니다(SCAN_CONTAINER_ENGINE=bomlens, 동적 스캔 실행기 참고). 스캔 대화 상자와 결과 화면은 같습니다. 다른 점은 두 가지입니다. 이미지 참조는 일반 레지스트리 참조([registry/]name[:tag][@sha256:digest])여야 하고, 인증 정보가 저장된 레지스트리(사설 레지스트리)의 이미지는 레지스트리 이름과 함께 기본 엔진을 쓰라는 메시지로 실패합니다. 빌드한 바로 그 이미지를 스캔하려면 다이제스트를 지정하십시오.

허용 레지스트리​

컨테이너 스캔은 워커가 이미지를 풀하게 하므로, 참조에 적힌 주소로 나가는 요청이 발생합니다. 운영자는 포털의 CONTAINER_SCAN_ALLOWED_REGISTRIES로 이것을 제한할 수 있습니다. 호스트(ghcr.io)나 호스트와 접두 경로(ghcr.io/your-org)를 쉼표로 나열합니다.

설정하지 않으면 제한이 없으며, 이것이 아직 설정하지 않은 모든 배포의 현재 동작입니다. 설정하면 목록에 없는 이미지는 스캔을 시작하는 시점에 레지스트리 이름과 함께 거부되므로, 워커 자리를 기다린 뒤가 아니라 즉시 알게 됩니다.

목록을 적을 때 알아 둘 점이 둘 있습니다.

  • 레지스트리 부분이 없는 이미지는 Docker Hub로 해석됩니다. alpine:3.19도 myorg/app도 문자열에는 나오지 않지만 목록에 docker.io가 있어야 합니다.
  • 호스트는 정확히 일치해야 합니다. ghcr.io는 ghcr.io.example.com을 허용하지 않고, 허용된 이름이 경로에만 나오는 evil.example.com/ghcr.io/app도 허용하지 않습니다.

사설 레지스트리​

컨테이너 이미지에 대한 내용입니다. 소스 의존성과는 다릅니다

이 절은 컨테이너 스캔에서 Trivy가 컨테이너 이미지를 pull할 때의 인증을 다룹니다. 소스 스캔에서 cdxgen이 사설 Maven·npm·pip 레지스트리에 인증하는 방법은 의존성 해석용 사설 레지스트리를 참고하세요.

기업 이미지는 대부분 공개되어 있지 않습니다. 슈퍼 관리자가 조직마다 레지스트리별로 로그인을 하나씩 저장합니다.

curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"registry_host":"ghcr.io","username":"bot","password":"'"$TOKEN_VALUE"'"}' \
"https://trustedoss.example.com/v1/admin/organizations/$ORG_ID/registry-credentials"

registry_host는 이미지 참조에 나오는 그대로의 호스트입니다(ghcr.io, registry.example.com, registry:5000). https://ghcr.io/처럼 붙여 넣어도 알아서 정규화합니다. 다만 https://bot:token@ghcr.io/처럼 로그인 정보가 들어 있는 URL은 정리해서 받아들이지 않고 거부합니다. 이 항목은 API 응답에 담기고 감사 로그에도 남기 때문에, 비밀번호가 여기까지 오면 암호화한 값 바로 옆에 평문으로 남게 됩니다.

Docker Hub는 docker.io로 적습니다. 레지스트리 구간이 없는 이미지가 해석되는 이름과 같습니다. index.docker.io로 저장하면 스캔할 때 조회가 그 행을 찾지 못합니다.

이 기능은 시크릿 암호화 설정(GITHUB_APP_ENCRYPTION_KEY)을 전제로 합니다. 포털이 저장하는 다른 자격증명도 같은 설정을 씁니다. 운영 환경에서 이 설정이 없으면 암호화하지 않은 채로 저장하는 대신 저장을 거부합니다.

저장한 자격증명이 어떻게 다루어지는지입니다.

  • 암호화해 저장합니다. TRUSCA가 다루는 다른 자격증명과 같은 방식입니다. 어떤 엔드포인트도 값을 돌려주지 않으며 방금 만든 응답에도 담기지 않으므로, 조회하면 레지스트리와 사용자 이름만 보입니다.
  • 자기 레지스트리로만 나갑니다. 워커는 지금 스캔하는 이미지가 온 레지스트리 하나만을 담은 Docker config.json을 씁니다. 한 레지스트리의 자격증명이 다른 레지스트리로 제시되지 않고, 이번 스캔과 무관한 자격증명은 파일에 아예 쓰이지 않습니다.
  • 스캐너의 환경변수에는 들어가지 않습니다. Trivy에는 환경변수가 아니라 파일로 전달되므로, 스캐너가 죽으면서 남기는 리포트나 오류 경로에 빠져나갈 자격증명이 없습니다. 파일은 백업이 담지 않도록 스캔 워크스페이스 밖에 두며, 스캔이 끝나면 실패한 경우를 포함해 지웁니다.

자격증명을 저장하기 전에 알아 둘 한계가 하나 있습니다. 파일 권한이 실제보다 강한 보호로 읽히기 쉬운 자리입니다. 파일은 워커 사용자만 읽을 수 있으므로 같은 호스트의 다른 사용자로부터는 보호됩니다. 그러나 동시에 도는 스캔들 사이는 나누지 못합니다. 워커 프로세스들이 같은 사용자로 돌기 때문에, 한 스캔의 Trivy가 같이 돌고 있는 다른 스캔의 파일을 읽을 수 있습니다. 그러려면 Trivy 안에서 임의 파일 읽기나 코드 실행이 성립해야 하므로 열려 있는 통로라기보다 남아 있는 위험이며, 스캔이 실제로 받아 오는 레지스트리 하나로 자격증명을 좁혀 둔 이유이기도 합니다. 서로의 레지스트리에 닿으면 안 되는 조직들의 이미지를 한 배포에서 스캔한다면, 컨테이너 스캔 동시성을 1로 두십시오.

허용 목록에 없는 레지스트리의 자격증명은 저장 자체가 거부됩니다. 쓸 수 없는 값이기 때문입니다. 목록을 나중에 좁혀서 쓸 수 없게 된 자격증명은 남아 있되 목록 조회에서 "allowed": false로 보이므로, 더 이상 동작하지 않는 설정이 조용히 묻히지 않습니다.

베이스 이미지 OS 지원 종료​

Trivy는 이미지의 베이스 OS 릴리스가 서비스 지원이 종료되었는지도 함께 보고합니다. 오래된 Debian이나 Alpine 버전처럼 상위 배포처에서 더 이상 보안 수정을 받지 못하는 릴리스가 이에 해당합니다. 지원이 종료된 경우 스캔 상세 페이지에 OS family와 릴리스를 표시하는 OS 지원 종료 패널이 나타납니다. 이는 소스 스캔의 컴포넌트별 지원 종료 배지와 구분되는 스캔 단위 신호입니다. 개별 패키지 CVE가 하나도 없더라도, 지원이 종료된 OS 위에 빌드된 이미지는 그 OS가 종료된 이후 공개되는 CVE에 대한 패치를 받지 못하므로, 지원 중인 릴리스로 다시 빌드하는 것을 권장합니다.

지원 종료 판정은 Trivy의 번들 취약점 데이터베이스에서 나오므로 추가 네트워크 접근이 필요 없습니다. 데이터베이스가 오래되면 아주 최근에 종료된 릴리스를 아직 표시하지 못할 수 있으니, 정확한 판정을 위해 Trivy 데이터베이스를 최신으로 유지하세요(관리자 상태 패널 참고).

컨테이너 스캔은 Git URL이 필요 없음

컨테이너 스캔은 저장소가 아닌 이미지 참조를 읽습니다. git_url이 없는 프로젝트도 컨테이너 스캔을 실행할 수 있습니다. Source / Container 선택은 프로젝트의 소스 설정과 무관합니다.

API에서​

curl -sS -X POST \
"https://trustedoss.example.com/v1/projects/${PROJECT_ID}/scans" \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"kind": "source"}' | jq .

응답에 스캔 UUID가 포함됩니다. 폴링:

curl -sS "https://trustedoss.example.com/v1/scans/${SCAN_ID}" \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" | jq .status

컨테이너 스캔은 kind를 container로 설정하고 이미지 참조를 metadata.image_ref에 전달합니다.

curl -sS -X POST \
"https://trustedoss.example.com/v1/projects/${PROJECT_ID}/scans" \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"kind": "container", "metadata": {"image_ref": "alpine:3.19"}}' | jq .

CI에서​

권장 경로는 GitHub Action, GitLab CI 템플릿, Jenkinsfile 예시입니다. 모두 API를 감싸고 빌드 게이트를 추가합니다.

SBOM 업로드(받은 SBOM)​

빌드나 CI가 이미 SBOM을 생성한다면, TRUSCA가 소스를 복제·스캔하게 하는 대신 그 SBOM을 업로드할 수 있습니다. 이렇게 하면 sbom 스캔이 생성됩니다. TRUSCA는 SBOM의 컴포넌트를 적재하고, Trivy로 CVE를 매칭하고, 선언 라이선스를 분류합니다 — 소스 스캔에서 얻는 컴포넌트·취약점·라이선스 화면을 그대로 제공하며, 빌드 게이트도 동일하게 실행됩니다.

  • 포맷: CycloneDX-JSON, 또는 SPDX(JSON·Tag-Value). 매칭에서는 Trivy가 포맷을 자동 감지하고, 컴포넌트 그래프를 위해 SPDX는 내부적으로 CycloneDX로 변환됩니다.
  • 업로드 방법: API Key로 POST /v1/projects/{project_id}/sbom-ingest. 필드·크기 제한·오류를 포함한 전체 안내는 SBOM 업로드에 있습니다.

적합성(conformance) 결과​

공급사가 제공한 SBOM은 버전·PURL·의존성 그래프가 빠진 "껍데기"일 수 있으므로, TRUSCA는 인제스트 시 SBOM의 품질을 채점하고 스캔 상세 페이지에 pass / warn / fail 배지와 요구사항별 표를 표시합니다. 이 결과는 **자문(advisory)**입니다 — fail이어도 인제스트를 막지 않으며(CVE 매칭은 계속 수행) SBOM을 받아들일지 공급사에 반려할지 판단하는 근거가 됩니다. 필수 검사에는 타임스탬프, 도구 정보, 최상위 컴포넌트, 컴포넌트 name+version 100%, PURL 커버리지 ≥ 90%, pkg:generic 자리표시자 없음, 전이 의존성 그래프가 포함됩니다. 이 가운데 컴포넌트 단위 검사는 패키지 컴포넌트만 대상으로 셉니다. 파일이나 데이터셋 항목은 패키지 버전도 PURL도 담을 수 없어 감점 대상이 아니며, 문서 전체가 그런 항목뿐이면 통과가 아니라 경고입니다. 한편 라이선스·해시 커버리지는 권장(warn만)입니다. UI 패널이나 GET /v1/projects/{project_id}/scans/{scan_id}/conformance로 읽습니다 — SBOM 업로드 → 적합성 결과 읽기 참고.

CycloneDX 문서라면 결과에 규제 필드 검사 5종 — SHA-512 체크섬, 컴포넌트 제작 주체, 컴포넌트 파일명, 소스·배포 URI, 전달 파일 속성 커버리지(기준 SBOM_CONFORMANCE_FIELD_MIN_PCT, 기본 80) — 도 함께 실립니다. 5종 모두 권고이며 판정 불변입니다. pass / warn / fail 배지를 움직이는 일이 없습니다. 대신 이 검사들은 **규제 대응표(crosswalk)**의 입력이 됩니다. 대응표는 각 검사를 4개 프레임워크 — BSI TR-03183-2(EU 사이버복원력법), 미국 최소 요소, EU 인공지능법 부속서 IV, AI 기본법(대한민국) — 의 문서화 요구와 상호 참조하고, 프레임워크별로 충족 / 결측 / 사람 검토 필요 / 실패를 집계합니다. 대응표는 컴플라이언스 판정이 아니라 문서화 준비를 돕는 자료이며, 응답에 그 면책 문구가 그대로 실립니다. SBOM 업로드 → 규제 필드 검사 참고.

모든 CycloneDX 문서는 2026 SBOM 최소 요소(데이터 필드 17종과 실무 6종, 권고 검사 23종)로도 함께 채점합니다. 이 기준은 2021년 NTIA 최소 요소를 대체했고 모든 소프트웨어에 적용됩니다. CycloneDX specVersion 1.7도 받으며, 업로드한 문서에 machine-learning-model 컴포넌트가 있으면 그 위에 권고 성격의 G7 AI SBOM 최소요소(Minimum Elements) 체크리스트(7개 클러스터, 51개 요소)가 더해집니다. 두 기준선 모두 권고입니다 — SBOM 적합성 기준선 참고.

수명 주기​

queued ─────► running ─────► succeeded
│ │
│ └────► failed
│ │
└──────────────────┴────► cancelled
상태의미
queued큐에 들어감; 빈 워커 슬롯 대기.
running워커가 작업을 받아 파이프라인 실행 중.
succeeded파이프라인 종료, 컴포넌트와 결과를 조회 가능.
failed워커가 오류를 일으킴. API 응답의 error_detail 또는 워커 로그를 확인.
cancelledqueued 또는 running 중에 사용자나 관리자가 취소함. 워커 작업이 중단되고 워크스페이스가 정리됩니다. 스캔 취소 참고.

queued, running은 비종단 상태이고, succeeded, failed, cancelled는 종단 상태입니다. 스캔은 비종단 상태에서만 취소할 수 있습니다.

파이프라인 단계 (source)​

진행 뷰는 단계 전환을 실시간 표시합니다.

  1. Bootstrapping — 작업 공간 준비.
  2. Fetching source — git clone(또는 기존 작업 공간이면 git fetch + checkout).
  3. Detecting components — cdxgen이 레포를 탐색하여 CycloneDX SBOM을 생성하고, 각 의존성의 패키지 메타데이터에서 declared 라이선스를 읽습니다. 직후 런타임 스코프 필터가 배포되지 않는 의존성(Maven test/provided, npm devDependencies)을 제거하여, 이후 모든 단계 — 서명·저장·취약점 매칭 — 가 배포 세트만 보게 합니다(컴포넌트·라이선스 → 런타임 스코프 필터링 참고).
  4. Detecting first-party licenses — scancode가 프로젝트 자체 소스 파일을 스캔하여 발견한 detected 라이선스를 기록하며, 각 항목에 라이선스가 발견된 파일의 source_path를 함께 태깅합니다(컴포넌트·라이선스 → declared vs. detected 참고). 이 단계는 best-effort 입니다: scancode가 미설치이거나 타임아웃이거나 트리가 너무 크면 declared 라이선스만으로 스캔을 계속합니다 — 저하되었으나 비치명적인 결과입니다. 이후 법적 단계 분류는 내장 분류 카탈로그에서 적용됩니다(컴포넌트·라이선스 → 분류 출처 참고).
  5. Resolving vulnerabilities — trivy sbom이 CycloneDX SBOM을 로컬 Trivy DB(NVD + OSV + GHSA + EPSS + KEV)에 매칭. 스캔당 네트워크 호출 없음.
  6. Persisting — 컴포넌트·라이선스·결과를 PostgreSQL에 저장.
ORT는 scancode로 교체됨

이전 빌드는 라이선스 단계에서 OSS Review Toolkit(ORT)을 실행했습니다. v0.10.0은 이를 first-party 탐지를 위한 scancode로 교체했습니다. 서드파티 의존성 소스는 의도적으로 다운로드하지 않으며 — 이는 스캔당 실행 시간을 예산 내로 유지하기 위함입니다 — 따라서 의존성 라이선스는 declared(cdxgen에서)로 유지되고, scancode는 팀이 실제로 작성한 코드에 대한 detected 라이선스를 추가합니다.

5단계 실행 시 로컬 Trivy DB가 아직 다운로드되지 않았다면(첫 설치에서 가장 흔함) 스캔은 취약점 finding 0개로 완료되며 Vulnerabilities 탭에 취약점 데이터 (Trivy DB)를 가리키는 배너가 표시됩니다. 자동 재매칭 beat이 DB 도착 후 finding을 가져옵니다 — 재스캔 불필요.

iOS 프로젝트 (CocoaPods / Swift Package Manager)​

iOS 저장소는 커밋된 lockfile만으로 오프라인 스캔됩니다 — 워커에 Xcode, Swift 툴체인, CocoaPods CLI가 필요하지 않습니다.

  • CocoaPods — cdxgen은 pod CLI 없이는 pod를 수집하지 못하므로, 스캐너가 해당 수집기를 제외하고(예전에는 스캔 전체가 죽던 크래시를 회피) Podfile.lock에서 pod 목록과 의존성 그래프를 직접 복원합니다. PODS: 블록이 완전히 해석된 진실입니다: 직접·전이 pod, 고정 버전, 각 pod의 하위 의존성이 모두 담겨 있고, Moya/Core 같은 서브스펙도 별도 컴포넌트로 나타납니다. Podfile.lock 에는 런타임/테스트 구분이 없어 pod의 Usage(Required/Optional) 값은 —로 표시됩니다.
  • Swift Package Manager — 커밋된 Package.resolved는 cdxgen이 그대로 파싱합니다(v1·v2 형식 모두). 파일이 커밋돼 있으면 사이드카 실행기는 swift package resolve를 건너뜁니다 — 커밋된 lockfile이 이미 해석된 그래프이고, 다시 해석하면 네트워크에 접근하게 됩니다.

Podfile.lock과 Package.resolved를 저장소에 커밋하십시오 — 없으면 iOS 스캔은 manifest가 선언한 것만 찾습니다.

스캔이 아무것도 찾지 못했을 때​

스캔이 성공하고도 컴포넌트가 하나도 나오지 않을 수 있습니다. 이것은 프로젝트에 의존성이 없다는 뜻이 아니며, 포털은 빈 화면만 보여 주고 짐작하게 두는 대신 둘 중 어느 경우인지 알려 줍니다.

이때도 스캔은 성공으로 끝납니다. cdxgen이 모든 빌드 시스템을 인식하지는 못하고, 읽지 못하는 트리에서 결과가 비는 것은 올바른 답이기 때문입니다. 여기서 실패로 처리하면 정상적으로 돌아가던 파이프라인이 깨집니다. 대신 프로젝트 개요 탭의 안내, Components 탭의 다른 빈 상태 문구, 그리고 CI 액션이 출력하는 게이트 결과의 component_outcome 필드로 드러납니다.

상황뜻할 일
컴포넌트를 찾음통상적인 경우입니다.없습니다.
컴포넌트가 없고 소스에 의존성 매니페스트도 없음스캐너가 이 빌드 시스템을 인식하지 못해 읽을 대상 자체가 없었습니다. 프로젝트의 모든 수치가 0인 것은 그 때문입니다.결과를 정상으로 보기 전에 지원 생태계를 확인하세요.
컴포넌트가 없는데 소스에는 의존성 매니페스트가 있음스캔 도중 문제가 생겼습니다. 빌드 준비 단계는 최선 노력이라, cdxgen에 필요한 락파일을 만들지 못하면 그 생태계가 통째로 비어 버립니다.스캔 로그에서 비어 있는 단계를 찾아 확인한 뒤 다시 실행하세요.

빈 스캔에서 빌드 게이트는 통과합니다. 읽는 수치가 전부 0이라 막을 근거가 없기 때문입니다. 통과가 이상 없음으로 읽히지 않도록 판정이 보이는 곳마다 경고가 함께 나옵니다. 어느 경고도 판정이나 종료 코드는 바꾸지 않습니다.

  • GitHub Action은 경고 주석을 출력합니다. component-outcome을 보세요.
  • GitLab CI 템플릿과 Jenkins 템플릿은 판정 다음 줄에 WARNING: 줄을 출력합니다. GitLab CI와 Jenkins를 보세요.
  • 풀 리퀘스트나 머지 리퀘스트 코멘트에는 판정 아래에 경고가 붙습니다.
  • 프로젝트 개요 탭의 빌드 게이트 카드에도 판정 아래에 경고가 나타납니다.

소스가 의존성을 선언했는데도 컴포넌트가 하나도 나오지 않은 경우를 빌드 실패로 처리하려면, 관리자가 포털에 GATE_INCOMPLETE_SCAN_ENABLED=true를 설정합니다(선택 게이트 축 참고). 이 축은 이 경우를 불완전한 스캔으로 보므로 게이트가 fail을 반환하고 incomplete_scan_basis는 empty_with_manifests가 됩니다. 매니페스트가 없는 소스는 이 축에서 unknown이라, GATE_INCOMPLETE_SCAN_ON_UNKNOWN=block까지 설정하지 않으면 계속 통과합니다.

과소보고는 이것과 다른 문제입니다. package.json은 있는데 package-lock.json을 커밋하지 않은 프로젝트는 직접 의존성만 나오고 전이 의존성이 빠집니다. 이쪽은 채워졌지만 불완전한 SBOM이고 원인도 다르며, 락파일을 커밋하면 해결됩니다.

스캔이 작업 일부를 건너뛰었을 때​

두 단계는 실패해도 스캔을 멈추지 않습니다. SBOM에 라이선스가 없는 컴포넌트의 라이선스를 조회하는 단계와, scancode로 프로젝트 자체 소스 파일의 라이선스를 탐지하는 단계입니다. 둘 중 하나가 끝나지 못하면 스캔은 성공으로 끝나고, 프로젝트 개요 탭에 무엇을 건너뛰었는지 알리는 경고가 나타납니다. 두 단계가 모두 실행됐거나 관리자가 scancode를 의도적으로 껐다면 아무것도 표시되지 않습니다.

그 밖의 선택 단계도 같은 방식으로 알립니다. 스캔 단계 일부가 끝까지 실행되지 않았습니다라는 경고 하나에 단계별 원인(실패, 시간 초과, 도구 미설치, 입력이 너무 큼)이 나열됩니다. 대상은 빌드 준비, CocoaPods 보완, 런타임 범위 필터, SBOM 문서 메타데이터, 서명, 증명, 탐지된 라이선스 저장, 승인 대기열, 복사된 코드 식별, 소스 보존, 도달 가능성 분석입니다. 미리 보기 기능인 BomLens 스캔 엔진(SCAN_ENGINE, 동적 스캔 실행기 참고)을 쓰면 SBOM 생성, 라이선스·식별자 정규화, 식별자·라이선스 보강의 세 단계가 더 나올 수 있고, 원인도 네 가지가 더 있습니다. 대체 방식을 사용한 경우(예: 매니페스트만 읽어 직접 의존성만 나열하는 방식), 단계가 읽는 데이터가 없었던 경우, 단계가 작업 한도에 도달한 경우, 소스가 의존성을 선언했는데(매니페스트나 lock 파일에 의존성이 있는데) SBOM 생성 결과에 컴포넌트가 하나도 없는 경우입니다. 이 마지막 경우는 기본 엔진에서도 SBOM 생성 단계와 "의존성이 선언됐지만 찾지 못함"이라는 원인으로 표시됩니다. 엔진이 직접 남긴 설명(예: manifests found: build.gradle)이 있으면 그 단계 아래에 일반 텍스트로 함께 보여 줍니다. 정상적으로 실행된 단계는 기록을 남기지 않으므로 정상 스캔에는 이 목록이 나타나지 않습니다. 스캔 기록에는 실제로 실행된 단계마다 시작과 종료 시각도 남습니다(스캔 메타데이터의 stage_timings). 의존성이 그대로여서 이전 SBOM을 재사용하는 경우처럼 알리기만 하고 작업하지 않은 단계는 항목이 없습니다.

경고의미할 일
라이선스 조회를 건너뛴 컴포넌트가 있습니다스캔당 시간 예산을 모두 사용했거나 라이선스 레지스트리가 응답하지 않았습니다. 경고에 조회하지 못한 컴포넌트 수가 나옵니다. 이 컴포넌트는 라이선스 미상으로 표시되므로 라이선스 충돌과 의무사항이 실제보다 적게 나타날 수 있습니다.나중에 다시 스캔합니다. 이미 조회한 컴포넌트는 캐시되어 있어 다음 스캔이 나머지를 채웁니다.
소스 파일의 라이선스 탐지가 실행되지 않았습니다워커에 scancode가 설치되어 있지 않거나, 오류로 종료했거나, 제한 시간을 넘겼거나, 소스 트리의 파일 수가 제한을 넘었습니다. 의존성이 선언한 라이선스는 그대로 표시되지만 프로젝트 자체 파일의 라이선스 헤더는 탐지하지 못했습니다. 그 파일에 카피레프트 라이선스가 있어도 라이선스 게이트에 반영되지 않습니다.관리자에게 워커의 scancode 단계를 확인해 달라고 요청합니다.

평균 소요 시간​

프로젝트 크기소스 스캔컨테이너 스캔
소형 (≤ 50 컴포넌트)3–8분1–3분
중형 (50–500)8–20분2–5분
대형 (≥ 500, 멀티 모듈)20–60분5–10분

소스 스캔의 비용은 cdxgen 탐색이 지배적이며, scancode는 first-party 트리의 크기에 비례하여 시간을 추가합니다. trivy sbom 매칭 단계는 빠릅니다 — Trivy DB는 로컬이며 스캔당 I/O는 컴포넌트 천 개당 1초 미만입니다. 컨테이너 스캔은 워커 캐시에 이미지가 없을 때 풀 시간이 지배적입니다.

전역 스캔 큐​

좌측 사이드바의 Scans는 모든 실행 중·대기 중 스캔의 조직 단위 뷰입니다. 큐는 5개의 상태 탭으로 나뉘어 있습니다: Running, Queued, Succeeded, Failed, All. 프로젝트·팀 단위 필터와 워커별 뷰는 로드맵 항목입니다.

전역 /scans 큐 — Running / Queued / Succeeded / Failed / All 상태 탭과 project · kind · started-at 컬럼의 최근 실행 표

Project 컬럼은 프로젝트의 표시 이름과 상세 페이지로 가는 링크를 보여줍니다. 기본 프로젝트 이름을 해석하지 못한 행(FK 폴백 경로)은 프로젝트 UUID 앞 8자로 폴백합니다. 목록 엔드포인트가 프로젝트 관계를 한 번의 round-trip에 batch-load 하기 때문에 수백 개 행이 있어도 행당 추가 조회 없이 컬럼이 채워집니다.

각 queued 또는 running 행에는 Actions 컬럼에 Cancel scan(스캔 취소) 동작이 있습니다 — 스캔 취소 참고.

스캔 취소​

아직 queued 또는 running 상태인 스캔을 중단할 수 있습니다 — 예: 잘못된 브랜치에 대해 트리거했거나, 큰 레포의 스캔이 예상보다 오래 걸려 워커 슬롯을 비우고 싶을 때.

대상 독자

소유 팀의 developer 이상 권한 보유 팀 멤버. 본인 팀의 스캔만 취소할 수 있으며, 다른 팀에 속한 스캔은 보이지 않고 취소할 수도 없습니다. super-admin은 관리자 스캔 큐에서 모든 스캔을 취소할 수 있습니다.

UI에서​

Cancel scan(스캔 취소) 동작은 두 곳에 나타납니다.

  • 스캔 진행 드로어(스캔을 트리거할 때 열리거나, 실행 중인 스캔을 다시 열 때).
  • 전역 스캔 큐(/scans)의 각 queued 또는 running 행 Actions 컬럼.

취소 방법:

  1. Cancel scan 클릭.
  2. 인라인 확인이 나타납니다. 다시 Cancel scan을 클릭해 확정하거나, Keep running(계속 실행)을 클릭해 닫습니다.
  3. 스캔이 cancelled로 이동하고 진행 바가 멈춥니다.

확정 시 서버에서 일어나는 일:

  • 워커 작업이 중단됩니다(Celery 작업이 SIGTERM으로 revoke 됨).
  • 스캔의 워크스페이스(클론된 소스 트리)가 정리됩니다.
  • 상태가 cancelled로 바뀌고, 완료 타임스탬프와 error_message = "cancelled by user"가 기록됩니다.
  • 이 동작은 감사 로그에 scans update로 기록됩니다.
브라우저를 닫아도 안전

취소는 전적으로 서버에서 처리됩니다. 확정 후에는 패널이나 브라우저 탭을 닫아도 됩니다 — 워커는 멈추고 워크스페이스는 어느 쪽이든 정리됩니다.

이미 종료된 스캔은 취소 불가

이미 종단 상태(succeeded, failed, cancelled)에 도달한 스캔은 취소할 수 없습니다. UI는 "This scan already finished and can no longer be cancelled." 메시지를 표시합니다. 이는 정상이며 — 멈출 것이 남아 있지 않습니다.

API에서​

curl -sS -X POST \
"https://trustedoss.example.com/v1/scans/${SCAN_ID}/cancel" \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" | jq .
응답의미
200 OK스캔이 취소되었습니다. 본문에 status: "cancelled"의 갱신된 스캔 레코드가 포함됩니다.
404 Not Found스캔이 존재하지 않거나, 멤버가 아닌 팀에 속해 있습니다. 다른 팀의 스캔은 존재가 숨겨집니다 — 404가 스캔의 존재를 확인해 주지 않습니다.
409 Conflict스캔이 이미 종단 상태입니다. RFC 7807 본문에 확장 필드 scan_already_cancelled: true가 포함됩니다.

취소 정상 동작 확인​

  1. 드로어와 /scans 큐에서 스캔 상태가 Cancelled(취소됨)로 표시됩니다(Cancelled는 All 탭에 나타납니다).
  2. 진행 바가 더 이상 진행하지 않습니다.
  3. 워커 슬롯이 비어 — 뒤에 있던 queued 스캔이 running을 시작합니다.
  4. 감사 로그에 새 상태의 scans update 이벤트가 기록됩니다.

스캔 진행 보기​

스캔이 큐에 들어가면 스캔 진행 드로어가 위→아래로 쌓인 세 패널로 열립니다.

  1. 단계 목록 (Stage list) — 모든 파이프라인 단계와 현재 상태(pending, running, succeeded, failed, skipped). 활성 단계에는 실시간 스피너가 표시됩니다.
  2. 단계별 로그 패널 (Per-stage log panel) — 현재 선택된 단계의 워커 로그 프레임을 미러링하는 스크롤 가능한 텍스트 패널. 다른 단계 행을 클릭하면 패널이 해당 단계의 프레임으로 전환됩니다. 패널은 하단 근처에 있을 때 최신 프레임으로 자동 스크롤하며, 이전 출력을 읽기 위해 위로 스크롤하면 자동 스크롤이 일시 정지됩니다. 프레임은 단계당 최근 ~500 줄까지 버퍼링되고, 그보다 오래된 줄은 롤오프됩니다.
  3. 액션 푸터 (Action footer) — 실행이 비-종단인 동안 Cancel scan, succeeded / failed / cancelled에 도달하면 close affordance.

이미 완료된 스캔에 대해 드로어를 다시 열면 DB에서 지속된 단계 전이와 최종 로그 프레임을 재생합니다(succeeded 행에서 스피너가 계속 도는 일은 없습니다 — 단계의 종단 상태가 표시됩니다). 실시간 프레임은 아래의 WebSocket으로 전달됩니다.

WebSocket 진행 피드​

UI는 단계·진행률 실시간 갱신을 위해 ws(s)://<host>/ws/scans/{scan_id}를 구독합니다. 네트워크가 끊어지면 지수 백오프로 자동 재연결합니다. 재연결 시 최신 단계를 다시 발행해 UI가 빠르게 동기화됩니다.

커스텀 클라이언트의 메시지 형식:

{
"step": "trivy",
"percent": 90,
"ts": "2026-05-09T13:42:11Z"
}

percent는 0–100 정수입니다. step은 파이프라인 슬러그(bootstrap, fetch, prep, cdxgen, sign, scancode, approvals, scanoss, trivy, finalize)와 2개의 종단 상태(succeeded, failed) 중 하나입니다. scancode 슬러그는 이전의 ort 슬러그를 대체했고, Dependency-Track 제거(ADR-0001) 이후 trivy가 이전의 dt_upload/dt_findings 쌍을 대체했습니다. 프레임은 scan_id를 다시 보내지 않습니다 — 구독자가 URL에서 이미 알고 있기 때문입니다.

이 결과가 나온 근거​

기대한 것보다 컴포넌트가 적게 나왔을 때, 결과만 봐서는 답할 수 없는 질문이 하나 남습니다. 스캐너가 그 패키지를 못 찾은 것인지, 아니면 그 패키지를 선언한 파일을 애초에 못 본 것인지입니다.

이제 끝난 스캔은 자기가 무엇을 앞에 두고 있었는지를 기록하고, 스캔 상세 화면의 "이 결과가 나온 근거"에서 볼 수 있습니다.

소스 스캔은 가져온 트리에 있던 의존성 매니페스트와 락파일을 나열합니다. package.json, go.mod, pom.xml과 각각의 락파일, 그리고 다른 생태계의 같은 역할을 하는 파일들이며 크기도 함께 보여 줍니다. 어떤 매니페스트도 선언하지 않은 컴포넌트는 스캔이 찾을 방법이 없던 컴포넌트이고, 대개 그것으로 답이 끝납니다.

목록은 저장소가 담고 있던 것이지 스캔이 만들어 낸 것이 아닙니다. TRUSCA는 락파일 없이는 전이 의존성을 해석할 수 없는 생태계를 위해 락파일을 생성하는데, 그렇게 생성한 파일은 여기에 일부러 넣지 않습니다. 쓸모 있는 사실은 저장소에 그 파일이 없었다는 쪽이기 때문입니다.

설치된 의존성은 나열하지 않습니다. node_modules, vendor, Pods 같은 디렉터리에는 설치된 패키지마다 매니페스트가 들어 있는데, 그것은 의존성이 자기 자신에 대해 적어 둔 내용이지 이 프로젝트가 선언한 내용이 아닙니다.

업로드한 SBOM에는 트리가 없으므로, 대신 문서가 스스로에 대해 적어 둔 것을 보여 줍니다. 포맷과 명세 버전, 생성한 도구, 문서가 적은 공급자와 작성자, 문서가 담은 시각, 그리고 선언한 컴포넌트 수입니다. 이 값들은 문서의 주장입니다. 생성 도구는 틀린 시각을 적을 수도 있고 존재하지 않는 공급자를 적을 수도 있으며, 그 주장을 결과와 견주어 보는 것이 이 화면의 목적입니다.

SPDX Tag-Value 업로드는 정상적으로 스캔되지만 여기에는 요약이 나오지 않습니다. 요약은 문서를 파싱해서 만드는데 이 용도로는 그 포맷을 파싱하지 않기 때문입니다.

기록된 것이 없으면 이 화면은 나오지 않습니다. 이 기능이 생기기 전에 돈 스캔과, 트리도 업로드 문서도 없는 컨테이너 스캔이 그렇습니다.

API로 읽기​

curl -sS -H "Authorization: Bearer $TOKEN" \
"$PORTAL/v1/scans/$SCAN_ID/provenance"

두 항목 모두 null일 수 있고, null은 "기록하지 않았다"는 뜻입니다. count가 0인 인벤토리와는 다릅니다. 후자는 스캔이 찾아봤고 매니페스트가 없었다는 뜻이며, 측정한 결과이자 대개 찾고 있던 답입니다.

정상 동작 확인​

스캔 완료 후:

  1. 프로젝트 상태가 Succeeded로 전환.
  1. 컴포넌트 수 > 0.
  1. 취약점 수가 표시(프로젝트가 정말 깨끗하면 0일 수도 있음).
  1. Overview 탭의 마지막 스캔 타임스탬프가 "방금"을 반영.

    SELECT count(*) FROM scans s
    JOIN projects p ON p.id = s.project_id
    WHERE p.slug = 'portal-web'
    AND s.status = 'succeeded'
    AND s.completed_at IS NOT NULL;
  1. 감사 로그에 target_table=scans&action=create와 target_table=scans&action=update 이벤트가 기록.

트러블슈팅​

compose 서비스명은 스택마다 다릅니다

아래 명령은 프로덕션 compose(docker-compose.yml)를 대상으로 하며, Celery 서비스명은 worker·beat입니다. dev compose(docker-compose.dev.yml)에서는 같은 서비스가 celery-worker·celery-beat이므로, dev 스택이라면 서비스명과 -f 파일을 바꿔서 실행하십시오.

스캔이 Queued에서 멈춤​

워커가 아직 받지 못했습니다. 워커가 다운되었거나 큐가 포화 상태입니다.

docker-compose -f docker-compose.yml ps worker
docker-compose -f docker-compose.yml logs --tail=200 worker

워커가 unhealthy면 재시작:

docker-compose -f docker-compose.yml restart worker

큐가 포화면 .env의 CELERY_CONCURRENCY를 늘리고 docker-compose up -d worker로 스케일 업. 동시 슬롯당 ~2 GB RAM 필요.

git clone 오류로 스캔 실패​

워커가 저장소에 도달하지 못했습니다. 확인:

  • 레포 URL이 정확한가? (워커에서 테스트: docker-compose exec worker git ls-remote <url>)
  • 사설 레포인가? git_url에 자격증명을 포함하세요 — 프로젝트 → 사설 저장소.
  • 워커가 git 호스트로 outbound HTTPS 가능? 사내 프록시는 .env(HTTP_PROXY, HTTPS_PROXY)에 설정.

스캔은 끝났는데 취약점이 누락​

로컬 Trivy DB가 아직 자리 잡지 않았을 수 있습니다. 워커에서 확인:

docker-compose -f docker-compose.yml exec worker \
ls -lh /var/lib/trivy/db/

db/ 디렉터리가 비어 있거나 없으면 부팅 시 다운로드가 완료되지 않은 것입니다. 첫 다운로드는 1~3분 소요됩니다. 자동 재매칭 beat이 DB 도착 후 기존 스캔의 finding을 재채움합니다 — 재스캔 불필요. 취약점 데이터 — 트러블슈팅 참조.

스캔이 4시간 이상 running 상태로 멈춤​

먼저 드로어나 /scans 큐에서 Cancel scan(스캔 취소)을 시도하세요(스캔 취소 참고). 예를 들어 브로커가 도달 불가하여 스캔이 cancelled로 이동하지 않으면, 강제 취소 + 워커 점검은 온콜 플레이북을 사용하세요: 온콜 런북 → 스캔 멈춤.

"Cancel scan"을 눌러도 아무 일이 없음 / 스캔이 계속 실행됨​

취소 요청은 API에 도달했으나 워커가 제때 멈추지 않았습니다.

  • 브로커(Redis)가 잠깐 도달 불가였다면, 스캔은 여전히 cancelled로 표시되고 워크스페이스는 고아-워크스페이스 정리기와 워커 hard-limit 백스톱이 회수합니다 — 재시도할 필요가 없습니다.
  • 1분 후에도 행이 running으로 표시되면, 워커가 떠 있는지 확인하고(docker-compose -f docker-compose.yml ps worker) 온콜 런북으로 에스컬레이션하세요.

"This scan already finished and can no longer be cancelled"​

페이지가 로드된 시점과 Cancel scan을 클릭한 시점 사이에 스캔이 종단 상태(succeeded / failed / cancelled)에 도달했습니다. 큐를 새로 고침하여 최신 상태를 확인하세요 — 별도 조치는 필요 없습니다.

두 번째 스캔이 시작되지 않음 — Scan 버튼이 비활성​

프로젝트에 이미 ref 없는(queued 또는 running) 스캔이 있고, 버튼은 같은 종류를 하나 더 트리거합니다. 브랜치당 활성 스캔은 하나만 허용되며 ref 없는 트리거는 전부 한 묶음으로 셉니다. 프로젝트 헤더의 진행중 칩(또는 글로벌 큐의 행)에서 기존 실행을 확인하고 완료를 기다리거나 Cancel 한 뒤에 새 스캔을 시작하세요. 이름 있는 브랜치에서 도는 스캔은 버튼을 막지 않습니다. 브랜치당 동시 스캔은 하나 참고.

완료된 스캔 드로어의 스피너가 멈추지 않음​

이전 빌드(P1 #11 이전)에서는 스캔이 이미 succeeded에 도달한 뒤에도 Finalizing 단계의 스피너가 계속 도는 회귀가 있었습니다. 수정은 완료된 실행에 대해 드로어를 열 때 종단 상태에서 스피너를 동결합니다. 여전히 증상이 보이면 프로젝트 페이지를 강제 새로 고침해 캐시된 스캔 레코드를 갱신하세요.

detected(first-party) 라이선스가 누락됨​

Detected 라이선스는 scancode에서 나오며 best-effort 입니다. 다음의 경우 누락될 수 있습니다.

  • 워커 이미지에 scancode가 미설치(스캔은 declared 라이선스만으로 성공 — 비치명적). docker-compose -f docker-compose.yml logs worker | grep scancode_stage_skipped로 확인.
  • first-party 트리가 SCANCODE_MAX_FILES 한도를 초과했거나, scancode가 타임아웃되었거나, 결과가 너무 큼 — 모두 경고를 남기고 declared 전용으로 폴백.
  • 해당 코드가 제외 디렉토리(node_modules, vendor, .git, dist, build, out, target, .venv 등) 안에 위치. 자원 가드로 인해 의도적으로 건너뜁니다 — 컴포넌트·라이선스 → declared vs. detected 참고.

로드맵​

향후 릴리스에서 다룰 항목.

  • 프로젝트 단위 Scan 트리거의 브랜치 오버라이드 필드 — 계획됨. (Source / Container 종류 선택 다이얼로그는 에 출시되었습니다 — 스캔 트리거 → UI에서 참고.)

함께 보기​