SARIF integration guide
Export Stella Ops Scanner results as SARIF 2.1.0 and feed them into GitHub, GitLab, Azure DevOps, or Gitea CI. This guide is for pipeline engineers wiring scanner output into a code-scanning surface; it covers both the Smart-Diff SARIF surface (risk changes between two scans) and the full-scan finding SARIF surface (one scan). For how the underlying priority scores are computed and tuned, see scoring configuration.
Provenance: SPRINT_3500_0004_0001 · SDIFF-BIN-032.
Reconciliation note (verify against source): This guide was reconciled against
src/Scannerin the main repo. The authoritative SARIF generator for Smart-Diff issrc/Scanner/__Libraries/StellaOps.Scanner.SmartDiff/Output/SarifOutputGenerator.cs(rule catalog, properties, schema URI, tool name) and the HTTP surface issrc/Scanner/StellaOps.Scanner.WebService/Endpoints/SmartDiffEndpoints.cs,ExportEndpoints.cs, andGitHubCodeScanningEndpoints.cs. Earlier drafts of this guide described an aspirational rule set (still visible in the stale fixturesrc/Scanner/__Tests/StellaOps.Scanner.SmartDiff.Tests/Fixtures/sarif-golden.v1.json) that does not match the shipping code; the tables below reflect what the code emits today.
Overview
StellaOps Scanner supports SARIF (Static Analysis Results Interchange Format) 2.1.0 output for integration with CI/CD platforms including GitHub, GitLab, and Azure DevOps. Two distinct SARIF surfaces exist today:
- Smart-Diff SARIF — material risk changes, VEX candidates, hardening regressions, and reachability flips between two scans, emitted by
StellaOps.Scanner.SmartDiff(rule IDsSDIFF00x). - Full-scan finding SARIF — vulnerability / secret / supply-chain / binary-hardening / license / configuration / language-detector findings for a single scan, emitted by
StellaOps.Scanner.Sarif(rule IDsSTELLA-<CATEGORY>-NNN).
This guide focuses on the Smart-Diff surface (the /smart-diff/.../sarif endpoint and the stellaops scan sarif CLI command). The full-scan finding rule catalog is summarized in Full-Scan Finding Rules.
Supported Platforms
| Platform | Integration Method | Native Support |
|---|---|---|
| GitHub Actions | Code Scanning API (POST /api/v1/scans/{scanId}/github/upload-sarif) plus upload-sarif action | Yes |
| GitLab CI | SAST report artifact (SARIF file upload) | Yes |
| Azure DevOps | SARIF Viewer extension (artifact upload) | Yes |
| Gitea Actions | Workflow generator target (CiPlatform.GiteaActions) | Yes |
| Other | File upload (SARIF 2.1.0 file artifact) | Yes |
Reconciliation note: Earlier drafts listed a “Jenkins / SARIF Plugin” row. No Jenkins workflow generator or integration exists in source —
src/Tools/StellaOps.Tools.WorkflowGenerator/CiPlatform.csdefines onlyGitHubActions,GitLabCi,AzureDevOps, andGiteaActions. The Jenkins row was removed; Gitea Actions (which is generated) was added. Native GitHub support is backed by the Code Scanning upload endpoint inGitHubCodeScanningEndpoints.cs. GitLab/Azure “native” support means the SARIF file is consumable by those platforms’ built-in viewers via artifact upload — there is no dedicated GitLab/Azure push integration in source.
Quarantine note (PLAN_20260704 A5 / D3): SARIF file export (the surfaces this guide documents) is fully available. The Stella CLI push verb
stella github upload-sarif(and the rest of thestella githubgroup) is quarantined — not wired in this release; it exits with a cleannot available in this releaseerror. To publish SARIF to GitHub Code Scanning, emit the file with the scanner and upload it with GitHub’s owngithub/codeql-action/upload-sarifaction. See PLAN_20260704 “Explicitly deferred / cut”.Direct-upload boundary (verified 2026-07-19): the Scanner WebService route and REST client are implemented and fail loud when unconfigured. The scan-scoped status/alert routes reject absent or cross-tenant scans before calling GitHub, and the accepted upload response uses named-route link generation so its status URL and
Locationpreserve the configured API base path. Production reachability rows currently reach the SARIF bridge without severity, affected-version, or source-location data, however, and repository credentials/default coordinates are host-level rather than bound to the tenant and scan. Use the file-export plus platform action path when those richer or tenant-specific bindings are required.
Quick Start
API Endpoint
The Smart-Diff SARIF endpoint is served under the configured API base path (default /api/v1):
# Get Smart-Diff SARIF output for a scan
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/sarif+json" \
"https://scanner.example.com/api/v1/smart-diff/scans/{scanId}/sarif"
# With pretty printing
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/sarif+json" \
"https://scanner.example.com/api/v1/smart-diff/scans/{scanId}/sarif?pretty=true"
The response content type is application/sarif+json. The endpoint requires the scanner.scans.read authorization policy. A full-scan finding SARIF document for a single scan is available at GET /api/v1/scans/{scanId}/exports/sarif (same scanner.scans.read policy).
Reconciliation note: The only query parameter the Smart-Diff SARIF handler reads is
pretty(HandleGetScanSarifAsync). The--include-hardening/--include-reachability/--min-severityoptions are CLI-side flags translated by the CLI’s backend client intoincludeHardening,includeReachability, andminSeverityquery parameters; the in-tree Smart-Diff handler always enables hardening, VEX, and reachability inclusion and does not currently honorminSeverity.
CLI Usage
# Export an existing scan's results as SARIF 2.1.0
stellaops scan sarif --scan-id <scanId> --output results.sarif
# Pretty-print to stdout, including hardening and reachability sections
stellaops scan sarif --scan-id <scanId> --pretty \
--include-hardening --include-reachability
# Limit to a minimum SARIF level
stellaops scan sarif --scan-id <scanId> --min-severity warning
CLI options (from CommandFactory / HandleScanSarifExportAsync):
| Option | Description |
|---|---|
--scan-id (required) | Scan identifier to export. |
--output, -o | Output file path (defaults to stdout). |
--pretty | Pretty-print JSON output. |
--include-hardening | Include binary hardening flags in SARIF output. |
--include-reachability | Include reachability analysis in SARIF output. |
--min-severity | Minimum SARIF level to include (none, note, warning, error). |
Reconciliation note: Earlier drafts showed
stellaops scan image:tag --output-format sarifand a top-levelstellaops smart-diff --output-format sarifcommand. Neither exists. The shipping command is thescan sarifsubcommand above (no--output-format,--tier, or--min-priorityflags on this command). The CLI backend client requestsapi/scans/{scanId}/sarif(seeBackendOperationsClient.GetScanSarifAsync).
SARIF Rule Definitions (Smart-Diff)
StellaOps.Scanner.SmartDiff emits exactly four rules. IDs, names, and default levels below are taken verbatim from SarifOutputGenerator.CreateRules():
| Rule ID | Name | Default Level | Description |
|---|---|---|---|
| SDIFF001 | MaterialRiskChange | warning | A vulnerability finding underwent a material risk state change between scans. (Level is note when the change direction is decreased.) |
| SDIFF002 | HardeningRegression | error | A binary lost security hardening flags compared to the previous scan. |
| SDIFF003 | VexCandidateGenerated | note | A VEX not_affected candidate was generated because vulnerable APIs are no longer present. |
| SDIFF004 | ReachabilityFlip | warning | The reachability of a vulnerability flipped between scans. |
Reconciliation note: Earlier drafts listed
SDIFF001 ReachabilityChange,SDIFF002 VexStatusFlip,SDIFF003 HardeningRegression,SDIFF004 IntelligenceSignal. That mapping matches the stalesarif-golden.v1.jsonfixture, not the code. The unit tests inSarifOutputGeneratorTests.csassert the IDs/names/levels above (e.g.SDIFF001 → MaterialRiskChange,SDIFF002 → Error,SDIFF003 → Note). There is no EPSS/KEV “IntelligenceSignal” rule in the Smart-Diff generator. The tool driver also advertises a CWE taxonomy reference and per-rulehelpUrivalues of the formhttps://stellaops.dev/docs/scanner/smart-diff/rules/SDIFF00x.
GitHub Actions Integration
StellaOps offers two GitHub paths: (1) export the SARIF file and use the standard github/codeql-action/upload-sarif action, or (2) push directly to the Code Scanning API via the scanner’s POST /api/v1/scans/{scanId}/github/upload-sarif endpoint.
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Export StellaOps SARIF
run: |
stellaops scan sarif \
--scan-id "${{ env.STELLAOPS_SCAN_ID }}" \
--output results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
category: stellaops
Reconciliation note: The previous example called
stellaops scan <repo> --output-format sarif --output results.sarif. The CLI exports an already-completed scan by--scan-id; it does not scan a repository slug inline. Substitute your own scan-id source (e.g. captured from a prior scan step).
GitLab CI Integration
security_scan:
stage: test
image: stellaops/cli:latest
script:
- stellaops scan sarif --scan-id "$STELLAOPS_SCAN_ID" --output gl-sast-report.sarif
artifacts:
reports:
sast: gl-sast-report.sarif
Azure DevOps Integration
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Bash@3
displayName: 'Export StellaOps SARIF'
inputs:
targetType: 'inline'
script: |
stellaops scan sarif --scan-id "$(stellaopsScanId)" --output $(Build.ArtifactStagingDirectory)/results.sarif
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)/results.sarif'
artifactName: 'security-results'
SARIF Schema Details
Result Levels
Smart-Diff levels are assigned per-rule (see the rule table above). The full-scan finding mapper (SarifRuleRegistry.GetLevel) maps severity to level and elevates KEV / runtime-reachable findings to error:
| SARIF Level | StellaOps Severity (full-scan mapper) | Notes |
|---|---|---|
error | Critical, High; or any KEV / runtime-reachable finding | Requires immediate attention |
warning | Medium (and the default fallback) | Should be reviewed |
note | Low | For awareness |
none | — | Suppressed / informational |
Reconciliation note: The
SarifLevelenum (SarifModels.cs) isnone,note,warning,error— there is no separate “Info” level. Severity-to-level mapping lives inSarifRuleRegistry.GetLevelfor full-scan findings; Smart-Diff results use the fixed per-rule levels above.
Result Kinds
Reconciliation note — NOT IMPLEMENTED: The Smart-Diff
SarifResultrecord (SarifModels.cs) does not emit akindproperty (fail/pass/notApplicable/informational); it carriesruleId,level,message,locations,fingerprints,partialFingerprints, andproperties. The earlier “Result Kinds” table described SARIF spec values that StellaOps does not populate today and has been removed to avoid implying behavior the code does not have.
Location Information
What the Smart-Diff generator actually emits for locations:
- MaterialRiskChange (SDIFF001) — a physical location (
artifactLocation.uri) only when the change carries aFilePath. The Smart-Diff HTTP handler currently passesFilePath: null, so these results have no location over the API path. - HardeningRegression (SDIFF002) — a physical location whose
artifactLocation.uriis the binary path. - VexCandidateGenerated (SDIFF003) and ReachabilityFlip (SDIFF004) — no
locations; correlation is via thefingerprints(purl,vulnId) and the result message text.
Reconciliation note: Earlier drafts claimed every result includes a component-PURL/function logical location and an OCI/SBOM URI. The generator emits
logicalLocationsfor none of the four rules, and does not attach function names. PURL and vuln id are carried infingerprints, not in a logical location. Component digests are surfaced at the run level (see properties below), not per result.
Example SARIF Output (Smart-Diff)
The following reflects the fields the generator actually produces (camelCase, nulls omitted; tool name and schema URI are constants in SarifOutputGenerator):
{
"version": "2.1.0",
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "StellaOps.Scanner.SmartDiff",
"version": "1.0.0",
"informationUri": "https://stellaops.dev/docs/scanner/smart-diff",
"rules": [
{
"id": "SDIFF001",
"name": "MaterialRiskChange",
"shortDescription": { "text": "Material risk change detected" },
"fullDescription": {
"text": "A vulnerability finding has undergone a material risk state change between scans."
},
"defaultConfiguration": { "level": "warning" },
"helpUri": "https://stellaops.dev/docs/scanner/smart-diff/rules/SDIFF001"
}
],
"supportedTaxonomies": [
{ "name": "CWE", "guid": "25F72D7E-8A92-459D-AD67-64853F788765" }
]
}
},
"results": [
{
"ruleId": "SDIFF001",
"level": "warning",
"message": {
"text": "Material risk change for CVE-2024-1234 in pkg:npm/lodash@4.17.20: New vulnerability introduced"
},
"fingerprints": {
"purl": "pkg:npm/lodash@4.17.20",
"vulnId": "CVE-2024-1234"
}
}
],
"invocations": [
{ "executionSuccessful": true, "startTimeUtc": "2025-01-15T10:30:00+00:00" }
],
"properties": {
"stellaops.diff.base.digest": "sha256:789xyz012abc",
"stellaops.diff.target.digest": "sha256:abc123def456"
}
}
]
}
Reconciliation note: Earlier drafts showed
tool.driver.name = "StellaOps Scanner", amain/sarif-2.1/schema/...schema URI, per-resultpropertiessuch asvulnerability/tier/direction, and logical locations with function names. None of those are emitted by the current generator. The constants above are verbatim fromSarifOutputGenerator(ToolName,ToolInfoUri,SchemaUri). The fixed message templates are"Material risk change for {vulnId} in {purl}: {reason}"(SDIFF001),"Hardening flag '{flag}' was {enabled/disabled} but is now {enabled/disabled} in {binaryPath}"(SDIFF002),"VEX not_affected candidate for {vulnId} in {purl}: {justification}"(SDIFF003), and"Vulnerability {vulnId} in {purl} became reachable|became unreachable"(SDIFF004).
Filtering Results
Reconciliation note — NOT IMPLEMENTED: Earlier drafts documented
--tier tainted_sink|executedand--min-priority 0.7flags on ascan/smart-diffSARIF command. Those flags do not exist on the SARIF export command. The closest in-source filters are:
stellaops scan sarif --min-severity <none|note|warning|error>(CLI-side level filter).- The Smart-Diff changes endpoint
GET /api/v1/smart-diff/scans/{scanId}/changes?minPriority=<0..1>filters material changes byPriorityScore(this returns JSON, not SARIF).--tierexists on other commands (e.g.stellaops witness list --tier confirmed|likely|present|unreachableand reachability commands), not on SARIF export.
StellaOps Property Keys (Smart-Diff)
Sprint: SPRINT_4400_0001_0001 (Signed Delta Verdict Attestation), SPRINT_3500_0004_0001
SARIF properties bag extensions actually populated by SarifOutputGenerator (CreateRunProperties / CreateMaterialChangeResult). Keys use dot notation (stellaops.<x>), not slash notation.
Run-Level Properties
| Property Key | Type | Description |
|---|---|---|
stellaops.diff.base.digest | string | Base image digest for the diff (omitted when absent). |
stellaops.diff.target.digest | string | Target image digest for the diff (omitted when absent). |
stellaops.deltaVerdictRef | string | Legacy delta-verdict reference (backwards compatibility). |
stellaops.attestation | object | Signed delta-verdict attestation: digest, predicateType, and optional ociReference, rekorLogId, signatureKeyId. |
When none of the above are present, the run properties bag is omitted entirely.
Result-Level Properties
| Property Key | Type | Description |
|---|---|---|
deltaVerdictRef | string | Present on SDIFF001 (MaterialRiskChange) results only, when a delta-verdict reference was supplied. |
Reconciliation note — NOT IMPLEMENTED: Earlier drafts documented a rich
stellaops/*(slash-form) result/run property set:stellaops/nodeHash,stellaops/pathHash,stellaops/topKNodeHashes,stellaops/evidenceUri,stellaops/attestationUri,stellaops/rekorUri,stellaops/witnessId,stellaops/witnessHash,stellaops/scanId,stellaops/graphHash,stellaops/sbomDigest,stellaops/feedSnapshot. None of these keys are emitted by any SARIF generator in source (a repo-wide search returns zero matches). The concepts (node hashes, path hashes, witness IDs, graph hashes) exist elsewhere in the platform but are not exported as SARIF properties. Where attestation/diff metadata is exported, it uses the dot-form run-level keys listed above. (Note: the full-scan finding mapper usesdetector/*slash-form keys — e.g.detector/fingerprint,detector/confidence— for language/secret detector signals; those are a different surface, documented under full-scan findings.)
Witness IDs
Witness identifiers in StellaOps are content-addressed and prefixed, e.g. sup:sha256:<64-hex> for suppression witnesses (see SuppressionWitnessBuilder) and wit:sha256:<hex> for reachability witnesses (CLI witness show accepts wit:sha256:...). They are not UUIDs and are not surfaced as a SARIF stellaops/witnessId property.
Full-Scan Finding Rules
For a single scan (not a diff), GET /api/v1/scans/{scanId}/exports/sarif returns findings mapped by StellaOps.Scanner.Sarif.SarifRuleRegistry. Rule IDs follow the STELLA-<CATEGORY>-NNN scheme:
| Prefix | Category | Example rules |
|---|---|---|
STELLA-VULN- | Vulnerabilities | STELLA-VULN-001 (Critical) … 004 (Low); 005 RuntimeReachable; 006 StaticReachable |
STELLA-SEC- | Secrets | 001 HardcodedSecret; 002 PrivateKeyExposure; 003 CredentialPattern |
STELLA-SC- | Supply chain | 001 UnsignedPackage; 002 UnknownProvenance; 003 TyposquatCandidate; 004 DeprecatedPackage |
STELLA-BIN- | Binary hardening | 001 MissingRelro; 002 MissingStackCanary; 003 MissingPie; 004 MissingFortifySource |
STELLA-LIC- | License | 001 LicenseCompliance |
STELLA-CFG- | Configuration | 001 ConfigurationIssue |
STELLA-LANG- | Language detector | 001 LanguageDetectorSignal |
Per-rule helpUri values point at https://stellaops.io/docs/findings/.... KEV and runtime-reachable findings are elevated to error regardless of base severity.
Troubleshooting
SARIF Validation Errors
If your CI platform rejects the SARIF output:
- Check required fields:
$schemaandversion("2.1.0") are present (always emitted).- Each result has
ruleId,level, andmessage.text.
- Confirm the schema URI matches the SARIF 2.1.0 schema your platform expects. StellaOps emits
https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json.
Reconciliation note — NOT IMPLEMENTED: Earlier drafts referenced a
stellaops validate-sarif <file>CLI command. No such command exists in the CLI. SARIF schema conformance is exercised in tests (SarifOutputGeneratorTests.GeneratedSarif_PassesSchemaValidation,SarifSchemaValidationTests), not via a shipped validation subcommand. Use a standard SARIF validator if you need to validate output in CI.
Empty Results
If SARIF contains no results:
- Confirm the scan completed successfully and the
scanIdis correct. - For Smart-Diff, confirm both base and target digests are present in the scan metadata (VEX candidates are only included when a target digest is resolved).
- Verify the image has vulnerability data and feed snapshots are current.
