argo-cd 7.7.0 제거 + CVE 게이트 요약표를 버전 최신순으로 정렬

카탈로그가 한 차트의 여러 버전을 들고 있어서, 게이트 요약표에 버전 정렬이 없으면
최신 버전과 구버전이 위험도 순으로 섞여 "지금 쓸 버전이 어느 정도인가" 를 한눈에
볼 수 없었다. 실측 사례 — argo-cd 10.4.0 이 구버전 7.8.11 사이에 끼어 표시됐다.

  변경 전 (chart asc → 실효 C/H desc)
    argo-cd@7.8.11  argocd:v2.14.5   14/101
    argo-cd@7.8.11  redis:7.4.2       5/60
    argo-cd@10.4.0  dex:v2.45.1       5/52     ← 최신이 구버전 사이에
    argo-cd@7.8.11  dex:v2.42.0       4/55
    argo-cd@10.4.0  argocd:v3.5.1     1/40

  변경 후 (chart asc → 버전 desc → 실효 C/H desc)
    argo-cd@10.4.0  dex:v2.45.1       5/52
    argo-cd@10.4.0  argocd:v3.5.1     1/40
    argo-cd@10.4.0  redis:8.6.4       0/0
    argo-cd@7.8.11  argocd:v2.14.5   14/101
    argo-cd@7.8.11  redis:7.4.2       5/60
    argo-cd@7.8.11  dex:v2.42.0       4/55

- scripts/pipeline/cve-gate.py
  - version_sort_key() 추가. 문자열 비교로는 "10.4.0" < "7.8.11" 이 되어 최신이
    아래로 내려간다. 숫자 구간을 정수로 파싱한다.
    프리릴리스는 코어와 분리해 다룬다 — 단순히 구간을 이어붙이면 리스트가 긴 쪽이
    커져 "1.2.0-rc1" 이 "1.2.0" 보다 최신으로 정렬된다(단위 테스트로 실측).
    semver 규칙대로 프리릴리스 없는 정식 릴리스를 더 크게 본다.
  - 정렬은 안정 정렬을 3번 겹쳐 만든다. 버전만 내림차순이라 reverse=True 가
    필요해 한 번에 튜플 하나로 묶을 수 없다.

- manifests/helm/argo-cd/7.7.0 제거
  게이트 차단 574건 중 237건이 이 버전의 이미지 3개였다
  (argocd:v2.13.0 117 / dex:v2.41.1 61 / redis:7.2.4-alpine 59 — 뒤 둘은 EOSL).
  제거 후 차단 337건.
  doc/catalog-stack-classification.md 의 버전 목록도 갱신했다(10.4.0 이 빠져 있어
  이미 stale 이었다). doc/chart-restructure-plan.md 는 과거 이관 기록이라 두었다.

로컬 파이프라인으로 검증했다(extract → generate → scan → gate, argo-cd 범위).
이미지 6개 / SBOM 6개 성공 / 커버리지 전부 ok.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
wbsong111
2026-08-19 11:51:46 +09:00
parent ae682674eb
commit f10d50e400
162 changed files with 44 additions and 37845 deletions
+43 -5
View File
@@ -408,15 +408,49 @@ def gate_verdict(results, missing):
return "PASS" if (total_block == 0 and not nodata and not missing) else "FAIL"
def version_sort_key(version):
"""차트 버전 문자열을 비교 가능한 키로 바꾼다.
문자열 그대로 비교하면 "10.4.0" < "7.8.11" 이 되어 최신 버전이 아래로 내려간다.
숫자 구간을 정수로 파싱해 자연스러운 순서를 만든다.
프리릴리스(`-rc1`, `-alpha`)는 코어와 분리해 다룬다. 단순히 구간을 이어붙여
비교하면 리스트가 긴 쪽이 커져 "1.2.0-rc1""1.2.0" 보다 최신으로 정렬된다
(실측). semver 규칙대로 프리릴리스가 없는 정식 릴리스를 더 크게 본다.
숫자가 아닌 구간은 문자열로 비교하되, 두 종류가 섞여도 비교가 깨지지 않도록
각 구간을 (숫자여부, 정수, 문자열) 튜플로 통일한다.
"""
import re
def parts_of(s, seps):
out = []
for p in re.split(seps, s) if s else []:
out.append((1, int(p), "") if p.isdigit() else (0, 0, p))
return out
raw = (version or "").lstrip("vV")
core, _, pre = raw.partition("-")
core_parts = parts_of(core, r"[._+]")
pre_parts = parts_of(pre, r"[.\-_+]")
# 프리릴리스가 없는 쪽이 더 크다 → 1.2.0 > 1.2.0-rc1
return (core_parts, 0 if pre_parts else 1, pre_parts)
def render_summary_table(results, charts_of=None):
"""차트·이미지별 취약점 '건수' 요약 표. 전체 리포트와 Job Summary 양쪽에서 재사용한다 —
이 표만으로는 CVE 개별 상세(ID·패키지·상태)를 담지 않으므로 크기가 카탈로그 규모에
선형으로만 늘어난다(차단 항목 상세 표와 달리 이미지당 CVE 수에 비례해 불어나지 않음).
차트 이름 오름차순으로 묶고, 같은 차트 안에서는 실효 CRITICAL/HIGH 내림차순으로
정렬한다(가장 위험한 이미지가 위로). `charts_of`(image → [(chart, version), ...])를
주면 이미지 하나가 여러 차트에 쓰일 때 차트마다 한 행씩 보여준다 — 안 주면(또는
해당 이미지 정보가 없으면) 결과가 이미 갖고 있는 대표 차트 하나만 쓴다."""
차트 이름 오름차순으로 묶고, 같은 차트 안에서는 **버전 내림차순(최신 먼저)**,
같은 버전 안에서는 실효 CRITICAL/HIGH 내림차순으로 정렬한다. 카탈로그가 한 차트의
여러 버전을 동시에 들고 있어서, 버전 정렬이 없으면 최신 버전과 구버전이 위험도 순으로
섞여 "지금 쓸 버전이 어느 정도인가" 를 한눈에 볼 수 없다(실측: argo-cd 10.4.0 이
구버전 7.x 사이에 끼어 표시됐다).
`charts_of`(image → [(chart, version), ...])를 주면 이미지 하나가 여러 차트에 쓰일 때
차트마다 한 행씩 보여준다 — 안 주면(또는 해당 이미지 정보가 없으면) 결과가 이미 갖고
있는 대표 차트 하나만 쓴다."""
charts_of = charts_of or {}
expanded = [] # (chart, version, result)
for r in results:
@@ -427,7 +461,11 @@ def render_summary_table(results, charts_of=None):
def eff(r, sev):
return r["counts"]["effective"][sev]
expanded.sort(key=lambda cvr: (cvr[0], -eff(cvr[2], "CRITICAL"), -eff(cvr[2], "HIGH")))
# 파이썬 sort 는 안정 정렬이므로, 우선순위가 낮은 키부터 차례로 정렬해 겹친다.
# (버전은 내림차순이라 reverse=True 가 필요해 한 번에 튜플로 묶을 수 없다.)
expanded.sort(key=lambda cvr: (-eff(cvr[2], "CRITICAL"), -eff(cvr[2], "HIGH")))
expanded.sort(key=lambda cvr: version_sort_key(cvr[1]), reverse=True)
expanded.sort(key=lambda cvr: cvr[0])
L = []
A = L.append