# How to Integrate SkillSpector into CI/CD Pipelines for SARIF Output

> Integrate SkillSpector into CI/CD pipelines to generate SARIF output for automated security annotations on pull requests. Streamline your workflow with GitHub Actions Azure Pipelines & GitLab CI.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: how-to-guide
- Published: 2026-06-18

---

**You can integrate SkillSpector into CI/CD pipelines by invoking the CLI with `--format sarif` and uploading the generated JSON file to compatible platforms like GitHub Actions, Azure Pipelines, or GitLab CI, which automatically annotate pull requests with security findings.**

NVIDIA/SkillSpector is a static analysis tool that generates **SARIF 2.1.0** (Static Analysis Results Interchange Format) output, the industry standard for security scan results. This guide explains how to configure SkillSpector to produce SARIF reports and integrate them into continuous integration workflows using the source code architecture found in [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py) and [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py).

## Understanding the SARIF Generation Pipeline

SkillSpector implements a LangGraph-based workflow that transforms security findings into standardized SARIF documents through three distinct stages.

### CLI Initialization and State Configuration

When you execute `skillspector scan --format sarif`, the CLI entry point in [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py) (lines 95-101) parses the flag and initializes the graph state with `"output_format": "sarif"`. This `_scan_state` dictionary propagates through the entire workflow, signaling downstream nodes to structure output accordingly.

### Graph Execution and Finding Collection

The `graph.invoke` method orchestrates a series of analyzer nodes—including static pattern matchers, optional LLM semantic checks, and OSV lookups—that collect vulnerabilities into a `filtered_findings` list within the state object. Each finding follows the `Finding` data class defined in [`src/skillspector/models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/models.py), containing rule IDs, severity levels, and precise file locations.

### Report Node and SARIF Serialization

The **report** node in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) (lines 48-66) calls `_build_sarif` to transform findings into SARIF-compliant objects. This function iterates over each `Finding`, creates a `SarifResult` with mapped severity levels, and assembles a `SarifLog` using Pydantic models from [`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py). The final payload is serialized via `model_dump(..., by_alias=True)` and returned as `sarif_report` in the node's output dictionary.

In [`cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/cli.py), the `_write_result` helper detects the SARIF format and writes the JSON payload using `json.dumps(..., indent=2)`, producing a file that CI/CD systems can ingest directly.

## CI/CD Integration Examples

### GitHub Actions Workflow

GitHub Actions can upload SARIF files to Code Scanning, which automatically creates security alerts and annotates pull requests.

```yaml
name: SkillSpector CI
on: [push, pull_request]

jobs:
  scan-skill:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install SkillSpector
        run: |
          python -m pip install --upgrade pip
          pip install skillspector[all]

      - name: Export LLM env vars
        env:
          SKILLSPECTOR_PROVIDER: ${{ secrets.SKILLSPECTOR_PROVIDER }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          echo "SKILLSPECTOR_PROVIDER=${SKILLSPECTOR_PROVIDER}" >> $GITHUB_ENV
          echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}" >> $GITHUB_ENV

      - name: Run SkillSpector (SARIF)
        id: scan
        run: |
          skillspector scan . \
            --format sarif \
            --output skill_report.sarif

      - name: Upload SARIF report
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: skill_report.sarif

```

The `upload-sarif` action creates Code Scanning alerts from `skill_report.sarif` without additional configuration.

### Azure Pipelines Configuration

Azure DevOps supports SARIF ingestion through the Publish Test Results task.

```yaml
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: |
    python -m pip install --upgrade pip
    pip install skillspector[all]
  displayName: 'Install SkillSpector'

- script: |
    skillspector scan $(Build.SourcesDirectory) \
      --format sarif \
      --output $(Build.ArtifactStagingDirectory)/skill.sarif
  displayName: 'Run SkillSpector'

- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'SARIF'
    testResultsFiles: '$(Build.ArtifactStagingDirectory)/skill.sarif'
    mergeTestResults: true

```

### GitLab CI Integration

GitLab's security reports UI renders SARIF findings automatically when declared as job artifacts.

```yaml
stages:
  - scan

skill_scan:
  image: python:3.12-slim
  stage: scan
  script:
    - pip install skillspector[all]
    - skillspector scan $CI_PROJECT_DIR --format sarif --output skill.sarif
  artifacts:
    paths:
      - skill.sarif
    reports:
      sarif: skill.sarif

```

### Python API for Custom Pipelines

For advanced workflows, invoke SkillSpector programmatically without shelling out to the CLI.

```python
from skillspector import graph
import json
import pathlib

# Configure the initial state with SARIF output format

state = {
    "input_path": "/path/to/skill",
    "output_format": "sarif",
    "use_llm": False,  # Static analysis only for faster CI execution

}

# Execute the analysis workflow

result = graph.invoke(state)

# Extract the SARIF document as a Python dictionary

sarif_report = result["sarif_report"]

# Write to file for downstream CI consumption

output_path = pathlib.Path("skill_report.sarif")
output_path.write_text(json.dumps(sarif_report, indent=2))
print(f"SARIF saved to {output_path}")

```

This approach bypasses the CLI's `_write_result` logic while producing identical output compatible with [`sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/sarif_models.py) definitions.

## Automating Pipeline Failures

SkillSpector supports automatic pipeline failure based on risk thresholds. When the aggregated `risk_score` across all findings exceeds **50**, the CLI exits with a non-zero status code. This behavior enables you to block merges or deployments when critical vulnerabilities are detected without parsing the SARIF file manually.

To leverage this in your CI configuration, simply run the scan step without error suppression. The non-zero exit code will fail the job stage automatically.

## Summary

- **Use `--format sarif`** to generate SARIF 2.1.0 output that GitHub Actions, Azure Pipelines, and GitLab CI can ingest natively.
- **The report node** in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) constructs findings using Pydantic models from [`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py), ensuring strict schema compliance.
- **CI platforms** automatically annotate pull requests and generate security dashboards when you upload the SARIF file to their respective upload tasks or artifact reports.
- **Risk-based failures** occur automatically when SkillSpector calculates a `risk_score` greater than 50, enabling pipeline gating without custom scripts.

## Frequently Asked Questions

### What SARIF version does SkillSpector generate?

SkillSpector generates **SARIF 2.1.0** output according to the OASIS standard. The Pydantic models in [`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py) define the schema structure, including required fields like `tool.driver`, `runs`, and `results` arrays.

### How do I configure a CI job to fail when SkillSpector finds critical vulnerabilities?

SkillSpector automatically exits with a non-zero status code when the aggregated `risk_score` exceeds 50. Your CI pipeline will fail the stage automatically if you run the scan without error suppression, making it unnecessary to parse the SARIF file for threshold checks.

### Can I integrate SkillSpector into CI/CD without using the CLI?

Yes. You can import the `graph` module from [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py) and invoke `graph.invoke()` directly with an `output_format` key set to `"sarif"`. This Python API approach returns a dictionary containing the `sarif_report` key, which you can serialize and handle programmatically within custom pipeline scripts.

### Where in the source code is the SARIF output actually formatted?

The SARIF document is constructed in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) within the `_build_sarif` function, which converts `Finding` objects into `SarifResult` instances. The final JSON serialization occurs in [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py) inside the `_write_result` helper, which uses `json.dumps` to format the Pydantic model output (via `model_dump`) with indentation for file writing.