From ab80bc19b16f16667fb592d5424434b24a46bfac Mon Sep 17 00:00:00 2001 From: Emily McMullan Date: Wed, 24 Apr 2024 15:30:17 -0400 Subject: [PATCH] add nodejs and python providers Signed-off-by: Emily McMullan --- cmd/analyze.go | 121 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/cmd/analyze.go b/cmd/analyze.go index 60e5bfa5..996cd1f2 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -49,8 +49,10 @@ var ( ) const ( - javaProvider = "java" - goProvider = "go" + javaProvider = "java" + goProvider = "go" + pythonProvider = "python" + nodeJSProvider = "javascript" ) // kantra analyze flags @@ -76,6 +78,7 @@ type analyzeCommand struct { noProxy string contextLines int incidentSelector string + depFolders []string // tempDirs list of temporary dirs created, used for cleanup tempDirs []string @@ -213,6 +216,7 @@ func NewAnalyzeCmd(log logr.Logger) *cobra.Command { analyzeCommand.Flags().StringVar(&analyzeCmd.noProxy, "no-proxy", loadEnvInsensitive("no_proxy"), "proxy excluded URLs (relevant only with proxy)") analyzeCommand.Flags().IntVar(&analyzeCmd.contextLines, "context-lines", 100, "number of lines of source code to include in the output for each incident") analyzeCommand.Flags().StringVar(&analyzeCmd.incidentSelector, "incident-selector", "", "an expression to select incidents based on custom variables. ex: (!package=io.konveyor.demo.config-utils)") + analyzeCommand.Flags().StringArrayVar(&analyzeCmd.depFolders, "dependency-folders", []string{}, "directory for dependencies") return analyzeCommand } @@ -267,6 +271,17 @@ func (a *analyzeCommand) Validate() error { SourceMountPath = path.Join(SourceMountPath, filepath.Base(a.input)) a.isFileInput = true } + if len(a.depFolders) != 0 { + for i := range a.depFolders { + stat, err := os.Stat(a.depFolders[i]) + if err != nil { + return fmt.Errorf("%w failed to stat dependency folder %v", err, a.depFolders[i]) + } + if stat != nil && !stat.IsDir() { + return fmt.Errorf("depdendecy folder %v is not a directory", a.depFolders[i]) + } + } + } if a.mode != string(provider.FullAnalysisMode) && a.mode != string(provider.SourceOnlyAnalysisMode) { return fmt.Errorf("mode must be one of 'full' or 'source-only'") @@ -446,6 +461,21 @@ func listOptionsFromLabels(sl []string, label string) { } } +func (a *analyzeCommand) getDepsFolders() (map[string]string, []string) { + vols := map[string]string{} + dependencyFolders := []string{} + if len(a.depFolders) != 0 { + for i := range a.depFolders { + newDepPath := path.Join(InputPath, fmt.Sprintf("deps%v", i)) + vols[a.depFolders[i]] = newDepPath + dependencyFolders = append(dependencyFolders, newDepPath) + } + return vols, dependencyFolders + } + + return nil, nil +} + func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]int) (map[string]string, error) { tempDir, err := os.MkdirTemp("", "analyze-config-") if err != nil { @@ -457,6 +487,8 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i var foundJava bool var foundGolang bool + var foundPython bool + var foundNode bool for _, p := range providers { if p == javaProvider { foundJava = true @@ -464,6 +496,12 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i if p == goProvider { foundGolang = true } + if p == pythonProvider { + foundPython = true + } + if p == nodeJSProvider { + foundNode = true + } } // TODO (pgaikwad): binaries don't work with alizer right now, we need to revisit this if !foundJava && a.isFileInput { @@ -522,6 +560,46 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i }, } + pythonConfig := provider.Config{ + Name: pythonProvider, + Address: fmt.Sprintf("0.0.0.0:%v", ports[pythonProvider]), + InitConfig: []provider.InitConfig{ + { + AnalysisMode: provider.SourceOnlyAnalysisMode, + ProviderSpecificConfig: map[string]interface{}{ + "lspServerName": "generic", + "workspaceFolders": []string{fmt.Sprintf("file://%s", otherProvsMountPath)}, + provider.LspServerPathConfigKey: "/usr/local/bin/pylsp", + }, + }, + }, + } + + nodeJSConfig := provider.Config{ + Name: nodeJSProvider, + Address: fmt.Sprintf("0.0.0.0:%v", ports[nodeJSProvider]), + InitConfig: []provider.InitConfig{ + { + AnalysisMode: provider.SourceOnlyAnalysisMode, + ProviderSpecificConfig: map[string]interface{}{ + "lspServerName": "nodejs", + "workspaceFolders": []string{fmt.Sprintf("file://%s", otherProvsMountPath)}, + provider.LspServerPathConfigKey: "/usr/local/bin/typescript-language-server", + }, + }, + }, + } + + vols, dependencyFolders := a.getDepsFolders() + if len(dependencyFolders) != 0 { + if providers[0] == pythonProvider { + pythonConfig.InitConfig[0].ProviderSpecificConfig["dependencyFolders"] = dependencyFolders + } + if providers[0] == nodeJSProvider { + nodeJSConfig.InitConfig[0].ProviderSpecificConfig["dependencyFolders"] = dependencyFolders + } + } + provConfig := []provider.Config{ { Name: "builtin", @@ -539,6 +617,12 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i if foundGolang && a.mode == string(provider.FullAnalysisMode) { provConfig = append(provConfig, goConfig) } + if foundPython { + provConfig = append(provConfig, pythonConfig) + } + if foundNode { + provConfig = append(provConfig, nodeJSConfig) + } // Set proxy to providers if a.httpProxy != "" || a.httpsProxy != "" { @@ -564,9 +648,12 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i return nil, err } - vols := map[string]string{ + settingsVols := map[string]string{ tempDir: ConfigMountPath, } + if len(vols) != 0 { + maps.Copy(settingsVols, vols) + } // attempt to create a .m2 directory we can use to speed things a bit // this will be shared between analyze and dep command containers @@ -582,7 +669,7 @@ func (a *analyzeCommand) getConfigVolumes(providers []string, ports map[string]i } } - return vols, nil + return settingsVols, nil } func (a *analyzeCommand) getRulesVolumes() (map[string]string, error) { @@ -794,7 +881,11 @@ func (a *analyzeCommand) RunProviders(ctx context.Context, networkName string, v // application source code volName: SourceMountPath, } - // this will make more sense when we have more than 2 supported providers + vols, _ := a.getDepsFolders() + if len(vols) != 0 { + maps.Copy(volumes, vols) + } + var providerImage string switch providers[0] { case javaProvider: @@ -803,6 +894,12 @@ func (a *analyzeCommand) RunProviders(ctx context.Context, networkName string, v case goProvider: providerImage = Settings.GenericProviderImage providerPorts[goProvider] = port + case pythonProvider: + providerImage = Settings.GenericProviderImage + providerPorts[pythonProvider] = port + case nodeJSProvider: + providerImage = Settings.GenericProviderImage + providerPorts[nodeJSProvider] = port default: return nil, fmt.Errorf("unable to run unsupported provider %v", providers[0]) } @@ -876,6 +973,7 @@ func (a *analyzeCommand) RunAnalysis(ctx context.Context, xmlOutputDir string, v if providers[0] != javaProvider { a.enableDefaultRulesets = false } + if a.enableDefaultRulesets { args = append(args, fmt.Sprintf("--rules=%s/", RulesetPath)) @@ -896,7 +994,9 @@ func (a *analyzeCommand) RunAnalysis(ctx context.Context, xmlOutputDir string, v args = append(args, "--jaeger-endpoint") args = append(args, a.jaegerEndpoint) } - if !a.analyzeKnownLibraries { + + // python and node providers do not yet support dep analysis + if !a.analyzeKnownLibraries && (providers[0] != pythonProvider && providers[0] != nodeJSProvider) { args = append(args, fmt.Sprintf("--dep-label-selector=(!%s=open-source)", provider.DepSourceLabel)) } @@ -907,9 +1007,14 @@ func (a *analyzeCommand) RunAnalysis(ctx context.Context, xmlOutputDir string, v if labelSelector != "" { args = append(args, fmt.Sprintf("--label-selector=%s", labelSelector)) } + if a.mode == string(provider.FullAnalysisMode) { - a.log.Info("running dependency retrieval during analysis") - args = append(args, fmt.Sprintf("--dep-output-file=%s", DepsOutputMountPath)) + if providers[0] == pythonProvider || providers[0] == nodeJSProvider { + a.mode = string(provider.SourceOnlyAnalysisMode) + } else { + a.log.Info("running dependency retrieval during analysis") + args = append(args, fmt.Sprintf("--dep-output-file=%s", DepsOutputMountPath)) + } } analysisLogFilePath := filepath.Join(a.output, "analysis.log")