Skip to content

DONT MERGE: feat: add kosli attest sarif command#838

Draft
AlexKantor87 wants to merge 3 commits intomainfrom
feat/sarif-attestation-type
Draft

DONT MERGE: feat: add kosli attest sarif command#838
AlexKantor87 wants to merge 3 commits intomainfrom
feat/sarif-attestation-type

Conversation

@AlexKantor87
Copy link
Copy Markdown
Contributor

⚠️ DO NOT MERGE — Depends on server #5538. CI will stay red until that PR merges and the new server image ships, because integration tests target a production server image that doesn't yet have the /sarif route.

Summary

Adds a first-class kosli attest sarif command, parallel to kosli attest snyk, that:

  • Accepts SARIF v2.1.0 results from any compatible scanner (Checkov, Trivy, Semgrep, Snyk, CodeQL, etc.) — not just Snyk.
  • Surfaces the tool name and version dynamically from the SARIF report's runs[0].tool.driver fields, so the Kosli UI tile reads "Checkov 3.2.1" / "Trivy 0.52" / etc. depending on what was run.
  • Adds a -C, --compliant flag (default true, mirroring attest generic) so the caller controls whether the attestation is compliant. The CLI does not derive compliance from the SARIF findings — that decision is the caller's, expressed via the flag (typically driven by their own policy or rego rules).

Why

Customers currently map non-Snyk SARIF scans (notably Checkov) to the snyk attestation type because no native SARIF type exists. This works for parsing/display but bakes Snyk-specific compliance logic ("any high/medium/low → fail") into a generic SARIF flow, hardcodes "Snyk" as the tool label, and gives customers no way to express their own threshold. The new sarif type fixes all three.

Changes

Slice 1 — refactor: internal/snykinternal/sarif

The existing snyk parser is genuinely a generic SARIF v2.1.0 parser — it just lives in a snyk-named package. Renamed to reflect actual scope; the attest snyk command keeps working unchanged via the renamed package. Wire format preserved (still posts snyk_results JSON key for backwards compatibility).

Slice 2 — new attest sarif command

  • New cmd/kosli/attestSarif.go with --scan-results, --upload-results, and --compliant flags
  • POSTs to new endpoint api/v2/attestations/{org}/{flow}/trail/{trail}/sarif (added in server#5538)
  • Payload includes is_compliant: bool taken from the flag verbatim
  • Full test suite parallel to snyk, plus --compliant=false and explicit --compliant=true cases

Local verification

End-to-end smoke-tested against a local demo running the server branch:

  • Reported kosli attest sarif --scan-results sarif-iac.json → 201, attestation stored with dynamic tool name "Snyk IaC" 1.1177.0 and is_compliant: true
  • Reported kosli attest sarif --scan-results sarif-helm.json --compliant=false → 201, is_compliant: false stored verbatim despite zero high-severity findings
  • go test ./internal/sarif/... — passes
  • Full suite (go test ./...) — would only pass against a server with the /sarif route, hence the merge block

Test plan

  • Wait for server#5538 to merge and ship to the published server image
  • CI re-runs green on this PR (no further code changes needed)
  • Smoke test on a review env: report a SARIF attestation with --compliant=true and --compliant=false, confirm both tiles render correctly

🤖 Generated with Claude Code

AlexKantor87 and others added 2 commits April 29, 2026 15:42
The package parses generic SARIF v2.1.0 (used by Snyk, Checkov, Trivy,
Semgrep, etc.), not Snyk-specifically. Rename to reflect the actual
scope ahead of adding a first-class `kosli attest sarif` command that
will share this parser.

No behaviour change: the snyk attestation wire format keeps the
`snyk_results` JSON key, and the snyk command continues to invoke the
shared parser via its new name (`sarif.ProcessSarifResultFile`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A first-class SARIF attestation type, parallel to `attest snyk`, that:

- Accepts SARIF v2.1.0 from any compatible scanner (Checkov, Trivy,
  Semgrep, Snyk, CodeQL, etc.) and reports the parsed findings to Kosli.
- Surfaces the tool name and version dynamically from the SARIF report's
  `runs[0].tool.driver` fields rather than hardcoding a label.
- Adds a `-C, --compliant` flag (default true, mirroring `attest generic`)
  so the caller controls whether the attestation is compliant. The CLI
  does not derive compliance from the SARIF findings — that decision
  is the caller's, expressed via the flag (and typically driven by their
  own policy or rego rules).

POSTs to a new server endpoint `api/v2/attestations/{org}/{flow}/trail/{trail}/sarif`
which is added in a follow-up commit on the server side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 29, 2026

Claude finished @AlexKantor87's task in 2m 33s —— View job


PR Review: kosli attest sarif command

  • Run static analysis (build, vet, mod tidy)
  • Review changed files
  • Post inline comments on specific issues
  • Post summary review

Static Analysis

All green — go build ./..., go vet ./..., and go mod tidy pass with no issues. No unused or missing dependencies.

Summary

Clean, well-structured PR that follows the established attestation command patterns closely. The refactor slice (renaming internal/snykinternal/sarif) is surgical — the attest snyk command continues to work unchanged with the same snyk_results JSON key for backwards compatibility. The new attest sarif command mirrors attest snyk faithfully, with the addition of the --compliant flag modeled after attest generic.

Issues

Nit — trailing whitespace (attestSarif.go:28): Two trailing spaces in attestSarifShortDesc. Inline comment posted.

Test coverage gap (attestSarif_test.go): All command-level test cases use testdata/snyk_sarif.json. Since the command is marketed as accepting "any compatible SARIF scanner", at least one test with a non-Snyk fixture (Checkov/Trivy) would close the loop at the integration level. The parser unit tests in internal/sarif/ only cover Snyk fixtures too. Inline comment posted.

Debug logging completeness (attestSarif.go:188-192): The debug log is guarded by len(Results) > 0 — a zero-run SARIF file produces no debug output. Consider an else branch. Inline comment posted.

Pre-existing: findLevel fragility (sarif.go:141-158): Now that this parser is generic, the findLevel fallback path matters more. It only checks the Snyk-specific Properties["problem"] map, and fails hard if absent. The commented-out DefaultConfiguration block (lines 146-149) is the standard SARIF mechanism — it should either be enabled or removed. Per the SARIF spec, when level is absent and no default is configured, it defaults to "warning". Consider enabling the DefaultConfiguration path and falling back to "warning" instead of erroring. Not a blocker for this PR, but worth a follow-up.

Pre-existing: unsafe type assertion (sarif.go:133-137): value.(float64) will panic if priorityScore exists but isn't a float64. Now that arbitrary tools may use this property key, a safe assertion (score, ok := value.(float64)) would be more defensive. Low risk since priorityScore is Snyk-specific.

Pre-existing: strict $schema validation (sarif.go:53): Already flagged in prior review. Some SARIF producers (Checkov, Trivy) omit or vary the $schema URL, which would cause this parser to reject valid files. Worth testing with real non-Snyk output and potentially relaxing.

What's Good

  • Clean separation of concerns: The refactor to internal/sarif correctly identifies that the parser was always generic — only the package name was Snyk-specific. Backwards compatibility is preserved via the snyk_results JSON key.
  • --compliant flag design: Letting the caller control compliance rather than deriving it from findings is the right call — it avoids baking a policy decision into the CLI.
  • Thorough test suite: 14 test cases covering args validation, fingerprint modes, trail attestation, dot syntax, external URLs, annotations, and both compliant states.
  • Good debug logging: The logger.Debug calls in the run method will help with production troubleshooting.
  • PR description quality: Excellent — clear motivation, slice breakdown, local verification, and explicit merge-block rationale.

Comment thread internal/sarif/sarif.go
if err != nil {
return nil, err
}
if !strings.HasPrefix(report.Schema, "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/") && !strings.HasPrefix(report.Schema, "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-") && !strings.HasPrefix(report.Schema, "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This very long line is hard to read. Consider breaking it into a slice of valid prefixes and iterating, e.g.:

validPrefixes := []string{
    "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/",
    "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-",
    "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
}

Also worth noting: not all SARIF v2.1.0 producers set the $schema field (e.g., some Checkov / Trivy versions emit a valid SARIF file without it, or use a different URL). Since this command is explicitly marketed as "any compatible SARIF scanner", this validation may reject valid SARIF from those tools. Worth testing with real Checkov/Trivy output to confirm.

(This is pre-existing logic from the snyk package, so not a blocker for this PR — but flagging it as something to revisit.)

Comment thread cmd/kosli/attestSarif.go
payload SarifAttestationPayload
}

const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow. `
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Trailing whitespace in the short description string (two spaces before the closing backtick). This is consistent with the existing attestSnykShortDesc, but if you're cleaning things up it's worth removing.

Suggested change
const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow. `
const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow.`

Emit a debug line when SARIF parsing starts (with file path) and another
once parsing succeeds with the tool name, severity counts, and the
caller-supplied --compliant flag value. Useful when troubleshooting CI
pipelines where the upload silently looks empty.

The error path is unchanged — parse failures still surface via the
wrapped error, consistent with `attest snyk`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread cmd/kosli/attestSarif.go
payload SarifAttestationPayload
}

const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow. `
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: trailing whitespace (two spaces before the closing backtick). Already flagged in the prior review — just confirming it's still present.

Suggested change
const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow. `
const attestSarifShortDesc = `Report a SARIF attestation to an artifact or a trail in a Kosli flow.`

Comment on lines +36 to +37
func (suite *AttestSarifCommandTestSuite) TestAttestSarifCmd() {
tests := []cmdTestCase{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite only exercises Snyk SARIF fixtures (testdata/snyk_sarif.json). Since the PR markets this command as accepting "any compatible SARIF scanner", consider adding at least one test case using a non-Snyk fixture (Checkov or Trivy output) to prove the parser handles different tool.driver.name values and different level formats end-to-end. The existing internal/sarif/sarif_test.go covers the parser, but a command-level integration test would close the loop.

Comment thread cmd/kosli/attestSarif.go
Comment on lines +188 to +192
if len(o.payload.SarifResults.Results) > 0 {
r := o.payload.SarifResults.Results[0]
logger.Debug("SARIF parsed: tool=%s findings=%d high, %d medium, %d low (compliant=%t)",
o.payload.SarifResults.Tool.Name, r.HighCount, r.MediumCount, r.LowCount, o.payload.Compliant)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: the len(o.payload.SarifResults.Results) > 0 guard protects the debug log, but if a valid SARIF file has zero runs (e.g. an empty scan), this silently skips without any debug output. Consider adding an else branch:

} else {
    logger.Debug("SARIF parsed: tool=%s, no results found", o.payload.SarifResults.Tool.Name)
}

Nice addition of the debug logging overall — it's a good practice for production troubleshooting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant