diff --git a/xray/audit/commonutils.go b/xray/audit/commonutils.go index fbc92fd76..f568d111c 100644 --- a/xray/audit/commonutils.go +++ b/xray/audit/commonutils.go @@ -123,10 +123,14 @@ func GetModule(modules []*services.GraphNode, moduleId string) *services.GraphNo return nil } -func LogExecutableVersion(executable string) { - // Get executable version and print to log if possible - verString, _ := exec.Command(executable, "--version").CombinedOutput() - if len(verString) > 0 { - log.Debug(fmt.Sprintf("Used %q version: %s", executable, strings.TrimSpace(string(verString)))) +// Gets executable version and prints to the debug log if possible. +// Only supported for package managers that use "--version". +func GetExecutableVersion(executable string) (version string, err error) { + verBytes, err := exec.Command(executable, "--version").CombinedOutput() + if err != nil || len(verBytes) == 0 { + return "", err } + version = strings.TrimSpace(string(verBytes)) + log.Debug(fmt.Sprintf("Used %q version: %s", executable, version)) + return } diff --git a/xray/audit/python/python.go b/xray/audit/python/python.go index 05021176c..604ff7e5b 100644 --- a/xray/audit/python/python.go +++ b/xray/audit/python/python.go @@ -2,19 +2,18 @@ package python import ( "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "github.com/jfrog/build-info-go/utils/pythonutils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/xray/audit" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" - clientLog "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray/services" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" ) const ( @@ -93,8 +92,12 @@ func getDependencies(pythonTool pythonutils.PythonTool, requirementsFile string) } dependenciesGraph, directDependencies, err = pythonutils.GetPythonDependencies(pythonTool, tempDirPath, localDependenciesPath) if err != nil { - audit.LogExecutableVersion("python") - audit.LogExecutableVersion(string(pythonTool)) + if _, innerErr := audit.GetExecutableVersion("python"); innerErr != nil { + log.Error(innerErr) + } + if _, innerErr := audit.GetExecutableVersion(string(pythonTool)); innerErr != nil { + log.Error(innerErr) + } } return } @@ -111,11 +114,11 @@ func runPythonInstall(pythonTool pythonutils.PythonTool, requirementsFile string } err = runPipInstall(requirementsFile) if err != nil && requirementsFile == "" { - clientLog.Debug(err.Error() + "\ntrying to install using a requirements file.") + log.Debug(err.Error() + "\ntrying to install using a requirements file.") reqErr := runPipInstall("requirements.txt") if reqErr != nil { // Return Pip install error and log the requirements fallback error. - clientLog.Debug(reqErr.Error()) + log.Debug(reqErr.Error()) } else { err = nil } @@ -141,10 +144,12 @@ func runPythonInstall(pythonTool pythonutils.PythonTool, requirementsFile string func executeCommand(executable string, args ...string) error { installCmd := exec.Command(executable, args...) - clientLog.Debug(fmt.Sprintf("Running %q", strings.Join(installCmd.Args, " "))) + log.Debug(fmt.Sprintf("Running %q", strings.Join(installCmd.Args, " "))) output, err := installCmd.CombinedOutput() if err != nil { - audit.LogExecutableVersion(executable) + if _, innerErr := audit.GetExecutableVersion(executable); innerErr != nil { + log.Error(innerErr) + } return errorutils.CheckErrorf("%q command failed: %s - %s", strings.Join(installCmd.Args, " "), err.Error(), output) } return nil diff --git a/xray/audit/yarn/yarn.go b/xray/audit/yarn/yarn.go index 797cd98c3..f8dece092 100644 --- a/xray/audit/yarn/yarn.go +++ b/xray/audit/yarn/yarn.go @@ -1,7 +1,8 @@ package yarn import ( - biutils "github.com/jfrog/build-info-go/build/utils" + biUtils "github.com/jfrog/build-info-go/build/utils" + "github.com/jfrog/gofrog/version" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/audit" "github.com/jfrog/jfrog-client-go/utils/errorutils" @@ -11,6 +12,8 @@ import ( const ( npmPackageTypeIdentifier = "npm://" + yarnV2Version = "2.0.0" + YarnV1ErrorPrefix = "jf audit is only supported for yarn v2 and above." ) func BuildDependencyTree() (dependencyTree []*services.GraphNode, err error) { @@ -18,21 +21,20 @@ func BuildDependencyTree() (dependencyTree []*services.GraphNode, err error) { if err != nil { return } - executablePath, err := biutils.GetYarnExecutable() + executablePath, err := biUtils.GetYarnExecutable() if errorutils.CheckError(err) != nil { return } - defer func() { - if err != nil && executablePath != "" { - audit.LogExecutableVersion(executablePath) - } - }() - packageInfo, err := biutils.ReadPackageInfoFromPackageJson(currentDir, nil) + if err = logAndValidateYarnVersion(executablePath); err != nil { + return + } + + packageInfo, err := biUtils.ReadPackageInfoFromPackageJson(currentDir, nil) if errorutils.CheckError(err) != nil { return } // Calculate Yarn dependencies - dependenciesMap, _, err := biutils.GetYarnDependencies(executablePath, currentDir, packageInfo, log.Logger) + dependenciesMap, _, err := biUtils.GetYarnDependencies(executablePath, currentDir, packageInfo, log.Logger) if err != nil { return } @@ -41,14 +43,27 @@ func BuildDependencyTree() (dependencyTree []*services.GraphNode, err error) { return } -// Parse the dependencies into an Xray dependency tree format -func parseYarnDependenciesMap(dependencies map[string]*biutils.YarnDependency, packageInfo *biutils.PackageInfo) (xrDependencyTree *services.GraphNode) { +// Yarn audit is only supported from yarn v2. +func logAndValidateYarnVersion(executablePath string) error { + versionStr, err := audit.GetExecutableVersion(executablePath) + if errorutils.CheckError(err) != nil { + return err + } + yarnVer := version.NewVersion(versionStr) + if yarnVer.Compare(yarnV2Version) > 0 { + return errorutils.CheckErrorf(YarnV1ErrorPrefix + "The current version is: " + versionStr) + } + return nil +} + +// Parse the dependencies into a Xray dependency tree format +func parseYarnDependenciesMap(dependencies map[string]*biUtils.YarnDependency, packageInfo *biUtils.PackageInfo) (xrDependencyTree *services.GraphNode) { treeMap := make(map[string][]string) for _, dependency := range dependencies { xrayDepId := getXrayDependencyId(dependency) var subDeps []string for _, subDepPtr := range dependency.Details.Dependencies { - subDeps = append(subDeps, getXrayDependencyId(dependencies[biutils.GetYarnDependencyKeyFromLocator(subDepPtr.Locator)])) + subDeps = append(subDeps, getXrayDependencyId(dependencies[biUtils.GetYarnDependencyKeyFromLocator(subDepPtr.Locator)])) } if len(subDeps) > 0 { treeMap[xrayDepId] = subDeps @@ -57,6 +72,6 @@ func parseYarnDependenciesMap(dependencies map[string]*biutils.YarnDependency, p return audit.BuildXrayDependencyTree(treeMap, npmPackageTypeIdentifier+packageInfo.BuildInfoModuleId()) } -func getXrayDependencyId(yarnDependency *biutils.YarnDependency) string { +func getXrayDependencyId(yarnDependency *biUtils.YarnDependency) string { return npmPackageTypeIdentifier + yarnDependency.Name() + ":" + yarnDependency.Details.Version }