How to Contribute to SkillSpector: A Developer's Guide to NVIDIA's LangGraph Security Scanner

To contribute to SkillSpector, clone the NVIDIA repository, install development dependencies with make install-dev, and extend the scanning pipeline by creating new analyzer modules in src/skillspector/nodes/analyzers/ and registering them in the analyzer registry.

SkillSpector is a LangGraph-based security scanner for AI-agent skills developed by NVIDIA. The tool resolves skills from Git repositories, ZIP files, or local directories, then executes a directed acyclic graph (DAG) of 22 parallel analyzers to detect vulnerabilities and emit SARIF/JSON/Markdown reports. Contributing to SkillSpector involves extending its modular analyzer suite, improving the graph workflow in src/skillspector/graph.py, or enhancing the risk-scoring logic in the report node.

Setting Up Your Development Environment

Begin by cloning the repository and creating an isolated Python environment. The project prefers uv for virtual environment management, though standard venv works equally well.


# Clone the repository

git clone https://github.com/NVIDIA/SkillSpector.git
cd SkillSpector

# Create and activate virtual environment (uv preferred)

uv venv .venv && source .venv/bin/activate

# Alternative: python3 -m venv .venv && source .venv/bin/activate

# Install development dependencies

make install-dev

All make targets assume an active virtual environment. For detailed build commands and environment variables, consult docs/DEVELOPMENT.md.

Understanding the SkillSpector Architecture

SkillSpector processes skills through a state-driven DAG defined in src/skillspector/graph.py. The workflow passes a SkillspectorState (a TypedDict defined in src/skillspector/state.py) through five distinct stages:

  1. Input resolution – src/skillspector/input_handler.py clones Git repos, extracts ZIPs, and normalizes inputs into local directories.
  2. Context building – src/skillspector/nodes/build_context.py walks the directory, caches file contents, builds ASTs, and flags executable scripts.
  3. Parallel analysis – 22 analyzer nodes (static regex/YARA, AST-based behavioral checks, CVE lookups, and MCP checks) each emit Finding objects defined in src/skillspector/models.py.
  4. Meta-analysis – src/skillspector/nodes/meta_analyzer.py optionally filters false positives using an LLM provider configured in src/skillspector/providers/.
  5. Reporting – src/skillspector/nodes/report.py applies baseline suppression, computes a numeric risk score, and renders the final output.

All state transitions use LangGraph’s reducer pattern, ensuring pure functions without side effects. This architecture makes the pipeline trivial to unit test and extend.

Adding a New Security Analyzer

The most common contribution is adding a new static analyzer to detect specific vulnerability patterns. Analyzers reside in src/skillspector/nodes/analyzers/ and follow a standardized interface.

Creating the Analyzer Module

Create a new Python file in the analyzers directory implementing the analyze function signature:


# src/skillspector/nodes/analyzers/static_patterns_secret_leak.py

from .common import AnalyzerFinding, Location, Severity
from .pattern_defaults import CATEGORY, EXPLANATION, REMEDIATION

def analyze(content: str, file_path: str, file_type: str) -> list[AnalyzerFinding]:
    findings: list[AnalyzerFinding] = []
    
    # Detect hard-coded AWS access key patterns

    if "AKIA" in content:
        lines = content.splitlines()
        for line_no, line in enumerate(lines, 1):
            if "AKIA" in line:
                findings.append(
                    AnalyzerFinding(
                        location=Location(file=file_path, line=line_no),
                        severity=Severity.HIGH,
                        rule_id="SEC1",
                        message="Hard-coded AWS access key detected",
                        category=CATEGORY,
                        explanation=EXPLANATION,
                        remediation=REMEDIATION,
                    )
                )
                break
    return findings

The function receives the file content, absolute path, and detected MIME type, then returns a list of AnalyzerFinding objects containing the location, severity, and remediation guidance.

Registering Your Analyzer

Make the new analyzer available to the graph by updating the registry in src/skillspector/nodes/analyzers/__init__.py:

from . import static_patterns_secret_leak

ANALYZER_NODE_IDS.append("static_secret_leak")
ANALYZER_NODES["static_secret_leak"] = static_runner.run_static_patterns(
    static_patterns_secret_leak.analyze
)

The ANALYZER_NODE_IDS list controls execution order, while the ANALYZER_NODES dictionary maps the node ID to the LangGraph node function.

Writing Unit Tests

Create a corresponding test file under tests/nodes/analyzers/:


# tests/nodes/analyzers/test_static_secret_leak.py

from skillspector.nodes.analyzers import static_patterns_secret_leak

def test_detects_aws_key():
    content = "aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE'"
    findings = static_patterns_secret_leak.analyze(content, "test.py", "text/x-python")
    
    assert len(findings) == 1
    assert findings[0].rule_id == "SEC1"
    assert findings[0].severity.name == "HIGH"

Run the specific test with pytest tests/nodes/analyzers/test_static_secret_leak.py or execute the full suite with make test.

Testing Your Contributions Locally

Verify your changes using the CLI or the interactive LangGraph development server.

Scan a local skill with your new analyzer:


# Run with static analysis only (fastest iteration)

skillspector scan ./examples/my-skill/ --no-llm

# Generate structured JSON output

skillspector scan ./examples/my-skill/ --format json -o report.json

Launch LangGraph Studio for interactive debugging:

make langgraph-dev

This starts a local HTTP server and opens LangGraph Studio, where you can step through individual nodes, inspect the SkillspectorState at each stage, and invoke the graph with custom inputs such as:

{
  "input_path": "/absolute/path/to/skill",
  "output_format": "json",
  "use_llm": false
}

Documentation and Pull Request Guidelines

Before submitting, ensure your contribution meets the repository's quality standards:

  • Update documentation – Add an entry to docs/DEVELOPMENT.md describing the analyzer's detection logic and any configuration flags. Update README.md if you introduce new CLI options.
  • Code quality – Run the linting and formatting tools: make lint && make format.
  • Full test suite – Execute make test to verify no regressions in the risk-score gating model.

Submit your contribution by forking the repository, creating a feature branch (feature/my-new-analyzer), and opening a pull request against main. Reviewers will verify that new rules correctly categorize findings and do not introduce false positives that could skew the final risk calculation.

Summary

  • Setup – Clone NVIDIA/SkillSpector, create a virtual environment, and run make install-dev to install dependencies.
  • Architecture – SkillSpector uses a LangGraph DAG with 22 analyzer nodes processing a SkillspectorState through resolution, context building, analysis, and reporting stages.
  • Development – Add new detectors by implementing the analyze function in src/skillspector/nodes/analyzers/, then register them in __init__.py and write unit tests under tests/nodes/analyzers/.
  • Testing – Use make langgraph-dev for interactive debugging and make test for regression testing; use --no-llm for rapid static analysis iteration.
  • Submission – Follow the guidelines in CONTRIBUTING.md, update relevant documentation, and ensure CI checks pass before requesting review.

Frequently Asked Questions

What programming language is SkillSpector written in?

SkillSpector is implemented in Python 3 using the LangGraph framework for orchestrating the security scanning workflow. The codebase leverages Pydantic models in src/skillspector/models.py for structured data validation and uses standard AST libraries for code analysis.

Do I need an LLM API key to contribute to SkillSpector?

No. While SkillSpector supports LLM-based meta-analysis via providers in src/skillspector/providers/, you can develop and test static analyzers without API keys by using the --no-llm flag. This mode runs only the static pattern matchers, AST detectors, and YARA rules, making it ideal for rapid development cycles.

How does the risk score calculation work?

The risk score is computed in src/skillspector/nodes/report.py based on the aggregated severity of findings after baseline suppression. Each Finding object carries a severity level (LOW, MEDIUM, HIGH, CRITICAL) that maps to numeric weights; the final score determines the severity rating and recommendation shown in SARIF or Markdown outputs.

Can I contribute bug fixes or documentation without adding new analyzers?

Yes. Contributions to improve error handling in src/skillspector/input_handler.py, enhance the reducer pattern logic in the state management, clarify documentation in docs/DEVELOPMENT.md, or fix edge cases in existing analyzers are welcome. All pull requests undergo the same risk-score gating review process to ensure code quality.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →