diff --git a/go.mod b/go.mod index 379439d..66c7d79 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/puzpuzpuz/xsync v1.5.2 github.com/rs/zerolog v1.34.0 github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905 - github.com/snyk/go-application-framework v0.9.0 + github.com/snyk/go-application-framework v0.10.1-0.20260802135238-d6a8132430a6 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.11.1 golang.org/x/net v0.55.0 diff --git a/go.sum b/go.sum index 76835bb..f681b47 100644 --- a/go.sum +++ b/go.sum @@ -246,8 +246,8 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905 h1:pUe6iOWHEOFY0t4u4ssXeTqpMmZBu1xq06VBFI9zUik= github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905/go.mod h1:Ytttq7Pw4vOCu9NtRQaOeDU2dhBYUyNBe6kX4+nIIQ4= -github.com/snyk/go-application-framework v0.9.0 h1:el8tH/5gHLXFVzi63ldQeUxzbnACDvrpP2yLvoGfLQ0= -github.com/snyk/go-application-framework v0.9.0/go.mod h1:0YC7xCETnFTdz6rq8OQPL0aWekMLOkBQu1FE4/cReMA= +github.com/snyk/go-application-framework v0.10.1-0.20260802135238-d6a8132430a6 h1:KBkC6mnjUyHJSoUIiM+LiJ80qM/Ej0gg3tBkJD+mN2I= +github.com/snyk/go-application-framework v0.10.1-0.20260802135238-d6a8132430a6/go.mod h1:9GV/CTAhM8PT9MbxwYt/Za7tKDtw/Wuq6SyCu1XFzvk= github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530 h1:s9PHNkL6ueYRiAKNfd8OVxlUOqU3qY0VDbgCD1f6WQY= github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530/go.mod h1:88KbbvGYlmLgee4OcQ19yr0bNpXpOr2kciOthaSzCAg= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= diff --git a/internal/commands/code_workflow/native_workflow.go b/internal/commands/code_workflow/native_workflow.go index 4893df8..47c4bc9 100644 --- a/internal/commands/code_workflow/native_workflow.go +++ b/internal/commands/code_workflow/native_workflow.go @@ -20,15 +20,18 @@ import ( "github.com/snyk/code-client-go/scan" "github.com/snyk/error-catalog-golang-public/code" "github.com/snyk/go-application-framework/pkg/analytics" + "github.com/snyk/go-application-framework/pkg/apiclients/testapi" "github.com/snyk/go-application-framework/pkg/configuration" "github.com/snyk/go-application-framework/pkg/instrumentation" "github.com/snyk/go-application-framework/pkg/local_workflows/content_type" errorutils "github.com/snyk/go-application-framework/pkg/local_workflows/error_utils" + "github.com/snyk/go-application-framework/pkg/local_workflows/json_schemas" "github.com/snyk/go-application-framework/pkg/local_workflows/local_models" "github.com/snyk/go-application-framework/pkg/networking" "github.com/snyk/go-application-framework/pkg/ui" "github.com/snyk/go-application-framework/pkg/utils" sarif2 "github.com/snyk/go-application-framework/pkg/utils/sarif" + "github.com/snyk/go-application-framework/pkg/utils/ufm" "github.com/snyk/go-application-framework/pkg/workflow" ) @@ -45,6 +48,7 @@ const ( ConfigurationSlceEnabled = "internal_snyk_scle_enabled" ConfigurationUploadToFileUploadApi = "internal_upload_to_fua" + ConfigurationUseUFM = "internal_code_use_ufm" MetadataBundleHash = "Snyk-Bundle-Hash" @@ -162,42 +166,85 @@ func EntryPointNative(invocationCtx workflow.InvocationContext, opts ...Optional output = append(output, summaryData) if resultAvailable { - // transform sarif to findings - localFindings, lfError := local_models.TransformToLocalFindingModelFromSarif(&result.Sarif, summary) - if lfError != nil { - return nil, lfError + useUFM := config.GetBool(ConfigurationUseUFM) + logger.Debug().Msgf("Use UFM: %v", useUFM) + + var findingsData workflow.Data + var findingsErr error + if useUFM { + findingsData, findingsErr = buildUFMFindings(id, config, logger, &result.Sarif, summary, resultMetaData, path) + } else { + findingsData, findingsErr = buildLocalFindings(id, config, logger, &result.Sarif, summary, resultMetaData, path) } - - logger.Debug().Msg("Coverage report for file(s):") - for _, coverage := range localFindings.Summary.Coverage { - logger.Debug().Msgf(" type: %s, format: %s, files: %d", coverage.Type, coverage.Lang, coverage.Files) - } - - // translate metadata to findings - local_models.TranslateMetadataToLocalFindingModel(resultMetaData, &localFindings, config) - - targetId, targetIdError := instrumentation.GetTargetId(config.GetString(configuration.INPUT_DIRECTORY), instrumentation.AutoDetectedTargetId, instrumentation.WithConfiguredRepository(config)) - if targetIdError != nil { - logger.Printf("Failed to derive target id, %v", targetIdError) + if findingsErr != nil { + return nil, findingsErr } - localFindings.Links["targetid"] = targetId - - findingsData, findingsError := createCodeWorkflowData( - workflow.NewTypeIdentifier(id, "findings"), - config, - localFindings, - content_type.LOCAL_FINDING_MODEL, - path, - logger) - if findingsError != nil { - return nil, findingsError + if findingsData != nil { + output = append(output, findingsData) } - output = append(output, findingsData) } return output, err } +func buildUFMFindings(id workflow.Identifier, config configuration.Configuration, logger *zerolog.Logger, sarifDoc *sarif.SarifDocument, summary *json_schemas.TestSummary, resultMetaData *scan.ResultMetaData, path string) (workflow.Data, error) { + severityThreshold := config.GetString(configuration.FLAG_SEVERITY_THRESHOLD) + testResult, err := ufm.TransformToUFMFromSarif(sarifDoc, summary, ufm.WithSeverityThreshold(severityThreshold)) + if err != nil { + return nil, err + } + + ufm.TranslateMetadataToTestResult(resultMetaData, testResult, config) + + targetId, targetIdError := instrumentation.GetTargetId(config.GetString(configuration.INPUT_DIRECTORY), instrumentation.AutoDetectedTargetId, instrumentation.WithConfiguredRepository(config)) + if targetIdError != nil { + logger.Printf("Failed to derive target id, %v", targetIdError) + } + testResult.SetMetadata("targetid", targetId) + + findingsData := ufm.CreateWorkflowDataFromTestResults( + workflow.NewTypeIdentifier(id, "findings"), + []testapi.TestResult{testResult}, + ) + if findingsData != nil { + findingsData.SetContentLocation(path) + } + return findingsData, nil +} + +func buildLocalFindings(id workflow.Identifier, config configuration.Configuration, logger *zerolog.Logger, sarifDoc *sarif.SarifDocument, summary *json_schemas.TestSummary, resultMetaData *scan.ResultMetaData, path string) (workflow.Data, error) { + localFindings, err := local_models.TransformToLocalFindingModelFromSarif(sarifDoc, summary) + if err != nil { + return nil, err + } + + logger.Debug().Msg("Coverage report for file(s):") + for _, coverage := range localFindings.Summary.Coverage { + logger.Debug().Msgf(" type: %s, format: %s, files: %d", coverage.Type, coverage.Lang, coverage.Files) + } + + local_models.TranslateMetadataToLocalFindingModel(resultMetaData, &localFindings, config) + + targetId, targetIdError := instrumentation.GetTargetId(config.GetString(configuration.INPUT_DIRECTORY), instrumentation.AutoDetectedTargetId, instrumentation.WithConfiguredRepository(config)) + if targetIdError != nil { + logger.Printf("Failed to derive target id, %v", targetIdError) + } + localFindings.Links["targetid"] = targetId + + findingsData, err := createCodeWorkflowData( + workflow.NewTypeIdentifier(id, "findings"), + config, + localFindings, + content_type.LOCAL_FINDING_MODEL, + path, + logger) + if err != nil { + return nil, err + } + findingsData.SetContentLocation(path) + return findingsData, nil +} + // default function that uses the code-client-go library func defaultAnalyzeFunction(ctx context.Context, path string, httpClientFunc func() *http.Client, logger *zerolog.Logger, config configuration.Configuration, userInterface ui.UserInterface, analyticsClient analytics.Analytics) (*sarif.SarifResponse, string, *scan.ResultMetaData, error) { var result *sarif.SarifResponse diff --git a/pkg/code/code_test.go b/pkg/code/code_test.go index b321580..1734fd9 100644 --- a/pkg/code/code_test.go +++ b/pkg/code/code_test.go @@ -261,7 +261,7 @@ func Test_Code_nativeImplementation_happyPath(t *testing.T) { actualBundleHash, metaErr := v.GetMetaData(code_workflow.MetadataBundleHash) assert.NoError(t, metaErr) assert.Equal(t, expectedBundleHash, actualBundleHash) - } else if v.GetContentType() == content_type.LOCAL_FINDING_MODEL { + } else if v.GetContentType() == content_type.LOCAL_FINDING_MODEL || v.GetContentType() == content_type.UFM_RESULT { _, ok := v.GetPayload().([]byte) assert.True(t, ok) } else {