21 KiB
GitHub Code Scanning Integration via SARIF
Status: Revised Original: 09-Jan-2026 (Lighting Up GitHub with SARIF) Revision: 09-Jan-2026 Author: Product/Engineering Epic: Platform Integrations
Executive Summary
This advisory defines the complete integration between StellaOps Scanner and GitHub Code Scanning via SARIF 2.1.0. The integration enables StellaOps findings to appear natively in GitHub's Security tab with zero custom UI, leveraging GitHub's existing annotation, filtering, and alerting infrastructure.
Current State
| Component | Status | Notes |
|---|---|---|
| SARIF 2.1.0 Models | Implemented | Full schema in Scanner.SmartDiff |
| SmartDiff SARIF Export | Implemented | Binary diff findings, production-ready |
| Findings SARIF Export | Not Implemented | Main vulnerability findings |
| GitHub App Connector | Implemented | Auth + health checks working |
| GitHub Code Scanning Upload | Not Implemented | REST API client needed |
| GitHub Actions Workflow | Not Implemented | Template generation needed |
Business Value
- Zero Custom UI: GitHub renders findings, annotations, and PR decorations
- Native Integration: Findings appear in Security tab alongside Dependabot/CodeQL
- Alert Management: GitHub's existing dismiss/reopen/severity workflow
- PR Blocking: Branch protection rules can require scan results
- Enterprise Ready: Supports GitHub.com and GitHub Enterprise Server
What SARIF Is
SARIF (Static Analysis Results Interchange Format) is an OASIS standard (version 2.1.0) for representing static analysis results. GitHub Code Scanning accepts a subset of SARIF 2.1.0 and renders it as security alerts.
SARIF Structure
SarifLog
├── $schema: "https://json.schemastore.org/sarif-2.1.0.json"
├── version: "2.1.0"
└── runs[]
├── tool
│ └── driver
│ ├── name: "StellaOps Scanner"
│ ├── version: "1.0.0"
│ └── rules[]
│ ├── id: "STELLA-VULN-001"
│ ├── name: "Critical Vulnerability"
│ └── properties (CWE, CVSS, etc.)
├── results[]
│ ├── ruleId: "STELLA-VULN-001"
│ ├── level: "error" | "warning" | "note"
│ ├── message.text
│ ├── locations[]
│ │ └── physicalLocation
│ │ ├── artifactLocation.uri
│ │ └── region.startLine
│ └── fingerprints (deduplication)
└── versionControlProvenance (git metadata)
Three Upload Options
Option 1: GitHub Actions (Recommended)
# .github/workflows/stellaops-scan.yml
name: StellaOps Scan
on:
push:
branches: [main, release/*]
pull_request:
branches: [main]
schedule:
- cron: "0 3 * * 1" # Weekly Monday 3 AM
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # Required for Code Scanning
steps:
- uses: actions/checkout@v4
- name: Run StellaOps Scanner
uses: stellaops/scanner-action@v1
with:
image: ${{ github.repository }}:${{ github.sha }}
output-format: sarif
output-file: results.sarif
# Optional: filter by severity
min-severity: medium
- name: Upload SARIF to Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
category: stellaops-scanner
# Optional: wait for processing
wait-for-processing: true
Option 2: REST API
For scans running outside GitHub Actions:
# Gzip + base64 encode the SARIF file
gzip -c results.sarif | base64 -w0 > sarif.b64
# Upload to GitHub Code Scanning API
curl -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/OWNER/REPO/code-scanning/sarifs" \
-d "{
\"commit_sha\": \"$(git rev-parse HEAD)\",
\"ref\": \"refs/heads/main\",
\"sarif\": \"$(cat sarif.b64)\",
\"tool_name\": \"StellaOps Scanner\"
}"
Required Scopes:
- Public repos:
public_repo - Private repos:
security_events
Option 3: StellaOps CLI
# Scan and upload in one command
stella scan \
--image myregistry/myapp:latest \
--sarif results.sarif \
--github-upload \
--github-token $GITHUB_TOKEN \
--github-repo owner/repo \
--github-ref refs/heads/main \
--github-sha $(git rev-parse HEAD)
# Or: scan first, upload separately
stella scan --image myregistry/myapp:latest --sarif results.sarif
stella github upload \
--sarif results.sarif \
--repo owner/repo \
--ref refs/heads/main
StellaOps SARIF Rule Taxonomy
Vulnerability Rules (STELLA-VULN-*)
| Rule ID | Name | Level | Description |
|---|---|---|---|
| STELLA-VULN-001 | Critical Vulnerability | error | CVSS >= 9.0 or KEV-listed |
| STELLA-VULN-002 | High Vulnerability | error | CVSS 7.0-8.9 |
| STELLA-VULN-003 | Medium Vulnerability | warning | CVSS 4.0-6.9 |
| STELLA-VULN-004 | Low Vulnerability | note | CVSS < 4.0 |
| STELLA-VULN-005 | Reachable Vulnerability | error | Runtime-confirmed reachable |
| STELLA-VULN-006 | Static Reachable Vulnerability | warning | Static-only reachable |
Secret Detection Rules (STELLA-SEC-*)
| Rule ID | Name | Level | Description |
|---|---|---|---|
| STELLA-SEC-001 | Hardcoded Secret | error | API key, password, token in code |
| STELLA-SEC-002 | Private Key Exposure | error | PEM, PKCS#8 private key |
| STELLA-SEC-003 | Credential Pattern | warning | Potential credential pattern |
Supply Chain Rules (STELLA-SC-*)
| Rule ID | Name | Level | Description |
|---|---|---|---|
| STELLA-SC-001 | Unsigned Package | warning | Package lacks signature |
| STELLA-SC-002 | Unknown Provenance | warning | No SLSA provenance |
| STELLA-SC-003 | Typosquat Candidate | error | Potential typosquatting |
| STELLA-SC-004 | Deprecated Package | note | Package marked deprecated |
Binary Hardening Rules (STELLA-BIN-*)
| Rule ID | Name | Level | Description |
|---|---|---|---|
| STELLA-BIN-001 | Missing RELRO | warning | No full RELRO |
| STELLA-BIN-002 | No Stack Canary | warning | Stack protection disabled |
| STELLA-BIN-003 | No PIE | warning | Position-independent disabled |
| STELLA-BIN-004 | No Fortify | note | FORTIFY_SOURCE not used |
SmartDiff Rules (SDIFF-*) - Already Implemented
| Rule ID | Name | Level | Description |
|---|---|---|---|
| SDIFF001 | Material Risk Change | warning | Risk profile changed |
| SDIFF002 | Binary Hardening Regression | error | Security control removed |
| SDIFF003 | VEX Candidate | note | VEX status changed |
| SDIFF004 | Reachability Change | warning | Reachability status changed |
SARIF Schema for Findings
Complete Finding Example
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [{
"tool": {
"driver": {
"name": "StellaOps Scanner",
"version": "3.2.1",
"informationUri": "https://stellaops.io/scanner",
"rules": [{
"id": "STELLA-VULN-001",
"name": "CriticalVulnerability",
"shortDescription": {
"text": "Critical vulnerability detected"
},
"fullDescription": {
"text": "A critical severity vulnerability (CVSS >= 9.0) was detected in a dependency."
},
"helpUri": "https://stellaops.io/rules/STELLA-VULN-001",
"properties": {
"precision": "high",
"problem.severity": "error",
"security-severity": "9.8",
"tags": ["security", "vulnerability", "critical"]
}
}],
"supportedTaxonomies": [{
"name": "CWE",
"guid": "d4c8a3c4-8f5e-4f3a-9a6b-2c7d8e9f0a1b"
}]
}
},
"taxonomies": [{
"name": "CWE",
"guid": "d4c8a3c4-8f5e-4f3a-9a6b-2c7d8e9f0a1b",
"taxa": [{
"id": "502",
"name": "Deserialization of Untrusted Data"
}]
}],
"results": [{
"ruleId": "STELLA-VULN-001",
"ruleIndex": 0,
"level": "error",
"message": {
"text": "Critical vulnerability CVE-2021-44228 (Log4Shell) in org.apache.logging.log4j:log4j-core@2.14.1. CVSS: 10.0. This vulnerability allows remote code execution via JNDI injection.",
"markdown": "**Critical vulnerability** [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) (Log4Shell) in `org.apache.logging.log4j:log4j-core@2.14.1`.\n\n**CVSS:** 10.0 (Critical)\n\n**Description:** Remote code execution via JNDI injection.\n\n**Remediation:** Upgrade to log4j-core >= 2.17.0"
},
"locations": [{
"physicalLocation": {
"artifactLocation": {
"uri": "pom.xml",
"uriBaseId": "%SRCROOT%"
},
"region": {
"startLine": 45,
"startColumn": 1,
"endLine": 49,
"endColumn": 1,
"snippet": {
"text": "<dependency>\n <groupId>org.apache.logging.log4j</groupId>\n <artifactId>log4j-core</artifactId>\n <version>2.14.1</version>\n</dependency>"
}
}
},
"logicalLocations": [{
"name": "org.apache.logging.log4j:log4j-core",
"kind": "package",
"fullyQualifiedName": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"
}]
}],
"fingerprints": {
"stellaops/v1": "sha256:a1b2c3d4e5f6...",
"primaryLocationLineHash": "abc123..."
},
"partialFingerprints": {
"primaryLocationLineHash": "abc123..."
},
"taxa": [{
"id": "502",
"toolComponent": {
"name": "CWE"
}
}],
"properties": {
"stellaops.finding.id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"stellaops.component.purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
"stellaops.vulnerability.cve": "CVE-2021-44228",
"stellaops.vulnerability.cvss": 10.0,
"stellaops.vulnerability.severity": "critical",
"stellaops.vulnerability.epss": 0.975,
"stellaops.vulnerability.kev": true,
"stellaops.reachability.state": "RuntimeObserved",
"stellaops.reachability.confidence": 0.92,
"stellaops.vex.status": "affected",
"stellaops.evidence.uris": [
"stella://reachgraph/blake3:abc123",
"stella://signals/runtime/tenant/sha256:def456"
]
}
}],
"artifacts": [{
"location": {
"uri": "pom.xml",
"uriBaseId": "%SRCROOT%"
},
"mimeType": "application/xml",
"hashes": {
"sha-256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
}],
"versionControlProvenance": [{
"repositoryUri": "https://github.com/example/myapp",
"revisionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"branch": "main",
"mappedTo": {
"uriBaseId": "%SRCROOT%"
}
}],
"properties": {
"stellaops.scan.id": "scan-12345",
"stellaops.scan.artifact": "sha256:abc123...",
"stellaops.scan.timestamp": "2026-01-09T10:30:00Z",
"stellaops.scan.version": "3.2.1",
"stellaops.attestation": {
"digest": "sha256:sig789...",
"predicateType": "https://stellaops.io/attestation/scan/v1",
"rekorLogId": 12345678
}
}
}]
}
Fingerprinting Strategy
Fingerprints enable GitHub to deduplicate alerts across scans:
Primary Fingerprint (stellaops/v1)
SHA-256(
ruleId + "|" +
component_purl + "|" +
vulnerability_id + "|" +
artifact_digest
)
Partial Fingerprints (GitHub-computed fallback)
When source code is available, provide:
primaryLocationLineHash: Hash of code at finding locationprimaryLocationContextHash: Hash of surrounding context
Deduplication Behavior
| Scenario | GitHub Behavior |
|---|---|
| Same fingerprint, new scan | Updates existing alert |
| New fingerprint | Creates new alert |
| Missing fingerprint in new scan | Closes alert as fixed |
| Fingerprint reappears | Reopens alert |
GitHub Code Scanning API Integration
Upload Endpoint
POST /repos/{owner}/{repo}/code-scanning/sarifs
Request Format
{
"commit_sha": "a1b2c3d4e5f6...",
"ref": "refs/heads/main",
"sarif": "<gzip+base64 encoded SARIF>",
"checkout_uri": "file:///home/runner/work/repo/repo",
"started_at": "2026-01-09T10:00:00Z",
"tool_name": "StellaOps Scanner"
}
Response
{
"id": "47177e22-5596-11eb-80a1-c1e54ef945c6",
"url": "https://api.github.com/repos/owner/repo/code-scanning/sarifs/47177e22-5596-11eb-80a1-c1e54ef945c6"
}
Status Polling
GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}
Response includes processing_status: pending | complete | failed
GitHub Enterprise Server Support
The existing GitHubAppConnectorPlugin supports GHES:
// Endpoint resolution
var apiBase = isEnterprise
? $"https://{hostname}/api/v3"
: "https://api.github.com";
GHES Configuration
# etc/integrations.yaml
github:
type: github_enterprise
hostname: github.mycompany.com
api_version: "2022-11-28"
app_id: 12345
private_key_path: /secrets/github-app.pem
Important Gotchas
1. One Tool Per Run (June 2025 Deadline)
GitHub is deprecating combined runs. Each tool must have its own run:
{
"runs": [
{ "tool": { "driver": { "name": "StellaOps Scanner" } }, "results": [...] },
{ "tool": { "driver": { "name": "StellaOps SmartDiff" } }, "results": [...] }
]
}
Not:
{
"runs": [
{
"tool": { "driver": { "name": "StellaOps" } },
"results": [/* mixed scanner + smartdiff */]
}
]
}
2. Permissions
| Context | Required Permission |
|---|---|
| GitHub Actions | security-events: write |
| REST API (public) | public_repo scope |
| REST API (private) | security_events scope |
| GitHub App | security_events: write |
3. PR Uploads from Forks
- Direct API uploads from forks have restrictions
- Use
github/codeql-action/upload-sarifaction instead - The action handles fork context correctly
4. SARIF Size Limits
| Limit | Value |
|---|---|
| Uncompressed | 10 MB |
| Compressed (gzip) | Recommended for API |
| Results per run | 10,000 (soft limit) |
5. Rate Limits
- 1000 requests/hour for Code Scanning API
- Use conditional requests (
If-None-Match) where possible
Integration Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ StellaOps Scanner │
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ Scan Engine │──>│ Findings Ledger │──>│ SARIF Export Service │ │
│ └─────────────┘ └─────────────────┘ │ - FindingsSarifMapper │ │
│ │ - SarifRuleRegistry │ │
│ │ - FingerprintGenerator │ │
│ └─────────────┬───────────────┘ │
└─────────────────────────────────────────────────────────┼───────────────────┘
│
│ SARIF 2.1.0
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ GitHub Integration Service │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ GitHubAppConnector │ │ CodeScanningClient │ │ SarifUploader │ │
│ │ (existing) │ │ (new) │ │ (new) │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┬───────────────────┘
│
│ REST API / Actions
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ GitHub Code Scanning │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Security Tab │ │ PR Annotations │ │ Branch Protection │ │
│ │ Alerts │ │ Check Runs │ │ Rules │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
CLI Commands
Scan with SARIF Output
# Basic scan with SARIF output
stella scan --image myapp:latest --format sarif --output results.sarif
# With severity filter
stella scan --image myapp:latest --format sarif --output results.sarif \
--min-severity high
# With reachability evidence
stella scan --image myapp:latest --format sarif --output results.sarif \
--include-reachability
# Pretty-printed for debugging
stella scan --image myapp:latest --format sarif --output results.sarif \
--pretty
GitHub Upload
# Upload SARIF to GitHub Code Scanning
stella github upload-sarif \
--sarif results.sarif \
--repo owner/repo \
--ref refs/heads/main \
--sha $(git rev-parse HEAD)
# With GitHub Enterprise
stella github upload-sarif \
--sarif results.sarif \
--repo owner/repo \
--ref refs/heads/main \
--sha $(git rev-parse HEAD) \
--github-url https://github.mycompany.com
# Wait for processing
stella github upload-sarif \
--sarif results.sarif \
--repo owner/repo \
--wait --timeout 5m
Generate Workflow
# Generate GitHub Actions workflow
stella github generate-workflow \
--repo owner/repo \
--output .github/workflows/stellaops-scan.yml \
--triggers push,pull_request,schedule \
--schedule "0 3 * * 1"
Success Criteria
Quantitative
| Metric | Target |
|---|---|
| SARIF schema validation | 100% pass rate |
| Upload success rate | > 99% |
| Processing time (1000 findings) | < 30 seconds |
| Fingerprint stability | 100% (same input = same fingerprint) |
Qualitative
- Findings appear correctly in GitHub Security tab
- PR annotations show at correct line numbers
- Alert deduplication works across scans
- Branch protection rules can gate on scan results
Related Documentation
- SARIF 2.1.0 Specification (OASIS)
- GitHub SARIF Support
- GitHub Code Scanning REST API
- upload-sarif Action
Sprint Index
| Sprint ID | Title | Status |
|---|---|---|
| SPRINT_20260109_010_000 | INDEX: GitHub Code Scanning | Planning |
| SPRINT_20260109_010_001 | Findings SARIF Exporter | Planning |
| SPRINT_20260109_010_002 | GitHub Code Scanning Client | Planning |
| SPRINT_20260109_010_003 | CI/CD Workflow Templates | Planning |
Last updated: 09-Jan-2026