1. 이 프로젝트가 뭔가요?

just-shield는 한 문장으로 이렇습니다:

💡 한 문장 정의

우리 저장소의 CI 설정 파일(.github/workflows)을 읽고, "바깥에서 받아 쓰는 부품(GitHub Action) 중에 오염됐거나 오염될 수 있는 게 있는지"를 실행 전에 검사해 주는 명령줄 도구.

바깥 세상 (서드파티 공급망)
aquasecurity/trivy-action 오염된 적 있음
checkmarx/kics-…-action 오염된 적 있음
actions/checkout GitHub 공식
uses: 로
받아서 실행
우리 CI (탈취 표적)
GITHUB_TOKEN 저장소 권한
AWS / 클라우드 키 시크릿
배포용 자격증명 시크릿
just-shield가 서는 자리 — 가운데 화살표(받아서 실행하는 순간) 직전입니다. 바깥 부품이 우리 시크릿 옆에서 실행되기 전에 "이거 진짜 맞아?"를 검사합니다.

2. 적을 알기: TeamPCP 사건

TeamPCP는 실제로 존재하는 해커 그룹입니다 (Google 추적명 UNC6780). 이들의 수법은 단순하지만 치명적입니다:

  1. 표적을 직접 안 때립니다. 대신 모두가 믿고 쓰는 보안 도구(Trivy, KICS, LiteLLM)의 배포 계정을 훔칩니다.
  2. 버전 이름표를 바꿔치기합니다. 예를 들어 Trivy의 버전 태그 77개 중 76개를 악성 코드가 든 커밋으로 옮겨 꽂았습니다. 사용자는 어제와 똑같이 @v3을 받았는데 내용물만 바뀐 겁니다.
  3. CI 안의 열쇠를 쓸어 담습니다. 악성 코드는 CI에서 실행되며 클라우드 키, 토큰 등을 외부 서버로 보냈습니다. 약 50만 개 자격증명이 털린 것으로 추정됩니다.
  4. 훔친 열쇠로 다음 피해자를 만듭니다. 훔친 토큰으로 47개 패키지를 60초 만에 추가 감염시켰습니다 — 웜(전염병)처럼 번집니다.
ℹ 우리 프로젝트에서 TeamPCP의 역할

TeamPCP "전용 백신"을 만드는 게 아닙니다. 일반적인 공급망 방어 도구를 만들되, "TeamPCP의 실제 공격을 우리 도구가 막았을까?"를 채점 기준(시험 문제)으로 쓰는 겁니다. (결정 ①)

3. 지금까지 내린 결정 7개

인터뷰에서 큰 결정부터 차례로 내려왔습니다. 각 결정은 다음 결정의 전제가 됩니다.

1범위: 특정 그룹 전용이 아닌 일반 방어

정한 것: TeamPCP 전용 탐지기가 아니라, 공급망 공격이라는 일반 문제를 푼다. TeamPCP 사건은 검증 시나리오로 쓴다.

왜: 특정 그룹의 흔적(악성 도메인, 파일 해시)만 쫓는 도구는 그룹이 수법을 바꾸면 쓸모없어집니다.

2방어 지점: 받기 전에 검사한다

정한 것: 공격 체인(① 업스트림 오염 → ② 오염된 것 섭취 → ③ 열쇠 수집 → ④ 유출) 중 ②를 끊는다. 실행 중 감시(③④ 차단)는 v2 후보로 미룸.

왜: ②에서 막으면 ③④는 아예 일어나지 않습니다. 그리고 ②는 파일만 읽으면 검사할 수 있어서 만들기가 현실적입니다. 비유: 택배를 열기 전에 송장과 봉인을 확인하는 것.

3검사 대상: GitHub Actions부터

정한 것: v1은 .github/workflows의 워크플로 파일이 참조하는 GitHub Action만 검사. npm/PyPI 패키지(락파일)는 v2 후보.

왜: 액션은 시크릿 바로 옆에서 실행되는 가장 위험한 부품이고, 검사 대상이 YAML 몇 개로 좁아서 v1으로 완성 가능한 크기입니다.

4형태: CLI 엔진 먼저, 포장은 나중에

정한 것: 검사 로직(엔진)을 명령줄 도구로 먼저 완성. GitHub Action 래퍼(CI에서 자동 실행)는 v1.5에 얹는다.

왜: GitHub Action·VS Code 확장·AI 에이전트 연동은 전부 같은 엔진을 호출하는 "포장"입니다. 엔진 없이 포장부터 만들 수는 없습니다. CI에서는 래퍼 없이도 run: just-shield scan 한 줄로 쓸 수 있습니다.

5규칙 범위: 두 계층, 9개로 고정 (ADR-0001)

정한 것: "받는 게 진짜인가"(섭취 검증 6개) + "털려도 덜 털리게"(피해 반경 3개) = 9개. 그 외 일반 CI 보안 검사(스크립트 인젝션 등)는 일부러 안 한다 — 그 영역은 기존 도구 zizmor가 이미 잘함.

왜: 다 하려다 보면 기존 도구의 열화 복제품이 됩니다. 좁고 깊은 정체성이 생존 전략입니다.

6신뢰 경계: 누구를 의심할 것인가

정한 것: 내 저장소·내 조직의 액션 = 퍼스트파티 (검사 제외), GitHub 공식 actions/* = 서드파티지만 완화된 경고, 그 외 전부 = 예외 없이 엄격.

왜: TeamPCP에게 털린 곳이 바로 "믿을 만한 보안 회사들"이었습니다. 평판은 신뢰 근거가 아닙니다. 단, 경고를 남발하면 사용자가 경고 자체를 무시하게 되므로 등급을 나눕니다.

7한계 인정: 무엇을 보증하지 않는가

정한 것: SHA 핀 고정은 "내용물이 안 바뀐다"(불변성)를 보증할 뿐 "내용물이 착하다"(무해성)는 보증하지 않는다. 아무도 모르는 오염(제로데이)은 v1 보증 범위 밖이며, 이를 ADR에 명시했다. 대신 이미 알려진 감염 버전을 잡는 규칙 R9를 추가했다.

왜: "다 막아준다"고 암시하는 보안 도구는 통과 마크가 거짓 안심이 되어 오히려 위험합니다.

4. 확정된 검사 규칙 9개

4-1. 섭취 검증 — "받는 물건이 진짜인가" Tier 1

규칙무엇을 잡나쉬운 비유
R1 가변 참조액션을 @v3 같은 태그로 받는 것. 태그는 공격자가 옮겨 꽂을 수 있음 → 커밋 SHA(지문)로 받아야 함"이름표"가 아니라 "지문"으로 신원 확인
R2 타이포스쿼팅aquasecurtiy처럼 한 글자 바꾼 짝퉁 이름의 액션짝퉁 상표 감별
R3 미검증 설치curl | sh처럼 검증 없이 인터넷에서 받아 바로 실행하는 스텝출처 미상 설치파일 더블클릭
R4 이미지 미고정image: foo:latest처럼 내용물이 바뀔 수 있는 컨테이너 참조내용물 바뀌는 택배 상자
R5 임포스터 커밋 온라인SHA로 핀했어도 그 커밋이 해당 저장소 족보(히스토리)에 없는 경우 — 강한 오염 신호족보에 없는 가짜 호적
R9 알려진 감염 버전 DB 동봉보안 커뮤니티가 이미 "악성"으로 공표한 버전을 아직 쓰고 있는 경우리콜 목록과 대조

4-2. 피해 반경 축소 — "털려도 덜 털리게" Tier 2

규칙무엇을 잡나쉬운 비유
R6 시크릿 노출서드파티 액션이 시크릿에 접근 가능한 잡에서 실행되는 것금고 열어둔 방에 외부 수리기사 들이기
R7 권한 과잉permissions 미선언 — 기본 토큰이 필요 이상으로 강력해짐알바생에게 마스터키 지급
R8 위험 트리거pull_request_target + 외부 PR 코드 체크아웃 조합 — 외부인이 시크릿 있는 환경에서 코드 실행 가능낯선 사람을 금고 방에 초대
⚠ 동작 원칙

기본은 완전 오프라인(저장소 파일만 읽음). 네트워크가 필요한 검사(R5, R9 최신화)는 --online 옵션을 켰을 때만 실행. 이유: 방어 도구 자신이 "아무 데도 접속 안 한다"고 말할 수 있어야 신뢰받습니다.

5. 아직 정하지 않은 것

⏸ 멈춰 있는 질문 (질문 8): 제로데이 대응 — 쿨다운 규칙 R10

아무도 모르는 오염은 탐지가 불가능하지만, "나온 지 7일 안 된 버전은 일단 쓰지 말라"는 규칙(쿨다운)으로 위험 기간을 피해갈 수 있습니다. 오염은 보통 며칠 안에 발각되므로, 남들이 먼저 밟게 하는 전략입니다.

현재 추천: R10으로 추가 (온라인 규칙, 기본 7일). 트레이드오프: 길수록 안전하지만 버그 패치도 그만큼 늦게 받습니다.

그 뒤에 남아 있는 결정들 (난이도 순으로 정렬, 위가 쉬움):

결정질문 내용전문성 필요도
리포트 형식검사 결과를 어떻게 보여줄까? (표 형태, 심각도 색깔, JSON 출력 등)낮음 — 취향 문제
실패 기준어떤 심각도부터 CI를 빨갛게(빌드 실패) 만들까?낮음
예외 처리"이 경고는 알고 쓰는 거야"라고 무시 표시하는 방법중간
구현 언어Python, Go, Rust 중 무엇으로 만들까?중간 — 팀이 아는 언어가 우선
테스트 전략TeamPCP 시나리오를 재현한 가짜 워크플로로 채점하는 방법중간
💡 길을 잃었을 때 기억할 것

어려운 결정(위협 모델, 규칙 범위, 신뢰 경계)은 이미 다 끝났습니다. 남은 것은 대부분 "도구를 어떻게 편하게 쓸까"에 대한 가벼운 결정이고, 모르겠으면 추천안을 그대로 받아들여도 안전합니다.

6. 용어 미니 사전

용어쉬운 뜻
공급망 공격나를 직접 해킹하는 대신, 내가 믿고 받아 쓰는 부품을 오염시켜서 나까지 감염시키는 공격
CI/CD코드를 올리면 자동으로 빌드·테스트·배포해 주는 시스템. 배포 권한(열쇠)을 잔뜩 들고 있어서 해커의 표적
GitHub ActionCI에서 받아 쓰는 남이 만든 부품 (예: 코드 체크아웃, 스캔 도구)
태그 / SHA태그(@v3)는 옮겨 붙일 수 있는 이름표, SHA는 바꿀 수 없는 지문
태그 하이재킹공격자가 이름표를 악성 코드에 옮겨 붙이는 것
임포스터 커밋저장소의 정식 기록에 없는 가짜 커밋
섭취 전 검증부품을 실행하기 전에 진짜인지 확인하는 것 — just-shield의 본업
피해 반경뚫렸을 때 얼마나 털리는가의 범위. 권한을 줄이면 반경이 줄어듦
제로데이아직 아무도 모르는 (그래서 목록 대조로 못 잡는) 오염
ADR"왜 이렇게 결정했는지" 기록한 문서. docs/adr/에 있음