MEMORY.md 는 3번째 줄에서 "지금 시점의 상태와 다음에 할 일만 담는다" 고 스스로 선언하는데
실제로는 완료 기록이 쌓여 264줄이 됐고, 이슈 참조가 한 건도 없었다. 그 결과:
- SBOM_PIPELINE_IMAGE 재빌드가 파일 안에서 3번 반복된다 (215·234·262줄)
- 게이트 강제력 전환이 2번 반복되고, 그 내용은 이미 이슈 #7 체크리스트와 #19 에 있다
- argo-cd 절 55줄은 커밋 33e91c0 메시지의 요약본이다
- 베이스 OS 정책·"벤더 릴리스 노트 믿지 말 것" 은 본문이 스스로 "이미 image-authoring.md /
skill 에 있다" 고 밝히면서 중복 서술한다
같은 사실이 여러 곳에 있으면 나중에 어느 쪽이 맞는지가 새 문제가 된다.
성격별로 목적지를 정하고 내보냈다
---------------------------------
추적이 필요한 미결 → GitHub 이슈 (#29~#33 발행, MEMORY.md 엔 한 줄 링크)
재발 방지 교훈 → .claude/ 문서·skill
단순 완료 기록 → 삭제 (커밋·PR·images/<image>/README.md 가 권위 있는 기록)
교훈 이관 — 아직 어디에도 없던 것만 옮겼다:
- image-authoring.md: 베이스 OS 를 CVE 수치만으로 고르면 배포에서 죽는다.
cp --update=none(coreutils 9.3+) 실측과, 차트가 실행하는 명령을 verify.sh 에서
재현하라는 규칙. 원칙 2 의 "BCI 버전은 실측해서 정한다" 바로 뒤에 붙였다.
- self-build-image skill: 같은 날 재빌드하면 태그가 겹쳐 노드 캐시가 반영을 막는다.
근본 해결은 #31, 여기엔 우회법만.
- cve-remediation skill: 레버 표를 보기 전에 "이 컴포넌트가 필요하긴 한가" 를 먼저 묻는다.
dex 가 한 줄로 차단 57건(58%)이었다. 가장 값싼 레버인데 표에 없어서 새 단계로 넣었다.
유지 규칙을 명시했다
--------------------
트리거는 시간이 아니라 상태다 — "1달 지났으니 정리" 는 아무도 시작하지 않고, 날짜로 자르면
오래됐지만 유효한 미결까지 잘린다. 항목이 "다음에 할 일" 이 아니게 되는 순간 내보낸다.
다만 놓친 것을 걷어내기 위해 월 1회 점검하고 파일 상단의 점검 날짜를 갱신한다.
규칙은 MEMORY.md 자체와 CLAUDE.md("작업 기록을 어디에 남기는가") 양쪽에 뒀다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 apisix 작업에서 만들어 실제로 적용한 skill 인데 커밋되지 않은 채 로컬에만
있었다. MEMORY.md 와 CLAUDE.md 가 .claude/skills/cve-remediation/SKILL.md 를 가리키므로
레포만 보는 사람에게는 깨진 링크였다.
CLAUDE.md 의 skill 표에도 같은 이유로 빠져 있던 줄을 넣는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
업스트림 quay.io/argoproj/argocd 의 차단 CVE 는 대부분 OS 패키지가 아니라 바이너리에
정적 링크된 Go 모듈이라, 상위 태그 교체(이미 최신)로도 베이스 OS 교체로도 잡히지 않는다.
부분 조치는 효과가 없었다 — 같은 stdlib·x/crypto 취약점이 다섯 바이너리(argocd·helm·
kustomize·git-lfs·pebble)에 각각 들어있어 본체만 고치면 거의 줄지 않는다. 업스트림
Dockerfile 이 helm/kustomize/git-lfs 를 hack/install.sh 로 **미리 빌드된 릴리스 바이너리**
로 내려받기 때문에 그 바이너리의 Go 버전을 우리가 통제할 수도 없다. 그래서 넷 다 소스에서
다시 컴파일한다.
- images/argocd/ 신규 (Go 4개 + C 2개 + Node UI — 기존 자체 빌드 중 가장 크다)
- 최종 베이스 ubuntu → SUSE BCI(원칙 2). ubuntu 가 딸려오던 pebble 도 함께 사라진다
- tini·connect-proxy 는 SLE_BCI 에 없어 소스 빌드 — 기능을 빼지 않기 위해
- UI 는 업스트림과 동일하게 node 로 빌드(Go embed 라 생략 불가)
- manifests/helm/argo-cd/10.4.0/custom-values.yaml 이 이 이미지를 가리킨다
global.image 하나로 argocd 바이너리를 쓰는 5개 컴포넌트가 공유한다
재사용 설계 — Dockerfile 에 버전을 박지 않는다
---------------------------------------------
이 이미지는 앞으로도 CVE 조치를 반복해서 받는다. 모듈 목록·패키지 목록을 전부 값으로 빼서
다음 조치에서 바뀌는 것이 source.build.env 한 곳뿐이게 했다. 이전 방식이라면 모듈 하나
추가에 build.env·ARG 선언·go get 목록·BUILD_ARGS 네 곳을 고쳐야 했다.
- scripts/build/suggest-go-upgrades.py 신규 — 스캔 리포트의 FixedVersion 에서
GO_MODULE_UPGRADES / GO_BUILDER_TAG 를 산출한다. 사람이 CVE 를 훑어 최대값을 고르지
않는다. 빌드 시점에 최신을 당기는 방식(go get -u)은 재현성을 버리므로 택하지 않았다 —
값은 제안만 하고 채택은 사람이 커밋한다.
- images/argocd/go-mod-upgrade.sh 신규 — 목록 하나를 네 프로젝트에 재사용한다.
go get 은 의존성에 없는 모듈도 go.mod 에 추가하므로 그래프에 있는 것만 골라 적용한다.
실측 함정 — verify.sh 에 반영했다
--------------------------------
차트의 repo-server init 컨테이너가 `cp --update=none` 을 쓴다. 이 형식은 GNU coreutils
9.3+ 에서만 되는데, BCI 15.7 로 빌드한 이미지가 게이트도 verify.sh 도 통과하고 **배포
시점에** Init:CrashLoopBackOff 로 죽었다. BCI 16.0(coreutils 9.6)으로 올려 해소했고,
verify.sh 가 이 명령과 copyutil 흐름을 직접 재현하도록 해서 다음엔 빌드 단계에서 걸린다.
16.0 의 패키지 가용성과 trivy 커버리지(CoverageProbe=ok, EOSL 아님)도 확인했다.
검증
----
게이트 PASS — 실효 CRITICAL/HIGH 0건, CoverageProbe ok
기능 VERIFY-OK (심볼릭 링크 9개 · 번들 도구 3종 · LFS 필터 · copyutil 흐름)
배포 docker.io/paasup 에 push 후 운영 argocd 교체 — 파드 8종 Running,
admin 로그인, Application 2건 Synced/Healthy, repo-server 렌더링 확인
(서버가 go1.26.6 · helm v4.2.4 로 보고 — 우리 빌드가 맞다)
카탈로그 차단은 574 → 239 건이 됐고(7.7.0 삭제 · dex 비활성 · 이 커밋), 남은 239 건은
전부 동결된 7.8.11 몫이다. 운영에 쓰는 10.4.0 은 0 건이다. 경과·미결은 MEMORY.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- CLAUDE.md 정적 카탈로그 규모를 실측치로 교체(차트 59 / 버전 디렉토리 70).
차트 디렉토리 5종 파일이 전부 갖춰지지 않은 곳이 있다는 사실과 dip-* 계열이
선택적이라는 점을 명시해 "5개 필수"로 오해하지 않게 했다.
- 디렉토리 트리에 scripts/deploy-test/·images/·MEMORY.md·doc/ 하위 문서를 추가.
scripts/build/ 의 "아직 실사용 이미지 없음" 문구는 실제 빌드 이미지가 생겨
더 이상 사실이 아니라 제거했다.
- chart-to-cnpg: 남은 전환 대상에서 flowise 제외(2026-08 완료), infisical-standalone 추가.
- self-build-image: 이미지 3종 → 7종. 목록이 또 어긋나지 않도록 images/ 디렉토리가
단일 출처임을 설명에 박아뒀다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## doc/sbom-pipeline.md — 중복·모순 정리 (328 → 286줄)
같은 사실이 여러 절에 흩어져 있었고, 일부는 문서가 아니라 변경 이력이었다.
- `SEVERITY` 를 전 심각도로 덮어써야 하는 이유가 환경변수 절·CI 절·요약 절 3곳에
있었다. 스크립트 절의 blockquote 하나로 합쳤다 — "게이트를 돌릴 거라면 전 심각도로
스캔해야 한다"가 핵심이고 나머지는 그 결과다.
- Job Summary 1MB 제한이 CI 절과 결과 확인 절에 중복됐다. CI 절 하나로 합쳤다.
- `--warn-only` 서술이 mermaid 라벨·CI 절·게이트 절·트리아지 절 4곳에 있었다.
게이트 절 하나로 합치고, `build-image.yml` 쪽은 이미 강제라는 대비를 함께 적었다.
- 자체 빌드 트리거 표가 "PR 은 push 안 함"을 말하는데 바로 아래 불릿이 같은 말을
반복했다. 표는 그대로 두고 불릿은 **왜** 그런지(REGISTRY 미전달 → localhost 태그라
push 를 시도할 수조차 없다)만 남겼다.
- "오해를 주던 단일 '총 소요'는 제거" 같은 변경 이력 서술을 걷어냈다. 문서는 현재
상태를 적는 곳이다.
- "첫 전체 실행 결과(2026-07-08)" 절은 수치를 싣고 바로 아래에서 "현재 수치가
아니다"로 무효화하는 구조였다. 절 자체를 없애고, 거기서 유일하게 쓸모 있던 사실
(SBOM 생성 실패는 대부분 사설/미인증 레지스트리이거나 대용량 timeout)만 스크립트
절로 옮겼다.
- "실행 이력(2026-08-04)" 절은 MEMORY.md 와 중복이라 제거했다. 거기서만 알 수 있던
사실(Actions 시크릿의 push 권한 확인)은 GitHub 설정 표에 반영했다.
- `CVE_API_KEY` 가 본문에만 있고 GitHub 설정 표에 빠져 있어 추가했다.
- 아키텍처 절 불릿이 mermaid 서브그래프 라벨과 같은 말을 하고 있어, "pull 을 ② 한
곳에 몰아둔 것이 핵심"이라는 결론 한 문장으로 줄였다.
## 이미지 목록을 문서에 박아두지 않는다
이미지는 계속 추가되므로 열거하면 추가할 때마다 낡는다. `images/` 디렉토리를 단일
출처로 삼고 CLAUDE.md·image-authoring.md·build-image.yml·sbom-pipeline.md 의 열거를
걷어냈다. keycloak README 의 베이스 OS 결정 근거도 "기존 3종" 대신 "먼저 들어온
이미지들"로 바꿨다 — 근거의 내용은 그대로다.
## 현황 서술 정정
- build-image.yml 주석이 "아직 도입된 자체 빌드 이미지가 없다(images/ 가 비어 있음)"
로 남아 있었다. 이 레포 CI 에서 빌드→검증→게이트→push→카탈로그 브랜치 push 까지
실제로 검증된 상태다.
- MEMORY.md: cve-exceptions.json 첫 예외 등록, 베이스 OS 정책 확정, PR 자동 생성이
조직 정책으로 불가하다는 실측(run 30882785612)을 반영했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR #18 이 카탈로그에 넣는 quay.io/keycloak/keycloak:26.6.4 가 게이트에서 차단
17건(실효 HIGH 17 / CRITICAL 0)이었다. sbom.yml 이 warn-only 라 PR 은 통과했지만
실제로는 게이트 실패 상태로 카탈로그에 들어간다.
## 상위 태그·베이스 OS 교체를 먼저 검토한 결과
차단 17건 중 12건이 배포본에 함께 실린 jar 다. keycloak 26.6.4 와 최신 26.7.1 의
quarkus.version 이 둘 다 3.33.2.1 이고 그 BOM 이 netty 4.1.135.Final /
jackson-bom 2.21.2 를 고정한다(keycloak pom.xml 두 태그 + quarkus BOM 실측).
필요한 수정 버전은 netty 4.1.136.Final, jackson 2.21.4 라 **상위 태그로도 풀리지
않고**, CVE 가 OS 패키지가 아니라 jar 자체라 **베이스 OS 교체도 통하지 않는다.**
jar 를 직접 교체하는 자체 빌드가 유일한 수단이다 — etcd 이미지의
`go.work replace golang.org/x/text` 와 같은 성격의 의존성 override.
## images/keycloak/
업스트림 quarkus/container/Dockerfile 을 기준으로 하되 셋이 다르다.
1. 런타임 rootfs 가 SUSE BCI. bci-micro 파일시스템을 **씨앗으로 깔고** 그 위에
zypper --installroot 로 설치한다. 업스트림 ubi-null.sh 처럼 별도 installroot 를
micro 위에 덮으면 micro 의 rpmdb 가 가려져 micro 자체 패키지가 SBOM 에서 통째로
사라진다 — CVE 가 주는 게 아니라 스캔 사각지대가 생긴다. 씨앗 방식으로 OS 패키지
65종이 정상적으로 잡히는 것을 SBOM 으로 확인했다.
2. 취약 jar 오버레이(overlay-jars.sh). netty 17종 → 4.1.136.Final, jackson
core/databind → 2.21.4, pgjdbc → 42.7.12. Quarkus fast-jar 의 클래스패스가
파일명을 그대로 참조하므로 **파일명은 유지하고 내용만** 바꾸고 sha1 로 검증한다.
trivy 는 jar 내부 메타데이터를 읽으므로 SBOM 에 새 버전이 정확히 잡힌다.
3. bin/client 제거. keycloak-admin-cli 가 jackson 을 shade 로 품은 uber-jar 라
교체가 불가능하다. 서버 JVM 이 로드하지 않는 독립 CLI 라 제거했다 — 업스트림
대비 유일한 기능적 차이이며 CUSTOM-README 에 대안을 적었다.
버전은 26.7.1 로 올렸다. 26.6.4 는 26.7.1(및 26.6.5)에서만 패치된 keycloak-services
HIGH 5건(CVE-2026-16102/16442/16443/15572/15573)에 취약하다. 차트(keycloakx 7.2.2)는
최신이고 그대로 둔다 — appVersion 26.6.4 는 codecentric 의 릴리스 캐던스 지연이다.
## 베이스 OS 정책 확정 (image-authoring.md 원칙 2 미결 해소)
SUSE BCI 로 통일하되 **버전은 이미지마다 실측해서 고른다.** BCI 16.0 이 나와 있지만
SLE_BCI 의 java-21-openjdk-headless 가 15.7 은 21.0.12, 16.0 은 21.0.11 이라 최신
베이스로 가면 CVE-2026-41254·CVE-2026-47063 이 오히려 남는다. bci-micro 에
sed·grep·find 가 셋 다 없다는 것과 SLE 패키지명 차이(tzdata→timezone 등)도 함께
기록했다.
## 실측 결과
로컬 빌드(linux/amd64) → verify.sh → SBOM → 전 심각도 스캔 → 게이트:
차단 17건 → **0건** (커버리지 자가진단 ok, OS=sles 15.7)
남은 1건 CVE-2025-59250 은 예외 등록했다 — 트리비가 같은 mssql-jdbc jar 하나로
컴포넌트를 둘 만들어(pom.properties 의 13.2.1.jre11 / 파일명의 13.2.1) 접미사가
잘린 쪽이 매칭된 파싱 오탐이다. 설치본은 FixedVersion 목록에 있는 13.2.1.jre11 이다.
dev 클러스터 격리 네임스페이스(kc-test-build)에 cnpg-cluster + keycloakx 로 실배포
검증: Pod Running, jdbc-postgresql 연결, liquibase 스키마 생성, admin 부트스트랩
(KC-SERVICES0077), apisix ingress 경유 OIDC discovery 200 / admin 토큰 발급 /
realm·client 생성(201) 까지 확인. 정리 시 Longhorn Volume 까지 삭제했다.
## custom-values.yaml ingress 수정
path 가 exact "/" 였다. apisix 에서는 루트만 매치되어 /realms/*, /admin/* 이 전부
404 가 난다 — airflow·superset·mlflow·lakekeeper 에서 이미 실측된 문제로 카탈로그가
regex 방식으로 통일돼 있다. path: /.* + k8s.apisix.apache.org/use-regex 로 맞췄고,
배포 검증에서 이 경로들이 실제로 뜨는 것을 확인했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세 파이프라인의 실행법이 문서에만 있어 "그 문서를 읽어야만" 알 수 있었다.
관련 작업 시 자동 로드되도록 Skill 로 등록한다.
- catalog-update-pipeline: agent/update_catalog 파이프라인. CATALOG_ROOT 가
개인 로컬 경로로 하드코딩돼 있어 오버라이드 필수라는 점, create_pr 단계가
주석 처리돼 PR 이 생성되지 않는다는 점을 명시했다.
- sbom-cve-gate: SBOM·스캔·게이트. CoverageProbe(ok/none/n-a) 해석과 게이트가
현재 warn-only 라는 점을 명시했다.
- self-build-image: 자체 빌드. 오케스트레이터는 하나뿐이라는 원칙과 전역 ARG
선언, 게이트 PASS 가 동작을 보장하지 않는다는 점을 명시했다.
Skill 은 절차 본문을 복제하지 않고 권위 문서를 가리킨다 — 문서가 단일 출처이고
Skill 은 실행 계약과 함정·현재 상태만 담는다.
함께 고친 stale 문서(image-authoring.md):
- "images/ 디렉토리 자체가 없다" → 실제로는 이미지 3종이 있고 push 까지 됐다
- "자동화된 배포 테스트 절차는 아직 없다" → deploy-test-procedure.md 와
전용 스크립트가 있다
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
이슈 #9에서 결정된 CloudNativePG 채택을 카탈로그 앱에 확장 적용한다.
airflow/lakekeeper/mlflow/superset/flowise 5개 차트가 내장하던 bitnami
postgresql 서브차트를 끄고 앱 전용 cnpg-cluster 인스턴스를 외부 DB로 쓰도록
전환했다. 5개 모두 dev 클러스터 격리 네임스페이스에서 배포 테스트로 실측 검증했다.
gitea/keycloak/dnsup 는 서브차트가 아니라 공유 postgresql-ha 를 외부 참조하며
paasup/dipup 레포 관리 대상이라 제외했다 — 인수인계 문서만 추가했다.
배포 구조:
- ArgoCD ApplicationSet 으로 DB(syncWave 0) → 앱(syncWave 1) 순서를 보장한다.
기존 openmetadata/victoria-metrics 관례를 따랐다. cnpg-cluster 차트는 범용
상태로 유지하고 앱별 값은 manifests/applicationset/<app>/ 에 둔다.
검증 중 발견해 함께 고친 문제:
- lakekeeper: cnpg 의 -ro 는 replica 전용이라 instances:1 에서 엔드포인트가
0개다. 읽기 연결을 -r(전체 라운드로빈)로 교체했다.
- airflow/superset: ingressClassName 누락 + kong 애노테이션 잔존으로 이
클러스터(apisix 전용)에서 ingress 접근이 아예 불가능했다. apisix + regex
path 로 교체했다.
- 배포 테스트가 PV 만 지우고 Longhorn Volume CR 을 남겨 storageScheduled 가
누적됐다(orphan 112개 ~1TB 로 배포 차단). 두 스크립트의 정리 로직을 고쳤다.
재사용 구조화:
- .claude/skills/chart-to-cnpg/ 신규. 남은 4개 차트(langflow-ide, langfuse,
litellm, nemo)에 같은 절차를 재사용한다. flowise 에 실제 적용해 검증했다.
- 배포 테스트 공통 절차는 .claude/deploy-test-procedure.md, 환경 함정은
.claude/pitfalls.md 로 단일화하고 앱별 README 는 참조만 남겼다.
관련: #9, #14
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
security-catalog 프로젝트에서 자체 빌드 이미지 3종을 실제로 로컬 빌드·게이트
검증하는 과정에서 발견한 문제들:
- extract-helm-images.sh 가 `image:` 필드만 잡고 `imageName:`(CNPG Cluster CRD 관례)
은 놓쳐, cnpg-cluster 차트의 이미지가 SBOM·스캔·게이트 어디에도 안 나타났다.
- patch-catalog-tag.py 의 split 포맷 패처가 registry 필드가 따로 없고 repository 에
registry+repo 를 합쳐 쓰는 차트(cloudnative-pg 오퍼레이터, 업스트림 템플릿이
image.registry 를 아예 참조하지 않음)에서 tag 만 조용히 갱신하고 repository 는
그대로 남겨 깨진 참조를 만들 수 있었다.
- CoverageProbe(센티널 패키지 주입 재스캔으로 "0건"과 "측정 안 됨"을 구분)가
이식되지 않아, 전 심각도 0건인 자체 빌드 이미지가 실제로는 깨끗한데도 게이트가
"데이터 커버리지 이상"으로 오탐 처리했다 — security-catalog 의 scan-sbom.sh 를
이식해 해소. cve-gate.py 는 이미 이 키를 읽도록 구현돼 있어 소비 쪽 변경은 없다.
rpm(SUSE)·deb(Debian)·apk(Alpine) 세 센티널 경로와 병렬 스캔 회귀를 로컬에서
확인했고, 이식 후 cloudnative-pg·cnpg-postgresql 게이트가 실제로 FAIL→PASS 로
바뀌는 것도 확인했다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
security-catalog 프로젝트에서 첫 실사용 자체 빌드 이미지 3종을 포팅한다 — 전부
상위 태그·베이스 OS 교체로 해소 안 되는 CVE(Go 모듈 정적 링크 또는 미수정 CRITICAL/
HIGH)를 자체 빌드(소스 컴파일 또는 SUSE BCI 재설치)로 대응한다:
- images/cloudnative-pg: CNPG operator, release-1.30 소스 컴파일 + bci-micro
- images/cnpg-postgresql: PostgreSQL 18.4, bci-base + zypper 재설치
- images/etcd: etcd v3.7.1, 소스 컴파일(x/text 강제 업그레이드) + bci-micro
함께 추가:
- manifests/helm/{cloudnative-pg,cnpg-cluster,etcd} — 위 이미지를 참조하는 카탈로그 차트
- scripts/deploy-test/*.sh, .claude/deploy-test-procedure.md — CVE 0건과 별개로
"실제로 뜨는가"를 검증하는 배포 스모크 테스트
- .claude/pitfalls.md — 자체 빌드/배포 테스트 중 실측한 함정 모음
검토 중 발견해 반영한 수정:
- cloudnative-pg 차트의 image 블록을 etcd와 동일한 registry/repository/tag 3필드+
따옴표 포맷으로 통일 — 기존 포맷(repository에 registry+repo 결합, 따옴표 없음)은
patch-catalog-tag.py 의 split 패처가 tag만 갱신하고 repository는 그대로 남기는
조용한 부분 치환을 일으켜, 향후 레지스트리 마이그레이션 시 깨진 참조를 만들 수 있었다
- cnpg-cluster 차트의 SLES 커버리지 코멘트를 최신 실측(trivy가 SLES 15.7을 정상
커버함, 2026-07-29 재측정)에 맞게 정정 — 폐기된 "측정 불가/OVAL 우회 필요" 결론이
남아있었다
- CLAUDE.md/MEMORY.md 의 "images/ 디렉토리 없음" 서술을 갱신하고, 레지스트리
마이그레이션(docker.io/wbsong111 → docker.io/paasup)·decisions/analysis 문서 이관·
리소스 프로파일 추가를 다음 작업으로 기록
이 3개 이미지는 아직 dip-catalog 자체 CI(build-image.yml)로 빌드·게이트·push 를
실행해본 적이 없다 — 현재 참조 태그는 security-catalog 쪽에서 이미 검증된 것이다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scan-sbom.sh 에 커버리지 자가진단이 없는데도 체크리스트가 cov= 확인을 지시해
같은 문서 104-106줄과 모순됐다. 실제 동작(findings 0건 시 보수적 실패)과 판단
방법으로 교체. Dockerfile 헤더 주석의 doc/scripts/ 경로도 scripts/pipeline/ 로
갱신(경로 이전 시 누락됨).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
security-catalog 에서 포팅: build-hardened-image.sh, patch-catalog-tag.py,
build-image.yml(REGISTRY_HOST=docker.io/paasup). images/ 는 아직 비어있다 —
베이스 OS 정책 미결 등은 .claude/image-authoring.md, MEMORY.md 참고.