Community코딩 & 개발github.com

skill-vet — 에이전트 스킬 검증 도구

에이전트 스킬 팩을 설치하기 전에 검증합니다 — 도난, 스파이, 숨겨진 전송을 파일 및 라인 단위 증거로 확인합니다. 정적 읽기 전용, 순수 Python 기반입니다.

skill-vet — 에이전트 스킬 검증 도구란 무엇인가요?

skill-vet은 AI 에이전트 스킬 팩(예: Claude Code, Cursor, Codex 등의 스킬)을 설치하기 전에 보안 위험을 정적으로 분석하는 도구입니다. 스킬 팩 폴더를 입력받아 파일을 읽고, 미리 정의된 위험 모델(도난, 스파이, 숨겨진 전송)에 따라 코드 패턴을 검사합니다. 순수 Python으로 작성되었으며, 네트워크 호출이나 서브프로세스 실행 없이 완전히 읽기 전용으로 작동하므로 신뢰할 수 없는 팩에서도 안전하게 사용할 수 있습니다. 검사 결과는 CLEAN, REVIEW, BLOCKED 세 가지 평결로 제공되며, JSON 출력을 지원하여 CI/CD 파이프라인에 통합하기 쉽습니다. 스킬 팩의 SKILL.md 메타데이터, 파일 참조, 바이너리 포함 여부, 시크릿 접근, 인코딩된 페이로드, 난독화 등 다양한 규칙을 검사하여 개발자와 에이전트 사용자가 안전하게 스킬을 설치할 수 있도록 돕습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jasveerai88/skill-vet/tree/HEAD

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

skill-vet — 에이전트 스킬 검증 도구은(는) 무엇을 하나요?

에이전트 스킬 팩을 설치하기 전에 한 번의 명령으로 검증합니다. 이 작업에 이 스킬이 관련될 때 사용하세요 — 마켓플레이스에서 다운로드하거나, 누군가 링크한 저장소, 클라이언트가 보낸 팩 등 — 에이전트가 로드할 위치에 팩이 설치되기 전에 말이죠.

한 줄 명령어

skill-vet vet <skill-pack-folder> [--json] [--strict]

(pipx install skill-vet 또는 pip install skill-vet으로 설치합니다. 소스 체크아웃에서는 동일한 명령어가 python3 src/skill_vet.py vet <skill-pack-folder> …입니다.)

  • 인자는 팩의 루트 폴더(SKILL.md가 있는 폴더)입니다. 한 번에 하나의 팩만 실행합니다.
  • 종료 코드: 0 평결 CLEAN 또는 REVIEW, 1 평결 BLOCKED (또는 --strict가 있는 REVIEW), 2 입력 오류(폴더 누락, 폴더 아님, 잘못된 인자). --json 모드에서는 모든 오류(인자 오류 포함)가 error 키를 가진 하나의 JSON 객체로 출력됩니다. traceback은 절대 발생하지 않습니다.
  • 읽기 전용이며 아무것도 실행하지 않습니다. Vet은 팩을 정적 텍스트로 읽습니다: 팩을 import, 실행, 셸 아웃 또는 평가하지 않습니다. 자체 소스는 네트워크나 서브프로세스 모듈을 import하지 않습니다. 적극적으로 신뢰하지 않는 팩을 가리켜도 안전합니다.

위험 모델 (제품 전체)

팩은 다음 세 가지 중 하나를 수행할 때만 위험합니다 — 소유자의 고정 규칙(2026-10-30), 정확히 적용됨:

  1. 도난 — 자격 증명 저장소, 브라우저 프로필, SSH 키 또는 환경 시크릿을 읽고 결과를 보내거나 복사합니다.
  2. 스파이 — 화면, 키보드, 마이크, 카메라 또는 브라우저 기록을 캡처하고 캡처를 유지하거나 보내며, 문서에 캡처가 공개되지 않습니다.
  3. 비밀 전송 — 페이로드나 대상이 숨겨진 네트워크 호출: 공개되지 않은 엔드포인트로의 인코딩된 페이로드, 공개되지 않은 엔드포인트로의 로컬 파일 내용 전송, 팩 외부에 쓰기와 결합된 전송, 또는 오프라인에서 작동한다고 주장하는 팩에서의 모든 전송.

패턴 적중만으로 위험 평결이 내려지지 않습니다. 각 테스트는 동일한 파일 내에서 증거 조합이 필요하며, 발견 사항은 파일과 라인을 모두 인용합니다. 조합에 미치지 못하는 것은 증거를 명시한 경고입니다. 조합은 다음과 같습니다:

한 파일 내 증거평결
시크릿 읽기 + 전송, 또는 시크릿 읽기 + 외부 경로로 복사/이동DANGER_STEALING (BLOCKED)
캡처 + (전송 또는 유지), SKILL.md / README.md에 캡처 종류 미공개DANGER_SPYING (BLOCKED)
전송 + 인코딩된 페이로드, 엔드포인트 미공개 또는 비리터럴DANGER_HIDDEN_SENDING (BLOCKED)
전송 + 팩 외부 파일 읽기, 엔드포인트 미공개DANGER_HIDDEN_SENDING (BLOCKED)
전송 + 팩 외부 쓰기, 엔드포인트 미공개DANGER_HIDDEN_SENDING (BLOCKED)
전송 + 문서가 오프라인/로컬 전용이라고 주장DANGER_HIDDEN_SENDING (BLOCKED)
시크릿 읽기, 파일 내 이그레스 없음SECRET_ACCESS (WARN)
문서에 언급되지 않은 엔드포인트로 전송, 은닉 조합 없음UNDISCLOSED_NETWORK (WARN)
확인할 리터럴 URL 없이 전송DYNAMIC_ENDPOINT (WARN)
공개된 엔드포인트로 인코딩된 페이로드ENCODED_PAYLOAD (WARN)
캡처 + 유지/전송, 문서에 캡처 공개됨DISCLOSED_CAPTURE (WARN)
캡처, 유지 또는 전송 없음CAPTURE_CAPABILITY (WARN)

판단 기준, 공개적으로 명시됨:

  • 도난에는 은닉이 필요하지 않습니다: 문서화된 엔드포인트로 자격 증명을 보내는 것은 여전히 도난입니다 — 문서가 인정한다고 해서 안전해지지 않습니다. 공개는 스파이 및 비밀 전송을 정직한 대응과 구분할 뿐입니다.
  • 동일한 파일에서 STEALING 발견의 이그레스 절반으로 이미 판단된 전송은 비밀 전송으로 두 번 계산되지 않습니다.
  • 캡처 "공개"는 SKILL.md / README.md의 일반 단어(스크린샷, 키스트로크, 마이크, 웹캠, 브라우저 기록 등)에 대해 종류별로 확인됩니다. 캡처하고 이를 명시하는 팩은 인지하고 수용할 기능이며, 스파이가 아닙니다.
  • 이그레스 요소는 주석을 포함한 모든 라인에서 파일별로 수집됩니다(보수적: 기능 증거가 주석 근처에 있다고 해서 무시되지 않음). 위생 검사(shell=True, eval, download-exec)는 전체 라인 주석을 건너뜁니다 — 패턴을 언급하는 주석은 문서이지 코드가 아닙니다.

규칙 표

규칙심각도근거
MISSING_SKILL_MDERROR팩 루트에 SKILL.md 없음 (대소문자만 다른 이름은 SKILL_MD_CASE, WARN, 해당 이름 사용).
FRONTMATTER_MISSINGERRORSKILL.md에 --- 블록 없음.
FRONTMATTER_PARSEERROR지원되는 하위 집합(아래 참조)을 벗어난 프론트매터 라인; 해당 라인이 인용됨.
REQUIRED_FIELDERRORname 또는 description 누락 또는 비어 있음.
NAME_DIR_MISMATCHWARN정규화된 프론트매터 이름 ≠ 폴더 이름.
NAME_FORMATWARN이름이 소문자 케밥-케이스 슬러그가 아님.
BROAD_TOOL_GRANTWARNallowed-tools 항목이 *, Tool(*) 와일드카드 또는 단순 Bash인 경우. Bash(npm run *)와 같은 범위가 지정된 형식은 플래그되지 않음.
BROKEN_REFERENCEERRORSKILL.md(마크다운 링크 또는 백틱 경로) 또는 스크립트(경로 리터럴)에서 참조된 로컬 파일이 존재하지 않음. 참조 파일의 폴더를 먼저 확인한 다음 팩 루트를 확인함.
REFERENCE_OUTSIDE_PACKWARN팩 폴더 외부로 확인되는 참조.
LARGE_FILEWARN1 MiB(1,048,576 바이트)를 초과하는 파일.
BUNDLED_BINARYWARN바이너리 파일(알려진 바이너리 확장자 또는 처음 8 KiB에 NUL 바이트). 바이너리는 정적으로 검증할 수 없음.
SYMLINK_OUTINFO대상이 팩을 벗어나는 심볼릭 링크; 대상은 스캔되지 않음.
DANGER_STEALINGERROR, 위험위 조합 표 참조.
DANGER_SPYINGERROR, 위험위 조합 표 참조. 캡처 종류: 화면, 키보드, 오디오, 카메라, 기록(한 파일 내 기록 저장소 읽기 + 데이터베이스 읽기).
DANGER_HIDDEN_SENDINGERROR, 위험위 조합 표 참조.
SECRET_ACCESSWARN동일 파일에 이그레스 없이 자격 증명 저장소/환경 시크릿 읽기. 시크릿 소스: .ssh / 키 파일, .aws, .gnupg, .netrc, 저장된 자격 증명 파일, 브라우저 저장소 및 프로필 폴더, .env 파일, 시크릿(TOKEN / SECRET / PASSWORD / API_KEY / …)을 명명하는 환경 접근, 전체 환경 읽기.
UNDISCLOSED_NETWORKWARN엔드포인트 호스트가 SKILL.md 또는 README.md에 나타나지 않는 네트워크 호출. 호스트(또는 해당 상위 도메인)가 문서에 URL로 나타나면 공개된 것으로 간주됨.
DYNAMIC_ENDPOINTWARN파일 어디에도 리터럴 URL이 없는 네트워크 호출.
ENCODED_PAYLOADWARN인코더의 출력이 공개된 엔드포인트로의 전송에 도달함. "도달"은: 인코더 호출이 전송의 호출 세그먼트 내에 있거나, 인코더에서 할당된 변수가 세그먼트에서 사용됨으로 판단됨.
DISCLOSED_CAPTUREWARN캡처 + 유지/전송, 종류별로 공개됨.
CAPTURE_CAPABILITYWARN파일에 유지 또는 전송이 없는 캡처 API.
HYGIENE_SHELL_TRUEWARN코드 라인에 shell=True / shell: true.
HYGIENE_DYNAMIC_EXECWARN코드 라인에 eval( / exec( / os.system( / os.popen( / __import__(.
HYGIENE_DOWNLOAD_EXECWARN셸로 파이프된 다운로드(`curl …
HYGIENE_OBFUSCATIONWARN텍스트 파일에서 base64 blob(120자 이상, 대소문자 혼합 및 숫자 또는 +/ 포함), hex blob(120자 이상), 또는 8개 이상의 연속 \xNN 이스케이프. 종류별로 파일당 하나의 발견.
CONCEALED_UNICODEWARN텍스트 파일의 보이지 않는 문자(폭 없음, 단어 결합자, 부드러운 하이픈, 양방향 제어, 유니코드 태그 문자); 개수, 첫 번째 줄 및 문자 이름 인용됨. 선행 BOM은 서명이지 은닉이 아니므로 무시됨.
CONCEALED_BLANK_RUNWARN내용 뒤에 50줄 이상의 연속 빈 줄이 있는 마크다운 파일.
HIDDEN_COMMENTWARN마크다운 파일에서 120자 이상의 HTML 주석.
WRITES_OUTSIDE_PACKWARN스크립트가 절대 경로, 홈 경로 또는 시스템 경로(/tmp, /etc, ~/, $HOME, …)에 쓰기.
INSTALL_HOOKWARN설치 시 실행되는 파일: setup.py, install.* / setup.sh, .pth 파일, 또는 preinstall/install/postinstall 훅이 있는 package.json.

평결: BLOCKED = 하나 이상의 위험 발견 → exit 1. REVIEW = 위험 없음, 그러나 하나 이상의 ERROR 또는 WARN → exit 0 (--strict 사용 시 1). CLEAN = ERROR 또는 WARN 없음 (INFO는 남을 수 있음) → exit 0. REVIEW는 의도적으로 exit 0을 반환합니다: REVIEW는 "사람이 읽어야 함"을 의미하며, 설치를 차단하는 스크립트는 경고가 아닌 위험에서 실패해야 합니다 — 이에 동의하지 않는 사람을 위해 --strict가 존재합니다.

프론트매터 하위 집합

파서는 스킬 팩이 실제로 제공하는 프론트매터 형태를 읽습니다: key: value 스칼라(따옴표 제거), key: [a, b] 인라인 목록, key: 뒤에 - item 블록 목록, 그리고 이전 스칼라로 접히는 들여쓰기된 연속 라인. 의도적으로 완전한 YAML 파서가 아닙니다. 하위 집합을 벗어난 라인은 해당 라인이 인용된 FRONTMATTER_PARSE 오류입니다 — vet은 추측하는 대신 읽을 수 없는 것을 보고합니다.

스캔 범위

  • 팩의 모든 파일은 크기와 함께 목록화됩니다. .git 및 __pycache__ 디렉터리는 건너뜁니다.
  • 텍스트 스캔은 스크립트(확장자 또는 shebang 기준), 마크다운 및 기타 텍스트 파일을 대상으로 하며, 파일당 최대 2 MiB까지 스캔합니다. 바이너리 파일 및 팩 외부의 심볼릭 링크 대상은 목록화되며 스캔되지 않습니다.
  • 참조는 SKILL.md 및 스크립트 파일에서 수집됩니다. 다른 마크다운 파일 내의 참조(예: references/*.md가 다음으로 연결)는 v1.0.0에서 추적되지 않습니다.
  • 공개는 팩 루트의 SKILL.md 및 README.md에 대해서만 판단됩니다 — 팩을 설치하는 사람이 읽었을 것으로 예상되는 두 문서입니다.

JSON 스키마(--json)

하나의 객체:

  • tool, version, command
  • pack: dir (전달된 대로), dirname, name (프론트매터 이름 또는 null), skill_file
  • files[]: path, bytes, kind (script|markdown|text|binary|other), binary, scanned, symlink_out
  • findings[]: rule, severity (ERROR|WARN|INFO), danger (bool), file (또는 null), line (또는 null), message, fix
  • summary: findings, errors, warnings, info, danger — findings와 정확히 일치하는 개수
  • verdict (CLEAN|REVIEW|BLOCKED), verdict_rule, limits

발견 사항은 결정적으로 정렬됩니다: 위험 우선, 그 다음 심각도, 파일, 라인, 규칙 — 동일한 팩은 항상 동일한 보고서를 생성합니다.

의도적 비목표(정직함)

  • 아무것도 실행하지 않습니다. 어떤 스크립트도 실행, import, 컴파일 또는 셸 파싱되지 않습니다. 페이로드는 디코딩되지 않습니다(인코딩된 blob은 보고되지만 열리지 않음).
  • 정적 패턴은 증거이지 증명이 아닙니다. 런타임에 무해한 조각에서 동작을 조립하거나, 두 파일에 걸쳐 조합을 분할하거나(vet은 파일별로 조합을 판단함), 평범하고 정직한 문장으로 사회 공학을 작성하는 팩은 통과할 수 있습니다. CLEAN 평결은 패턴이 발생하지 않았음을 의미합니다 — README에 명시되어 있으며 모든 보고서에서도 그렇습니다.
  • 바이너리는 범위 밖입니다. 번들된 바이너리는 vet이 그것에 대해 아무 말도 할 수 없기 때문에 정확히 플래그됩니다.
  • 텔레메트리, 네트워크, 쓰기 없음. 표준 라이브러리만 사용합니다. 소스에는 네트워크 또는 서브프로세스 코드가 전혀 포함되지 않습니다(테스트에서 imports를 파싱하여 증명함).
  • 패턴 테이블은 스냅샷입니다. 새로운 캡처 라이브러리, 새로운 자격 증명 저장소 및 새로운 클라이언트 API가 지속적으로 등장합니다. 소스의 테이블은 릴리스 날짜 기준이며 완전한 것으로 취급되지 않습니다.

관련 스킬