SkillSpector

NVIDIAApache-2.0活跃维护

安装前扫描 Claude Code、Codex 和 MCP 技能的安全工具,能发现漏洞、恶意模式、提示注入、数据外泄和供应链风险。

Security scanner for AI agent skills. Detect vulnerabilities, malicious patterns, security risks, prompt injection, data exfiltration, and supply-chain risks in Claude Code, Codex, and MCP skills before you install them.

v2.13.4发布于 2026-11-04 22:40 UTC25,119star +326 / 7 天2,198fork同步于 2026-11-10 05:21 UTC

怎么上手

由 README 提炼,执行前请确认
uv tool install git+https://github.com/NVIDIA/skillspector.git
skillspector scan ./my-skill/
硬件要求按 README 说明

README 未说明 GPU 或显存要求;提供基于 Python 3.12-slim 的 Docker 镜像,无需本机安装 Python。

支持 Apple Silicon有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

安装前扫描 AI agent skill 的安全风险,判断它是否值得信任

安装前扫描 Claude Code、Codex 和 MCP 技能的安全工具,能发现漏洞、恶意模式、提示注入、数据外泄和供应链风险。

核心能力

  • 覆盖 71 种漏洞模式、17 个类别,含提示注入与数据外泄
  • 两阶段分析:快速静态扫描加可选 LLM 语义评估
  • 支持 Git 仓库、URL、zip、目录、单文件多种输入
  • 输出终端、JSON、Markdown、SARIF 四种报告格式
  • 给出 0-100 风险分与是否可安全安装的建议

容易踩的坑

需 Python 3.12+,且 make 目标要求先激活虚拟环境;用 skillspector mcp 需安装时加 [mcp] extra;LLM 语义分析需另配 provider 和 API key,否则只能静态扫描。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v2.13.4
  1. v2.13.4SkillSpector v2.13.4最新正式版2026-11-04 · 2026-11-04 22:40 UTC

    SkillSpector 2.13.4

    This maintenance release includes the v2.13.3 fixes and selected improvements to provider compatibility, source inspection, and recovery of malformed AI responses.

    Fixes

    • #806: provider compatibility and structured responses for GPT-6.1 Sol and Claude Opus 5.5 across supported routes.
    • #947, #950, and #973: bounded parsing of shell, PowerShell, JavaScript templates, Python strings, and Markdown documentation.
    • #974: inspect supported generated shell helpers and archived sources without executing uploaded code. Dependencies outside the uploaded skill and unprovable runtime behavior remain incomplete.
    • #997 and #999: distinguish unsupported syntax, unresolved parser boundaries, and exhausted resource limits.
    • #1000: bounded TAR/GZIP inspection with explicit accounting for omitted archive members.
    • #979, #998, and #1007: preserve provider failure details, bound retries, and distinguish outages from security findings.
    • #1011: modern JavaScript parsing and narrower classification of passive presentation assets and formatting metadata.
    • #1014: shared recovery of malformed AI assessments, including plain-text and individual-field requests. Recovered findings remain visible. Unverifiable formatting-only responses carry an explicit warning and retain unverified coverage; refusals, provider failures, and unrelated inspection gaps retain their separate diagnostics.
    • The bounded advisory-count correction from #1010, plus required literal, documentation, reference, and text-pattern prerequisites. The broader Python docstring exemption from that PR is excluded.

    Integration corrections preserve malformed single-object findings, avoid repeated shell-comment scans, and limit decoded signature metadata ownership to parser boundaries while retaining checks on unknown fields and decoded payloads. The maintenance branch's scoped allowed-tools and output-file protections are retained. Markdown list fences end with their containing list, so following prose does not extend a shell string. Source distributions contain the runtime package and public package metadata.

    Scope and upgrading

    This release does not resolve every parser limitation or guarantee that every large skill completes. File, archive, retained-content, report, and time limits remain enforced. Additional container support can consume the shared byte budget and displace previously inspected members in oversized inputs; those omissions remain explicitly incomplete, and disappearing findings behind a limit are not security fixes.

    Downstream consumers need compatible handling of complete_with_caveats and the format-recovery warning policy. Updating the scanner alone does not change an evaluator's decision to block incomplete inspection. Some consumers also need separate handling for passive BMP assets inside chart archives.

    Regenerate and review version-bound baselines for 2.13.4. Release artifacts are published on GitHub as a wheel and source distribution; this workflow does not publish to PyPI.

  2. v2.13.3SkillSpector v2.13.32026-11-02 · 2026-11-02 06:44 UTC
    查看更新说明

    SkillSpector 2.13.3

    This patch release builds on the published v2.13.2 maintenance release, preserving its fixes and supported Python versions while adding selected merged P0 security and scan-integrity fixes, including four dependency security updates.

    Fixes

    • #945: Make the tool-description comparison batch limit configurable with SKILLSPECTOR_TP4_MAX_BATCHES, default 256, and raise the default workflow deadline to 1,080 seconds (18 minutes), configurable with SKILLSPECTOR_MAX_WORKFLOW_SECONDS. Other file, byte, token, concurrency, and provider limits remain enforced. The release also includes the required bounded parallel batch execution change from upstream commit 9919c159, honoring SKILLSPECTOR_MAX_LLM_CONCURRENCY and preserving batch result order.
    • #946: Validate whether AI responses actually finished and contain the required fields. Report refusals, truncated responses, missing fields, and invalid tool results as incomplete inspection. Permit bounded formatting recovery only when a completed response preserves the same values and structure. Use consistent structured-response configuration across analyzers, including the supported Opus 5 gateway route, and run semantic discovery through the shared asynchronous path.
    • #948: Recognize additional bounded shell syntax, Python string ownership, and generated browser-template text without treating ordinary data as unfinished shell commands. Preserve findings and incomplete inspection when text can reach command execution, syntax is ambiguous, or analysis limits are exceeded.

    Security and scan integrity

    • #924 and #938: Reject baseline and recursive report destinations that would overwrite the primary input or a discovered child skill's input, including file aliases.
    • #919: Report incomplete inspection when a requested custom YARA rule directory is unavailable.
    • #772: Include non-English gap-fill findings in contributed batch-scan scores, recommendations, and exit status instead of retaining a stale SAFE verdict.
    • #906: Inspect extensionless Python scripts with CRLF shebangs while recording interpreter uncertainty.
    • #874: Restore PowerShell coverage for SC3 obfuscation and P2 hidden-instruction checks.
    • #882: Preserve shell-line boundaries across Markdown code fences during marker reconstruction, keeping separate command lines from being combined into a misleading directive. Long rejected backtick runs are scanned linearly within the current text bound.
    • #826: Follow credential reads nested in urllib request headers into network sinks.
    • #776 and #881: Inspect npx commands in JSON MCP configurations and recognize additional Claude, Cline, and Roo Code credential/configuration filenames. The required #683 dependency accepts version pins only on the relevant package operand, so an unrelated later pin cannot hide an unpinned package. JSON command findings retain correct source lines when keys use escapes or appear more than once.
    • #893: Detect writes to known agent-memory files and directories from automatically run test carrier
  3. v2.13.2SkillSpector v2.13.22026-10-28 · 2026-10-28 14:19 UTC
    查看更新说明

    SkillSpector 2.13.2

    This patch release builds on the published v2.13.1 maintenance release and backports three fixes for false incomplete-analysis reports. It preserves the existing maintenance fixes, third-party dependency lock, and provider defaults.

    Fixes

    • #779: Recognize command wrappers with no wrapped command at a proven clause boundary. Markdown table cells and ordinary source references to timeout, sudo, nice, and xargs no longer cause false parser-limit reports. Redirections, unfinished fragments, and commands in subsequent clauses retain their existing checks.
    • #917: Treat sudo -n as a flag and parse descriptor redirections such as 2>&1 as complete operators. Align the MCP TP4 batch limit with its existing 128-file limit so supported inputs are not prematurely truncated after 64 batches. Byte, token, declaration, file, runtime, and provider-error limits remain enforced.
    • #921: Recognize direct template messages on verified built-in Node assertion imports, avoiding false shell substitution limits in JavaScript helpers. Classify .mjs and .cjs files consistently, including archive members. Raw security findings and their source locations are preserved. Ambiguous syntax, assertion aliases or mutations, effectful interpolation, and exceeded bounds remain conservative; TypeScript retains its existing parser path.

    Scope and upgrading

    The JavaScript correction is a bounded assertion-message proof, not a general JavaScript parser or dataflow analysis. It assumes ordinary built-in assertion semantics and does not prove the absence of arbitrary runtime mutation or object getters. Existing findings and independent inspection limitations remain visible; these fixes do not suppress malformed provider responses.

    Regenerate and review exact version-bound finding baselines for 2.13.2 rather than relabeling baselines from another scanner version. The GitHub release provides a wheel and source distribution; the publication workflow does not publish to PyPI.

    Validation and known limitations

    This release preserves every v2.13.1 commit and includes #779, #917 and #921. All six candidate CI jobs passed, including 12,395 unit tests. Fresh local packaging checks, 72 offline acceptance scans and 22 additional boundary checks also passed.

    Live acceptance completed all 28 planned cases: 26 passed and two large-skill TP4 cases failed under the unchanged 600-second workflow budget. The candidate planned 118 batches, but completed 65 from source and 66 from the installed wheel; all remaining ranges were explicitly reported as runtime-limited. The old 64-batch planning cap is removed, but complete live inspection of this large input is not established. Live LLM coverage used one configured model through the NVIDIA gateway; other provider routes were not qualified. The CAD checks exercised preparation plus TP4, not the entire CLI analysis workflow.

    The batch cap is now 128, so a reported 145-batch workload still exceeds it. Large-skill runtime completion, remaining obfuscated-instruction limitations, evaluator binary-scope admission policy and deduplication limits, and general malformed-provider-response failures remain unresolved follow-up work. Independent content findings and missing-reference limitations remain visible; upgrading does not guarantee that a downstream admission gate passes.

    See release PR #922 for validation details.

  4. v2.13.1SkillSpector v2.13.12026-10-23 · 2026-10-23 09:40 UTC
    查看更新说明

    SkillSpector 2.13.1

    This patch release is based on the published v2.13.0 maintenance branch. It corrects false incomplete-analysis reports for ordinary Python report strings, quoted script text, regular expressions, and possessive Markdown prose, and false P2 hidden-instruction findings that spanned visible text between HTML comments. It also includes quoted-version metadata and passive documentation image handling, validated Office worksheet and printer metadata support, and faster security text normalization.

    The maintenance backports include three security fixes: safer report and baseline file output, incomplete-coverage reporting for failed batch gap-fill passes, and bounded tool-misuse context and deadlines. Five are analyzer, report and CLI fixes: TM1 repeated assignments, MP3 hardware reset states and state adoption, recursive MEDIUM recommendations, and rejection of unsupported recursive and registry option combinations. A further customer fix prevents ordinary code comparisons from causing incomplete inspection.

    The selected changes below include the minimal command-token prerequisite from #697 and the prohibition-context prerequisite from #658 used by MP3; the remaining changes from those PRs are excluded.

    Selected changes

    • Office worksheets identified by matching content types and workbook relationships can use the existing 16 MiB analysis allowance. This lets the reported 1.97 MB worksheet receive complete static inspection. Unrelated archive members keep their 1 MB limit; shared expansion, compression, integrity and runtime limits remain enforced.

    • Validated Office printer settings no longer receive executable-content findings solely because their filenames end in .bin. Content types, relationships and the complete printer-record structure must agree. Malformed records, executable signatures and independent findings retain their safeguards; opaque driver data keeps explicit inspection limits.

    • Security text normalization now reuses bounded character caches and processes unchanged text in bounded chunks. This reduces PDF/GIF scan overhead while preserving findings, source offsets, cancellation checks and existing limits.

    • Shell heredoc parsing retains quote ownership for Python formatted strings, avoiding false unfinished-shell reports.

    • Follow-up parser corrections preserve opening quotes in inline JSON examples and inline-code delimiters in ordinary nested Markdown list paragraphs. Quotes cannot incorrectly consume later examples; actual shell bodies and uncertain block contexts retain their existing checks and bounds.

    • Explicitly documented dotted configuration fields no longer invent missing files when the bundle contains no matching artifact. Explicit file uses, existing files and ambiguous basename matches retain normal accounting.

    • #525 treats quoted version numbers in YAML version fields as metadata. Real file references, including numeric filenames, keep their normal missing or resolved status.

    • #877 avoids format-only AE1 findings for corroborated passive PNG/JPEG documentation images. Exact source provenance, supported image structure and complete limitation evidence are required. Image coverage remains partial; this does not perform OCR or image semantic inspection. Executable uses, malformed images and independent findings retain their safeguards.

    • #777 keeps comparisons such as len(value.strip()) < 12 from being read as an unfinished tag-removal instruction. Real tag directives, including ones after a comparison, retain their existing detection and incomplete-inspection safeguards. Its quoted-command controls also require the curl active-token entry from #697. This release includes that one-token prerequisite; the broader flag and mapping-key exemptions from #697 remai

  5. v2.13.0SkillSpector v2.13.02026-10-16 · 2026-10-16 18:09 UTC
    查看更新说明

    SkillSpector v2.13.0

    Summary

    This release selectively backports security hardening, blocking scan fixes, and executable Python support onto v2.12.0. It includes 55 selected PRs; #743 is included through #734. It does not include every change merged into main since v2.12.0.

    Compatibility notes

    • NVIDIA Build defaults to GLM-5.3 after retirement of the previous default. Explicit model overrides remain supported.
    • The Codex CLI analysis provider is disabled because its read-only sandbox permits access to host secrets. Use another supported provider.
    • MCP HTTP listeners are restricted to loopback. Remote deployments require an authenticating proxy.
    • Baseline generation uses atomic replacement. Updates that cannot preserve ownership or group safely are refused; write a new baseline file.
    • If a real directory ending in ! collides with an archive-member path, rename that directory to allow both files to be fully inspected.
    • Exact-fingerprint baselines are version-bound and must be regenerated after upgrading.
    • Static-only scans do not require an active LLM provider. LLM-enabled scans still reject unknown provider settings.
    • Other provider defaults remain as in v2.12.0. The newer-model default change from #806 and its rollback in #824 are not included.

    Included changes

    Security and scan integrity

    • #471: Reads SKILL.md metadata correctly when the file starts with an invisible encoding marker.
    • #473: Detects text-direction control characters that can disguise code in bundled scripts, not just Markdown.
    • #499: Marks recursive scan coverage as incomplete when linked skill folders are deliberately skipped.
    • #545: Detects access to sensitive files even when their paths are assembled from fixed strings.
    • #557: Marks analysis incomplete when custom YARA rules cannot be loaded or compiled.
    • #577: Preserves dangerous shell-command findings when deeper Python analysis cannot confidently replace them.
    • #578: Keeps distinct shell-command findings separate, even when their displayed snippets look identical.
    • #593: Adds lower-confidence warnings for multiline scripts that appear to open a remote shell, while preserving existing one-line detections.
    • #595: Shows how many linked skill folders were skipped and includes that count in recursive scan coverage.
    • #601: Checks complete TypeScript declaration files before treating them as non-executable, so partial or disguised runtime code is not skipped.
    • #604: Flags bundled settings that run commands, redirect AI traffic, or automatically approve connected tool servers.
    • #611: Finds Python flows from secret reads to risky uses even when the assignments and uses appear at different nesting depths.
    • #632: Inspects hooks declared in Claude plugin manifests and reports incomplete coverage when those declarations cannot be fully checked.
    • #637: Makes saved baselines cover every occurrence of a repeated finding, so later scans suppress them consistently.
    • #655: Detects bundled Node settings that use the short -r flag to load and run extra code.
    • #657: Preserves exact findings in baselines and protects existing baseline files during regeneration.
    • [#691](https://git

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    SkillSpector

    Security scanner for AI agent skills. Detect vulnerabilities, malicious patterns, and security risks before installing agent skills.

    Python 3.12+ License: Apache 2.0

    Overview

    AI agent skills (used by Claude Code, Codex CLI, Gemini CLI, etc.) execute with implicit trust and minimal vetting. In the 31,132-skill analyzed subset of the research dataset, 26.1% of skills contain vulnerabilities and 5.2% show likely malicious intent.

    SkillSpector helps you answer: "Is this skill safe to install?"

    SkillSpector is part of the NVIDIA Verified Skills pipeline, which scans, evaluates, and signs agent skills before publication. Skills that pass are published to the NVIDIA skills catalog.

    Documentation

    • Scan agent skills before installation — Hosted guide: when to scan, how to read a report, and how to gate installs.
    • Development guide — Architecture, package layout, and how to extend the analyzer pipeline.
    • Security policy — Report suspected vulnerabilities privately to NVIDIA PSIRT.
    • Analysis resource bounds — Fail-closed bundle, parser, nested-artifact, ledger, and finding ceilings.
    • Pi extension — Install SkillSpector as a Pi tool for scanning skills from inside agent sessions.
    • OpenCode extension — Install SkillSpector as an OpenCode tool and /skillspector command for scanning skills from inside agent sessions.

    Features

    • Multi-format input: Scan Git repos, URLs, zip files, directories, or single files
    • 71 vulnerability patterns across 17 categories: prompt injection, data exfiltration, privilege escalation, supply chain, excessive agency, output handling, system prompt leakage, memory poisoning, tool misuse, rogue agent, anti-refusal, trigger abuse, dangerous code (AST), taint tracking, YARA signatures, MCP least privilege, and MCP tool poisoning
    • Two-stage analysis: Fast static analysis + optional LLM semantic evaluation
    • Live vulnerability lookups: SC4 queries OSV.dev for real-time CVE data with automatic offline fallback
    • Multiple output formats: Terminal, JSON, Markdown, and SARIF reports
    • Risk scoring: 0-100 score with severity labels and clear recommendations
    • Baseline / false-positive suppression: Accept known findings via a glob-rule or fingerprint baseline so re-scans surface only new issues (docs)

    Quick Start

    Installation

    Open-source software notice: This project will download and install additional third-party open source software projects. Review the license terms of these open source projects before use.

    Create and activate a virtual environment first (all make targets assume the venv is active). Use uv or pip; the Makefile uses uv if available, otherwise pip.

    Quick install with uv (CLI-only):

    uv tool install git+https://github.com/NVIDIA/skillspector.git
    # Update later: uv tool update skillspector
    

    If you plan to run skillspector mcp, install the MCP extra at install time:

    uv tool install 'skillspector[mcp] @ git+https://github.com/NVIDIA/skillspector.git'
    

    From source:

    # Clone the repository
    git clone https://github.com/NVIDIA/skillspector.git
    cd skillspector
    
    # Create and activate virtual environment
    uv venv .venv && source .venv/bin/activate
    # or: python3 -m venv .venv && source .venv/bin/activate
    
    # Install for production use
    make install
    

    To contribute, follow the Development setup for a supported Python interpreter and development dependencies.

    Docker (no Python required)

    Run SkillSpector without installing Python by building it locally from the included Dockerfile. The image is based on the Docker Official Python 3.12-slim-bookworm image.

    Build the image:

    make docker-build
    # or: docker build -t skillspector .
    

    Scan a local directory by mounting your current directory into /scan, the container's working directory:

    docker run --rm -v "$PWD:/scan" skillspector scan ./my-skill/ --no-llm
    

    Scan with LLM analysis by passing credentials with a local .env file:

    cat > .env <<'EOF'
    SKILLSPECTOR_PROVIDER=anthropic
    ANTHROPIC_API_KEY=sk-ant-...
    EOF
    
    docker run --rm \
      -v "$PWD:/scan" \
      --env-file .env \
      skillspector scan ./my-skill/
    

    Or pass credentials directly from your shell environment:

    docker run --rm \
      -v "$PWD:/scan" \
      -e SKILLSPECTOR_PROVIDER=anthropic \
      -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
      skillspector scan ./my-skill/
    

    Write a report to the host filesystem by writing to the mounted directory:

    docker run --rm --user "$(id -u):$(id -g)" \
      -v "$PWD:/scan" \
      skillspector scan ./my-skill/ --no-llm --format json --output report.json
    

    Optional alias for repeated static scans:

    alias skillspector-docker='docker run --rm -v "$PWD:/scan" skillspector'
    skillspector-docker scan ./my-skill/ --no-llm
    

    Basic Usage

    # Scan a local skill directory
    skillspector scan ./my-skill/
    
    # Scan a single SKILL.md file
    skillspector scan ./SKILL.md
    
    # Scan a Git repository
    skillspector scan https://github.com/user/my-skill
    
    # Scan a zip file
    skillspector scan ./my-skill.zip
    
    Size limits

    SkillSpector enforces two independent caps on remote and archive inputs to bound the impact of oversized downloads and zip bombs:

    • Per-ingest cap: INGEST_MAX_BYTES (100 MiB) — applied to streamed URL downloads, total uncompressed size of zip archives, and post-clone disk usage of Git repos.
    • Zip member cap: INGEST_MAX_ZIP_MEMBERS (10,000) — caps the number of entries in a single zip.

    Note that the per-file 1 MB analysis cap (MAX_FILE_BYTES) is a separate, downstream limit: it bounds what individual analyzers will read out of an already-ingested directory. The ingest caps above bound how much content can land on disk in the first place. A breach of either ingest cap fails closed with an IngestLimitExceededError.

    Output Formats

    # Terminal output (default) - pretty formatted
    skillspector scan ./my-skill/
    
    # JSON output - machine readable
    skillspector scan ./my-skill/ --format json --output report.json
    
    # Markdown output - for documentation
    skillspector scan ./my-skill/ --format markdown --output report.md
    
    # SARIF output - for CI/CD integration and IDE tooling
    skillspector scan ./my-skill/ --format sarif --output report.sarif
    

    Recursive Collection Scanning

    --recursive scans immediate subdirectories containing SKILL.md as independent skills, with a default aggregate limit of 32 skills. To raise or lower it:

    skillspector scan ./my-skills/ --recursive --max-skills 100 --no-llm --format json
    

    --max-skills requires a local recursive collection and a positive integer; 0 does not mean unlimited. Discovery, byte, finding-record, time and report-size budgets still apply, so increasing the count does not guarantee complete coverage. When the count limit is reached, the CLI names the limit and suggests adjusting it or scanning sub-skills separately. Omitted skills remain visible in the aggregate report and coverage stays incomplete. Use --fail-on-incomplete in CI to fail on incomplete coverage; the default exit policy is unchanged.

    Batch Scanning

    Scan entire directories of skills in parallel from contrib/batch_scan/:

    python -m contrib.batch_scan.batch_scan ./my-skills/ --no-llm
    python -m contrib.batch_scan.batch_scan ./my-skills/ --workers 20 -f json -o report.json
    python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 20
    

    Supports multilingual detection (zh/ja/ko) and terminal/JSON/Markdown output.

    For LLM scans with higher concurrency, configure multiple API keys following .env.example — the pool improves throughput and resilience, provided the keys don't share an account-level rate limit.

    See the contrib guide for details.

    Note on LLM support: The default configuration targets DeepSeek as the cheapest public option. DeepSeek-Chat is expected to sunset, and the contributor does not have hardware to test against local models. The batch scanner was originally tested with OpenAI-compatible endpoints — DeepSeek's lack of structured-output support required manual JSON-parsing patches. If you can contribute a more universal backend (Ollama, vLLM, or a different provider), PRs are very welcome.

    Comparing MCP Registry snapshots

    Start with raw registry payload captures (registry-before.json and registry-after.json). Save a scan report as previous.json, then compare a later scan against that generated local report:

    skillspector scan registry-before.json --mcp-registry --format json --output previous.json
    skillspector scan registry-after.json --mcp-registry --format json \
      --mcp-registry-compare previous.json --output compared.json
    

    The optional comparison object lists added and removed server identities, changed normalized fields with their previous and current values, and an unchanged_count. Identity is the server name and version, so a new version appears as an addition and the old version as a removal if it is absent from the new scan. Acquisition source and scan timestamp are excluded from comparison. unmodeled_changes lists same-identity records whose raw-record hash changed while normalized fields match; these are not counted as unchanged. This can indicate a change to fields outside the snapshot model, or array reordering in the raw record. Package and remote ordering alone is not a normalized field change. Entries in changed may also contain changes outside the snapshot model; inspect the raw record to see those changes. Compare reports with the same selection scope: a server absent from the current input is reported as removed, which does not prove it was removed from a registry.

    The comparison file must be a local report produced by this registry mode with valid normalized snapshots. Malformed, oversized or duplicate server identities are rejected. This option requires --mcp-registry and is separate from the finding-suppression --baseline: it never suppresses findings or changes risk scores, and it does not fetch or execute listed server endpoints.

    Registry mode rejects skill-only controls such as --use-shipped-baseline, --transitive, --transitive-depth, --transitive-allow-prefix, and --transitive-deny-prefix with exit code 2, including an explicitly supplied default depth (--transitive-depth 1). Omit these options for registry scans.

    Suppressing False Positives (baseline)

    Suppress known/accepted findings so the risk score reflects only un-triaged issues and re-scans surface only new findings. See the suppression guide for the full reference.

    # Accept all current findings into a baseline (run once), then commit it.
    skillspector baseline ./my-skill/ -o .skillspector-baseline.yaml
    
    # Scan against the baseline — only NEW findings are reported and scored.
    skillspector scan ./my-skill/ --baseline .skillspector-baseline.yaml
    
    # Review what was suppressed (still excluded from the score).
    skillspector scan ./my-skill/ --baseline .skillspector-baseline.yaml --show-suppressed
    

    A baseline can also use drift-tolerant glob rules (by rule id, file path, or message) — see .skillspector-baseline.example.yaml. Exact fingerprint baselines are evidence-bound: changing the scanned source or SkillSpector version keeps the finding active until it is reviewed again. When a selected baseline or baseline output is stored inside the skill directory, SkillSpector excludes that exact file from content analysis so its suppression text cannot create findings or enter regenerated fingerprints; sibling files remain in normal scan scope.

    Explicit scan scope

    For a local directory containing one SKILL.md or skill.md, repeat --exclude to omit selected files before content analysis:

    skillspector scan ./my-skill --exclude 'tests/*' --exclude 'fixtures/*.json'
    

    Patterns are case-sensitive globs matched against the entire relative POSIX path; * also matches /. Quote patterns so your shell does not expand them. This is an explicit caller option, not an author-controlled ignore file. Patterns matching either manifest name (SKILL.md or skill.md) are rejected, as are recursive, multi-skill, transitive, registry, and non-directory inputs. No-match patterns are still recorded in the report.

    Files already outside the scan inventory, such as policy-excluded dependencies, retain their existing policy handling and are not counted as caller exclusions. Explicitly excluded inventory files remain in the coverage denominator as entirely uninspected. The report lists the applied patterns, excluded count and individual skipped paths; any matched exclusion makes coverage partial and prevents a SAFE recommendation. Use --fail-on-incomplete when partial scope should fail CI. These exclusions do not suppress findings in included files or override existing safety limits.

    LLM Analysis

    For the best results, configure an OpenAI-compatible LLM endpoint for semantic analysis. Pick a provider with SKILLSPECTOR_PROVIDER; hosted providers ship bundled default models, while CLI providers fall back to the local runtime's default model unless SKILLSPECTOR_MODEL is set. SkillSpector also works against local OpenAI-compatible servers (Ollama, vLLM, llama.cpp) and managed inference gateways.

    Provider (SKILLSPECTOR_PROVIDER)Credential env varEndpointDefault model
    openaiOPENAI_API_KEY (+ optional OPENAI_BASE_URL)api.openai.com (or any OpenAI-compatible URL)gpt-5.4
    anthropicANTHROPIC_API_KEYapi.anthropic.comclaude-opus-4-6 (meta_analyzer: claude-sonnet-4-6)
    anthropic_proxyANTHROPIC_PROXY_API_KEY + ANTHROPIC_PROXY_ENDPOINT_URLAny Vertex-style raw-predict proxyclaude-sonnet-4-6
    bedrockAWS_PROFILE (optional) + AWS_REGION — SigV4 via boto3AWS Bedrock Runtimeus.anthropic.claude-sonnet-4-6
    nv_buildNVIDIA_INFERENCE_KEYbuild.nvidia.comz-ai/glm-5.3
    geminiGOOGLE_CLOUD_PROJECT (+ optional GOOGLE_CLOUD_LOCATION) via ADCGoogle Cloud OpenAI-compatible Gemini endpointgemini-3.8-flash
    ollama(none)OLLAMA_BASE_URL (default http://localhost:11434/v1)llama3.1:8b
    azure_openaiAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINTAzure OpenAI Servicegpt-4o (deployment defaults to the model label)
    openai_compatibleSKILLSPECTOR_COMPAT_API_KEY + SKILLSPECTOR_COMPAT_BASE_URLAny OpenAI-compatible endpointllama-3.1-70b-versatile
    claude_cli(none — uses local CLI auth)local claude binarylocal Claude runtime fallback, or SKILLSPECTOR_MODEL
    codex_cliDisabledRegistered for compatibility; its read-only sandbox permits host-file readsUse an HTTP API provider or another supported CLI provider
    gemini_cli(none — uses local CLI auth)local gemini binarylocal Gemini runtime fallback, or SKILLSPECTOR_MODEL
    opencode_cli(none — uses local CLI auth)local opencode (verified 1.18.31–1.18.35, 1.19.0–1.19.4) binarylocal OpenCode runtime fallback, or SKILLSPECTOR_MODEL

    For openai_compatible gateways requiring extra HTTP headers, set SKILLSPECTOR_COMPAT_HEADERS='{"X-Tenant-ID":"my-tenant","X-API-Key":"gateway-key"}'. It must be a JSON object with string keys and values and applies to sync and async requests. Unset or blank values add no headers; invalid configuration is rejected without exposing header values. Treat this environment variable as a secret when it contains gateway API keys.

    gpt-6.1-sol, claude-opus-5-5, and claude-sonnet-5-5 remain opt-in through SKILLSPECTOR_MODEL or per-slot overrides. Promoting them to defaults requires live scanner acceptance covering benign input, credential disclosure, prompt injection, refusals, and incomplete responses. Record findings, completion status, latency, and usage, and compare against previous defaults where accessible.

    For NVIDIA Build's z-ai/glm-5.3, SkillSpector requests high reasoning effort. Set SKILLSPECTOR_REASONING_EFFORT to override it with low, high, or max. The bundled 128,000-token context and 32,000-token output budgets are conservative application limits; they do not claim the hosted endpoint's maximum capacity.

    Structured output is requested through LangChain's with_structured_output. The default is client-specific: ChatOpenAI uses json_schema, while other clients may use tool calling. Some models reject a forced tool call with HTTP 400 (tool_choice: type "tool" and "any" are not supported for this model). The anthropic and anthropic_proxy providers route those models (claude-opus-5-5, claude-sonnet-5-5, claude-fable-5-1, claude-mythos-5-1, or any registry entry with structured_output: json_schema) to the native JSON-schema response format. Bedrock has no JSON-schema output for them, so the bedrock provider leaves toolChoice at auto, asks for the tool call in the prompt, and retries a prose answer; it recognises the model from the model ID, a geo/global inference-profile ID, or a foundation-model / inference-profile ARN. An application-inference-profile ARN hides the model, so add that ARN to the registry (SKILLSPECTOR_MODEL_REGISTRY) with tool_choice: auto. The openai_compatible provider honours the same tool_choice: auto entry for endpoints that ignore both response_format and a forced tool_choice and answer in prose (for example iFlytek's spark-x2.5, which is bundled). The anthropic, anthropic_proxy, openai, and openai_compatible providers also recognise those Claude models inside gateway model IDs such as anthropic/claude-opus-5-5 or aws/anthropic/bedrock-claude-opus-5-5. Some OpenAI-compatible gateways turn a json_schema response format for Claude into a forced tool call, so the openai and openai_compatible providers bind the schema as a tool with tool_choice left at auto and retry a prose answer. SKILLSPECTOR_STRUCTURED_OUTPUT_METHOD=json_schema|function_calling overrides the method for any provider. The method precedence is SKILLSPECTOR_STRUCTURED_OUTPUT_METHOD, the provider's method hint, an analyzer preference, then the client default. TP4 prefers function_calling only for provider openai and the exact model label azure/anthropic/claude-opus-5, with reasoning/thinking controls unset; dated or suffixed labels and other providers keep their existing behavior. An explicit environment override or provider hint still wins when reasoning is configured.

    # Stock OpenAI
    export SKILLSPECTOR_PROVIDER=openai
    export OPENAI_API_KEY=sk-...
    skillspector scan ./my-skill/
    
    # Anthropic
    export SKILLSPECTOR_PROVIDER=anthropic
    export ANTHROPIC_API_KEY=sk-ant-...
    skillspector scan ./my-skill/
    
    # Anthropic via Vertex-style proxy (corporate gateways, GCP Vertex AI)
    export SKILLSPECTOR_PROVIDER=anthropic_proxy
    export ANTHROPIC_PROXY_ENDPOINT_URL=https://my-gateway.example.com/models/claude-sonnet-4-6:streamRawPredict
    export ANTHROPIC_PROXY_API_KEY=your-bearer-token
    export SKILLSPECTOR_MODEL=claude-sonnet-4-6
    skillspector scan ./my-skill/
    
    # AWS Bedrock (Claude via SigV4)
    export SKILLSPECTOR_PROVIDER=bedrock
    # Optional: select an AWS named profile. When unset, the standard
    # boto3 credential chain (env vars, instance metadata, SSO, etc.) resolves.
    # export AWS_PROFILE=my-profile
    export AWS_REGION=us-west-2  # default if unset
    # Default model: us.anthropic.claude-sonnet-4-6
    # Override with any Bedrock model ID, cross-region inference-profile
    # ID, or your own application-inference-profile ARN. Claude 5.5 is opt-in:
    # us./eu. profiles keep requests in that geography, global. routes worldwide.
    # export SKILLSPECTOR_MODEL=us.anthropic.claude-sonnet-5-5
    skillspector scan ./my-skill/
    
    # NVIDIA build.nvidia.com
    export SKILLSPECTOR_PROVIDER=nv_build
    export NVIDIA_INFERENCE_KEY=nvapi-...
    skillspector scan ./my-skill/
    
    # Gemini on Google Cloud (Application Default Credentials / Workload Identity)
    # Prerequisites:
    #   1. Google Cloud project with billing enabled.
    #   2. Enable Gemini Enterprise Agent Platform / Vertex AI API: `aiplatform.googleapis.com`.
    #   3. IAM permission: grant `roles/aiplatform.user` (or at minimum `aiplatform.endpoints.predict`)
    #      to your user account or Kubernetes service account.
    #   4. Local authentication: run `gcloud auth application-default login`.
    #      Configure a quota project if needed: `gcloud auth application-default set-quota-project PROJECT_ID`.
    #   5. Kubernetes / GKE: configure Workload Identity and leave GOOGLE_APPLICATION_CREDENTIALS unset
    #      rather than exporting service account keys.
    # Note on Data Residency:
    #   The default `global` endpoint does not support data-residency requirements. While you can target
    #   `us`, `eu`, or regional endpoints (e.g. `us-central1`), endpoint selection alone does not guarantee
    #   data residency or in-region processing without appropriate organizational policies. Always verify
    #   that your selected model is supported in your target location.
    # Optional credentials:
    #   GOOGLE_APPLICATION_CREDENTIALS is optional and can reference Workload or Workforce Identity Federation
    #   configuration files; exporting long-lived service account keys is discouraged.
    export SKILLSPECTOR_PROVIDER=gemini
    export GOOGLE_CLOUD_PROJECT=my-project-id
    # export GOOGLE_CLOUD_LOCATION=global  # default is global; or us, eu, or specific region (e.g. us-central1)
    # Default model: gemini-3.8-flash
    # export SKILLSPECTOR_MODEL=gemini-3.7-flash
    skillspector scan ./my-skill/
    
    # Local Claude CLI — no API key; uses your existing `claude auth login` session
    # Requires: claude CLI installed and authenticated (claude auth login)
    export SKILLSPECTOR_PROVIDER=claude_cli
    # Uses the local Claude CLI runtime fallback unless SKILLSPECTOR_MODEL is set.
    # export SKILLSPECTOR_MODEL=claude-sonnet-4-6
    skillspector scan ./my-skill/
    
    # Gemini (via OpenAI compatibility layer)
    export SKILLSPECTOR_PROVIDER=openai
    export OPENAI_API_KEY="YOUR_GEMINI_API_KEY"
    export OPENAI_BASE_URL="https://generativelanguage.googleapis.com/v1beta/openai/"
    export SKILLSPECTOR_MODEL=gemini-3.5-flash
    skillspector scan ./my-skill/
    
    # Local Ollama — no API key
    export SKILLSPECTOR_PROVIDER=ollama
    # export OLLAMA_BASE_URL=http://localhost:11434/v1  # shown default
    export SKILLSPECTOR_MODEL=llama3.1:8b
    skillspector scan ./my-skill/
    
    # Azure OpenAI
    export SKILLSPECTOR_PROVIDER=azure_openai
    export AZURE_OPENAI_API_KEY=...
    export AZURE_OPENAI_ENDPOINT=https://example.openai.azure.com/
    export AZURE_OPENAI_DEPLOYMENT=my-deployment
    skillspector scan ./my-skill/
    
    # Any other OpenAI-compatible endpoint
    export SKILLSPECTOR_PROVIDER=openai_compatible
    export SKILLSPECTOR_COMPAT_API_KEY=...
    export SKILLSPECTOR_COMPAT_BASE_URL=https://api.groq.com/openai/v1
    export SKILLSPECTOR_MODEL=llama-3.1-70b-versatile
    skillspector scan ./my-skill/
    
    # Override the provider's default model
    export SKILLSPECTOR_MODEL=gpt-5.2
    skillspector scan ./my-skill/
    
    # Skip LLM analysis (faster, static analysis only)
    skillspector scan ./my-skill/ --no-llm
    

    MCP Server

    Run SkillSpector as a Model Context Protocol server so local MCP-capable agents (Claude Code, Codex CLI, Gemini CLI) can call scanning as a tool and gate skill/MCP installs on the result — turning SkillSpector into a runtime guardrail instead of an out-of-band audit step.

    skillspector mcp requires skillspector[mcp].

    # Install, or reinstall if you already used the CLI-only path
    uv tool install --force 'skillspector[mcp] @ git+https://github.com/NVIDIA/skillspector.git'
    
    # FastMCP stdio transport for local CLI agents
    skillspector mcp
    
    # Streamable HTTP transport on a local loopback interface
    skillspector mcp --transport http --host 127.0.0.1 --port 8000
    

    The stdio transport is the current FastMCP path for local CLI agents, and the initialize hang reported in issue #199 still applies there.

    The server exposes a single tool:

    • scan_skill(target, use_llm=true, output_format="json") — scans a Git URL, file URL, .zip, .md file, or directory and returns a structured verdict: risk_score (0-100), severity, recommendation, safe_to_install, and findings. It also reports llm_used / scan_mode so a low score from a static-only scan is never mistaken for a clean full scan.

    Register it with Claude Code via:

    claude mcp add skillspector -- skillspector mcp
    

    Security — HTTP transport trust model

    The HTTP transport ships without authentication. Any caller that can reach the port can invoke scan_skill. HTTP bindings are restricted to loopback IPs (127.0.0.1 or ::1); localhost binds to 127.0.0.1 without DNS resolution. Wildcard, routable and other hostname bindings are rejected.

    • For remote access, put an authenticating reverse proxy (e.g. nginx + mTLS) in front of the loopback listener.
    • Local paths and file:// URLs are automatically rejected over HTTP to prevent unauthenticated callers from reading arbitrary host files. Only remote Git and .zip URLs are accepted.

    HTTP scans accept only credential-free https:// targets. Git scans never use the server's Git login or Git config. If a proxy or custom CA is needed, set HTTPS_PROXY, NO_PROXY or GIT_SSL_CAINFO in the server environment. Use stdio or the CLI for private repositories. Git redirects are rejected; use the current repository URL after a rename or transfer.

    Vulnerability Patterns

    SkillSpector detects 71 vulnerability patterns across 17 categories:

    Prompt Injection (6 patterns)

    IDPatternSeverityDescription
    P1Instruction OverrideHIGHCommands to ignore safety constraints
    P2Hidden InstructionsHIGHMalicious directives in comments/invisible text
    P3Exfiltration CommandsHIGHInstructions to transmit context externally
    P4Behavior ManipulationMEDIUMSubtle instructions altering agent decisions
    P5Harmful ContentCRITICALInstructions that could cause physical harm
    P9Whitespace PaddingMEDIUMLarge whitespace padding hiding instructions below/beside the visible area

    Anti-Refusal (3 patterns)

    IDPatternSeverityDescription
    AR1Refusal SuppressionHIGHInstructions to never refuse or always comply (e.g. "never refuse", "always comply")
    AR2Disclaimer SuppressionHIGHInstructions to omit warnings, disclaimers, or ethical commentary (e.g. "no disclaimers", "do not moralize")
    AR3Safety Policy NullificationHIGHJailbreak framing that nullifies guardrails (e.g. "you have no restrictions", "ignore your guidelines", "do anything now")

    Data Exfiltration (4 patterns)

    IDPatternSeverityDescription
    E1External TransmissionMEDIUMSending data to external URLs
    E2Env Variable HarvestingHIGHEnumerating, copying, or searching environment data to collect secrets
    E3File System EnumerationMEDIUMScanning directories for sensitive files
    E4Context LeakageHIGHTransmitting conversation context externally

    Privilege Escalation (3 patterns)

    IDPatternSeverityDescription
    PE1Excessive PermissionsLOWRequesting access beyond stated functionality
    PE2Sudo/Root ExecutionMEDIUMInvoking elevated system privileges
    PE3Credential AccessHIGHReading SSH keys, tokens, passwords

    Supply Chain (10+ patterns)

    IDPatternSeverityDescription
    SC1Unpinned DependenciesLOWNo version constraints on packages
    SC2External Script FetchingHIGHcurl | bash and remote code execution
    SC3Obfuscated CodeHIGHBase64/hex encoded execution
    SC4Known Vulnerable DependenciesHIGHDependencies with known CVEs (live OSV.dev lookup)
    SC5Abandoned DependenciesMEDIUMUnmaintained packages without security updates
    SC6TyposquattingHIGHPackage names similar to popular packages
    SC8Shipped Python BytecodeHIGH__pycache__ / .pyc present (discovery skips; malicious bytecode bypass)
    SC9Concealed Executable ArtifactHIGHExecutable nested in a document container or hidden/disguised artifact; root-level visible source helpers in manifested .agents/skills/<name>/ and .claude/skills/<name>/ folders are exempted but remain local-only and are marked as not LLM-reviewed
    SC10Dependency Source RedirectionHIGHPackage-manager source added, replaced, or unresolved

    Excessive Agency (5 patterns)

    IDPatternSeverityDescription
    EA1Unrestricted Tool AccessHIGHUnfettered tool access without constraints
    EA2Autonomous Decision MakingHIGHHigh-impact decisions without human-in-the-loop
    EA3Scope CreepMEDIUMCapabilities extending beyond stated purpose
    EA4Unbounded Resource AccessMEDIUMNo rate limits or quotas on resource consumption
    EA5External Model or Provider SelectionMEDIUM/HIGHModel/provider pins or coding-CLI shell-outs that can switch billing accounts

    Output Handling (3 patterns)

    IDPatternSeverityDescription
    OH1Unvalidated Output InjectionHIGHModel output used without sanitization
    OH2Cross-Context OutputMEDIUMOutput flows across trust boundaries without validation
    OH3Unbounded OutputMEDIUMNo limits on output size or generation rate

    System Prompt Leakage (3 patterns)

    IDPatternSeverityDescription
    P6Direct LeakageHIGHInstructions that expose system prompts or internal rules
    P7Indirect ExtractionMEDIUMExtraction via rephrasing, translation, or side-channels
    P8Tool-Based ExfiltrationHIGHSystem prompts exfiltrated via file writes or network requests

    Memory Poisoning (3 patterns)

    IDPatternSeverityDescription
    MP1Persistent Context InjectionHIGHContent designed to persist across interactions
    MP2Context Window StuffingMEDIUMFiller content displacing safety constraints
    MP3Memory ManipulationHIGHTampering with agent memory or stored state

    Tool Misuse (3 patterns)

    IDPatternSeverityDescription
    TM1Tool Parameter AbuseHIGHCrafted parameters for unintended behavior (shell=True, --force)
    TM2Chaining AbuseHIGHTool chains that bypass individual safety checks
    TM3Unsafe DefaultsMEDIUMOverly permissive defaults (disabled TLS, no auth)

    Rogue Agent (2 patterns)

    IDPatternSeverityDescription
    RA1Self-ModificationCRITICALModifying own code or configuration at runtime
    RA2Session PersistenceHIGHUnauthorized persistence via cron jobs or startup scripts

    Trigger Abuse (3 patterns)

    IDPatternSeverityDescription
    TR1Overly Broad TriggerMEDIUMTrigger patterns matching common words
    TR2Shadow Command TriggerHIGHTriggers that shadow built-in commands or other skills
    TR3Keyword Baiting TriggerMEDIUMGeneric triggers designed to maximize activation

    Behavioral AST (9 patterns)

    IDPatternSeverityDescription
    AST1exec() CallCRITICALDirect exec() enabling arbitrary code execution
    AST2eval() CallHIGHDirect eval() evaluating arbitrary expressions
    AST3Dynamic ImportHIGH__import__() loading arbitrary modules at runtime
    AST4subprocess CallHIGHExternal command execution via subprocess
    AST5os.system / exec-familyHIGHShell commands via os module
    AST6compile() CallMEDIUMCode object creation from strings
    AST7Dynamic getattr()MEDIUMArbitrary attribute access with non-literal names
    AST8Dangerous Execution ChainCRITICALexec/eval combined with dynamic source (network, encoded data)
    AST9Reflective getattr() SinkHIGHReflective exec via getattr(os,'system') / getattr(builtins,'exec') that evades AST1/AST5

    Taint Tracking (5 patterns)

    IDPatternSeverityDescription
    TT1Direct Taint FlowHIGHData flows directly from a source to a sink without sanitization
    TT2Variable-Mediated Taint FlowMEDIUMData flows from source to sink through intermediate variables
    TT3Credential Exfiltration ChainCRITICALCredentials (env vars, secrets) flow to network output sinks
    TT4File Read to Network ExfiltrationHIGHFile contents flow to network output sinks
    TT5External Input to Code ExecutionCRITICALNetwork or user input flows to exec/eval/subprocess sinks

    YARA Signatures (4 patterns)

    IDPatternSeverityDescription
    YR1Malware MatchCRITICALYARA rule match for known malware signatures
    YR2Webshell MatchCRITICALYARA rule match for webshell patterns
    YR3Cryptominer MatchHIGHYARA rule match for crypto mining indicators
    YR4Hack Tool / Exploit MatchHIGHYARA rule match for hack tools or exploit code

    MCP Least Privilege (4 patterns)

    IDPatternSeverityDescription
    LP1Underdeclared CapabilityHIGHCode uses capabilities not listed in declared permissions
    LP2Wildcard PermissionMEDIUMPermission list contains wildcards (*, all, full, any)
    LP3Missing Permission DeclarationMEDIUMNo permissions field but code has detectable capabilities
    LP4Overdeclared PermissionLOWPermission declared but no corresponding code capability found

    MCP Tool Poisoning (4 patterns)

    IDPatternSeverityDescription
    TP1Hidden InstructionsHIGHHidden directives in metadata (HTML comments, zero-width chars, base64, data URIs)
    TP2Unicode DeceptionHIGHHomoglyphs, RTL overrides, mixed-script identifiers in tool metadata
    TP3Parameter Description InjectionMEDIUMInjection patterns in parameter definitions (overrides, system tokens, malicious defaults)
    TP4Description-Behavior MismatchMEDIUMDeclared tool description does not match actual code behavior (LLM-powered)

    All detected patterns are listed in the tables above.

    Risk Scoring

    Score Calculation

    • CRITICAL issues: +50 points
    • HIGH issues: +25 points
    • MEDIUM issues: +10 points
    • LOW issues: +5 points
    • Executable scripts: 1.3x multiplier

    Severity Levels

    ScoreSeverityRecommendation
    0-20LOWSAFE
    21-50MEDIUMCAUTION
    51-80HIGHDO NOT INSTALL
    81-100CRITICALDO NOT INSTALL

    Example Output

    Terminal Output

     SkillSpector Security Report  v2.0.0
    
    Skill: suspicious-skill
    Source: ./suspicious-skill/
    Scanned: 2026-01-29 10:30:00 UTC
    
            Risk Assessment
     Metric          Value
     Score           78/100
     Severity        HIGH
     Recommendation  DO NOT INSTALL
    
            Components (3)
     File              Type      Lines  Executable
     SKILL.md          markdown    142  No
     scripts/sync.py   python       87  Yes
     requirements.txt  text          3  No
    
    Issues (2)
    
      HIGH: Env Variable Harvesting (E2)
        Location: scripts/sync.py:23
        Finding: for key, val in os.environ.items():...
        Confidence: 94%
        Explanation: This code collects environment variables containing
        API keys and secrets, then sends them to an external server.
    
      HIGH: External Transmission (E1)
        Location: scripts/sync.py:45
        Finding: requests.post("https://api.skill.io/env"...
        Confidence: 89%
        Explanation: Data is being sent to an external server. Combined
        with env harvesting above, this indicates credential exfiltration.
    

    Configuration

    Environment Variables

    VariableDescriptionRequired
    SKILLSPECTOR_PROVIDERActive LLM provider: openai, anthropic, anthropic_proxy, bedrock, nv_build, gemini, ollama, azure_openai, openai_compatible, claude_cli, gemini_cli, or opencode_cli. Hosted providers use bundled model_registry.yaml defaults; CLI providers fall back to the local runtime's default model unless SKILLSPECTOR_MODEL is set. Defaults to nv_build.Optional
    GOOGLE_CLOUD_PROJECTGoogle Cloud project ID for the gemini provider. Authenticates via Google Cloud Application Default Credentials (ADC) or GKE Workload Identity.Required for LLM analysis when SKILLSPECTOR_PROVIDER=gemini
    GOOGLE_CLOUD_LOCATIONGoogle Cloud location for the gemini provider endpoint (e.g. global, us, eu, us-central1). Defaults to global.Optional (used when SKILLSPECTOR_PROVIDER=gemini)
    GOOGLE_APPLICATION_CREDENTIALSOptional path to ADC credential/config file (e.g. Workload or Workforce Identity Federation config; exported service account keys are discouraged). For local development, use gcloud auth application-default login; for GKE, use Workload Identity.Optional (used when SKILLSPECTOR_PROVIDER=gemini)
    NVIDIA_INFERENCE_KEYCredential for the nv_build provider (build.nvidia.com).Required for LLM analysis when SKILLSPECTOR_PROVIDER=nv_build
    OPENAI_API_KEYCredential for the OpenAI provider (SKILLSPECTOR_PROVIDER=openai). Also serves as the tier-2 fallback in the credential waterfall when the active provider returns no credentials; the fallback then uses the openai default model (gpt-5.4) for any slot without a model override. Claude Opus or Sonnet 5.5 gateway IDs are not supported through the fallback, because it keeps the active provider's structured-output method; select SKILLSPECTOR_PROVIDER=openai for them.Required for LLM analysis when SKILLSPECTOR_PROVIDER=openai
    OPENAI_BASE_URLOverride the OpenAI endpoint (e.g. point at Ollama).Optional
    SKILLSPECTOR_REASONING_EFFORTOptional provider- and model-dependent reasoning-effort setting. Non-empty values are trimmed and passed through unchanged. When unset or blank, SkillSpector sends high for nv_build with z-ai/glm-5.3; other provider/model combinations keep their endpoint defaults. The value applies to every model slot, so the anthropic meta_analyzer default receives the same effort as the analyzers. gpt-6.1-sol accepts only low, medium, high, xhigh, or max; any other value stops a scan before analysis (exit code 2).Optional
    SKILLSPECTOR_OUTPUT_LANGUAGEShort, single-line language label (letters, numbers, spaces, _, or -; maximum 64 characters) for human-readable LLM finding text such as messages, explanations, and remediation. Rule IDs, severity values, paths, code, and other machine-readable values remain unchanged. Unset, blank, or invalid values preserve the default output language.Optional
    SKILLSPECTOR_TEMPERATUREOptional sampling temperature from 0 to 1 for hosted providers. Unset or blank preserves the provider default. Lower values can reduce run-to-run variation but do not guarantee identical output. gpt-6.1-sol rejects every explicit temperature, including 1. Claude Opus 4.7 and later, Sonnet 5, Fable 5, Mythos 5 and Haiku 5 models (for example claude-opus-5-5, claude-fable-5-1, claude-haiku-5-5) accept only 1.0 on the native Anthropic endpoint (API compatibility rule); gateways, proxies, and Bedrock still reject every explicit value for these models. Unsupported settings on active LLM slots stop a scan before analysis (exit code 2). Static scans (--no-llm) skip provider initialization and control validation. A Bedrock application-inference-profile ARN hides the model name, so declare sampling: rejected for it in the model registry.Optional
    SKILLSPECTOR_SEEDOptional integer sampling seed for OpenAI-compatible and Azure OpenAI providers. Other hosted providers and CLI providers do not receive it. Provider support remains model-dependent; gpt-6.1-sol receives it, but OpenAI treats seed as best-effort, so output can still differ between runs.Optional
    SKILLSPECTOR_COMPACT_PROMPTSOpt-in compact line numbering in LLM prompts: numbered lines render as L1:, L2: instead of zero-padded L01:, L02:. Accepted truthy values are 1, true, and yes (case-insensitive; surrounding whitespace is trimmed). Unset or any other value keeps the default zero-padded format.Optional
    ANTHROPIC_API_KEYCredential for the Anthropic provider (SKILLSPECTOR_PROVIDER=anthropic).Required for LLM analysis when SKILLSPECTOR_PROVIDER=anthropic
    ANTHROPIC_BASE_URLOverride the native Anthropic endpoint (default: https://api.anthropic.com).Optional
    ANTHROPIC_AUTH_SCHEMESet to bearer to send ANTHROPIC_API_KEY as Authorization: Bearer instead of x-api-key, for gateways exposing the Messages API.Optional
    ANTHROPIC_PROXY_ENDPOINT_URLFull endpoint URL for the Anthropic proxy provider (Vertex-style raw-predict).Required when SKILLSPECTOR_PROVIDER=anthropic_proxy
    ANTHROPIC_PROXY_API_KEYBearer token for the Anthropic proxy provider.Required when SKILLSPECTOR_PROVIDER=anthropic_proxy
    ANTHROPIC_PROXY_API_VERSIONanthropic_version value sent in the request body (default: vertex-2023-10-16).Optional
    AWS_PROFILENamed AWS profile for the Bedrock provider — authenticates via SigV4 through boto3. When unset, the standard boto3 credential chain (env vars, instance metadata, SSO, etc.) resolves.Optional (used when SKILLSPECTOR_PROVIDER=bedrock)
    AWS_REGIONAWS region for the Bedrock Runtime endpoint. Defaults to us-west-2.Optional (used when SKILLSPECTOR_PROVIDER=bedrock)
    OLLAMA_BASE_URLOllama OpenAI-compatible endpoint. Defaults to http://localhost:11434/v1.Optional (used when SKILLSPECTOR_PROVIDER=ollama)
    AZURE_OPENAI_API_KEYAPI key for the Azure OpenAI provider.Required when SKILLSPECTOR_PROVIDER=azure_openai
    AZURE_OPENAI_ENDPOINTAzure resource endpoint for the Azure OpenAI provider.Required when SKILLSPECTOR_PROVIDER=azure_openai
    AZURE_OPENAI_DEPLOYMENTAzure deployment name. Defaults to the selected model label.Optional
    AZURE_OPENAI_API_VERSIONAzure OpenAI API version. Defaults to 2024-06-01.Optional
    SKILLSPECTOR_COMPAT_API_KEYAPI key for a generic OpenAI-compatible provider.Required when SKILLSPECTOR_PROVIDER=openai_compatible
    SKILLSPECTOR_COMPAT_BASE_URLBase URL for a generic OpenAI-compatible provider.Required when SKILLSPECTOR_PROVIDER=openai_compatible
    SKILLSPECTOR_MODELOverride the active provider model. For hosted providers, this replaces the bundled default from the LLM Analysis table. For CLI providers, this is forwarded as --model instead of using the local runtime fallback.Optional
    SKILLSPECTOR_MODEL_<SLOT>Override the model for one analyzer slot (for example SKILLSPECTOR_MODEL_META_ANALYZER). It takes precedence over SKILLSPECTOR_MODEL, which in turn replaces provider slot defaults such as the anthropic meta_analyzer default. See model provenance for the full precedence.Optional
    SKILLSPECTOR_MODEL_REGISTRYOverride the bundled per-provider YAML registry (src/skillspector/providers/<provider>/model_registry.yaml) with a custom path.Optional
    SKILLSPECTOR_LOG_LEVELLog level: DEBUG, INFO, WARNING, ERROR (default: WARNING).Optional
    SKILLSPECTOR_MAX_WORKFLOW_SECONDSAggregate scan deadline in seconds; defaults to 1080 (18 minutes). Accepts a positive finite number. Invalid values warn and retain the default. Set before starting the process; caller-supplied budgets and external tool timeouts still apply.Optional
    SKILLSPECTOR_TP4_MAX_BATCHESMaximum description-behavior review batches per TP4 analyzer execution; defaults to 256. Accepts an integer from 1 to 4096. Unset or blank uses the default; invalid values warn and retain it. Set before starting the process; file, byte, finding, concurrency, and workflow-time limits still apply.Optional
    SKILLSPECTOR_MAX_DEPENDENCY_SOURCE_ANALYSIS_SECONDSCeiling for the dependency-source analysis pass, in seconds. Defaults to 5.0, which is the historical value. Raise it on slow or heavily loaded machines so the same unchanged tree does not come back partially inspected, which would set safe_to_install to false and record runtime_limit. The remaining aggregate workflow time (SKILLSPECTOR_MAX_WORKFLOW_SECONDS, 1080 seconds by default) still caps the effective value; values above that remaining time have no effect. Invalid, zero, negative, infinite, and NaN values keep the 5-second default. The setting is resolved when the module is imported, so a new process is required after changing it. See analysis resource bounds.Optional

    Disabled provider: codex_cli remains registered but cannot run LLM analysis because its read-only sandbox allows host-file reads. Existing users should select an HTTP API provider or another supported CLI provider.

    CLI providers (claude_cli, gemini_cli, opencode_cli): No API key is needed. Authentication is managed entirely by the agent CLI's own login session. SkillSpector never reads or forwards API keys when these providers are active. The subprocess is run with capabilities restricted, and untrusted skill content is delivered only via stdin.

    opencode_cli currently fails closed unless the installed OpenCode version is one of the individually verified releases (1.18.31, 1.18.32, 1.18.33, 1.18.34, 1.18.35, 1.19.0, 1.19.1, 1.19.2, 1.19.3, 1.19.4), whose configuration precedence and deny-all semantics are verified by this release; every other version, including prereleases, still fails closed.

    CLI Options

    skillspector scan --help
    
    Options:
      -f, --format [terminal|json|markdown|sarif]  Output format [default: terminal]
      -o, --output PATH                            Output file path
      --no-llm                                     Skip LLM analysis (static only)
      --yara-rules-dir PATH                        Extra YARA rules directory
      -b, --baseline PATH                          Suppress findings listed in a baseline
      --show-suppressed                            List baseline-suppressed findings
      -V, --verbose                                Show detailed progress
      --help                                       Show this message and exit
    
    # Generate a baseline of all current findings (see docs/SUPPRESSION.md)
    skillspector baseline <path> [-o FILE] [--no-llm] [--reason TEXT]
    

    Report files and new baselines use private permissions (0600). Existing baselines retain their access metadata when safely replaced; updates requiring in-place writes are refused. All file output uses atomic replacement. The parent directory must be writable; symlinked parents and non-regular destinations such as devices are refused. On macOS, ancestor directories must also be readable. File output requires POSIX no-follow directory operations and is unavailable on Windows. Unsupported file output fails before analysis. Omit --output for scan/batch stdout, or use skillspector baseline <path> -o - for JSON on stdout, then redirect only to a trusted destination. Run Docker with your user ID as above so you can read its files.

    Integrating SkillSpector

    SkillSpector is built to be driven by other tools (CI pipelines, install gates, editor integrations). Its exit code and JSON output are a stable contract.

    Exit codes

    skillspector scan exits with:

    CodeMeaning
    0Scan completed, risk_score ≤ 50 (recommendation SAFE or CAUTION), and no enabled strict gate fired
    1Scan completed and either risk_score > 50, --fail-on-findings found an active finding, --fail-on-incomplete found partial/incomplete analysis, or --min-coverage found coverage below its threshold
    2Error (bad input, unreadable source, internal failure)

    By default, the exit code collapses SAFE and CAUTION into 0. Use --fail-on-findings to gate on any active finding, --fail-on-incomplete to gate on incomplete coverage, --min-coverage PERCENT to gate on a coverage floor, or read the JSON recommendation field for custom policy.

    Machine-readable output

    --format json produces a JSON report; with no --output/-o it is written to stdout:

    skillspector scan ./my-skill/ --format json
    

    The top-level shape is (this example shows a full LLM-backed scan; with --no-llm, metadata.llm_requested is false):

    {
      "skill": { "name": "...", "source": "...", "scanned_at": "<ISO 8601>" },
      "risk_assessment": { "score": 0, "severity": "LOW", "recommendation": "SAFE" },
      "components": [ { "path": "...", "type": "...", "lines": 0, "executable": false, "size_bytes": 0 } ],
      "issues": [ { "id": "...", "category": "...", "severity": "...", "confidence": 0.0, "location": { "file": "...", "start_line": 0 }, "surface": "code" } ],
      "metadata": {
        "has_executable_scripts": false,
        "skillspector_version": "...",
        "llm_requested": true,
        "llm_available": true,
        "inference_usage": [
          {
            "node": "semantic_security_discovery",
            "request_kind": "structured_output",
            "provider": "nv_inference",
            "model": "azure/anthropic/claude-opus-4-6",
            "model_source": "provider_response",
            "usage_source": "provider_response",
            "prompt_tokens": 1000,
            "completion_tokens": 100,
            "cached_tokens": 400,
            "cache_write_tokens": 50,
            "total_tokens": 1100
          }
        ]
      }
    }
    
    • risk_assessment.severity ∈ LOW | MEDIUM | HIGH | CRITICAL.
    • risk_assessment.recommendation ∈ SAFE | CAUTION | DO_NOT_INSTALL, mapped from severity: LOW → SAFE, MEDIUM → CAUTION, HIGH/CRITICAL → DO_NOT_INSTALL.
    • issues[].surface and suppressed[].surface are advisory, heuristic labels for where a finding landed: code | instructions | docs | tests | comments | config. JSON expands compacted groups into one issues[] entry per occurrence, each with its own surface; SARIF emits properties.surface on each result. The label is path/line-based and the scanned author can influence it, so do not use it as the sole suppression or security signal.
    • metadata.llm_error appears only when LLM analysis was requested but unavailable.
    • Archive-member components retain their virtual path and include outer_path, nested_path, and container provenance. Virtual paths identify in-memory members, not on-disk files; see JSON component locations.
    • AE1 findings use Incomplete referenced artifact analysis. Their source location identifies the reference; evidence identifies the affected target, analyzer reasons, and available bounds. Review the target's completeness ledger when reasons_truncated is true. See referenced-artifact diagnostics and Perl help text for interpretation and corrective actions.
    • metadata.inference_usage contains one sanitized record per LLM response when the provider exposes token counters. It is an empty list when usage is unavailable; SkillSpector never estimates missing tokens. Prompt totals are inclusive of cache reads and writes so downstream pricing can separate those partitions safely. model_source distinguishes an independently identified provider model from the exact requested model used when response identity is absent or ambiguous. SkillSpector does not currently send Anthropic prompt-cache controls, so its scan requests cannot select the separate 5-minute or 1-hour cache-write tiers; TTL-specific response fields are normalized defensively into the aggregate cache-write counter.
    • See Inference usage telemetry for the complete provenance, cache-accounting, privacy, fail-closed ingestion, and downstream pricing contract.
    • The full per-issue shape is defined by Finding.to_dict() in models.py; rely on the fields above and treat any additional fields as best-effort.

    For CI/IDE tooling, --format sarif emits SARIF 2.1.0. Each result carries its security severity; rule descriptors carry a score only when all of their results share that severity. Mixed-severity rules remain unannotated, so consumers that read only rule-level severity cannot distinguish their result severities. Recursive scans chain each percent-encoded skill-directory URI through an absolute scan-root file URI. External dependency locations retain their source provenance instead of being rebased into the local skill directory.

    Recommended gate mapping

    When using SkillSpector as an install gate, map the recommendation to an action:

    recommendationSuggested action
    SAFEallow
    CAUTIONprompt / warn the user
    DO_NOT_INSTALLblock

    SkillSpector computes the score band and recommendation; how strict the gate is (e.g. whether CAUTION blocks in CI) is a policy decision for the integrating tool.

    Development

    Setup

    This is the canonical setup recipe for contributing from this checkout. Use Python 3.12-3.15 (>=3.12,<3.16, as defined in pyproject.toml), Git, and make. A release tag can have a different Python range; check that tag's metadata. The commands below use Bash or Zsh.

    With uv installed:

    git clone https://github.com/NVIDIA/skillspector.git
    cd skillspector
    uv venv --python 3.12 .venv
    source .venv/bin/activate
    make install-dev
    

    If uv is not installed, clone the repository and enter it as above, then use an installed supported Python interpreter with venv and pip:

    python3.12 -m venv .venv
    source .venv/bin/activate
    make install-dev
    

    Create and activate the virtual environment before running any make target. make install-dev uses uv sync --all-extras when uv is available, otherwise pip install -e ".[dev]".

    Start with these local checks:

    make test-unit
    make lint
    make format-check
    

    make test-unit excludes tests marked integration or provider. This selection does not cover every CI job or guarantee network isolation.

    Some unit tests start loopback HTTP servers and local multiprocessing sockets. Restricted agent sandboxes can deny these operations. Keep the failing command and traceback, and use an appropriately permitted environment for a diagnostic rerun. Report that result separately; a passing subset does not make the original full target pass. See the contributor onboarding check.

    Before submitting a PR, CONTRIBUTING.md requires make test. It runs both test-unit and test-integration; integration tests invoke the full graph and may send test content to a configured LLM provider. Review the provider configuration and credentials or local-service prerequisites before running it:

    make test
    

    make test-provider makes live provider requests and requires the corresponding provider configuration; see Live provider tests. make test-cov runs the same marker-filtered selection as test-unit, with coverage.

    make format applies lint fixes and formats files in place. Run it before committing when changes are needed, review the diff, then recheck with make lint and make format-check.

    How It Works

    SkillSpector uses a two-stage detection pipeline:

    Stage 1: Static Analysis

    • Fast regex-based pattern matching across 11 static analyzers
    • AST-based behavioral analysis detecting dangerous calls (exec, eval, subprocess, etc.)
    • Live vulnerability lookups via OSV.dev for known CVEs in dependencies
    • Scans all analyzer-eligible files in the skill
    • High recall (catches most issues)
    • Moderate precision (some false positives)

    OpenSSF Model Signing / DSSE bundles remain in content analysis, regardless of filename. Static and LLM analyzers inspect decoded JSON payloads, readable DER certificate/signature content, and all unknown wrapper fields. Strictly parsed binary fields are projected rather than treated as executable base64; canonical bytes remain available to YARA and fingerprints. Unsupported encodings (including environments without the optional cryptography dependency) retain raw fields and produce an explicit incomplete-analysis warning. Findings in decoded content point to the original bundle and label their projection in evidence. None of this verifies signatures, certificate chains, transparency logs, or signer identity.

    Stage 2: LLM Semantic Analysis (Optional)

    • Evaluates context and intent
    • Confirmed findings may gain an explanation and higher confidence, never lower
    • Every deterministic finding stays in the report whether the model confirms it, disputes it, or does not address it
    • Findings the model reviews but does not confirm (disputed, low-confidence, or unaddressed) are tagged llm-unconfirmed in JSON and SARIF output
    • Findings whose review fails are kept without that tag; evidence.llm_review_outcome distinguishes confirmed, disagreed, low-confidence, missing, and failed in JSON and SARIF output
    • Results depend on the configured model and prompt context; Stage 2 does not replace deterministic findings or guarantee a particular precision rate

    The LLM prompt includes anti-jailbreak protections to prevent malicious skills from manipulating the analysis.

    Live Vulnerability Lookups (SC4)

    SC4 uses the OSV.dev API to check dependencies against the full Open Source Vulnerabilities database — covering tens of thousands of advisories across PyPI and npm.

    • No API key required — OSV.dev is free and unauthenticated.
    • Batch queries — all dependencies are checked in a single HTTP call.
    • Automatic fallback — if OSV.dev is unreachable (air-gapped/offline), a small built-in fallback list is used.
    • Caching — result