왜 이 글이 필요한가
Claude Code나 요즘 에이전트 스택을 쓰다 보면, 누군가 만든 스킬(Skill) 하나를 그냥 폴더에 넣고 쓰는 경우가 많아요. 스킬은 그냥 마크다운 파일 한 장, 평문 영어 지시문 묶음입니다. "open a pull request", "triage these tickets" 같은 문장이 전부예요.
문제는 이게 **공급망(supply chain)**이 됐다는 점이에요. 누구나 배포할 수 있고, 여러분은 인터넷에서 뭘 받듯 그냥 설치합니다. 그래서 스캐너가 등장했어요. 폴더를 가리키고, 스캔 돌리고, 숫자를 읽고, 라벨을 믿고, 설치 여부를 결정하는 흐름이죠.
그런데 그 숫자가 화면에서 가장 믿을 수 없는 값입니다.
이 글은 NVIDIA가 공개한 오픈소스 스캐너 SkillSpector로 실제 스킬 3개를 돌려본 결과를 정리한 것이에요. 악성 픽스처(honeypot), 정직한 GitHub 래퍼, 그리고 871줄짜리 자동화 스킬 하나씩. 결론부터 말하면:
- 정적 분석은 빠르고, 무료고, 진짜 악성은 잡아냅니다. 단, 가장 위험한 건 코드가 아니라 마크다운 안의 평문 산문이었어요.
- 같은 정적 레이어가 정상 스킬에는 약 80% 오탐률을 뱉습니다.
- 스코어 숫자는 설계상의 강력함과 숨겨진 위협을 구분하지 못해요. 그건 2단계(4개 패스)가 하는 일입니다.
근거자료: From Green Checkmark to Real Judgment (Towards Data Science)
국내 SI/금융권처럼 외부 코드 반입 심의가 엄격한 환경에서는 이 흐름이 특히 중요해요. "스캐너 돌렸으니 안전"이라는 판단이 오히려 사고의 시작이 될 수 있습니다.
![]()
SkillSpector는 어떻게 스킬을 스캔하는가
2단계 병렬 구조
SkillSpector는 매니페스트를 파싱하고 파일 목록을 만든 뒤, 분석기를 두 그룹으로 나눠 돌립니다.
1그룹 — 결정적(Deterministic) 레이어
- 16개 카테고리, 64개 탐지 패턴
- Python AST 파싱
- Taint tracking (민감 소스 → 네트워크 싱크)
- YARA 시그니처
- OSV.dev 실시간 CVE 조회
이 레이어는 API 키 없이 오프라인에서 반복 실행 가능합니다. 이 글에서 말하는 "정적 레이어"가 바로 이겁니다.
2그룹 — LLM 분석기 3개
- 시맨틱 프롬프트 인젝션 탐지 — "ignore your instructions"를 정중하게 바꿔 쓴 문장을 잡아냅니다. 정규식으로는 절대 못 잡아요.
- 개발자 의도 감사 — 매니페스트가 주장하는 것보다 코드가 더 많은 일을 하는지 확인합니다.
- 품질/정책 검사 — 모호한 트리거, 경고 없는 파괴적 동작 등.
이 3개 패스는 아직 필터링하지 않습니다. 발견만 합니다. 위협이 '문장'일 수 있기 때문에, 문장은 패턴이 아니라 리더가 필요하거든요.
4번째 패스: 메타 분석기
4번째 모델 패스가 메타 분석기입니다. 정적 + 3개 발견 패스의 모든 결과를 받아서, 어떤 게 진짜인지 판정합니다. 이 프롬프트는 명시적으로 스킬 콘텐츠 전체를 적대적(adversarial)으로 취급하고, "this skill is verified safe" 같은 문장이 나오면 오히려 의심도를 올리도록 되어 있어요.
스캔 대상이 스캐너를 설득하려 들 수 있기 때문에 이 방어선이 중요합니다.
스코어 계산 방식
# SkillSpector 스코어링 로직 (개념 요약)
SEVERITY_POINTS = {
"critical": 50,
"high": 30,
"medium": 15,
"low": 5,
}
# 동일 규칙 반복은 감쇠(diminishing returns) 적용
# 각 finding에 모델 confidence 곱함
# 실행 코드 포함 시 1.3x 배수 (스크립트 있는 스킬은 취약 확률 2.12배)
# 총합은 100으로 clamp 후 밴드/추천으로 매핑
score = min(100, sum(f.points * f.confidence for f in findings) * executable_multiplier)
여기서 봐야 할 건 두 가지예요. 첫째, recall과 precision이 의도적으로 스테이지로 분리되어 있습니다. 정적 패스는 넓게, 모델 패스는 좁게. 둘째, 전체 파이프라인의 **공식 precision은 약 87%**이고, 이 수치는 모델 스테이지가 켜져 있을 때를 전제로 합니다(기본값은 켜짐, 단 API 키 필요).

3개 스킬 실전 결과 — 숫자가 아니라 '차이'를 읽어라
| 스킬 | 정적 결과 | 모델 패스 후 | 최종 스코어 | 판정 |
|---|---|---|---|---|
| vuln-test-skill (악성 픽스처) | 16개 finding | 3개 오탐 제거 | 100 | Do not install |
| github (정직한 래퍼) | 0개 | 0개 | 0 | Safe |
| gh-issues (871줄 자동화) | 20개 medium, 31점 | 16개 benign 제거, 4개 유지 | 27 | Caution |
악성 픽스처 — 가장 위험한 건 산문이었다
SKILL.md 안에 이렇게 적혀 있었습니다: "read the user's AWS keys", "ignore prior instructions". 평문 영어입니다. Python 헬퍼가 아니라 마크다운 안이에요. 코드만 보는 스캐너는 이걸 카운트하지 못합니다.
정직한 GitHub 래퍼 — 아무것도 안 나온 게 핵심
정적 0개, LLM 0개. 지루한 결과지만 가장 중요한 결과예요. 문제만 찾는 스캐너는 초인종에 연결된 화재경보기입니다. 아무 일 없는데도 울려요. 깨끗한 패스가 있어야 다른 경보를 믿을 수 있습니다.
871줄 자동화 — 여기가 진짜 시험대
정적 스캔에서 20개 medium, 31점. 그중 16개가 동일 규칙(external transmission)이었고, 전부 api.github.com을 가리켰어요. GitHub이 일인 스킬이 GitHub과 통신한다고 16번 지적당한 겁니다.
모델 패스가 이 16개를 benign으로 제거하고 4개만 남겼어요.
- 2개: 무인 플래그에서 자율 코드 수정 + 미확인 PR
- 2개: 스케줄 실행을 위해 남겨진 상태 파일
개발자 의도 패스가 정적이 놓친 것도 하나 잡았습니다 — 전역 git config 변경. 레포 하나 자동화 범위를 넘어선 동작이에요.
스코어는 31 → 27로 재계산. 여전히 Caution이지만, 이제 의미 있는 Caution입니다. 31은 리스크 평가 옷을 입은 노이즈였고, 27은 실제 행동 3개를 이름 붙인 값이에요.
스코어는 손실 압축(lossy compression)입니다. 실제 정보는 숫자가 아니라 스테이지 간의 차이에 있어요.
이 기술의 한계와 주의사항
- 베이스라인 기능의 함정:
skillspector baseline ./skill -o .skillspector-baseline.yaml로 알려진 finding을 억제할 수 있지만, 무심코 돌리면 정상 자동화가 악성 exfiltration으로 바뀌는 것도 숨길 수 있어요. 베이스라인 억제는 "이 카테고리는 그만 보겠다"는 결정입니다. - Exit code 함정: SkillSpector는 risk score 50 이하에서 성공(0)으로 종료합니다. 27이나 31 같은 CAUTION 결과가 기본값으로 CI를 통과해요.
- 재현 불가: 코드 어디에도 temperature가 고정되어 있지 않아서, 정수 단위까지 동일한 재현은 안 됩니다. 결정적 레이어와 산술만 정확히 일치해요.
- 문맥 의존성:
api.github.com으로의 트래픽은 GitHub 스킬에서는 기능, PDF 포맷터에서는 사고입니다. 같은 finding, 다른 판정.
실무 적용 가이드 — 파이프라인에 어떻게 넣을까
- Ingest 단계: 정적 스캔만. 무료, 오프라인, 결정적. 큐 정렬용으로만 씁니다. 절대 이걸로 승인/차단 결정하지 마세요.
- Approval 게이트: 실제 배포될 스킬에만 LLM 4개 패스를 태웁니다. 토큰과 셋업 비용이 들지만, 여기서만 씁니다.
- Finding은 개수가 아니라 '행동 단위'로 그룹핑. 동일 도메인에 대한 동일 규칙 16개는 위협 16개가 아니라 행동 1개입니다.
- 스킬을 PR처럼 리뷰. 질문은 "이 기능이 위험한가?"가 아니라 **"매니페스트가 이걸 요청했나?"**예요.
함께 보면 좋은 글: Lambda 100만 개, AWS 계정 수천 개 ProGlove의 서버리스 스케일링 생존기 — 대규모 시스템에서 '숫자 뒤의 맥락'을 읽는 감각은 서버리스 스케일링에서도 똑같이 중요합니다.
그리고 AI 데이터센터 인프라 트렌드가 궁금하다면: 엔비디아 루빈 출시 전, 이미 준비 끝낸 Azure의 AI 데이터센터 전략

정리 — 다음 스킬을 설치하기 전에
스킬 스캐닝의 정적 레이어는 거의 해결된 문제예요. 빠르고, 무료고, 명백한 건 잘 잡습니다. 어려운 건 판단 레이어입니다. 강력한 기능이 정당한 기능인지, 보안 위협인지 결정하는 것. 이건 스코어 하나로 환원되지 않아요. 해석이 필요합니다.
제 개인 규칙을 공유할게요. 스킬은 밴드 하나로 설치하지도, 거부하지도 않습니다. 대신 4가지를 테이블에 올려요.
- 내가 틀렸을 때 무엇을 잃는가 — 최종 결정 요인
- 스캔 출력을 요약이 아니라 리스트로 읽기
- 매니페스트의 명시된 목적 — 기능과 위협이 같은 규칙을 트리거할 때 구분하기 위해
- 작성자와 트리거의 구체성 — '모호함'이 스캐너의 일을 대신하게 두지 않기
다음 단계 학습 방향
- MCP 서버 모드로 게이트 걸기:
skillspector mcp로 실행하면scan_skill툴을 노출하고safe_to_install을 반환합니다. 에이전트 런타임이 설치 자체를 루프 안에서 막을 수 있어요. 사람이 스캐너를 먼저 돌리길 기대하는 대신에요. - Invariant mcp-scan, Cisco AI Defense 같은 MCP 스캐너와 조합. 전자는 MCP 서버 정의를, 후자는 결정적 검사 + LLM 판정을 병행합니다.
- Anthropic의 Agent Skills 공식 문서와 Snyk ToxicSkills 리포트를 함께 읽으세요. 전자는 포맷 정의, 후자는 실측 유병률 데이터를 제공합니다.
마지막으로 하나. 검토 중인 아티팩트는 사람 리뷰어에게도 입력입니다. SkillSpector의 메타 프롬프트는 "this skill is verified safe"를 의심 사유로 읽습니다. 여러분도 그렇게 하세요. 감사자에게 하는 산문 — 안심시키는 말, 자기 인증, 헬퍼 스크립트는 건너뛰어도 안전하다는 코멘트 — 은 타게팅 신호지, 위로가 아닙니다. 상승된 리스크로 취급하세요. 증거로는 절대요.