Skip to content

Commit e00a7d9

Browse files
senglezounorthdpole
authored andcommitted
construct uniform paths from existing supported sarif tools
path is of the form "file://<relative path to an option workspace root>"
1 parent 949cc30 commit e00a7d9

File tree

3 files changed

+358
-77
lines changed

3 files changed

+358
-77
lines changed

sarif/sarif.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type SarifTransformer struct {
3232
ruleToEcosystem map[string]string
3333
richDescription bool
3434
dataSource *ocsffindinginfo.DataSource
35+
36+
// the root path to which all file paths should be relative to, will be ultimately removed from findings,
37+
// this is used to handle CI/CD cases where findings have absolute path to the filesystem as opposed to project root.
38+
workspacePath string
3539
}
3640

3741
var typeName = map[int64]string{
@@ -76,6 +80,7 @@ func NewTransformer(
7680
guidProvider StableUUIDProvider,
7781
richDescription bool,
7882
dataSource *ocsffindinginfo.DataSource,
83+
workspacePath string,
7984
) (*SarifTransformer, error) {
8085
if scanResult == nil {
8186
return nil, errors.Errorf("method 'NewTransformer called with nil scanResult")
@@ -97,6 +102,11 @@ func NewTransformer(
97102
return nil, errors.Errorf("invalid data source provider: %w", err)
98103
}
99104

105+
cleanedWorkspacePath := filepath.Clean(workspacePath)
106+
if !filepath.IsAbs(cleanedWorkspacePath) {
107+
return nil, errors.Errorf("workspace path must be an absolute path")
108+
}
109+
100110
return &SarifTransformer{
101111
clock: clock,
102112
sarifResult: *scanResult,
@@ -106,6 +116,7 @@ func NewTransformer(
106116
taxasByCWEID: make(map[string]sarif.ReportingDescriptor),
107117
richDescription: richDescription,
108118
dataSource: dataSource,
119+
workspacePath: cleanedWorkspacePath,
109120
}, nil
110121
}
111122

@@ -161,7 +172,10 @@ func (s *SarifTransformer) transformToOCSF(
161172
res *sarif.Result,
162173
) (*ocsf.VulnerabilityFinding, error) {
163174
slog.Debug("parsing run from", slog.String("toolname", toolName))
164-
affectedCode, affectedPackages := s.mapAffected(res)
175+
affectedCode, affectedPackages, err := s.mapAffected(res)
176+
if err != nil {
177+
return nil, errors.Errorf("could not map affected code/packages: %w", err)
178+
}
165179

166180
var (
167181
ruleID *string
@@ -346,6 +360,32 @@ func (s *SarifTransformer) isSnykURI(uri string) bool {
346360
return strings.HasPrefix(uri, "https_//")
347361
}
348362

363+
// constructPath will take a given path and construct a file url pointing to the file relative to the workspacePath
364+
func (s *SarifTransformer) constructPath(path string) (string, error) {
365+
// if path is a file url (file://) we need to get the path after file before adding it back
366+
if strings.HasPrefix(path, "file://") {
367+
pathURL, err := url.Parse(path)
368+
if err != nil {
369+
return "", errors.Errorf("could not parse file url %s: %w", path, err)
370+
}
371+
if pathURL.Host != "" {
372+
path = pathURL.Host + pathURL.Path
373+
} else {
374+
path = pathURL.Path
375+
}
376+
}
377+
cleanedPath := filepath.Clean(path)
378+
if s.workspacePath != "" && strings.HasPrefix(cleanedPath, s.workspacePath) {
379+
pth, err := filepath.Rel(s.workspacePath, cleanedPath)
380+
if err != nil {
381+
return "", errors.Errorf("could not get relative path from path %s using prefix %q", cleanedPath, s.workspacePath)
382+
}
383+
cleanedPath = pth
384+
}
385+
finalPath := fmt.Sprintf("file://%s", cleanedPath)
386+
return finalPath, nil
387+
}
388+
349389
func (s *SarifTransformer) mapAffectedPackage(fixes []sarif.Fix, purl packageurl.PackageURL) *ocsf.AffectedPackage {
350390
affectedPackage := &ocsf.AffectedPackage{
351391
Purl: utils.Ptr(purl.String()),
@@ -440,11 +480,11 @@ func (s *SarifTransformer) rulesToEcosystem() map[string]string {
440480
return result
441481
}
442482

443-
func (s *SarifTransformer) mapAffected(res *sarif.Result) ([]*ocsf.AffectedCode, []*ocsf.AffectedPackage) {
483+
func (s *SarifTransformer) mapAffected(res *sarif.Result) ([]*ocsf.AffectedCode, []*ocsf.AffectedPackage, error) {
444484
var affectedCode []*ocsf.AffectedCode
445485
var affectedPackages []*ocsf.AffectedPackage
446486
if s.dataSource.TargetType == ocsffindinginfo.DataSource_TARGET_TYPE_WEBSITE { // websites do not carry code or package info
447-
return nil, nil
487+
return nil, nil, nil
448488
}
449489

450490
for _, location := range res.Locations {
@@ -469,14 +509,21 @@ func (s *SarifTransformer) mapAffected(res *sarif.Result) ([]*ocsf.AffectedCode,
469509
}
470510

471511
if physicalLocation.ArtifactLocation != nil && physicalLocation.ArtifactLocation.Uri != nil {
512+
uri := *location.PhysicalLocation.ArtifactLocation.Uri
472513
if p := s.detectPackageFromPhysicalLocation(*physicalLocation, pkgType); p != nil {
473514
affectedPackages = append(affectedPackages, s.mapAffectedPackage(res.Fixes, *p))
474-
} else if !s.isSnykURI(*location.PhysicalLocation.ArtifactLocation.Uri) {
475515
// Snyk special case, they use the repo url with some weird replacement as the artifact location
516+
} else if !s.isSnykURI(uri) {
517+
finalPath, err := s.constructPath(uri)
518+
if err != nil {
519+
return nil, nil, errors.Errorf("could not construct path for affected code: %w", err)
520+
}
521+
476522
ac := &ocsf.AffectedCode{
477523
File: &ocsf.File{
478-
Name: *location.PhysicalLocation.ArtifactLocation.Uri,
479-
Path: utils.Ptr(fmt.Sprintf("file://%s", *location.PhysicalLocation.ArtifactLocation.Uri)),
524+
// we will also need to change this line as well, if we can
525+
Name: strings.ReplaceAll(finalPath, "file://", ""),
526+
Path: utils.Ptr(finalPath),
480527
},
481528
}
482529

@@ -500,7 +547,7 @@ func (s *SarifTransformer) mapAffected(res *sarif.Result) ([]*ocsf.AffectedCode,
500547
}
501548
}
502549

503-
return affectedCode, affectedPackages
550+
return affectedCode, affectedPackages, nil
504551
}
505552

506553
func (s *SarifTransformer) mapSeverity(sarifResLevel sarif.ResultLevel) ocsf.VulnerabilityFinding_SeverityId {
@@ -657,9 +704,16 @@ func (s *SarifTransformer) mergeDataSources(
657704
if s.isSnykURI(*location.PhysicalLocation.ArtifactLocation.Uri) {
658705
dataSource.Uri = nil
659706
} else {
707+
uri := *location.PhysicalLocation.ArtifactLocation.Uri
708+
709+
finalPath, err := s.constructPath(uri)
710+
if err != nil {
711+
return nil, errors.Errorf("could not construct path for repository data source: %w", err)
712+
}
713+
660714
dataSource.Uri = &ocsffindinginfo.DataSource_URI{
661715
UriSchema: ocsffindinginfo.DataSource_URI_SCHEMA_FILE,
662-
Path: "file://" + filepath.Clean(*location.PhysicalLocation.ArtifactLocation.Uri),
716+
Path: finalPath,
663717
}
664718
}
665719

0 commit comments

Comments
 (0)