Help us shape the future of Go! Take the 2026 Go Developer Survey

sarifingest

package
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package sarifingest accepts SARIF 2.1.0 from third-party scanners so external findings enter the same asset model, prioritisation and governance path as first-party ones — without ever being presented as this system's own analysis.

A SARIF document is UNTRUSTED input. It is decoded as a STREAM and every repeated array — runs, results, and the rule tables they reference — is bounded, so a document that fits the byte bound can never expand into an unbounded number of Go values. Everything derived from a rule is computed ONCE per rule rather than once per result, so shared data cannot be multiplied by the result budget. Every path the document names is percent-decoded and checked before use (see uri.go), every bound is enforced before anything is persisted, and every result that cannot be attributed is refused with a typed reason rather than silently dropped.

Index

Constants

View Source
const (
	DefaultMaxDocumentBytes = 32 << 20
	DefaultMaxRuns          = 64
	DefaultMaxResults       = 100000
	// DefaultMaxRules bounds one tool component's rule table. Without it a document could spend its
	// whole byte budget on rules: they are held for the length of the run, and a rule is far larger in
	// memory than the JSON that declares it. Real tools ship hundreds; CodeQL's full suite is under
	// two thousand.
	DefaultMaxRules = 20000
)

Bounds on an untrusted document. Exceeding any of them is a typed error and NOTHING is persisted: a partially ingested report is worse than a refused one, because the gap is invisible.

The byte bound is deliberately modest. Because the parser streams and bounds every repeated array, peak memory is roughly the document itself plus the text it actually keeps, so the bound is a direct memory ceiling rather than a number that gets multiplied by the decoder. A report larger than this is refused with an explicit error that names the size — never truncated, which would look like a clean ingest of a smaller report.

Variables

This section is empty.

Functions

func Digest

func Digest(document []byte) string

Digest returns the SHA-256 of a document, which is the idempotency key: re-ingesting identical bytes must not duplicate findings.

Types

type Disagreement

type Disagreement struct {
	FindingID       shared.ID
	Rule            string
	Tool            string
	FirstPartyLevel shared.Severity
	ExternalLevel   shared.Severity
}

Disagreement records that a first-party finding and an external result describe the same issue but assign it different severities.

type IngestRequest

type IngestRequest struct {
	TenantID     shared.ID
	EngagementID shared.ID
	Document     []byte
	Actor        shared.ID
	AssetID      shared.ID
}

IngestRequest is one ingest of one document.

type IngestResult

type IngestResult struct {
	// Accepted is the number of findings newly stored; Deduped already existed.
	Accepted int
	Deduped  int
	// Matched counts findings linked to an agreeing first-party finding.
	Matched int
	// Disagreements are surfaced rather than resolved: when this system and an external tool assign
	// different severities to the same issue, that difference is information a reader must see.
	Disagreements []Disagreement
	// Refused lists every result that could not be ingested, with a typed reason. It is a list rather
	// than a count because a silent refusal is indistinguishable from a clean ingest.
	Refused  []importedfinding.RefusalReason
	Coverage []importedfinding.CoverageIssue
}

IngestResult reports exactly what happened, including what was NOT ingested.

func Validate

func Validate(ctx context.Context, document []byte, limits Limits) (IngestResult, error)

Validate parses a document and reports exactly what an ingest WOULD accept and refuse, without a store, an engagement, a tenant or an actor — and therefore without persisting anything.

It exists so the CLI can share this package's parsing and refusal rules rather than reimplementing them, while being structurally incapable of writing. A validate-only command backed by a real Service pointed at a throwaway store would print an accepted count that means nothing; this cannot.

type Limits

type Limits struct {
	MaxDocumentBytes int
	MaxRuns          int
	MaxResults       int
	MaxRules         int
}

Limits are the ingest bounds.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns the production bounds.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service ingests SARIF documents.

func NewService

func NewService(store ports.ImportedFindingStore, findings findingReader, engagements engagementReader, audit ports.AuditLogger, clock ports.Clock, ids ports.IDGenerator) (*Service, error)

NewService validates and returns the ingest service.

func (*Service) Ingest

func (s *Service) Ingest(ctx context.Context, req IngestRequest) (IngestResult, error)

Ingest parses a SARIF document and persists the results that can be attributed.

A bound breach or an unparseable document returns an error and persists NOTHING. A result-level problem is a typed refusal, so the caller sees what was dropped.

func (*Service) SetAttributor

func (s *Service) SetAttributor(a ports.FindingAttributor)

SetAttributor wires explicit asset attribution for SARIF producers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL