fix(sbom-ci): summary shows only scanned severities (drop always-zero MED/LOW)

SEVERITY 로 스캔한 심각도만 요약표 컬럼으로 동적 출력. 기본(HIGH,CRITICAL)에서
항상 0이던 MED/LOW 컬럼 제거. SEVERITY 에 MEDIUM/LOW 추가 시에만 해당 컬럼 표시.
TSV 는 CRITICAL 을 첫 카운트 열로 유지(게이트 호환).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wbsong111
2026-07-08 14:14:27 +09:00
parent a61d6730dd
commit f8ca673630
+17 -11
View File
@@ -139,6 +139,10 @@ python3 - "$RESULTS" "$CHARTMAP" "$SEVERITY" "$TOTAL" "$(ts)" "$SUMMARY_TSV" <<'
import sys import sys
results, chartmap, sev, total, tstamp, tsv_out = sys.argv[1:7] results, chartmap, sev, total, tstamp, tsv_out = sys.argv[1:7]
total=int(total) total=int(total)
# 표시 심각도 = 실제 스캔한 SEVERITY 만 (순위 내림차순). MED/LOW 는 스캔 안 하면 항상 0이므로 제외.
RANK=["CRITICAL","HIGH","MEDIUM","LOW"]; IDX={"CRITICAL":1,"HIGH":2,"MEDIUM":3,"LOW":4}
scanned=[s.strip().upper() for s in sev.split(",") if s.strip()]
shown=[s for s in RANK if s in scanned] or ["CRITICAL","HIGH"]
cm={} cm={}
try: try:
for l in open(chartmap): for l in open(chartmap):
@@ -152,29 +156,31 @@ for l in open(results):
img=p[0] img=p[0]
if p[5]=="OK" or img not in best or best[img][5]!="OK": best[img]=p if p[5]=="OK" or img not in best or best[img][5]!="OK": best[img]=p
rows=list(best.values()) rows=list(best.values())
def key(r): def cnt(r,s):
try: return (-int(r[1]),-int(r[2])) try: return int(r[IDX[s]])
except: return (1,0) except: return 0
rows.sort(key=key) rows.sort(key=lambda r: tuple(-cnt(r,s) for s in shown))
ok=[r for r in rows if r[5]=="OK"]; err=[r for r in rows if r[5]!="OK"] ok=[r for r in rows if r[5]=="OK"]; err=[r for r in rows if r[5]!="OK"]
tc=sum(int(r[1]) for r in ok); th=sum(int(r[2]) for r in ok) totals={s:sum(cnt(r,s) for r in ok) for s in shown}
secs=[int(r[6]) for r in rows if len(r)>=7 and r[6].isdigit()] secs=[int(r[6]) for r in rows if len(r)>=7 and r[6].isdigit()]
# TSV: chart, image, <shown counts...>, status, sec (CRITICAL 은 첫 카운트 열 = col3)
with open(tsv_out,"w") as f: with open(tsv_out,"w") as f:
for r in rows: for r in rows:
f.write(f"{cm.get(r[0],'')}\t{r[0]}\t{r[1]}\t{r[2]}\t{r[3]}\t{r[4]}\t{r[5]}\t{r[6] if len(r)>=7 else ''}\n") cols=[cm.get(r[0],""), r[0]] + [str(cnt(r,s)) for s in shown] + [r[5], r[6] if len(r)>=7 else ""]
f.write("\t".join(cols)+"\n")
print("# Trivy SBOM 취약점 요약\n") print("# Trivy SBOM 취약점 요약\n")
print(f"- 생성: {tstamp}") print(f"- 생성: {tstamp}")
print(f"- 총 소요: **{total//60}분 {total%60}초** (SBOM {len(rows)}개)") print(f"- 총 소요: **{total//60}분 {total%60}초** (SBOM {len(rows)}개)")
print(f"- 성공/실패: **{len(ok)} / {len(err)}**") print(f"- 성공/실패: **{len(ok)} / {len(err)}**")
print(f"- 집계 심각도: `{sev}`") print(f"- 집계 심각도: `{sev}`")
print(f"- 취약점 합계: **CRITICAL {tc}**, **HIGH {th}**") print("- 취약점 합계: " + ", ".join(f"**{s} {totals[s]}**" for s in shown))
if secs: print(f"- SBOM당 스캔시간: 최소 {min(secs)}s / 최대 {max(secs)}s / 평균 {sum(secs)//len(secs)}s") if secs: print(f"- SBOM당 스캔시간: 최소 {min(secs)}s / 최대 {max(secs)}s / 평균 {sum(secs)//len(secs)}s")
print() print()
print("| Chart | Image | CRIT | HIGH | MED | LOW | Status | Sec |") print("| Chart | Image | " + " | ".join(shown) + " | Status | Sec |")
print("|---|---|---:|---:|---:|---:|---|---:|") print("|---|---|" + "---:|"*len(shown) + "---|---:|")
for r in rows: for r in rows:
sec=r[6] if len(r)>=7 else "-" cells=" | ".join(str(cnt(r,s)) for s in shown)
print(f"| {cm.get(r[0],'')} | `{r[0]}` | {r[1]} | {r[2]} | {r[3]} | {r[4]} | {r[5]} | {sec} |") print(f"| {cm.get(r[0],'')} | `{r[0]}` | {cells} | {r[5]} | {r[6] if len(r)>=7 else '-'} |")
PY PY
{ {