프로젝트
프로젝트는 포털이 인지하는 소스 추적 단위입니다. 스캔, 컴포넌트, 취약점, 라이선스 결과, 의무사항, 자동 생성된 NOTICE 파일을 보유합니다. 대부분의 워크플로우는 프로젝트 추가에서 시작합니다.
자체 서비스를 스캔하는 엔지니어와 팀 리드. 로그인 필요. 생성·아카이브는 프로젝트 소속 팀의 developer 이상, 가시성 변경은 team_admin 권한이 필요합니다.
프로젝트 구성 요소
| 필드 | 설명 |
|---|---|
| 이름 (Name) | 표시용 라벨(자유 텍스트). 유일할 필요는 없습니다 — 같은 팀의 두 프로젝트가 이름을 공유할 수 있고, 유일 키는 슬러그입니다. |
| 슬러그 (Slug) | URL-safe 식별자([a-z0-9-]+). 팀 내에서 유일하며, 중복 슬러그는 409로 거부됩니다. |
| 설명 (Description) | 선택. 프로젝트 목록과 Overview 탭에 노출되는 자유 텍스트 요약. |
| Git URL | 스캔 파이프라인이 클론할 git URL. HTTPS 지원. 사설 저장소는 URL에 자격증명을 포함해야 합니다 — 사설 저장소를 보세요. |
| 기본 브랜치 (Default branch) | 스캔 파이프라인이 체크아웃할 브랜치(기본값 main). 생성 후 Project Settings에서 수정. |
| 가시성 (Visibility) | team (v0.10.0 시점에서 허용되는 유일한 값 — 소속 팀 멤버만 조회). 생성 시 자동 설정되며 PATCH로만 변경 가능. |
| 소속 팀 (Owning team) | 프로젝트가 속한 팀. 글로벌 바에서 선택한 팀이 기본값이며, 여러 팀에 속해 있으면 생성 폼에서 바꿀 수 있습니다. |
| 소유 조직 | 선택 항목. 본부, 부서, 팀 등 조직에서 부르는 단위를 자유롭게 적습니다. 프로젝트 목록을 좁히는 데 씁니다. |
| 담당 | 선택 항목. 이름, 팀 별칭, 주소 등 물어볼 곳을 자유롭게 적습니다. |
| 배포 방식 | 선택 항목. 사용자에게 전달되는 방식입니다. 배포 방식 참고. |
조직 속성
앞의 두 항목은 일부러 자유 입력입니다. 본부, 원가 단위, 스쿼드는 조직마다 같은 자리를 다르게 부르고, 값을 고정해 두면 대부분의 조직에 맞지 않습니다. 입력한 값은 앞뒤 공백을 지워 저장하므로 Platform과 Platform 이 화면에서 같아 보이면서 서로 다른 두 묶음이 되는 일은 없습니다.
비워 두면 아무것도 달라지지 않습니다. 속성이 없는 프로젝트는 지금처럼 필터 없는 목록에 그대로 나옵니다. 채워 두면 목록을 소유 조직 단위로 좁힐 수 있습니다.
배포 방식
앞의 두 항목과 달리 이 값은 고정 목록입니다. 조직이 붙이는 이름표가 아니라 어떤 라이선스 의무가 걸리는지를 정하는 값이기 때문입니다. 네트워크 서비스로 제공하는 것과 바이너리로 배포하는 것은 AGPL 13조가 적용되는지를 가릅니다.
| 값 | 뜻 |
|---|---|
| 내부 사용 | 조직 안에서만 쓰고 외부에 전달하지 않습니다. |
| 네트워크 서비스 | 네트워크를 통해 사용자에게 제공합니다. |
| 바이너리 | 컴파일된 산출물로 배포합니다. |
| 소스 | 소스 형태로 배포합니다. |
| 기기 내장 | 기기 안에 넣어 배포합니다. |
잘못 적은 값은 저장되지 않고 거절됩니다. 오타가 아무 데도 걸리지 않는 여섯 번째 값이 되는 일을 막기 위해서입니다.
정하지 않음은 채워야 할 빈칸이 아니라 유효한 답입니다. 배포 방식을 밝히지 않은 프로젝트는 모든 방식으로 배포된다고 보고 판단하 며, 이는 가장 보수적인 해석이자 포털이 지금도 전제하고 있는 값입니다. 이번 버전에서 이 값을 채운다고 판정이 달라지지는 않습니다. 사실을 기록하고 필터를 움직입니다.
프로젝트 목록의 [배포 방식] 필터에는 저장된 값이 아닌 항목이 하나 있습니다. [정하지 않음]은 아직 채우지 않은 프로젝트를 찾습니다. 빈 컬럼은 등호로 찾을 수 없어 별도 항목이 필요하고, 포트폴리오를 정리하는 동안 가장 자주 쓰게 되는 항목입니다.
프로젝트 추가 — UI
사이드바의 Projects 항목은 프로젝트 목록을 보여줍니다 — 소속 팀으로 닿는 모든 프로젝트, 상태 배지, severity 카운트, 인라인 Scan 액션, 그리고 n scans · m releases · last scan <상대 시간> 형식의 프로젝트별 컴팩트 메타 행:

메타 행 집계:
- scans — 프로젝트가 누적 실행한 스캔 총 개수(모든 상태, 아카이브된 실행 포함).
- releases — 프로젝트가 누적한 릴리스 스냅샷 수(릴리스 참고).
- last scan — 마지막 스캔이 종단 상태에 도달한 이후의 상대 시간. 첫 스캔이 완료되기 전까지는
—.
목록 엔드포인트가 세 필드를 한 번의 쿼리로 서버 측에서 집계하므로, 수백 개 프로젝트 포트폴리오에서도 행 렌더링 비용이 낮습니다.
프로젝트 찾기
툴바 검색창은 프로젝트의 이름, 슬러그, git URL을 함께 찾습니다. 화면에 보이는 목록만이 아니라 포트폴리오 전체를 서버에서 검색합니다. 세 값 중 어느 것이든 일부만 입력하면 되고, 입력은 디바운스를 거쳐 URL(?search=)에 반영되므로 걸러진 목록을 새로 고침하거나 링크로 공유해도 그대로 유지됩니다.
이 화면은 한 번에 서버 페이지 하나를 보여 줍니다. 조건에 맞는 프로젝트가 한 페이지보다 많으면 목록 위에 몇 건 중 몇 건을 표시했는지 안내가 나옵니다. 검색어를 좁히면 나머지를 확인할 수 있습니다.
-
로그인.
-
사이드바의 Projects 클릭.
-
우측 상단 New project 클릭.
-
폼 작성:
- 이름 (필수)
- 설명 (선택)
- Git URL (소스 스캔에 필수)
-
Create 클릭.

프로젝트의 Overview 탭으로 이동합니다. 여기서 첫 스캔을 실행할 수 있습니다 — 스캔 참고.

기본 브랜치(main), 가시성(team), 소속 팀(글로벌 바에서 선택한 팀)은 서버에서 자동 설정되며 Project Settings에서 확인 가능합니다.
프로젝트 상세 탭 구성
상세 페이지의 탭은 왼쪽→오른쪽 순서로 다음과 같습니다.
| 탭 | 보여주는 내용 |
|---|---|
| Overview | 리스크 두 축(Security + License), 빌드 게이트 판정, Project info 카드(Git URL, default branch, 소속 팀, 생성 시각, 마지막 스캔 시각), 최근 스캔. |
| Releases | 종단 스캔별 프로젝트 스냅샷 — 스냅샷 목록, "View snapshot" 핀 액션, 릴리스 간 diff 진입점. Releases 탭 참고. |
| Components | 스캔이 발견한 모든 컴포넌트. 컴포넌트·라이선스 참고. |
| Vulnerabilities | 열린/트리아지된 CVE 결과. 취약점 참고. |
| Licenses | 같은 데이터를 SPDX 식별자·티어 기준으로 본 뷰. |
| Obligations | 컴포넌트별 의무사항 + NOTICE 파일 생성. 컴포넌트·라이선스 → 의무사항 참고. |
| SBOM | CycloneDX / SPDX 익스포트, byte-stable. SBOM 참고. |
| Reports | NOTICE / SBOM / Vuln-PDF / VEX 생성 카드 + 프로젝트의 통합 다운로드·익스포트 이력. Reports 탭 참고. |
| Source | 최근 성공한 스캔에서 fetch 된 first-party 소스 트리. 파일 단위 라이선스 결과 하이라이팅. 탭 재정렬로 Reports와 Remediation 사이로 이동. |
| Remediation | 최근 스캔 기준 컴포넌트별 업그레이드 권고. 선택적 npm 자동 PR 플로 포함. |
| Settings | 프로젝트 메타데이터, 아카이브 액션, CI 연동 헬퍼. |
Source 탭은 이전에 Licenses 바로 뒤에 있었으나, 데이터 출력 클러스터(SBOM / Reports / Source)가 연속되도록 Reports 오른쪽으로 이동했습니다. 북마크와 ?tab=source 딥링크는 슬러그가 동일하므로 계속 동작합니다.
프로젝트 추가 — API
curl -sS -X POST https://trustedoss.example.com/v1/projects \
-H "Authorization: Bearer ${TRUSTEDOSS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"team_id": "8f0c1e2a-...팀 UUID...",
"name": "checkout-service",
"slug": "checkout-service",
"description": "Storefront checkout service",
"git_url": "https://github.com/acme/checkout-service.git"
}' | jq .
응답에 프로젝트 UUID가 포함됩니다 — GitHub Action의 project-id 입력값과 GitLab CI 변수에 사용하므로 보관하세요.
필수 필드: team_id, name, slug. 선택: description, git_url, default_branch, visibility. 스키마는 알 수 없는 필드를 거부합니다(extra="forbid"). team_id나 slug를 빠뜨리면 422(missing: body.team_id)가 반환됩니다.
team_id 찾기: UI의 프로젝트 생성 폼에는 팀 선택기가 있어 UUID를 직접 입력할 필요가 없습니다. API에서는 로그인한 모든 사용자가 GET /auth/me로 자기 팀을 읽습니다(auth 라우터는 /v1이 아니라 /auth에 마운트됩니다) — 응답의 memberships[] 배열 각 항목에 team_id·team_name과 해당 팀에서의 role이 담깁니다. (GET /v1/admin/teams도 팀 목록을 주지만 super-admin 전용이라 team_admin이 호출하면 404입니다.) 이번 릴리스에서 team_id는 세션에서 도출되지 않으며 요청 본문에 담아야 합니다.
가시성
team(v0.10.0 기본값이자 유일하게 허용되는 값) — 소속 팀 멤버만 프로젝트·스캔·결과를 볼 수 있습니다.
가시성은 생성 시 자동으로 설정됩니다. PATCH는 현재 team 외의 값을 거부합니다. 모든 PATCH 호출에서 감사 로그가 행위자를 기 록합니다.
organization(조직 전체 읽기) 가용 시점은 로드맵 참고.
아카이브
- 아카이브 — 프로젝트와 그 이력·스캔·결과는 유지하되 기본 목록에서 숨기고 새 스캔을 막습니다. 서비스가 종료되었지만 컴플라이언스 추적이 필요한 경우에 유용합니다.
DELETE /v1/projects/{id}는 soft-delete(아카이브)를 수행합니다. 영구 삭제 동작은 현재 노출되지 않으며, 감사 로그 항목은 어떤 경우에도 유지됩니다.
아카이브 동작은 Project Settings → Archive에 있으며, 사고 방지를 위해 인라인 확인 스트립을 사용합니다.
사설 저장소
소스 스캔은 워커 컨테이너 안에서 저장소를 클론합니다. 현재 릴리스에서 지원되는 인증 옵션:
- HTTPS + Personal Access Token — URL을
https://<token>@github.com/acme/checkout-service.git형태로 설정. 토큰은git_url의 일부로 저장되며, 읽기 엔드포인트가 평문으로 반환하지 않습니다.
현재 지원되는 자격증명 모델은 git URL에 PAT를 임베드한 HTTPS
(https://<token>@github.com/acme/payment-service.git) 뿐입니다.
PAT는 프로젝트 행에 영구 저장됩니다(읽기 엔드포인트가 평문 PAT를
절대 반환하지 않으며, git_url은 감사 로그에서 마스킹됩니다).
함의:
- 유출된 DB 스냅샷은 임베드된 모든 PAT를 함께 유출합니다. read-only scope의 단기 PAT를 사용하세요.
- SSH key와 GitHub-App 설치는 로드맵 항목입니다; 그때까지 적극적으로 회전하세요.
SSH 배포 키는 로드맵을 보세요.
거버넌스 밴드
프로젝트 헤더와 탭 사이에 다섯 개 타일이 있습니다. 어느 탭에서 작업하든 프로젝트의 현재 상태를 이동 없이 읽게 하려는 것입니다.
| 타일 | 내용 |
|---|---|
| 리스크 스코어 | Overview 탭의 종합 점수와 같은 계산 — 보안 축과 라이선스 축 중 나쁜 쪽. |
| 빌드 게이트 | 지금 CI가 내릴 판정: 통과, 차단(차단 건수와 함께, Vulnerabilities 탭으로 이동), 스캔한 적 없음. |
| KEV 기한 | CISA 조치 기한과 대조한 알려진 악용 finding. 기한 초과와 일주일 내로 나뉩니다. |
| 대기 중인 승인 | 이 프로젝트에서 아직 pending·under_review 인 컴포넌트 승인. |
| critical 추이 | 최근 성공한 스캔 몇 회의 critical 수와 그 변화량. 스캔이 두 번 미만이면 선을 그리지 않습니다. 점 하나는 추세가 아닙니다. |
모든 숫자는 제품의 다른 곳에서 그 값을 소유한 서비스가 만듭니다. 밴드가 바로 아래 탭과 다른 값을 말할 수 없다는 뜻입니다. 두 상태는 의도적으로 구분합니다. 성공한 스캔이 없는 프로젝트는 통과가 아니라 "스캔한 적 없음"으로 표시하고, 밴드를 불러오지 못하면 0을 그리는 대신 실패했다고 밝힙니다. 없는 답이지 안심시키는 답이 아닙니다.
리스크 점수
Overview 탭은 이제 하나의 합산 점수 대신 두 개의 리스크 축을 게이지에 표시합니다. 두 가지 실패 모드를 독립적으로 읽을 수 있도록 분리했습니다.
- Security risk (보안 리스크) — 프로젝트의 열린 취약점 구성에 따라 결정됩니다. 밴드(Critical / High / Medium / Low / Info)는 가장 심각한 열린 finding이 정합니다. 밴드 내 점수는
n / (n + 4)로 스케일(비포화 — finding이 더 많아진다고 밴드를 한 단계 올리지 못함). - License risk (라이선스 리스크) — 프로젝트의 라이선스 티어 구성에 따라 결정됩니다. Forbidden 라이선스가 밴드를 지배. Conditional 행은 밴드 내 점수를 올리지만 단독으로
Critical로 승격하지 않습니다(이전의 "Conditional 컴포넌트 하나라도 있으면 Risk 100" 동작은 W1에서 제거).
기존 단일 risk_score 필드는 빌드 게이트와 CI 연동의 하위 호환을 위해 API에서 max(security_axis, license_axis)로 계속 노출됩니다. UI는 두 축 분해를 사용합니다.
두 축 모두 매 스캔 후, 그리고 매 CVE 재탐지 후 갱신됩니다. 절대적 SLA가 아닌 포트폴리오 내 상대 지표로 읽으세요 — 프로젝트로 들어가면 축별 분해도가 보입니다.
W1 이전에 찍은 스크린샷은 단일 게이지("Risk")를 보여줍니다. 두 축 카드가 이를 대체합니다. 옛 단일 점수와 새 두 축 중 어느 쪽도 단순 비교할 수 없으므로, 업그레이드 이후 포트폴리오 기준으로 재설정하세요.
빌드 게이트 판정 (Overview 탭)
Overview 탭은 리스크 게이지 옆에 Build gate(빌드 게이트) 카드를 표시합니다. CI 연동이 계산하는 것과 동일한 빌드 차단 판정을 노출하므로 — CI 로그를 열지 않고도 포털에서 게이트 결과를 확인할 수 있습니다. 이 카드는 프로젝트의 주 브랜치를 기준으로 평가합니다. 프로젝트의 기본 브랜치와 일치하는 브랜치의 가장 최근 성공한 스캔을 쓰고, 기본 브랜치가 비어 있으면 main을 가정하며, 둘 다 맞지 않으면 브랜치를 가리지 않고 가장 최근 성공한 스캔으로 물러납니다. 여러 브랜치를 스캔하는 프로젝트에서 판정이 마지막에 끝난 브랜치를 따라 흔들리지 않게 하려는 것입니다.
API에서는 CI 작업이 자기 브랜치를 지정하는 편이 좋습니다. GET /v1/projects/{id}/gate-result?ref=<브랜치>는 그 브랜치만 평가하므로, 릴리스 브랜치의 critical CVE가 main 파이프라인을 막지 못합니다. refs/heads/main이나 $GITHUB_REF 같은 전체 형식은 스캔 트리거와 같은 방식으로 정규화합니다. 성공한 스캔이 없는 브랜치는 다른 브랜치의 결과를 빌려 오지 않고 신호 없음으로 통과를 반환합니다.
빌드 게이트(또는 정책 게이트)는 빌드에 critical CVE 나 금지 등급 라이선스가 있으면 0이 아닌 종료 코드를 반환하는 CI 차단 메커니즘입니다. 개념과 파이프라인 연동 방법은 GitHub Actions → 출력과 용어집 → 빌드 게이트에 있으며, 이 카드는 동일한 판정을 UI에서 읽기 전용으로 보여 줍니다.
카드가 표시하는 항목:
| 요소 | 의미 |
|---|---|
| Pass / Fail 배지 | 최근 성공한 스캔에 critical CVE와 금지 라이선스가 없으면 Pass(녹색, 방패 체크), 그렇지 않으면 Fail(빨강, 방패 X). |
| 사유 (Reason) | Fail 일 때 게이트를 트립시킨 원인을 한 줄로 설명. |
| Critical CVEs | 평가된 스캔의 미해결 critical 심각도 결과 수. 미해결 = 상태가 not_affected, fixed, false_positive가 아닌 경우. |
| Forbidden licenses | 금지 등급 라이선스를 하나 이상 가진 고유 컴포넌트 수. |
EPSS ≥ {threshold} | 운영자가 EPSS 게이트를 활성화한 경우(포털에 GATE_EPSS_THRESHOLD 설정)에만 표시. EPSS 점수가 임계값 이상인 미해결 결과 수. EPSS 게이트가 비활성(기본값)이면 숨겨집니다. EPSS로 빌드 게이팅 참고. |
성공한 스캔이 한 번도 없는 프로젝트는 녹색 pass 대신 중립적인 No scan yet 상태를 표시합니다 — 평가할 대상이 없기 때문입니다. 스캔을 실행하면(스캔 참고) 카드가 채워집니다.
CVE — Common Vulnerabilities and Exposures, EPSS — Exploit Prediction Scoring System. 둘 다 용어집을 보세요.
이 카드는 읽기 전용입니다 — 판정을 반영하되 정책을 변경하지는 않습니다. 임계값(심각도 하한, EPSS)은 운영자·CI 측 설정입니다. GitHub Actions와 GATE_EPSS_THRESHOLD를 보세요.
프로젝트 정보 (Overview 탭)
Overview 탭의 리스크 게이지 옆에는 Project info 카드가 있습니다. 프로젝트의 식별 메타데이터를 한 블록에 모아 읽기 전용으로 보여줘서, 간단한 조회를 위해 Settings까지 들어갈 필요가 없도록 합니다.
| 필드 | 출처 | 비고 |
|---|---|---|
| Git URL | 프로젝트 git_url. | 클릭해서 복사. PAT가 임베드된 경우 토큰 세그먼트는 ***로 마스킹되어 표시됩니다. read 엔드포인트는 원본 값을 노출하지 않습니다. |
| Default branch | 프로젝트 default_branch. | Settings에서 편집 가능. |
| Owning team | 프로젝트 team. | 뷰어가 super_admin 이면 /admin/teams/{id} 관리자 뷰로 링크. |
| Created | 프로젝트 created_at. | 호버 시 절대 시각, 표면에는 상대 시간. |
| Last scan | 프로젝트 last_scan_at(프로젝트 목록 메타 행과 동일 값). | 첫 스캔이 종단 상태에 도달하기 전까지는 —. |
카드의 데이터는 프로젝트 목록 메타 행이 노출하는 값과 동일하며, 상세 페이지 상단에 한 번 더 노출해 사용자가 레포 URL을 확인하기 위해 목록으로 돌아가지 않아도 됩니다.
Releases 탭
네 가지 말이 서로 다른 것을 가리킵니다. 프로젝트는 관리 대상입니다. 저장소 하나 또는 이미지 하나입니다. 스캔은 그 대상을 한 번 분석한 실행이며 queued·running·succeeded·failed·cancelled 상태를 가집니다. 릴리스는 성공한 스캔을 시점 고정 기록으로 읽은 것입니다. 성공한 스캔 하나가 릴리스 한 건이므로, 실제로 출시한 빌드보다 릴리스 탭의 행이 많습니다. 버전(v1.2.3)은 스캔을 걸 때 선택으로 붙이는 라벨이고 출시할 단위의 이름입니다. 별개의 개체가 아니라 릴리스의 속성이며, 라벨이 붙은 릴리스는 자동 정리에서 제외됩니다. 브랜치는 스캔이 겨눈 개발 갈래로, 버전과 달리 계속 움직이며 같은 브랜치의 새 스캔이 이전 것을 대체합니다.
스캔이 succeeded 종단 상태에 도달할 때마다 포털은 release snapshot을 기록합니다 — 컴포넌트 목록, 라이선스 티어 혼합, 취약점 결과, 스캔 id를 포함한 시점 고정 immutable 뷰. 스캔 완료 시각으로 태깅됩니다. 프로젝트의 Releases 탭은 이 스냅샷들을 최신순으로 나열합니다.
| 컬럼 | 표시 내용 |
|---|---|
| Snapshot | 스캔 완료 시각 (yyyy-mm-dd HH:MM) + 상대 시간. |
| Branch | 스캔이 대상으로 삼은 정규화된 git ref(main, pr-12). 트리거에 ref가 없었으면 지정 없음. |
| Scan kind | source 또는 container. |
| Severity counts | 스냅샷 시점의 Critical / High / Medium / Low. |
| License mix | 스냅샷 시점의 Allowed / Conditional / Forbidden 바. |
| Actions | View components(해당 스캔에 핀된 Components 탭으로 이동), View snapshot(?scan=<id>를 핀하고 Overview를 스냅샷 데이터로 reload). |
릴리스 행을 직접 클릭하면 핀된 스냅샷 상태로 Components 탭에 도달합니다. 핀은 URL의 ?scan=<id> 파라미터로 전파되어 reload 후에도 살아남고 동료에게 공유할 수 있습니다 — 프로젝트의 모든 탭(Components, Vulnerabilities, Licenses, …)이 핀을 풀기 전까지 핀된 스냅샷을 데이터 앵커로 읽습니다. 핀을 빵부스러기에서 해제하면 최근 성공한 스캔이 모든 곳의 데이터 앵커로 복귀합니다.
다운로드도 핀을 따릅니다. 릴리스를 핀한 상태에서 NOTICE를 내려받으면 최신 스캔이 아니라 그 릴리스의 라이선스로 문서를 구성합니다. 이전 릴리스와 함께 출하한 고지 파일을 이후 스캔이 성공한 뒤에도 그대로 받을 수 있다는 뜻입니다. API로는 GET /v1/projects/{id}/notice?scan_id=<id>이며, 스냅샷을 앵커로 삼는 다른 조회와 마찬가지로 이 프로젝트의 성공한 스캔이 아닌 id를 주면 404를 반환합니다.
버전 이름으로 찾기
API에서는 GET /v1/projects/{id}/releases?release=4.0이 그 버전 라벨을 단 스냅샷을 돌려줍니다. 라벨이 붙은 스캔은 같은 라벨을 쓰던 이전 스캔을 밀어내므로 답은 많아야 한 행입니다. 그 행의 scan_id를 읽어 상세 엔드포인트에 ?scan_id=로 고정하면 해당 릴리스의 컴포넌트·라이선스·취약점·SBOM·NOTICE를 읽을 수 있습니다. 없는 라벨은 404가 아니라 빈 목록을 돌려줍니다. 아직 9.9를 출시하지 않은 것은 정상적인 답이고, 404로 답하면 "그런 프로젝트가 없다"와 구분되지 않기 때문입니다.
검색이 아니라 정확히 일치하는 값을 찾습니다. 4.0으로는 4.0.1을 찾지 못합니다. 앞뒤 공백은 양쪽 모두 제거하고 비교합니다.
상세 조회도 라벨을 직접 받으므로 두 단계를 거칠 일은 드뭅니다. GET /v1/projects/{id}/notice?release=4.0, .../overview?release=4.0, .../vulnerabilities?release=4.0 등이 그 버전 기준으로 응답합니다. 라벨은 현재 그것을 가진 스냅샷을 따라가므로 티켓에 적어 둘 수 있는 영구 주소가 됩니다. 변하지 않는 스캔 하나를 고정하려면 ?scan_id=를 쓰고, 둘 다 주면 scan_id가 이깁니다. 없는 라벨은 404를 반환하는데 쓸 수 없는 scan_id와 같은 응답이라, 어느 쪽도 상대의 존재 여부를 알려 주지 않습니다.
브랜치로 거르기
GET /v1/projects/{id}/releases?ref=main과 GET /v1/projects/{id}/scans?ref=main 모두 브랜치 하나로 좁힙니다. 짧은 브랜치명과 전체 형식(refs/heads/main, refs/pull/12/merge) 둘 다 받습니다. 스캔 트리거와 같은 방식으로 정규화하므로 어느 쪽으로 적어도 그 브랜치의 행에 닿습니다.
두 목록은 답하는 질문이 다릅니다. Releases는 성공한 스캔만 담으므로 그 브랜치에서 쓸 수 있는 이력이고, 스캔 목록은 모든 상태를 담으므로 실패한 시도가 남아 있는 곳입니다.
브랜치와 버전은 별개의 축이며 서로를 대신하지 않습니다. 브랜치는 계속 움직여서 새 스캔이 이전 것을 대체하지만, 버전은 출시한 단위의 이름이고 같은 버전을 주장하는 다른 스캔에만 밀려납니다.
Releases 탭 툴바의 Compare 버튼(릴리스가 둘 이상일 때 활성화)으로 진입하는 Compare 화면은 두 스냅샷 id — base와 target — 를 받아 그 사이에 무엇이 변했는지 보여줍니다: 추가·제거·버전 변경된 컴포넌트, 새로 발생·해소된 취약점, 그리고 리스크 점수·severity별·라이선스 티어·빌드 게이트 델타. swap 컨트롤로 base와 target을 뒤바꿉니다. 버튼은 최신 릴리스(target)를 그 직전 릴리스(base)와 비교하도록 기본 설정되며, 두 id는 URL(?base=&target=)에 담겨 특정 비교를 공유할 수 있습니다. "릴리스 X와 Y 사이에 무엇이 변했는가"의 정식 diff 뷰입니다.
Reports 탭
Reports 탭은 프로젝트의 다운로드 가능한 산출물을 통합하는 단일 랜딩 페이지입니다.
- NOTICE, SBOM, Vulnerability PDF, VEX 네 가지 generate card. 각 카드는 딥링크입니다 — 카드의 액션 버튼을 클릭하면 실제 포맷 선택기와 다운로드 버튼이 있는 해당 도메인 탭(Obligations / SBOM / Vulnerabilities / Vulnerabilities)으로 전환됩니다. 현재 핀된
?scan=스냅샷 컨텍스트는 점프 전후로 보존됩니다. - 우측 export history (이력) 표의 컬럼: When (상대 + 절대 시각), Who (행위자 이메일 — 감사용으로 익명화 보존된 행은
—), Type (NOTICE / SBOM / Vulnerability PDF / VEX 중 하나), Format (정확한 포맷 문자열 —cyclonedx-json,spdx-tv,openvex등), Scan (스캔 id 앞 8자), Size (humanize — 렌더러가 크기를 기록하지 않은 경우—). - 툴바의 Type 멀티셀렉트 필터와 Prev / Next 페이지네이션. 둘 다 URL에
?rpt_type=<type>/?rpt_page=<n>으로 미러링되므로 필터된 뷰는 reload 안전·링크 공유 가능합니다.
권한은 SBOM·PDF 익스포트와 동일한 자세입니다 — 팀에 developer 이상 멤버는 이력을 읽을 수 있고, 비멤버는 404(existence-hide)를 받습니다. 이력 표는 append-only 입니다 — 편집·삭제·재실행 액션이 없습니다. 산출물을 다시 다운로드하려면 해당 generate card를 클릭해 도메인 탭에서 재익스포트하세요.
Generate card는 Reports 탭 안에 생성 다이얼로그를 띄우지 않고 항상 도메인 탭(Obligations / SBOM / Vulnerabilities)으로 딥링크합니다. 의도는 포맷별로 정식 UX를 하나만 유지하고 두 다운로드 표면이 어긋나지 않도록 하는 것입니다. 탭의 부가가치는 모든 포맷에 걸친 history 뷰를 한 곳에서 본다는 점입니다.
정상 동작 확인
프로젝트 생성 후:
- Projects에 프로젝트가 Idle(스캔 없음) 상태로 표시됩니다.
-
Overview 탭은 컴포넌트·취약점 모두 0을 보여줍니다.
-- 컴포넌트·취약점은 스캔 단위 로 귀속되므로,-- 스캔이 없는 프로젝트는 둘 다 0이어야 합니다SELECT count(*) FROM projects pWHERE p.name = 'docs-uat-new-project'AND NOT EXISTS (SELECT 1 FROM scans s WHERE s.project_id = p.id);
-
감사 로그(
/admin/audit, super-admin 전용)에 본인의user_id로target_table=projects&action=create가 기록됩니다.SELECT count(*) FROM audit_logsWHERE target_table = 'projects'AND action = 'create'AND actor_user_id IS NOT NULLAND created_at > now() - interval '1 hour';
트러블슈팅
"저장소 URL이 유효하지 않음"
마법사는 URL이 http:// 또는 https://로 시작해야 합니다(HTTPS 강력 권장). v0.10.0 시점에서 git@…·ssh://… URL은 폼이 받지 않습니다. HTTPS 클론 URL을 사용하세요. 포털은 도달 가능성을 검증하지 않으며 그것은 스캔 시점에 일어납니다. 폼 제출에서 거부되면 오타를 다시 확인하세요.
"이미 사용 중인 프로젝트 슬러그"
이름이 아니라 슬러그가 팀별로 유일합니다 — 같은 팀의 다른 프로젝트가 그 슬러그를 이미 쓰면 409가 발생합니다. 이름은 중복될 수 있습니다. 다른 슬러그를 고르세요(예: checkout-service-legacy).
프로젝트 생성 시 Forbidden
소속 팀에서 본인의 역할이 developer 미만입니다. 팀 admin에게 적절한 역할로 초대를 요청하세요 — 사용자 및 팀.
로드맵
매뉴얼이 이전에 약속했으나 v0.10.0에 포함되지 않은 항목 — 향후 릴리스에서 다룹니다.
- 포트폴리오 그룹핑용 프로젝트 태그 — 예정.
organization(조직 전체) 가시성 — 예정.- Project Settings에서의 SSH 배포 키 생성 — 예정.
- 이름 입력 확인을 동반한 프로젝트 영구 삭제 — 설계 중. 현재는 soft-delete(아카이브)만 가능.
- 생성 마법사의 SSH(
git@…,ssh://…) URL 수용 — 예정.