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

> Integrate SkillSpector into CI/CD pipelines with SARIF output. Automate security scanning and annotations in GitHub Actions, Azure DevOps, and GitLab CI for enhanced code quality.

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

---

**SkillSpector generates standardized SARIF 2.1.0 output through the `--format sarif` CLI flag or Python API, enabling direct ingestion by GitHub Actions, Azure DevOps, and GitLab CI for automated security scanning and pull request annotations.**

NVIDIA's SkillSpector analyzes AI skill packages for security vulnerabilities and configuration errors. When configured to output SARIF (Static Analysis Results Interchange Format), it produces a standardized JSON document that CI/CD platforms can ingest directly to annotate code reviews, block merges, and enforce security gates.

## Understanding the SARIF Generation Architecture

The SARIF pipeline in SkillSpector follows a LangGraph-based workflow that transforms raw findings into a standardized report.

### From CLI Flags to Graph State

In [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py), the `skillspector scan` command parses the `--format` flag. When set to `sarif`, the CLI stores `"output_format": "sarif"` in the initial graph state (`_scan_state` at lines 95-101). This state object orchestrates the entire analysis workflow, including static pattern matchers, optional LLM semantic checks, and OSV lookups.

### The Report Node and SARIF Assembly

The LangGraph workflow (`graph.invoke`) executes analyzer nodes that populate `filtered_findings` in the state. The **report** node in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) (lines 48-66) processes these findings through the `_build_sarif` function. This function iterates over each `Finding` object and constructs a `SarifResult` using Pydantic models defined in [`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py). Each result includes the rule ID, severity-mapped level, and precise file location (line/column ranges).

### Serialization and Exit Behavior

The `SarifLog` object serializes to a Python dictionary via `model_dump(..., by_alias=True)` and returns as `sarif_report` in the node's output dictionary. The CLI's `_write_result` helper writes this as pretty-printed JSON using `json.dumps(..., indent=2)`. SkillSpector exits with a non-zero status when the aggregated `risk_score` exceeds 50, enabling automatic pipeline failure configurations.

## GitHub Actions Integration

GitHub Actions can upload SARIF files directly to Code Scanning alerts using the `upload-sarif` action.

```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 automatically creates Code Scanning alerts in the Security tab and annotates pull requests with findings.

## Azure Pipelines Integration

Azure DevOps supports SARIF ingestion through the PublishTestResults 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/CD Integration

GitLab's security reports UI automatically renders SARIF artifacts when declared in the `artifacts:reports:sarif` section.

```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

```

GitLab parses the SARIF file and displays findings in the pipeline's Security tab and merge request security widget.

## Programmatic Integration with the Python API

For custom pipeline scripts or orchestration tools, invoke the graph directly without subprocess calls.

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

# Build the initial state with SARIF output format

state = {
    "input_path": "/path/to/skill",
    "output_format": "sarif",
    "use_llm": False,   # optional, static-only for speed

}

# Execute the full analysis workflow

result = graph.invoke(state)

# The SARIF document is available as a dict under sarif_report

sarif_report = result["sarif_report"]

# Write to file for downstream CI consumption

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

```

## Key Source Files for Custom Integrations

When extending SkillSpector's CI/CD capabilities, these files constitute the SARIF generation path:

- **[`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py)**: Entry point that sets `output_format` in graph state and handles file output via `_write_result`.
- **[`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py)**: Contains the `report()` node and `_build_sarif` function that translates internal `Finding` objects to SARIF results.
- **[`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py)**: Pydantic definitions for `SarifLog`, `SarifRun`, and `SarifResult` adhering to the SARIF 2.1.0 schema.
- **[`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py)**: Orchestrates the LangGraph workflow invoked by both CLI and Python API.
- **[`src/skillspector/models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/models.py)**: Defines the `Finding` data class that feeds into SARIF result generation.

## Summary

- SkillSpector produces **SARIF 2.1.0** output via the `--format sarif` flag, compatible with major CI/CD platforms.
- The reporting pipeline uses a LangGraph **report** node (`report()`) in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) to assemble findings into standardized results.
- **Exit codes** are non-zero when `risk_score` exceeds 50, enabling automatic pipeline failure.
- GitHub Actions, Azure Pipelines, and GitLab CI all support native SARIF ingestion for security dashboards and pull request annotations.
- The **Python API** exposes the same SARIF generation through `graph.invoke()` without requiring subprocess calls.

## Frequently Asked Questions

### What is SARIF and why does SkillSpector use it?

SARIF (Static Analysis Results Interchange Format) is an industry-standard JSON schema for static analysis results. SkillSpector uses SARIF 2.1.0 because GitHub, Azure DevOps, and GitLab natively parse it to create security alerts and annotate pull requests with precise line-level findings. The format includes rich metadata such as rule IDs, severity levels, and artifact locations that make diagnostics reproducible across different analysis environments.

### How do I fail a CI/CD pipeline based on SkillSpector findings?

SkillSpector automatically exits with a non-zero status code when the aggregated `risk_score` exceeds 50. You can rely on this behavior to fail CI jobs naturally, or parse the SARIF output programmatically to check the `results` array length and implement custom thresholds. For platform-specific configurations, use GitHub's `soft_fail` option or Azure's `failOnStandardError` settings alongside the SARIF upload tasks.

### Can I customize the SARIF output format?

Yes, by modifying [`src/skillspector/sarif_models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/sarif_models.py) to extend the Pydantic models (such as adding custom properties to the `SarifLog` or `SarifResult` objects) or by transforming the dictionary output in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) before serialization. The `_build_sarif` function serves as the primary extension point for adding custom fields without breaking downstream consumers that expect standard SARIF 2.1.0 structure.

### Does the Python API return SARIF differently than the CLI?

No, both use the same underlying graph implementation in [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py). The Python API returns the SARIF document as a Python dictionary in the `sarif_report` key of the result object, while the CLI writes the identical data to a JSON file via `json.dumps(..., indent=2)`. Both formats conform to the SARIF 2.1.0 specification and can be consumed by the same CI/CD ingestion mechanisms.