Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 77 additions & 21 deletions sarif/sarif.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,46 +69,102 @@ func validateDataSource(dataSource *ocsffindinginfo.DataSource) error {
return nil
}

func NewTransformer(
scanResult *sarif.SchemaJson,
findingsEcosystem string,
clock clockwork.Clock,
guidProvider StableUUIDProvider,
richDescription bool,
dataSource *ocsffindinginfo.DataSource,
) (*SarifTransformer, error) {
if scanResult == nil {
return nil, errors.Errorf("method 'NewTransformer called with nil scanResult")
type Config struct {
ScanResult *sarif.SchemaJson // required, the SARIF scan result to transform
FindingsEcosystem string // optional, if the SARIF doesn't contain package ecosystem info, this will be used as a hint to create the PURL for affected packages, valid example value is e.g. npm/docker/pypi
Clock clockwork.Clock // optional, if not provided, a real clock will be used
GuidProvider StableUUIDProvider // optional, if not provided, a basic stable UUID provider will be created
RichDescription bool // optional, if true, the description field of the finding will contain more information about the finding if available
DataSource *ocsffindinginfo.DataSource // required, the data source info to attach to each finding
}

type TransformerOption func(*Config)

// WithScanResult sets the SARIF scan result to transform
func WithScanResult(scanResult *sarif.SchemaJson) TransformerOption {
return func(cfg *Config) {
cfg.ScanResult = scanResult
}
}

// WithFindingsEcosystem sets the ecosystem of the findings, e.g. npm/docker/pypi
// This is used to create the PURL for affected packages if the SARIF doesn't contain package
func WithFindingsEcosystem(findingsEcosystem string) TransformerOption {
return func(cfg *Config) {
cfg.FindingsEcosystem = findingsEcosystem
}
}

// WithClock sets the clock to use for timestamps
func WithClock(clock clockwork.Clock) TransformerOption {
return func(cfg *Config) {
cfg.Clock = clock
}
}

// WithGuidProvider sets the stable UUID provider to use for generating finding UIDs
func WithGuidProvider(guidProvider StableUUIDProvider) TransformerOption {
return func(cfg *Config) {
cfg.GuidProvider = guidProvider
}
}

// WithRichDescription enables rich description in the finding if available
func WithRichDescription() TransformerOption {
return func(cfg *Config) {
cfg.RichDescription = true
}
}

// WithDataSource sets the data source info to attach to each finding
func WithDataSource(dataSource *ocsffindinginfo.DataSource) TransformerOption {
return func(cfg *Config) {
cfg.DataSource = dataSource
}
}

// NewTransformer creates a new SarifTransformer with the provided options
// ScanResult and DataSource are required options, if not provided, an error will be returned
func NewTransformer(options ...TransformerOption) (*SarifTransformer, error) {
cfg := &Config{}
for _, opt := range options {
opt(cfg)
}

if cfg.ScanResult == nil {
return nil, errors.Errorf("scan results must be provided, received nil")
}

if clock == nil {
clock = clockwork.NewRealClock()
if cfg.Clock == nil {
cfg.Clock = clockwork.NewRealClock()
}

if utils.IsNil(guidProvider) {
if utils.IsNil(cfg.GuidProvider) {
var err error
guidProvider, err = NewBasicStableUUIDProvider()
cfg.GuidProvider, err = NewBasicStableUUIDProvider()
if err != nil {
return nil, errors.Errorf("could not bootstrap stable UUID provider: %w", err)
}
}

if err := validateDataSource(dataSource); err != nil {
if err := validateDataSource(cfg.DataSource); err != nil {
return nil, errors.Errorf("invalid data source provider: %w", err)
}

return &SarifTransformer{
clock: clock,
sarifResult: *scanResult,
findingsEcosystem: findingsEcosystem,
guidProvider: guidProvider,
clock: cfg.Clock,
sarifResult: *cfg.ScanResult,
findingsEcosystem: cfg.FindingsEcosystem,
guidProvider: cfg.GuidProvider,
ruleToTools: make(map[string]sarif.ReportingDescriptor),
taxasByCWEID: make(map[string]sarif.ReportingDescriptor),
richDescription: richDescription,
dataSource: dataSource,
richDescription: cfg.RichDescription,
dataSource: cfg.DataSource,
}, nil
}

// ToOCSF transforms the SARIF scan result to a list of OCSF VulnerabilityFindings
// It returns the list of findings and an error if any occurred during the transformation
func (s *SarifTransformer) ToOCSF(ctx context.Context) ([]*ocsf.VulnerabilityFinding, error) {
slog.Debug(
"working with",
Expand Down
36 changes: 29 additions & 7 deletions sarif/sarif_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,10 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "", clock, nil, true, datasource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(datasource),
)
require.NoError(t, err)
actualIssues, err := transformer.ToOCSF(context.Background())
Expand Down Expand Up @@ -777,7 +780,11 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "npm", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithFindingsEcosystem("npm"),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)
actualIssues, err := transformer.ToOCSF(context.Background())
Expand Down Expand Up @@ -1078,7 +1085,9 @@ func Test_ParseOut(t *testing.T) {
}

transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithClock(clock),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)

Expand Down Expand Up @@ -1385,7 +1394,10 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)
actualIssues, err := transformer.ToOCSF(context.Background())
Expand Down Expand Up @@ -1602,7 +1614,11 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "docker", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithFindingsEcosystem("docker"),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)
actualIssues, err := transformer.ToOCSF(context.Background())
Expand Down Expand Up @@ -1944,7 +1960,10 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)

Expand Down Expand Up @@ -2227,7 +2246,10 @@ func Test_ParseOut(t *testing.T) {
},
}
transformer, err := sariftransformer.NewTransformer(
&sarifOutput, "", clock, nil, true, dataSource,
sariftransformer.WithScanResult(&sarifOutput),
sariftransformer.WithClock(clock),
sariftransformer.WithRichDescription(),
sariftransformer.WithDataSource(dataSource),
)
require.NoError(t, err)
actualIssues, err := transformer.ToOCSF(context.Background())
Expand Down
Loading