
SecureAI-Scan은 AI 기반 앱에 특화된 보안 문제(프롬프트 인젝션, MCP 도구 남용, RAG 데이터 오염, 에이전트 신뢰 위반 등)를 찾기 위해 TypeScript 및 JavaScript 코드베이스를 스캔하는 CLI 도구입니다.
이 분야의 대부분 스캐너는 키워드를 패턴 매칭한 뒤 이를 발견 항목으로 간주합니다. SecureAI-Scan은 실제 import 해석된 코드를 통해 실제 소스 → 흐름 → 싱크 경로를 추적하며, 기본 스캔은 증명할 수 있는 것만 보여줍니다. 계정 불필요, 클라우드 업로드 없음, 어떤 데이터도 머신을 벗어나지 않습니다.
공식 OWASP Top 10 for LLM Applications 2026, Top 10 for Agentic Applications (2026), 그리고 MCP Top 10을 출시 주부터 모두 다룹니다.
npx --yes [email protected] scan .
계정, 클라우드 업로드, Python 인터프리터, 또는 구성이 필요하지 않습니다. TypeScript, JavaScript, Python, MCP 구성, 그리고 Agent Skill 번들은 자동으로 감지됩니다.
**측정된 `0.9.0` 릴리스 후보:** 136/136 테스트 · 88.08% 문장 커버리지 · 9개 공개 저장소의 12,676개 파일 · 검토된 기준선 대비 신규 기본 계층 지문 0건. [증거](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [방법론 및 한계](https://github.com/akanthed/secureai-scan/blob/main/docs/ReleaseAssurance.md)```
▌ HIGH AI001 Prompt injection via user input
PROVEN LLM01:2026 Prompt Injection
source src/chat.ts:8 request data `req.body.input`
flow src/chat.ts:13 passed as `systemPrompt`
sink src/chat.ts:10 openai.chat.completions.create — system role (OpenAI)
fix Keep system prompts static; pass user input as a user-role message.
이것이 당신을 위한 것인가요? SecureAI-Scan은 의도적으로 LLM, MCP, RAG/에이전트 위험 — 프롬프트 인젝션, 도구 중독, 안전하지 않은 출력 처리, 벡터 스토어 접근 제어, 에이전트 스킬 중독 — 에 범위를 한정합니다. 일반적인 SAST 또는 시크릿 스캐너가 아니며, 그렇게 되려고 하지도 않습니다. LLM 형태의 페이로드가 없는 알려진 악성 패키지(예: 이메일 API 호출에 하드코딩된 유출 주소)는 패턴 규칙이 아닌 오프라인 권고 목록(DEP003)으로 탐지됩니다. 코드베이스가 LLM, MCP 서버, 벡터 스토어와 통신하거나 Agent Skills를 제공한다면, 이 도구는 당신을 위해 만들어졌습니다.
새 기능: LiteLLM Proxy(config.yaml)에 대한 정적 구성 스캔 — 하드코딩된 시크릿, 평문 공급자 엔드포인트, 누락된 가드레일. Rules(LLC001–LLC003) 참조.
proven(추적된 데이터 흐름 또는 파싱된 구성 사실), likely(해석된 싱크, 휴리스틱 1단계), 또는 heuristic입니다. 기본 스캔은 proven + likely만 표시합니다. 휴리스틱은 --paranoid를 통해 선택적으로 활성화됩니다.openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …)로 해석될 때만 "LLM 호출"로 간주됩니다. Google Maps 클라이언트가 다시 LLM으로 플래그 지정되는 일은 없습니다.npm run regression은 실제 공개 저장소(OpenAI/Anthropic/Vercel AI SDK, 공식 MCP 서버, LlamaIndex)를 커밋된 수동 검토 기준선과 대조하여 스캔하고, 새로운 proven/likely 발견 사항이 있으면 실패합니다. 실제 전후 수치는 Testing & benchmarking을, 그 배경 이야기는 What we found scanning real repos를 참조하세요 — 레이블이 지정된 악성 스킬 코퍼스에서 6/6 탐지율, 그리고 라이브러리 수준의 정직한 발견 사항에 대해 llama_index를 "취약"하다고 부르지 않는 이유. Discussion write-up →--output report.sarif는 풀 리퀘스트와 Security 탭에 발견 사항을 인라인으로 표시합니다.secureai-scan bom .은 SDK, 모델 ID, 벡터 스토어, 에이전트 프레임워크, MCP 서버의 구문 기반 인벤토리를 구축하여 OWASP LLM Top 10 / EU AI Act 문서화 요구 사항에 매핑합니다..mcp.json, claude_desktop_config.json, .cursor/mcp.json을 파싱합니다: 고정되지 않은 npx -y 서버, 인라인 시크릿, 평문 HTTP 전송.command/args를 플래그 지정합니다 — 2026 MCP STDIO RCE 공개 뒤의 패턴.SKILL.md 파일에 적용합니다 — Agent Skills는 전체를 컨텍스트에 로드하므로, 중독된 스킬은 다른 이름의 중독된 도구 설명입니다.SKILL.md뿐만 아니라 디렉토리로 스캔되며, 모든 콘텐츠 검사는 난독화 해제된 텍스트 변형에 대해 실행됩니다. 이는 Cloak and Detonate(arXiv:2607.02357)에서 조사된 9개 스캐너의 >90%를 우회한 공개 기법 — 호모글리프, 제로폭 분할, .git/ 또는 build/에 스테이징된 페이로드, *.test.ts 파일에 숨겨진 유출 — 을 대상으로 합니다. Evasion resistance 참조.scripts/sync-advisories.js로 재생성됩니다. 플래그 없이 모든 스캔에서 오프라인으로 실행됩니다. CVE는 고정된 버전이 영향 범위 내에 증명 가능하게 있을 때만 발생하며, 문서화된 악성 패키지는 모호한 범위에서도 발생합니다. 백도어 설치는 복구 불가능하기 때문입니다.SecureAI-Scan은 일반 SAST 도구나 컨테이너/IaC 스캐너의 대체재가 아닙니다 — 대신이 아니라 함께 실행하세요. LLM/MCP/RAG 공격 표면을 위해 특별히 설계되었으며, 평면적인 키워드 발견 사항보다 데이터 흐름 증거를 강조합니다.
| SecureAI-Scan | Semgrep (OSS 규칙) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| 프롬프트 인젝션(소스→싱크 추적) | ✅ import 해석 데이터 흐름 | ⚠️ 패턴 규칙만, 커뮤니티 유지 | ❌ | ⚠️ CodeQL 가능하지만 AI 전용 규칙셋 없음 |
| MCP 도구 중독 / 구성 위험 | ✅ MCP007–010, 구성 스캐너 | ❌ | ❌ | ❌ |
Agent Skill 중독(SKILL.md) | ✅ 회피 저항, 번들 인식 | ❌ | ❌ | ❌ |
| RAG / 벡터 스토어 잘못된 구성 | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| 알려진 악성 AI 패키지 권고 | ✅ DEP003, 오프라인, 버전 인식 | ❌ | ⚠️ 일반 CVE 피드, AI 전용 아님 | ⚠️ Dependabot, 일반 CVE 피드 |
| 일반 SAST(SQLi, XSS, 경로 탐색) | ❌ 설계상 범위 외 | ✅ | ❌ | ✅ |
| 컨테이너 / IaC 스캔 | ❌ | ❌ | ✅ | ⚠️ CodeQL/Actions 통해 |
| 증거 계층(proven/likely/heuristic) | ✅ | ❌ 발견 사항은 평면적 | ❌ | ⚠️ CodeQL 일부 있음, AI 튜닝 아님 |
| SARIF 출력(GitHub 코드 스캐닝) | ✅ | ✅ | ✅ | 기본 제공 |
| 오프라인 실행, 계정 불필요 | ✅ | ✅ (OSS 규칙) | ✅ | ❌ GitHub 필요 |
이미 Semgrep 또는 GHAS를 실행 중이라면 유지하세요 — SecureAI-Scan은 그들이 전혀 모델링하지 않는 위험 표면을 위해 추가하세요.
먼저 질문하고 싶으신가요? 무료 **SecureAI-Scan AI Security Advisor on ChatGPT**를 사용해 보세요.
GitHub이나 Twitter에서 찾은 MCP 서버를 실행하려고 하시나요? 먼저 도구 설명을 **MCP X-Ray**에 붙여넣으세요 — 설치 없이 브라우저에서 숨겨진 유니코드, 인젝션된 지시, 알려진 악성 패키지를 확인합니다.
secureai-scan scan .을 실제(작고 의도적으로 취약한) 파일에 대해 처음부터 끝까지 실행한 실제 출력 — 소스:

스캐너가 끝까지 추적하는 공격 형태:
| MCP 도구 중독 데이터 흐름 | RAG 컨텍스트 인젝션 데이터 흐름 |
|---|---|
![]() | ![]() |
95%의 경우 필요한 명령어:```bash secureai-scan scan .
그 외 필요한 것은 모두 준비되어 있습니다. `secureai-scan scan . --help`를 실행하면 터미널에서 이 모든 내용을 동일한 그룹으로 확인할 수 있습니다:
**일상 사용**
| 플래그 | 기능 |
|------|---------------|
| *(없음)* | `proven` + `likely` 등급 발견 항목 — 기본값이며 플래그가 필요 없습니다 |
| `--paranoid` | `heuristic` 등급 발견 항목도 포함 |
| `-s, --severity <level>` | `low`\|`medium`\|`high`\|`critical` 이상의 발견 항목만 표시 |
| `--output <file>` | 전체 보고서 작성 — `.sarif`(GitHub 코드 스캐닝), `.json`, `.md` 또는 `.html` |
**실행할 규칙 범위**
| 플래그 | 기능 |
|------|---------------|
| `-r, --rules <list>` | 지정한 규칙 ID만 실행(예: `AI001,MCP007`) |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | 규칙 카테고리 하나만 실행 |
| `--check-dependencies` | `package.json`/`requirements.txt`를 npm/PyPI 레지스트리와 대조하여 오타 및 환각 패키지(`DEP001`/`DEP002`) 확인. `-r`로 해당 규칙을 직접 선택하면 자동 활성화되므로 둘 다 전달할 필요가 없습니다. 항상 오프라인으로 실행되는 `DEP003`(악성 패키지)에는 필요하지 않습니다 |
**CI / 워크플로우**
| 플래그 | 기능 |
|------|---------------|
| `--fail-on <severity>` | 해당 심각도 이상의 발견 항목이 있으면 `1`로 종료 |
| `--baseline <file>` | 저장된 기준선 대비 새로 발생하거나 변경된 이슈만 추적 |
| `--policy <file>` | `.secureai-policy.json`에서 임계값, 건너뛸 경로, 차단할 규칙 로드(파일이 있으면 자동 감지 — `secureai-scan init`으로 생성 가능) |
**고급**
| 플래그 | 기능 |
|------|---------------|
| `--min-confidence <0-1>` | `--paranoid`보다 세밀한 제어: 정확한 신뢰도 점수(`0.9` proven / `0.65` likely / `0.35` heuristic) 미만의 발견 항목 숨김 |
| `--limit <n>` | 터미널에 표시할 최대 규칙 그룹 수(기본값 `10`) — 전체 상세 내용은 항상 `--output`으로 출력 |
| `--debug` | 스캔한 모든 파일과 실행된 규칙 출력 |
**설치 전에 스캔 — 클론도, 설정도 필요 없음:**```bash
secureai-scan skill anthropics/skills # a GitHub "owner/repo" shorthand
secureai-scan skill https://github.com/… # or a full git URL
secureai-scan skill ./some/local/skill-dir # or a local path
secureai-scan mcp some-mcp-server-package # a bare npm package name
secureai-scan mcp owner/mcp-server-repo # or git, same as `skill`
skill 및 mcp는 대상을 가져와서 스캔한 다음, 가져온 복사본을 삭제합니다(--keep를 사용하면 대신 검사할 수 있습니다). 가져온 것은 절대 실행되지 않습니다: npm 대상은 npm pack으로 다운로드되며 — tarball만, install도, 라이프사이클 스크립트도 없습니다 — git 대상은 일반 git clone --depth 1입니다. 이것이 가장 중요한 순간입니다: 스킬이 ~/.claude/skills/에 들어가거나 서버가 .mcp.json에 들어가기 전이지, 그 후가 아닙니다.
기타 명령어:```bash secureai-scan bom . --output AI_BOM.md # AI Bill of Materials secureai-scan explain AI001 # why + exploit + fix example, for any rule secureai-scan threat-model . # THREAT_MODEL.md with the OWASP coverage matrix — example: docs/examples/THREAT_MODEL.example.md secureai-scan init # policy file + CI workflow, one-time setup
코드에서 검토된 결과를 억제합니다:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist
name: SecureAI-Scan on: [pull_request] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: akanthed/[email protected] with: scanner-version: 0.10.0 fail-on: high
PR에 인라인 주석과 저장소의 보안 탭에 결과가 표시됩니다. (`secureai-scan init`은 CLI를 직접 사용하여 동일한 워크플로를 생성합니다.)
스캔 결과가 깨끗한가요? 자신의 README에 배지를 추가하세요:```md
[](https://github.com/akanthed/SecureAI-Scan)
결과가 푸시되기 전에 발견하는 것을 선호하시나요? GitHub Action 대신 또는 그와 함께 이 저장소를 pre-commit 훅 소스로 추가하세요:```yaml repos:
훅은 모든 커밋에서 전체 프로젝트를 스캔합니다(변경된 파일만 스캔하는 것이 아니라 — 파일 A로의 데이터 흐름 추적은 파일 B에 의존할 수 있는데, 부분 스캔은 이를 놓칠 수 있습니다) 그리고 기본적으로 `high`+ 심각도 발견 사항에서 커밋을 차단합니다. 자체 구성에서 임계값을 재정의하세요:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
42개 규칙, 공식 OWASP Top 10 for LLM Applications(2026)에 매핑 — 해당되는 경우 OWASP Top 10 for Agentic Applications(2026, ASI), OWASP MCP Top 10(2025), EU AI Act 조항도 포함. 버전별 2026 적용 범위 및 한계 참조; threat-model은 각 스캔 대상 프로젝트에 대한 매트릭스를 렌더링합니다.
| 규칙 | 검증 내용 | OWASP |
|---|---|---|
| AI001 | 사용자 입력이 시스템/개발자 프롬프트로 유입됨(소스 → 싱크 추적, 함수/파일 경계를 넘어 포함) | LLM01 |
| AI002 | 프롬프트 내용 또는 비밀값이 로그에 기록됨(LLM SDK를 사용하는 파일에서) | LLM02 |
| AI003 | 요청 핸들러에서 인증 검사 없이 LLM 호출이 발생함 | LLM06 |
| AI004 | 전체 사용자/세션 객체가 프롬프트로 직렬화됨(필드 선택은 플래그되지 않음) | LLM02 |
| AI005 | LLM 출력이 eval/exec/SQL/HTML 싱크에 도달함 | LLM10 |
| AI006 | 고영향 도구(삭제, 결제, 배포 등)가 승인 게이트 없이 노출됨 | LLM03 |
| AI007 | 검색된 RAG 콘텐츠가 권한 있는 프롬프트에 삽입됨 | LLM01 |
| AI008 | 시스템 프롬프트 텍스트에 비밀값이 포함됨 | LLM08 |
| AI009 | 무제한 사용자 입력 / 토큰 한도 누락 | LLM06 |
| AI010 | 가져온 외부 콘텐츠가 프롬프트로 유입됨 | LLM01 |
| AI011 | 에이전트 출력이 다운스트림 호출에서 시스템 역할로 승격됨 | LLM03 |
| AI012 | LLM 출력이 스키마 검증 없이 파싱됨 | LLM10 |
| MCP001 | MCP 도구 메타데이터가 검증 없이 시스템 프롬프트에 도달함 | LLM01 |
| MCP002 | MCP 서버 URL이 사용자 입력으로 구성됨 | LLM04 |
| MCP003 | MCP 도구 결과가 시스템 역할로 승격됨 | LLM10 |
| MCP004 | MCP 서버가 고정되지 않은 npx -y 패키지로 실행됨 | LLM04 |
| MCP005 | 커밋된 MCP 구성에 비밀값이 인라인으로 포함됨 | LLM02 |
| MCP006 | MCP 서버가 평문 HTTP로 실행됨 | LLM04 |
| MCP007 | MCP 도구 이름 또는 설명에 보이지 않는/양방향 유니코드가 숨겨져 있음 | LLM01 · MCP03 |
| MCP008 | MCP 도구 설명에 에이전트 지시 주입 문구가 포함됨 | LLM01 · MCP03 |
| MCP009 | 다른 도구로 호출을 유도하는 도구 설명(섀도잉) | LLM01 · MCP03 |
| MCP010 | MCP stdio 서버 명령/인자가 사용자 입력으로 구성됨(RCE) | LLM04 · MCP05 |
| SKL001 | 에이전트 스킬 번들 어디에든 보이지 않는/양방향 유니코드가 존재함 | LLM01 |
| SKL002 | 스킬 설명 또는 본문에 에이전트 지시 주입 문구가 포함됨(난독화를 통한 매칭) | LLM01 |
| SKL003 | 스킬 콘텐츠가 다른 스킬의 사용 시점/방식을 조종함(섀도잉) | LLM01 |
| SKL004 | 단계적/자체 추출 페이로드: 불투명 블롭 + 디코딩 및 실행 지침 | LLM04 · MCP04 |
secureai-scan explain <RULE_ID>는 모든 규칙에 대한 익스플로잇 워크스루와 전/후 코드 예제를 제공합니다.
세 개의 독립적인 스캔 표면이 하나의 병합되고 중복 제거된 발견 목록으로 이어집니다:``` ┌─────────────────────┐ *.ts / *.js ───▶ │ ts-morph AST rules │───┐ │ (import-resolved │ │ │ sinks + dataflow) │ │ └─────────────────────┘ │ │ ┌─────────────────────┐ │ ┌──────────────┐ ┌─────────────────┐ *.py ───▶ │ tree-sitter AST + │───┼───▶ │ scan.ts │───▶ │ evidence filter │ │ local taint flow │ │ │ merge/dedupe│ │ → confidence │ └─────────────────────┘ │ │ + suppress │ │ → severity │ │ │ (// secure- │ │ → baseline diff │ .mcp.json, ┌─────────────────────┐ │ │ ai-ignore) │ │ → report │ SKILL.md ───▶ │ Config/bundle scan │──┘ └──────────────┘ └─────────────────┘ │ (off-disk, evasion- │ │ │ resistant) │ ▼ └─────────────────────┘ terminal · sarif · json · md · html
package.json, requirements.txt ─▶ dependency-guard.ts (advisories.ts, offline, version-aware)
Every AST rule only calls a function an "LLM call" if it resolves through real imports to a known SDK — never by name-matching alone. See [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) for the full breakdown of each surface, and [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) for how the evidence-tier contract works.
## MCP server (use it from Claude)
The package ships an MCP server exposing `scan_repository`, `explain_rule`, `generate_bom`, and `scan_untrusted_target` (fetch and scan a skill or MCP server before Claude recommends installing it — same fetch-without-executing behavior as the `skill`/`mcp` CLI commands):```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Claude Code / Claude.ai 사용자를 위해, skills/secureai-scan/SKILL.md는 Claude가 언제 스캔을 실행해야 하는지(AI/LLM 코드 검토, 또는 MCP 서버/Agent Skill을 설치하기 전에 확인)와 결과를 읽는 방법을 가르칩니다 — 위의 MCP 서버와 달리 별도 프로세스를 실행할 필요가 없습니다. 사용하려면 skills/secureai-scan/ 디렉터리를 .claude/skills/로 복사하세요.
2026년 7월, 두 건의 공개 연구에서 Agent Skill 스캐너가 사소하게 우회된다는 사실이 밝혀졌습니다.
Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357)는 1,613개의 실제 악성 스킬을 사용하여 9개의 공개된 스킬 스캐너에 대한 적대적 연구를 수행했습니다. 두 가지 공격 계열 모두 페이로드를 정확히 보존합니다:
.git/에 아카이브 및 인코딩하여 넣고, 첫 실행 시 이를 재구성하는 무해한 커버 SKILL.md를 둡니다. 테스트된 모든 스캐너에서 ≥90% 우회율, 대부분에서 ≥96%. 한 하이브리드 스캐너의 탐지율은 ~99%에서 ~10%로 떨어졌습니다.별도로, Gecko Security (VentureBeat)는 *.test.ts 파일에 페이로드를 배포했습니다. 모든 공개 스캐너가 이를 건너뛰었지만 — Jest와 Vitest는 다음 npm test에서 해당 파일을 자동으로 발견하고 실행합니다.
SecureAI-Scan은 각 공개된 기법을 처리합니다. 아래 각 행에는 test/deobfuscate.test.js의 해당 테스트 또는 test-fixtures/vulnerable/skills/의 픽스처가 있습니다:
| 기법 (논문 명칭) | 예시 | 처리 방식 |
|---|---|---|
| 제로폭 삽입 | Ignore all previous… | 텍스트가 보이지 않는 문자 제거 변형과 매칭됨 |
| 혼동 가능 글리프 | сurl (키릴 문자 с) | 매칭 전에 동형 글리프를 ASCII로 접기 |
| 대소문자 변환 | EvAl | 모든 콘텐츠 패턴이 대소문자 구분 없음 |
| 변수 분할 | 'cu' + 'rl' | 매칭 전에 인접한 문자열 리터럴 결합 |
| 줄 단위 분할 | ev⏎al(x) | 단어 내 줄바꿈을 추가 변형으로 결합 |
| 파일별 추출 | 소스된 헬퍼의 페이로드 | SKILL.md뿐만 아니라 전체 번들이 스캔됨 |
| 디렉터리 이동 | scripts/x.sh → build/x.sh | 번들 내 디렉터리 건너뛰기 목록 없음 |
| 확장자 변경 | x.sh → x.txt | 파일이 확장자가 아닌 콘텐츠로 스캔됨 |
| 크기 패딩 | 스캐너의 한도를 넘도록 패딩 | 초대형 파일은 헤드 읽기로 처리, 건너뛰지 않음 |
| SFS packing | .git/skillpack.dat의 페이로드 | 번들의 .git/ 아래 비-git 파일은 proven으로 처리됨 (SKL004) |
| 테스트 파일 스테이징 | *.test.ts의 페이로드 | 번들 스캔은 의도적으로 테스트 경로를 강등하지 않음 (SKL005) |
난독화 해제는 일반적으로 정밀도 위험입니다 — 매칭이 더 많아지면 노이즈도 더 많아집니다. 여기서 논리는 반전됩니다: 난독화 해제 후에만 나타나는 매칭은 강등이 아니라 proven으로 승격됩니다. 일반 문서에는 "ignore previous instructions" 안에 제로폭 조이너가 포함되지 않으며, curl 안에 키릴 문자 с도 포함되지 않습니다. 은닉 자체가 의도의 긍정적 증거입니다.
비교는 원시 매칭의 집합에 대한 것이며, 단순히 "원시 텍스트가 전혀 매칭되었는가"가 아닙니다 — 그렇지 않으면 공격자가 무해한 문구 하나를 평문으로 남겨 신호를 가릴 수 있습니다.
두 개의 새 번들 규칙은 키워드가 아닌 접속사에서만 발동합니다:
tar -x를 언급하는 README만으로는 충분하지 않습니다. 실제 아카이브(gzip/zip/png/pdf/wasm — 확장자가 아닌 매직 바이트로 확인)는 압축 방식과 관계없이 처음부터 "불투명"하지 않습니다.~/.aws/credentials, "token"이라는 단어가 아님) 또는 대량 환경 변수 열거(os.environ.items(), os.environ["API_KEY"]가 아님) — 및 하드코딩된 비로컬 호스트로의 이그레스가 같은 파일 내 25줄 이내에 있어야 하며, 또는 하나 이상의 이름 변경을 통해 재할당된 후 실행되는 원격 페치가 필요합니다. 한 함수에서 ~/.npmrc를 읽고 40줄 뒤에 레지스트리를 호출하는 게시 헬퍼는 깨끗하게 유지되며, API 호출을 위해 명명된 환경 변수 하나를 읽는 것은 절대 플래그되지 않습니다 — 두 형태 모두 안전 픽스처로 고정되어 있습니다.우리가 직접 작성한 픽스처뿐만 아니라 두 개의 실제 세계 코퍼스로 검증됨: anthropics/skills의 18개 실제 스킬 번들 전체에서 0건의 발견 및 vercel/ai의 14개 전체, 그리고 cisco-ai-defense/skill-scanner의 라벨링된 평가 코퍼스에서 6/6 정확 (각각 _expected.json 판정이 있는 20개 스킬) — 안전으로 라벨링된 모든 항목에서 거짓 양성 0건. 테스트 및 벤치마킹 참조.
정직한 한계: 논문의 결론은 런타임 폭파가 정적 분석보다 낫다는 것이며, 이는 맞습니다. 이 규칙들을 아는 적응형 공격자는 이 규칙들이 다루지 않는 변환을 작성할 수 있습니다. 여기서 바뀌는 것은 회피의 비용입니다 — 공개되고 현재 유통되는 기법은 더 이상 작동하지 않으며, 이를 무찌르는 데 필요한 난독화는 이제 그 자체로 발견의 심각도를 높입니다. 정적 스캐닝은 필터이지 보안 경계가 아닙니다. 스캐너가 무엇을 말하든 신뢰할 수 없는 스킬을 신뢰할 수 없는 코드로 취급하세요.
prepublishOnly를 통해 테스트, 커버리지 하한, 검토된 실제 저장소 회귀 게이트 및 tarball 검사를 호출합니다.이 프로젝트는 계약상 SLA나 독립 인증이 없는 단일 유지관리자 프로젝트입니다. 위의 통제는 위험을 줄입니다; 정적 스캔을 보안의 증거로 바꾸지는 않습니다.
거짓 양성은 스캐너를 죽입니다. SecureAI-Scan의 규칙 엔진은 세 가지 엄격한 규칙을 따릅니다:
test-fixtures/safe/에는 거짓 양성을 유발하던 패턴(비식별화된 PII 페이로드, Google Maps 클라이언트, LLM 클라이언트 옆의 환경 변수 API 키, 일반 응답 로깅, OAuth 메타데이터 필드, 스트리밍 응답 chunks, 픽션/내러티브 프롬프트 텍스트)이 포함되어 있습니다. 해당 위치의 모든 발견은 스위트를 실패시킵니다.세 계층 — 하나만으로는 스캐너의 주장을 신뢰하기에 충분하지 않기 때문입니다 — 정밀도와 재현율은 서로 다른 실패 모드이며, 둘 다 확인됩니다.
1. 픽스처 코퍼스 — 정밀도 + 재현율, 모든 빌드에서 실행됩니다.```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) 및 [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe)는 함께 스캔됩니다. 모든 취약한 픽스처는 `proven`/`likely` 증거(재현율)에서 예상 규칙을 발생시켜야 하며, 모든 안전한 픽스처는 **0건**의 `proven`/`likely` 결과(정밀도)를 생성해야 합니다. 빠르고 결정적이지만, 이는 스캐너가 테스트를 위해 특별히 작성된 코드에서만 올바르게 동작함을 입증할 뿐입니다.
**2. 실제 세계 회귀 벤치마크 — 우리가 작성하지 않은 공개 저장소를 대상으로.**```bash
npm run regression # scan the full curated repo set
npm run regression -- --fresh # re-clone everything first
npm run regression -- openai-node # scan just one repo by name
npm run regression -- --update-baseline # accept the current findings
scripts/regression-scan.js는 큐레이션된 다양한 실제 공개 저장소(OpenAI/Anthropic/Vercel AI SDK, 공식 MCP 서버 및 TypeScript SDK, LlamaIndex, 그리고 스킬 번들 커버리지를 위한 anthropics/skills 및 cisco-ai-defense/skill-scanner — TS와 Python, SDK 소비자 예제 코드와 SDK 작성자 소스를 아우름)를 복제하고, 각각을 빌드된 CLI로 스캔합니다.
test/regression-baseline.json에 이미 없는 proven/likely 발견 사항이 있으면 0이 아닌 종료 코드로 종료합니다 — 이 파일은 소스 라인과 대조하여 이미 검토된 발견 사항의 수작업 검토 기록입니다. 지문은 repo|rule|file이며 줄 번호가 아니므로, 일반적인 업스트림 변경으로 인한 노이즈가 발생하지 않습니다. 새 지문은 스캐너가 정당화해야 하는 주장입니다: 실제 문제가 아니라면 규칙 버그이며, 근본 원인에서 수정되고 test-fixtures/safe/ 픽스처로 고정됩니다. 읽지 않은 발견 사항을 베이스라인에 추가하는 것은 메커니즘 전체를 무력화합니다.
스킬 번들 커버리지는 별도로 다룹니다 — cisco-ai-defense/skill-scanner의 evals/ 코퍼스에는 레이블이 지정되어 있기 때문입니다. 20개의 픽스처 각각은 _expected.json 판정을 제공하며 문자 그대로 malicious/ 또는 safe/라는 디렉터리 아래에 있으므로, 정밀도 확인뿐 아니라 재현율 확인 역할도 합니다: 범위 내 악성 픽스처 6/6이 탐지되고, safe로 레이블된 항목에서는 0건의 발견 사항이 있으며, anthropics/skills의 실제 번들 18개 전체와 vercel/ai의 14개 전체에서도 0건의 발견 사항이 있습니다. (나머지 Cisco 범주 — SQL 삽입, 경로 탐색, 리소스 고갈, 함수 인자의 일반적인 eval(), 네 개 파일에 걸쳐 의도적으로 분할된 페이로드 — 는 문서화된 LLM/MCP/RAG 범위를 벗어나거나 단일 파일 내 결합 분석을 넘어섭니다. 각각에 대한 구체적인 근거는 0.6.0 변경 로그 항목을 참조하세요.)
원래 정밀도 수정을 주도한 실행의 과거 전/후 비교(기본 증거 수준의 발견 사항, --paranoid 없음):
| 저장소 | 이전 | 이후 | 무엇이 잘못되었나 |
|---|---|---|---|
| vercel/ai | 773 | 1 | examples/, 최상위 tests/, 하이픈으로 연결된 ecosystem-tests/ 스타일 디렉터리가 낮은 신뢰 경로로 인식되지 않았음; chunks(일반적인 스트리밍 응답 변수)가 명확한 RAG 증거로 처리됨 |
| openai/openai-node | 47 | 0 | 동일한 경로 감지 격차가 SDK 자체의 examples//ecosystem-tests/에 적용됨 |
| anthropics/anthropic-sdk-typescript | 2 | 0 | 최상위 tests/ 디렉터리의 동일한 경로 감지 격차 |
| modelcontextprotocol/typescript-sdk | 3 | 0 | token_endpoint/tokenType 스타일 OAuth 메타데이터 필드가 유출된 비밀로 플래그됨 |
| run-llama/llama_index | 18 | 15 | Python 검사가 "system prompt"를 포함한 모든 description= 필드를 컨텍스트와 무관하게 proven MCP 도구 중독으로 플래그함. 나머지 15개는 라이브러리 자체의 일반 검색기 정의에 대한 VEC001 적중 — 벡터 DB SDK의 자체 소스를 스캔하는 것이므로 확인할 필터가 존재할 수 없음. 정직하고 본질적인 한계이며 버그가 아님 |
현재 실행 (2026-08-06) — 버전별 증거는 docs/benchmarks/v0.9.0.json에 기록됨:
| 저장소 | 발견 사항 | 규칙 | 상태 |
|---|---|---|---|
| openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers | 0 | — | 깨끗함 |
| anthropics/skills (실제 스킬 번들 18개) | 0 | — | 깨끗함 — SKL001–005에 대한 순수 정밀도 확인 |
| vercel/ai (파일 5,691개) | 0 | — | 트리아지 전에는 40건(AI001, AI003, AI005, AI010, MCP002)이었음 — 모든 항목을 소스와 대조하여 수작업 검토했고 모두 오탐으로 확인되었으며, 3개의 독립적인 근본 원인 버그(아래 참조)로 추적되어 수정되었고, 전체 재스캔에서 다시 깨끗함이 확인됨 |
| run-llama/llama_index | 46 | VEC001 | 본질적 한계, 버그 아님 — 테넌트 필터가 존재할 수 없는 라이브러리 자체의 일반 검색기 정의 |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | 모두 malicious/로 레이블된 픽스처에 있음 — 범위 내 6/6, safe/로 레이블된 항목에서는 0건 |
vercel/ai 트리아지는 세 가지 실제 근본 원인 버그를 발견했습니다 — v0.6.0 스킬 규칙에 특화된 것은 없으며, 모두 여러 규칙에서 사용되는 공유 로직에 있었습니다:
resolveLlmSink가 메서드 이름과 무관하게 LLM SDK 모듈로 해석된 모든 호출을 모델 호출로 처리함 — generateText와 함께 ai 패키지가 내보내는 타입 가드인 isToolUIPart를 LLM 호출로 플래그함. 이것만으로 5개 발견 그룹 중 3개(AI001, AI003, AI010)가 발생했습니다.DANGEROUS_CALLEES에 SQL 삽입 스타일 싱크용 "query"가 포함되어 있지만, "query"는 합법적인 LLM/에이전트 호출 동사이기도 함 — Claude Agent SDK 자체 모델 호출인 claudeSdk.query({ prompt, options })가 공유 메서드 이름만으로 "위험한 싱크로 전달된 LLM 출력"으로 플래그되었습니다.REQUEST_SOURCES(MCP002, MCP010, VEC003에 걸쳐 동일하게 중복됨)가 단순한 "params."와 일치함 — 반드시 HTTP 요청 데이터가 아닌, 관례적으로 params라는 이름의 모든 함수 매개변수. URL 스킴 검증기(assertOpenLinkParams(params: unknown))가 "사용자 입력의 MCP 서버 URL"로 플래그되었습니다.세 가지 모두 근본 원인(특정 호출 사이트가 아님)에서 수정되었고 test-fixtures/ 아래 영구 픽스처로 고정되었습니다. 전체 세부 사항은 CHANGELOG.md에 있습니다.
3. 취약-대-패치 검증 — 정밀도가 아닌 재현율을 증명합니다.
위의 두 계층은 스캐너가 안전한 코드에서 조용히 유지되는지만 확인합니다. DEP003의 권고 검사는 반대 방향으로 검증됩니다: 문서화된 취약 버전에 패키지를 고정하고 플래그되는지 확인한 다음, 패치된 버전에 고정하고 플래그되지 않는지 확인합니다.```bash
node --test test/dependency-guard.test.js
covers: `[email protected]` (CVE-2025-6514, 취약) 플래그됨 / `[email protected]` (패치됨) 통과; `[email protected]` (백도어 이전) 통과 / `[email protected]` (이후 — 악성 패키지에는 합법적인 패치가 존재하지 않음) 여전히 플래그됨; `llama-cpp-python==0.2.71` (CVE-2024-34359, OSV 생성 세트에서) 플래그됨 / `==0.2.72` (패치됨) 통과, PyPI 이름 정규화(`llama_cpp_python`) 하에서도 포함; 그리고 `langchain>=0.1.0` 스타일의 고정되지 않은 지정자는 기본 보고에서 **0건**의 결과를 생성함. 이 테스트를 구축하면서 실제 격차를 발견했습니다: `DEP003`은 패키지 이름으로만 권고를 매칭했고, 선언된 버전을 권고의 영향 범위와 실제로 비교하지 않았습니다 — [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts)에서 수정됨.
모호성은 권고 종류별로 의도적으로 다르게 해결됩니다. **악성** 패키지는 선언된 버전을 확인할 수 없어도 플래그가 발생합니다 — 백도어 설치를 되돌릴 수 없으므로 플래그 쪽으로 실패합니다. **CVE**는 선언된 버전이 영향 범위 내에 있음이 입증 가능한 정확한 고정 버전일 때만 `proven`으로 발생합니다. 고정되지 않았지만 영향을 받을 가능성이 있는 경우 `heuristic`(`--paranoid` 전용)으로 내려갑니다. 악성 종류 규칙을 162개 항목의 CVE 스냅샷에 적용하면 `langchain>=0.1.0`을 선언하는 모든 저장소에 치명적 결과가 발생합니다 — 규모에 따라 실행 불가능한 노이즈입니다.
## 로드맵
출시된 기능과 계획된 기능은 [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md)를 참조하세요. 두 언어 엔진 모두 AST 기반입니다: TypeScript/JavaScript용 ts-morph와 Python용 Tree-sitter. Python의 import, 호출, 할당, 데코레이터, 스코프, 키워드 인자, 딕셔너리 필드, 문자열은 모두 구문 노드입니다. 대상 코드는 절대 import되거나 실행되지 않으며, Python 인터프리터도 필요하지 않습니다. 남은 Python 격차는 함수/파일 간 오염 깊이로 제한되며, 파싱 문제가 아닙니다. 스캔 성능과 알려진 제한 사항은 [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md)에 문서화되어 있습니다.
## 기여
기여를 환영합니다 — 워크플로는 [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md)를, 위의 정밀도 기준을 충족하는 탐지 규칙을 추가하는 방법은 [`docs/WritingRules.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/WritingRules.md) / [`docs/RuleDevelopment.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/RuleDevelopment.md)를 참조하세요. 모든 새 규칙에는 [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable)과 [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) 양쪽에 픽스처, `src/scanner/catalog.ts`의 항목, `test/corpus.test.js`의 테스트 케이스가 필요합니다 — `npm test`가 세 가지를 모두 강제합니다.
## 라이선스
MIT © Akshay Kanthed
| SKL005 | 번들 동반 파일에서 자격 증명 읽기 + 하드코딩된 외부 이그레스 | LLM02 · MCP04 |
| SKL006 | Claude Code의 동적 컨텍스트 주입 구문(!`cmd`/```!)을 통한 로드 시 명령 실행, 도구 권한 게이트 이전에 발생 | LLM04 · MCP05 |
| SKL007 | 스킬의 allowed-tools 프론트매터에 범위 제한 없는 Bash 권한 부여 | LLM03 |
| SKL008 | 스킬이 외부 URL에서 지침을 가져와 에이전트가 이를 따르도록 지시함("Circus of Skills") | LLM04 |
| SKL009 | 스킬이 다른 컨텍스트 파일(MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)에 기록하여 백도어를 유지함 | LLM05 |
| SKL010 | 스킬 프론트매터 또는 번들 구성 파일에 안전하지 않은 YAML/JSON 역직렬화 태그 | LLM04 |
| VEC001 | 테넌트/사용자 필터 없는 벡터 검색 | LLM09 |
| VEC002 | 무제한 또는 사용자 제어 검색 한도 | LLM06 |
| VEC003 | 사용자 콘텐츠가 공유 벡터 저장소에 수집됨 | LLM05 |
| VEC004 | 테넌트/네임스페이스 태깅 없는 수집 | LLM09 |
| DEP001 | 레지스트리에서 찾을 수 없는 종속성 이름(옵트인 --check-dependencies) | LLM04 |
| DEP002 | 인기 패키지와 한 글자 차이인 종속성 이름(옵트인) | LLM04 |
| DEP003 | 문서화된 악성 릴리스 또는 중요 CVE가 있는 종속성 — 모든 스캔에서 오프라인으로 확인, 버전 범위 인식(postmark-mcp, mcp-remote CVE-2025-6514 등) | LLM04 · MCP04 |
| LLC001 | LiteLLM 프록시 config.yaml에 하드코딩된 비밀값 | LLM02 |
| LLC002 | LiteLLM 프록시 api_base가 평문 HTTP로 접근 가능 | LLM04 |
| LLC003 | LiteLLM 프록시 구성에 guardrails: 섹션 없음(휴리스틱, --paranoid 전용) | LLM03 |