GitHub

Basic Usage

This guide covers working with SARIF objects directly. Each SARIF type maps to a Crystal class with typed properties and JSON serialization.

Creating Results

A Result is the core finding object. At minimum, it requires a Message:

result = Sarif::Result.new(
  message: Sarif::Message.new(text: "Unused variable 'x'")
)

Add a rule reference and severity:

result = Sarif::Result.new(
  message: Sarif::Message.new(text: "Unused variable 'x'"),
  rule_id: "LINT001",
  level: Sarif::Level::Warning
)

Adding Locations

Specify where the issue was found:

result = Sarif::Result.new(
  message: Sarif::Message.new(text: "SQL injection risk"),
  rule_id: "SEC001",
  level: Sarif::Level::Error,
  locations: [
    Sarif::Location.new(
      physical_location: Sarif::PhysicalLocation.new(
        artifact_location: Sarif::ArtifactLocation.new(
          uri: "src/controllers/user_controller.cr",
          uri_base_id: "%SRCROOT%"
        ),
        region: Sarif::Region.new(
          start_line: 42,
          start_column: 10,
          end_line: 42,
          end_column: 55
        )
      )
    ),
  ]
)

Defining Rules

Rules are defined on the tool's driver component:

driver = Sarif::ToolComponent.new(
  name: "SecurityScanner",
  version: "2.0.0",
  rules: [
    Sarif::ReportingDescriptor.new(
      id: "SEC001",
      name: "SqlInjection",
      short_description: Sarif::MultiformatMessageString.new(
        text: "SQL Injection vulnerability"
      ),
      help_uri: "https://example.com/rules/SEC001",
      default_configuration: Sarif::ReportingConfiguration.new(
        level: Sarif::Level::Error
      )
    ),
  ]
)

Assembling a Complete Document

Combine tool, results, and other metadata into a SarifLog:

log = Sarif::SarifLog.new(
  runs: [
    Sarif::Run.new(
      tool: Sarif::Tool.new(driver: driver),
      results: [result],
      artifacts: [
        Sarif::Artifact.new(
          location: Sarif::ArtifactLocation.new(uri: "src/controllers/user_controller.cr"),
          mime_type: "text/x-crystal",
          roles: [Sarif::ArtifactRole::AnalysisTarget]
        ),
      ],
      column_kind: Sarif::ColumnKind::Utf16CodeUnits
    ),
  ]
)

JSON Output

All SARIF types support to_json and to_pretty_json:

puts log.to_json          # compact JSON
puts log.to_pretty_json   # indented JSON

Crystal's snake_case properties are automatically mapped to SARIF's camelCase keys. For example, start_line becomes startLine and rule_id becomes ruleId.

Default Values

SARIF defines default values for some fields. sarif.cr keeps these as nil and provides effective_* methods:

result = Sarif::Result.new(message: Sarif::Message.new(text: "test"))

result.level              # => nil
result.effective_level    # => Sarif::Level::Warning (SARIF default)

result.kind               # => nil
result.effective_kind     # => Sarif::ResultKind::Fail (SARIF default)

Nil Omission

Optional fields set to nil are omitted from JSON output. This produces clean, minimal SARIF:

result = Sarif::Result.new(
  message: Sarif::Message.new(text: "test"),
  rule_id: "R1"
)
result.to_json
# => {"message":{"text":"test"},"ruleId":"R1"}
# No "level", "locations", "codeFlows", etc.