scan-sbom.sh: macOS(BSD) xargs 병렬 스캔 버그 수정

xargs -I{} 가 탭을 공백으로 바꿔 이미지명·SBOM 경로가 한 덩어리가 되고
"failed to open sbom file" 로 스캔이 실패했다(로컬 macOS 검증 중 발견).
security-catalog 프로젝트에서 이미 확인된 동일 버그 — read 루프 +
배치 wait 방식으로 교체하고, 대상/결과 수 불일치 시 실패시키는 누락
검출 가드를 추가했다(스캔 안 됨이 취약점 없음으로 오인되는 것을 막기 위함).
로컬에서 main 모드로 재검증: 2/2 성공, 이전 우회 실행과 동일한 집계치.
This commit is contained in:
wbsong111
2026-08-03 10:23:12 +09:00
parent 5e1422e0e4
commit 100e8cfc61
+31 -10
View File
@@ -8,8 +8,8 @@
# 취약점 DB 만 있으면 되며, CVE DB 갱신 시 재pull 없이 재스캔할 수 있다.
#
# 사용:
# bash doc/scripts/generate-sbom.sh images_final.tsv . # 선행: sbom/ 생성
# bash doc/scripts/scan-sbom.sh . # SBOM 취약점 스캔
# bash scripts/pipeline/generate-sbom.sh images_final.tsv . # 선행: sbom/ 생성
# bash scripts/pipeline/scan-sbom.sh . # SBOM 취약점 스캔
#
# 인자:
# $1 SBOM 작업 디렉토리 (sbom/ 과 sbom-index.tsv 가 있는 곳. 기본: 현재 디렉토리)
@@ -124,16 +124,37 @@ trivy image --download-db-only >>"$LOG" 2>&1 \
echo ">> [$(ts)] 스캔 시작 (parallel=$PARALLEL)" | tee -a "$LOG" >&2
P1=$(date +%s)
# 각 타깃 라인(image⇥sbom_file)을 워커로. -I{} 는 라인 단위로 아이템을 읽으므로
# {} 에 탭 포함 전체 라인이 들어온다. bash -c 안에서 cut 으로 탭 분해(BSD/GNU 공통).
xargs -P "$PARALLEL" -I{} bash -c '
im=$(printf %s "$1" | cut -f1)
sf=$(printf %s "$1" | cut -f2)
[ -n "$im" ] && bash "$0" --scan-one "$im" "$sf" "$2" "$3"
' "$SELF" {} "$REPORTS" "$LOG" < "$TARGETS" \
>> "$RESULTS"
# xargs 를 쓰지 않는다. macOS(BSD)에서 두 가지가 깨진다 — 둘 다 실측으로 확인했다
# (동일 파이프라인의 security-catalog 프로젝트에서 먼저 발견됨).
#
# 1. `xargs -I{}` 가 치환 시 **탭을 공백으로 바꾼다.** 그러면 `cut -f`(탭 구분)가
# 구분자를 못 찾고 BSD cut 은 라인 전체를 반환하므로 이미지명과 SBOM 경로가
# 한 덩어리가 되어 `failed to open sbom file` 로 실패한다.
# 2. `xargs -I` 는 조립 인자를 짧게 제한한다. SBOM 경로가 길면 대상이 조용히
# 누락되고도 스크립트가 성공으로 보고할 수 있다.
#
# `read` 로 탭을 직접 분해하고 배치 단위로 wait 한다. `wait -n` 은 bash 4.3+ 이므로
# macOS 기본 bash 3.2 에서 쓸 수 없어 batch-wait 방식을 쓴다.
n=0
while IFS="$(printf '\t')" read -r im sf; do
[ -n "$im" ] && [ -n "$sf" ] || continue
bash "$SELF" --scan-one "$im" "$sf" "$REPORTS" "$LOG" >> "$RESULTS" &
n=$((n+1))
[ $(( n % PARALLEL )) -eq 0 ] && wait
done < "$TARGETS"
wait
echo ">> [$(ts)] 스캔 완료 ($(fmt_elapsed $(( $(date +%s) - P1 ))))" | tee -a "$LOG" >&2
# 누락 검출 — 대상 수와 결과 수가 다르면 실패시킨다. "스캔되지 않음" 이 "취약점
# 없음" 으로 오인되는 것이 이 파이프라인의 최악 실패다.
GOT="$(grep -c . "$RESULTS" 2>/dev/null || echo 0)"
if [ "$GOT" -ne "$N" ]; then
echo "!! 스캔 대상 $N 개 중 $GOT 개만 결과가 있다 — 누락 발생" | tee -a "$LOG" >&2
awk -F'\t' 'NR==FNR{seen[$1]=1;next} !($1 in seen){print "!! 누락: "$1}' \
"$RESULTS" "$TARGETS" | tee -a "$LOG" >&2
exit 1
fi
TOTAL=$(( $(date +%s) - RUN_START )) # 취약점 스캔 단계(오프라인) 소요
GEN_SECS=$(cat "$OUT_DIR/.sbom-gen-seconds" 2>/dev/null || echo "") # SBOM 생성 단계(이미지 pull) 소요
python3 - "$RESULTS" "$CHARTMAP" "$SEVERITY" "$TOTAL" "$(ts)" "$SUMMARY_TSV" "${GEN_SECS:-}" <<'PY' | tee "$SUMMARY_MD" >&2