How to Integrate SkillSpector into CI/CD Pipelines Using SARIF Output
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, 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 (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. 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.
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.
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.
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.
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: Entry point that setsoutput_formatin graph state and handles file output via_write_result.src/skillspector/nodes/report.py: Contains thereport()node and_build_sariffunction that translates internalFindingobjects to SARIF results.src/skillspector/sarif_models.py: Pydantic definitions forSarifLog,SarifRun, andSarifResultadhering to the SARIF 2.1.0 schema.src/skillspector/graph.py: Orchestrates the LangGraph workflow invoked by both CLI and Python API.src/skillspector/models.py: Defines theFindingdata class that feeds into SARIF result generation.
Summary
- SkillSpector produces SARIF 2.1.0 output via the
--format sarifflag, compatible with major CI/CD platforms. - The reporting pipeline uses a LangGraph report node (
report()) insrc/skillspector/nodes/report.pyto assemble findings into standardized results. - Exit codes are non-zero when
risk_scoreexceeds 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 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 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. 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →