6.1 KiB
VD-09: 공급망 inventory, license, vulnerability, SBOM과 provenance
- 상태: Accepted
- 결정일: 2026-07-26
- 적용 브랜치:
feature-frontend-supply-chain-verification - 재검토: 조직 vulnerability scanner, signing/attestation provider와 dependency exception 승인 체계가 선정될 때
배경
기존 release script는 package.json의 직접 dependency 이름과 버전, lockfile
전체 digest, dist checksum만 기록했다. 전이 dependency, 패키지별 integrity와
license, 실제 baseline diff가 없었고 highRiskUnreviewed: []는 계산 결과가 아닌
고정값이었다. secret scan도 src와 dist만 검사해 config, scripts, test와
generated release metadata를 놓쳤다.
반면 저장소에는 조직이 선택한 vulnerability source, severity exception 승인자, signing identity와 attestation 저장소가 없다. 외부 provider가 없는 상태를 빈 finding과 서명 성공으로 표현하면 local 검증과 release promotion을 혼동한다.
결정
pnpm-lock.yaml의 모든packagesrow와pnpm list --depth Infinity의 실제 graph를 결합해 직접/전이, production/development, required/platform-optional, version, SHA-512 SRI, license와 dependency edge를 기록한다.- inventory row 수는 lockfile package row 수와 같아야 한다. 누락된 전이
dependency, malformed integrity와 non-optional
NOASSERTION은 local gate를 실패시킨다. - license는 설치된 package manifest에서 읽고 closed allow/deny policy로
검사한다. 현재 OS에 materialize되지 않은 platform optional만
NOASSERTION과 그 이유를 명시적으로 허용한다. - 승인 dependency baseline과 approval digest를 보존하고 현재 lock inventory와 actual add/remove/change/upgrade diff를 계산한다. 새 direct production dependency는 owner와 서로 다른 reviewer, reason과 rollback evidence가 필요하다.
- inventory를 CycloneDX 1.6 SBOM으로 투영한다. component 수, lockfile digest, SRI, license와 dependency edge가 inventory와 일치해야 한다.
- local in-toto/SLSA 형태 provenance statement는 source set, lockfile, SBOM과
distdigest를 연결하되LOCAL_UNSIGNED로 표시한다. 이 문서는 외부 provenance를 대신할 수 없다. immutable_build는 rawpnpm-lock.yaml,dist, build/module inventory와 모든 local verification evidence를 한 번만 archive한다. Candidate manifest는 raw lock bytes SHA-256, dependency inventory lock digest와 manifestlockfileSha256의 exact 일치를 요구한다.- 두 provider job은 동일 archive를 각각 받아 외부 command를 실행한다.
Vulnerability report는 raw lock digest와
distSha256, provenance attestation은{name: "dist", digest.sha256}를 포함한다. 두 문서 모두 strict schema와 별도 trust path/key ID로 선택한 실제 Ed25519 public key 서명을 통과해야 한다. - provider report나 trusted key가 없으면 local
inventory/license/SBOM/coherence는
PASS, promotion은FAIL_UNVERIFIED다. 저장소 generator나 fixture가 production용 빈 finding 또는 signed PASS를 만들지 않는다. - secret scan은 source, scripts, tests, tracked config/schema, public,
dist와 generated release metadata를 검사한다. allowlist는 test path에만 허용하며 owner, reason과 expiry가 필요하다. 발견한 secret 원문은 artifact에 쓰지 않고 rule, path, line과 fingerprint만 남긴다. SOURCE_DATE_EPOCH를 지원하고 supply-chain timestamp도 build manifest의 동일 epoch에 결합한다. 같은 source/lock/config의 production build를 두 번 실행해 전체 dist digest 일치를 검증한 뒤 일반 build를 복원한다.
실행 경계와 증적
package.json + frozen pnpm-lock.yaml + installed graph
-> deterministic dependency inventory
-> license policy + approved actual baseline diff
-> CycloneDX SBOM
source/config/lock + production dist
-> local provenance statement
-> immutable archive + candidate manifest + distSha256
-> external vulnerability provider + external provenance provider
-> read-only local revalidation + signature/digest verification
-> promotion PASS | FAIL_UNVERIFIED
- policy:
config/security/ - generator:
scripts/generate-supply-chain.ts - coherence:
scripts/verify-supply-chain-artifacts.ts - secret scan:
scripts/security-scan.ts - reproducibility:
scripts/verify-reproducible-build.ts - inventory:
artifacts/release/dependency-inventory.json - SBOM/provenance:
artifacts/release/sbom.cdx.json,artifacts/release/provenance.json - local/promotion status:
artifacts/security/supply-chain-verification.json
검증
- 현재 lockfile의 561개 package row와 inventory row가 양방향 일치한다.
- ordering-only digest, removal, integrity tamper, baseline tamper, high-risk self approval, denied license, critical vulnerability와 만료 exception, provider/digest 오류, SBOM/provenance 불일치 fixture를 검사한다.
- isolated temporary candidate/PEM/report fixture는 실제 environment path wiring을
통해 valid immutable 입력만 promotion
PASS임을 증명한다. Production artifact를 덮어쓰거나 generator를 provider 모드로 재실행하지 않는다. - frozen install은 manifest/lock mismatch fixture를 실제 pnpm으로 거절한다.
- source/config/dist 각각의 synthetic secret fixture가 실제 scan을 실패시키고 scoped test allowlist만 통과한다.
한계와 재검토 조건
로컬 manifest license는 법률 검토가 아니며 vulnerability report도 외부 scanner가 제공한 데이터의 최신성 자체를 보증하지 않는다. 실제 프로젝트는 provider 버전, database freshness, network outage, exception 승인 조직, signing identity, attestation transparency/retention과 비밀 관리를 결정해야 한다.
Rollback
외부 scanner/attestor command, report path 또는 trusted key 설정을 제거하면 즉시
FAIL_UNVERIFIED로 돌아간다. local inventory, lock integrity, license, SBOM,
secret, reproducibility와 actual diff gate는 유지한다. scanner 장애를 이유로
promotion을 PASS로 변경하지 않는다.