Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

//replace github.com/snyk/go-application-framework => ../go-application-framework
replace github.com/snyk/go-application-framework => ../go-application-framework

//replace github.com/snyk/code-client-go => ../code-client-go

Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,6 @@ github.com/snyk/code-client-go v1.26.1 h1:09g3z462ZxSmXxuH2s13DbKjUPH5ve20af2+RV
github.com/snyk/code-client-go v1.26.1/go.mod h1:0NcZZHB48Sr4UAucEH2H10HwV7gjI2Ue0c+FxPWaTNo=
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.0.0-20260211155351-c4fb58433d93 h1:LfhYf3LpiekqfuLVT2/qnuDiKpWmYevs8ZK1smOSyAA=
github.com/snyk/go-application-framework v0.0.0-20260211155351-c4fb58433d93/go.mod h1:6MuCxSVGYNY3gfNKPZc5oMuy5/Q+yxbLxKnVtOMSB8Y=
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/snyk/studio-mcp v1.2.3 h1:4zvKlfl1wL91QHvF1pjn4vXWRiqixL9X0Wc/Bo+Ukr8=
Expand Down
13 changes: 4 additions & 9 deletions infrastructure/oss/cli_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (cliScanner *CLIScanner) scanInternal(ctx context.Context, commandFunc func
}

// convert scan results into issues
issues := cliScanner.unmarshallAndRetrieveAnalysis(ctx, output, workDir, path, cliScanner.config.Format())
issues := cliScanner.unmarshallAndRetrieveAnalysis(ctx, output)

// mark scan done
cliScanner.mutex.Lock()
Expand Down Expand Up @@ -436,16 +436,11 @@ func (cliScanner *CLIScanner) isSupported(path types.FilePath) bool {
return uri.IsDirectory(path) || cliScanner.supportedFiles[filepath.Base(string(path))]
}

func (cliScanner *CLIScanner) unmarshallAndRetrieveAnalysis(
ctx context.Context,
scanOutput any,
workDir types.FilePath,
path types.FilePath,
format string,
) (issues []types.Issue) {
issues, err := ProcessScanResults(ctx, scanOutput, cliScanner.errorReporter, cliScanner.learnService, cliScanner.packageIssueCache, true, format)
func (cliScanner *CLIScanner) unmarshallAndRetrieveAnalysis(ctx context.Context, scanOutput any) (issues []types.Issue) {
issues, err := ProcessScanResults(ctx, scanOutput, cliScanner.packageIssueCache, true)

if err != nil {
path := ctx2.FilePathFromContext(ctx)
cliScanner.errorReporter.CaptureErrorAndReportAsIssue(path, err)
return []types.Issue{}
}
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/oss/cli_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ func TestConvertScanResultToIssues_IgnoredIssuesNotPropagated(t *testing.T) {
packageIssueCache := make(map[string][]types.Issue)

// Convert scan results to issues
issues := convertScanResultToIssues(c, scanResult, workDir, targetFilePath, fileContent, learnService, errorReporter, packageIssueCache, c.Format())
issues := convertScanResultToIssues(c, scanResult, workDir, targetFilePath, fileContent, learnService, errorReporter, packageIssueCache)

// Verify that only non-ignored issues are included in the result
assert.Equal(t, 1, len(issues), "Expected only one non-ignored issue")
Expand Down
39 changes: 25 additions & 14 deletions infrastructure/oss/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,50 @@ import (
"github.com/snyk/snyk-ls/internal/uri"
)

// ConvertJSONToIssues converts OSS JSON output to Issue objects with optional learn service
// This is a standalone version of CLIScanner.unmarshallAndRetrieveAnalysis
// ConvertJSONToIssues converts OSS JSON output to Issue objects with optional learn service.
// This is a standalone version of CLIScanner.unmarshallAndRetrieveAnalysis; it builds a context with the given deps.
func ConvertJSONToIssues(logger *zerolog.Logger, jsonData []byte, learnService learn.Service, workDir string) ([]types.Issue, error) {
issues, err := ProcessScanResults(context.Background(), jsonData, error_reporting.NewTestErrorReporter(), learnService, make(map[string][]types.Issue), false, config.FormatMd)

return issues, err
ctx := ctx2.NewContextWithWorkDirAndFilePath(context.Background(), types.FilePath(workDir), "")
ctx = ctx2.NewContextWithDependencies(ctx, map[string]any{
ctx2.DepLearnService: learnService,
ctx2.DepErrorReporter: error_reporting.NewTestErrorReporter(),
ctx2.DepConfig: config.CurrentConfig(),
})
return ProcessScanResults(ctx, jsonData, make(map[string][]types.Issue), false)
}

// ProcessScanResults takes the results from the scanner and transforms them into
// our internal issue format. It also populates the given package cache with the
// found problems per package.
// Config, workDir, filePath, learnService and errorReporter are taken from context (see internal/context/context.go).
// - scanOutput: the output of the scan (can be either a []byte or []workflow.Data)
func ProcessScanResults(ctx context.Context, scanOutput any, errorReporter error_reporting.ErrorReporter, learnService learn.Service, packageIssueCache map[string][]types.Issue, readFiles bool, format string) ([]types.Issue, error) {
func ProcessScanResults(ctx context.Context, scanOutput any, packageIssueCache map[string][]types.Issue, readFiles bool) ([]types.Issue, error) {
if ctx.Err() != nil {
return nil, nil
}
logger := ctx2.LoggerFromContext(ctx).With().Str("method", "ProcessScanResults").Logger()
deps, found := ctx2.DependenciesFromContext(ctx)
c := config.CurrentConfig()
if found {
ctxConfig, ok := deps[ctx2.DepConfig].(*config.Config)
if !ok {
return nil, errors.New("failed to get config from context")
if ctxConfig, ok := deps[ctx2.DepConfig].(*config.Config); ok {
c = ctxConfig
}
c = ctxConfig
}
workDir := ctx2.WorkDirFromContext(ctx)
filePath := ctx2.FilePathFromContext(ctx)
var learnService learn.Service
var errorReporter error_reporting.ErrorReporter
if found {
learnService, _ = deps[ctx2.DepLearnService].(learn.Service)
errorReporter, _ = deps[ctx2.DepErrorReporter].(error_reporting.ErrorReporter)
}

// new ostest workflow result processing
if output, ok := scanOutput.([]workflow.Data); ok {
return processOsTestWorkFlowData(ctx, output, packageIssueCache)
return processOsTestWorkFlowData(ctx, output, packageIssueCache, readFiles)
}

// unchanged legacy workflow
// legacy workflow ([]byte)
var allIssues []types.Issue
scanOutputBytes, ok := scanOutput.([]byte)
if !ok || len(scanOutputBytes) == 0 {
Expand All @@ -79,7 +88,9 @@ func ProcessScanResults(ctx context.Context, scanOutput any, errorReporter error

scanResults, err := UnmarshallOssJson(scanOutputBytes)
if err != nil {
errorReporter.CaptureErrorAndReportAsIssue(filePath, err)
if errorReporter != nil {
errorReporter.CaptureErrorAndReportAsIssue(filePath, err)
}
return nil, nil
}

Expand All @@ -88,7 +99,7 @@ func ProcessScanResults(ctx context.Context, scanOutput any, errorReporter error

fileContent := getFileContent(targetFilePath, readFiles, logger)

issues := convertScanResultToIssues(c, &scanResult, workDir, targetFilePath, fileContent, learnService, errorReporter, packageIssueCache, format)
issues := convertScanResultToIssues(c, &scanResult, workDir, targetFilePath, fileContent, learnService, errorReporter, packageIssueCache)
allIssues = append(allIssues, issues...)
}

Expand Down
5 changes: 3 additions & 2 deletions infrastructure/oss/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func getRangeFromNode(issueDepNode *ast.Node) types.Range {
// to keep it close to the code that needs it.
var packageIssueCacheMutex sync.Mutex

func convertScanResultToIssues(c *config.Config, res *scanResult, workDir types.FilePath, targetFilePath types.FilePath, fileContent []byte, learnService learn.Service, ep error_reporting.ErrorReporter, packageIssueCache map[string][]types.Issue, format string) []types.Issue {
func convertScanResultToIssues(c *config.Config, res *scanResult, workDir types.FilePath, targetFilePath types.FilePath, fileContent []byte, learnService learn.Service, ep error_reporting.ErrorReporter, packageIssueCache map[string][]types.Issue) []types.Issue {
logger := c.Logger().With().Str("method", "convertScanResultToIssues").Logger()
var issues []types.Issue

Expand All @@ -180,7 +180,8 @@ func convertScanResultToIssues(c *config.Config, res *scanResult, workDir types.
continue
}
node := getDependencyNode(&logger, targetFilePath, ossLegacyIssue.PackageManager, ossLegacyIssue.From, fileContent)
snykIssue := toIssue(c, workDir, targetFilePath, ossLegacyIssue, res, node, learnService, ep, format)
// TODO: remove format param from toIssue; config.FormatMd is the default format, so this is redundant.
snykIssue := toIssue(c, workDir, targetFilePath, ossLegacyIssue, res, node, learnService, ep, config.FormatMd)
packageIssueCacheMutex.Lock()
packageIssueCache[packageKey] = append(packageIssueCache[packageKey], snykIssue)
packageIssueCacheMutex.Unlock()
Expand Down
5 changes: 3 additions & 2 deletions infrastructure/oss/oss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/snyk/go-application-framework/pkg/apiclients/testapi"
"github.com/snyk/go-application-framework/pkg/configuration"
"github.com/snyk/go-application-framework/pkg/local_workflows/content_type"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -515,8 +516,8 @@ func Test_processOsTestWorkFlowData_AggregatesIssues(t *testing.T) {
return []types.Issue{issue2}, nil
}

data := workflow.NewData(workflow.NewTypeIdentifier(workflow.NewWorkflowIdentifier("test"), "payload"), "application/json", []byte("{}"))
issues, err := processOsTestWorkFlowData(ctx, []workflow.Data{data}, map[string][]types.Issue{})
data := workflow.NewData(workflow.NewTypeIdentifier(workflow.NewWorkflowIdentifier("test"), "payload"), content_type.UFM_RESULT, []byte("{}"))
issues, err := processOsTestWorkFlowData(ctx, []workflow.Data{data}, map[string][]types.Issue{}, false)
require.NoError(t, err)
assert.ElementsMatch(t, []types.Issue{issue1, issue2}, issues)
}
Expand Down
58 changes: 51 additions & 7 deletions infrastructure/oss/ostest_scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ import (
"fmt"

"github.com/snyk/go-application-framework/pkg/configuration"
"github.com/snyk/go-application-framework/pkg/local_workflows/content_type"
"github.com/snyk/go-application-framework/pkg/utils/ufm"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/subosito/gotenv"

"github.com/snyk/cli-extension-os-flows/pkg/flags"

"github.com/snyk/snyk-ls/application/config"
"github.com/snyk/snyk-ls/infrastructure/learn"
ctx2 "github.com/snyk/snyk-ls/internal/context"
"github.com/snyk/snyk-ls/internal/observability/error_reporting"
"github.com/snyk/snyk-ls/internal/types"
)

Expand All @@ -35,6 +40,12 @@ var (
convertTestResultToIssuesFn = convertTestResultToIssues
)

// isLegacyCliStdoutData returns true when data is legacy CLI stdout (type id legacycli/stdout from cliv2).
func isLegacyCliStdoutData(data workflow.Data) bool {
id := data.GetIdentifier()
return id != nil && id.Scheme == "did" && id.Host == "legacycli" && id.Path == "stdout"
}

func (cliScanner *CLIScanner) ostestScan(_ context.Context, path types.FilePath, cmd []string, workDir types.FilePath, env gotenv.Env) ([]workflow.Data, error) {
c := cliScanner.config
logger := c.Logger().With().
Expand Down Expand Up @@ -103,18 +114,51 @@ func processOsTestWorkFlowData(
ctx context.Context,
scanOutput []workflow.Data,
packageIssueCache map[string][]types.Issue,
readFiles bool,
) ([]types.Issue, error) {
var issues []types.Issue
var err error
logger := ctx2.LoggerFromContext(ctx).With().Str("method", "processOsTestWorkFlowData").Logger()
deps, _ := ctx2.DependenciesFromContext(ctx)
c := config.CurrentConfig()
if ctxConfig, ok := deps[ctx2.DepConfig].(*config.Config); ok {
c = ctxConfig
}
workDir := ctx2.WorkDirFromContext(ctx)
filePath := ctx2.FilePathFromContext(ctx)
learnService, _ := deps[ctx2.DepLearnService].(learn.Service)
errorReporter, _ := deps[ctx2.DepErrorReporter].(error_reporting.ErrorReporter)

for _, data := range scanOutput {
testResults := getTestResultsFromWorkflowData(data)
for _, testResult := range testResults {
var testIssues []types.Issue
testIssues, err = convertTestResultToIssuesFn(ctx, testResult, packageIssueCache)
if data.GetContentType() == content_type.UFM_RESULT {
testResults := getTestResultsFromWorkflowData(data)
for _, testResult := range testResults {
testIssues, err := convertTestResultToIssuesFn(ctx, testResult, packageIssueCache)
if err != nil {
return nil, fmt.Errorf("couldn't convert test result to issues: %w", err)
}
issues = append(issues, testIssues...)
}
continue
}

// Legacy flow: accept content type LEGACY_CLI_STDOUT or type id legacycli/stdout.
if data.GetContentType() == content_type.LEGACY_CLI_STDOUT || isLegacyCliStdoutData(data) {
payload, ok := data.GetPayload().([]byte)
if !ok {
continue
}
if len(payload) == 0 {
continue
}
legacyResults, err := UnmarshallOssJson(payload)
if err != nil {
return nil, fmt.Errorf("couldn't convert test result to issues: %w", err)
return nil, fmt.Errorf("couldn't unmarshal legacy json: %w", err)
}
for _, legacyResult := range legacyResults {
targetFilePath := getAbsTargetFilePath(&logger, legacyResult.Path, legacyResult.DisplayTargetFile, workDir, filePath)
fileContent := getFileContent(targetFilePath, readFiles, logger)
issues = append(issues, convertScanResultToIssues(c, &legacyResult, workDir, targetFilePath, fileContent, learnService, errorReporter, packageIssueCache)...)
}
issues = append(issues, testIssues...)
}
}
return issues, nil
Expand Down
Loading