SARIF

Generate SARIF v2.1.0 output for CI/CD integration with GitHub, GitLab, and Azure DevOps.

Output results as SARIF v2.1.0 (Static Analysis Results Interchange Format), the OASIS standard that GitHub Code Scanning, GitLab, and Azure DevOps can read. Each result carries a severity level and a file location, so a pipeline can gate on it.

Usage

Generate SARIF output:

noir scan . -f sarif --no-log

Save to file:

noir scan . -f sarif -o results.sarif --no-log

Example Output

A SARIF file has a runs array. Each run describes the tool (driver with name and version), the analysis rules, and the results (one entry per discovered endpoint with its source file location and line number). It also has an invocations entry whose executionSuccessful mirrors the errors key of the JSON output: false when any analyzer failed or skipped a file. File paths are relative to %SRCROOT%, which originalUriBaseIds maps to the directory Noir ran from.

{
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "OWASP Noir",
          "version": "1.4.0",
          "informationUri": "https://github.com/owasp-noir/noir",
          "rules": [
            {
              "id": "endpoint-discovery",
              "name": "Endpoint Discovery",
              "shortDescription": {
                "text": "Discovered API endpoints through static analysis"
              },
              "fullDescription": {
                "text": "This rule identifies API endpoints, their HTTP methods, and parameters discovered through static code analysis"
              },
              "defaultConfiguration": {
                "level": "note"
              },
              "helpUri": "https://github.com/owasp-noir/noir"
            }
          ]
        }
      },
      "results": [
        {
          "ruleId": "endpoint-discovery",
          "ruleIndex": 0,
          "level": "note",
          "message": {
            "text": "GET /api/users/:id (Parameters: path: id)"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/routes.cr",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 42
                }
              }
            }
          ]
        },
        {
          "ruleId": "endpoint-discovery",
          "ruleIndex": 0,
          "level": "note",
          "message": {
            "text": "POST /api/users (Parameters: json: username, json: email)"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "src/routes.cr",
                  "uriBaseId": "%SRCROOT%"
                },
                "region": {
                  "startLine": 56
                }
              }
            }
          ]
        }
      ],
      "invocations": [
        {
          "executionSuccessful": true
        }
      ],
      "originalUriBaseIds": {
        "%SRCROOT%": {
          "uri": "file:///path/to/project/"
        }
      }
    }
  ]
}

SARIF Features in Noir

Endpoint Discovery

Each discovered endpoint is reported as a SARIF result with:

  • Rule ID: endpoint-discovery
  • Level: note (informational)
  • Message: HTTP method, URL path, and discovered parameters
  • Location: file path and line number where the endpoint was found

Passive Scan Integration

With passive scan (-P or --passive-scan), security findings are added to the SARIF output. Severity maps to level like this:

  • critical, high → error
  • medium → warning
  • low → note
  • anything else → none

By default -P keeps only high and critical findings. Lower the bar with --passive-scan-severity medium (or low) to see the rest.

Each passive scan rule is added to the rules array with its name and description, and its first reference as helpUri. A finding's message is the rule name, never the matched text, so a detected secret does not end up in your code scanning alerts.

Callees and AI Context

When endpoints carry callees (--include callee) or AI review context (--ai-context), each endpoint result gets them under properties.noir.callees and properties.noir.ai_context.

Diff Mode

With --diff-path or --diff-ref, -f sarif reports only the new attack surface, using the rules diff-endpoint-added, diff-params-added and diff-auth-removed. Removed routes have no line to annotate in the new tree, so they are left out. See Diff.

Integration Examples

GitHub Code Scanning

Upload SARIF results to GitHub Code Scanning so findings show up in your repo's Security tab:

# Generate SARIF output
noir scan . -f sarif -o noir-results.sarif --no-log

# Upload to GitHub with the GitHub CLI (the API takes the file gzipped and base64-encoded)
gh api /repos/{owner}/{repo}/code-scanning/sarifs \
  -f sarif="$(gzip -c noir-results.sarif | base64 | tr -d '\n')" \
  -f ref=refs/heads/main \
  -f commit_sha="$(git rev-parse HEAD)"

GitLab Security Dashboard

GitLab reads SARIF through the sarif report type (GitLab Ultimate, recent versions). The sast report type expects GitLab's own JSON schema, so don't point it at a SARIF file. Add this job to .gitlab-ci.yml:

noir_scan:
  script:
    - noir scan . -f sarif -o noir.sarif --no-log
  artifacts:
    reports:
      sarif: noir.sarif

Azure DevOps

Publish the SARIF file as a build artifact in Azure Pipelines. The SARIF SAST Scans Tab extension picks it up automatically:

- script: noir scan . -f sarif -o noir.sarif --no-log
  displayName: 'Run Noir Scan'

- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: 'noir.sarif'
    ArtifactName: 'CodeAnalysisLogs'

Additional Resources