Skip to content

RFC: SARIF Envelope Schema (ATR rule-id + target.digest) #24

Description

@aliksir

RFC: SARIF Envelope Schema (ATR rule-id + target.digest)

Context

Derived from anthropics/skills#492. This RFC proposes a standardized SARIF envelope schema for the claude-code-skill-security-check scanner output, incorporating agreements reached with @eeee2345.

The goal is to enable consistent, interoperable security scan results across multiple scanners while maintaining compatibility with SARIF 2.1.0 tooling.


Envelope Schema Overview

The proposed envelope wraps standard SARIF 2.1.0 output with three additions: a namespaced ATR rule-id for cross-scanner deduplication, a target.digest field (SHA-256) for precise artifact identification, and SARIF-native taxonomy binding via result.taxa[] and run.taxonomies[]. Invocation metadata (executionSuccessful, timing fields, toolExecutionNotifications[]) is included to support audit trails and failure diagnosis.


Agreed Specifications

1. ATR Rule-ID Namespace

ID assignment follows a PR-to-ATR first policy:

  • Primary: IDs are assigned via the PR-to-ATR process. Stable, scanner-comparable IDs are only available after ATR assignment.
  • Emergency / pre-assignment escape hatch: Use DRAFT-<scanner>-<slug> as a temporary ID (e.g., DRAFT-bandit-sql-injection).
    • A tracking issue URL must be embedded in the rule metadata when using a DRAFT ID.
    • DRAFT IDs must not be used for cross-scanner comparison until an ATR ID is assigned.
"rules": [
  {
    "id": "ATR-SEC-0042",
    "shortDescription": { "text": "SQL Injection via string concatenation" },
    "properties": {
      "draft-id": "DRAFT-bandit-sql-injection",
      "tracking-issue": "https://github.com/aliksir/claude-code-skill-security-check/issues/NNN"
    }
  }
]

2. target.digest

The target artifact is identified by SHA-256 digest using the standard artifactLocation + hashes pattern:

"artifacts": [
  {
    "location": { "uri": "src/app.py" },
    "hashes": {
      "sha-256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    }
  }
]
  • Format: lowercase hex, 64 characters.
  • Required for all file-level results to enable deduplication across scan runs.

3. SARIF-Native Taxonomy Binding

Decision: Use SARIF 2.1.0 standard result.taxa[] and run.taxonomies[] instead of a custom taxonomy_uri property.

  • In v1, taxonomy binding is MAY / optional — do not block adoption on taxonomy completeness.
  • Lock-in of taxonomy URIs deferred until multi-taxonomy consumers emerge in practice.
  • Versioning policy: Promoting taxonomy binding from MAY to MUST constitutes a breaking change and requires a semver major bump to the envelope schema.
"run": {
  "taxonomies": [
    {
      "name": "CWE",
      "version": "4.12",
      "organization": "MITRE",
      "shortDescription": { "text": "Common Weakness Enumeration" },
      "taxa": [
        { "id": "CWE-89", "name": "SQL Injection" }
      ]
    }
  ],
  "results": [
    {
      "ruleId": "ATR-SEC-0042",
      "taxa": [
        {
          "toolComponent": { "name": "CWE" },
          "id": "CWE-89"
        }
      ]
    }
  ]
}

4. Invocation Metadata

All invocations must include:

Field Type Required Notes
executionSuccessful boolean MUST false if scanner exited non-zero or threw
startTimeUtc string (ISO 8601) SHOULD UTC, e.g. "2026-06-25T03:00:00.000Z"
endTimeUtc string (ISO 8601) SHOULD UTC
toolExecutionNotifications[] array MUST if failed One entry per error; level: "error" for failures. Include properties.standardError (stderr excerpt) for debuggability
"invocations": [
  {
    "executionSuccessful": false,
    "startTimeUtc": "2026-06-25T03:00:00.000Z",
    "endTimeUtc": "2026-06-25T03:00:05.123Z",
    "toolExecutionNotifications": [
      {
        "message": { "text": "Scanner exited with code 2: config file not found" },
        "level": "error",
        "properties": {
          "standardError": "Error: ENOENT: config.yml not found"
        }
      }
    ]
  }
]

Envelope JSON Schema Draft

Top-level structure (simplified; not a complete definition):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://github.com/aliksir/claude-code-skill-security-check/schemas/sarif-envelope-v1.json",
  "title": "SARIF Envelope v1",
  "description": "ATR-compatible SARIF 2.1.0 envelope for claude-code-skill-security-check",
  "type": "object",
  "required": ["version", "runs"],
  "properties": {
    "version": {
      "type": "string",
      "enum": ["2.1.0"]
    },
    "runs": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["tool", "invocations", "artifacts", "results"],
        "properties": {
          "tool": {
            "type": "object",
            "required": ["driver"],
            "properties": {
              "driver": {
                "type": "object",
                "required": ["name", "version", "rules"],
                "properties": {
                  "name": { "type": "string" },
                  "version": { "type": "string" },
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["id"],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^(ATR-[A-Z]+-[0-9]+|DRAFT-[a-z]+-[a-z0-9-]+)$"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "invocations": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": ["executionSuccessful"],
              "properties": {
                "executionSuccessful": { "type": "boolean" },
                "startTimeUtc": { "type": "string", "format": "date-time" },
                "endTimeUtc": { "type": "string", "format": "date-time" },
                "toolExecutionNotifications": { "type": "array" }
              }
            }
          },
          "artifacts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "location": { "type": "object" },
                "hashes": {
                  "type": "object",
                  "properties": {
                    "sha-256": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          },
          "taxonomies": { "type": "array" },
          "results": { "type": "array" }
        }
      }
    }
  }
}

Open Questions

  • Multi-file results: When a result spans multiple files (e.g., taint flow), which artifact carries the digest? Resolved (2026-06-25): Each artifact carries its own artifacts[].hashes["sha-256"]; set of per-file digests = flow identity in v1. No flow-level composite digest. Per SARIF 3.24.11 + 3.36-3.38. Composite flow identity deferred to v1.x.
  • DRAFT ID lifecycle: What is the maximum allowed duration for a DRAFT ID before ATR assignment is required? Resolved (2026-06-25): Scanner-side mechanics only: (a) tracking-issue URL required before first emit, (b) historical SARIF frozen at scan-time ruleId + previous-draft-id for 2 minor versions, (c) id-alias-map.json sidecar with tombstone entries, (d) stale/retired thresholds set by ATR not envelope, (e) CI gate behavior is scanner-author choice. Extension fields in reportingDescriptor.properties: draft-id, tracking-issue, previous-draft-id, draft-status.
  • Digest scope (Resolved 2026-06-28): Should target.digest be required for all result levels, or only error / warning? (Currently: required for file-level results)
  • Taxonomy version pinning: When a taxonomy version (e.g., CWE 4.12) is updated, should existing results be re-tagged or frozen at scan-time version?
  • Schema versioning: Should $id include a semver suffix (e.g., sarif-envelope-v1.0.0.json) or keep v1 as a floating pointer?

Next Steps

  • Publish JSON Schema to schemas/sarif-envelope-v1.json in this repo (include reportingDescriptor.properties extension fields: draft-id, tracking-issue, previous-draft-id, draft-status)
  • Update scanner output to include invocations[].executionSuccessful + timing fields
  • Add result.taxa[] support (optional in v1; document the opt-in path)
  • Define DRAFT ID lifecycle policy (tracking issue + expiry) Resolved: scanner-side mechanics defined (see Open Questions)
  • Define id-alias-map.json sidecar schema (tombstone = null for rejected IDs)
  • Add schema validation step to CI (e.g., ajv validate)
  • Add CI gate: fail build if any DRAFT-* rule-id appears in results[] (scanner-author choice; strict default)
  • Update README with envelope schema reference
  • Review PDA SARIF emitter draft PR (expected ~2 weeks from 2026-06-25)

CC: @eeee2345 @ppcvote

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions