github.com/jfrog/frogbot@v1.1.1-0.20231221090046-821a26f50338/packagehandlers/commonpackagehandler.go (about)

     1  package packagehandlers
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/jfrog/frogbot/utils"
     6  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
     7  	"github.com/jfrog/jfrog-client-go/utils/log"
     8  	"os/exec"
     9  	"strings"
    10  )
    11  
    12  // PackageHandler interface to hold operations on packages
    13  type PackageHandler interface {
    14  	UpdateDependency(details *utils.VulnerabilityDetails) error
    15  }
    16  
    17  func GetCompatiblePackageHandler(vulnDetails *utils.VulnerabilityDetails, details *utils.ScanDetails) (handler PackageHandler) {
    18  	switch vulnDetails.Technology {
    19  	case coreutils.Go:
    20  		handler = &GoPackageHandler{}
    21  	case coreutils.Poetry:
    22  		handler = &PythonPackageHandler{}
    23  	case coreutils.Pipenv:
    24  		handler = &PythonPackageHandler{}
    25  	case coreutils.Npm:
    26  		handler = &NpmPackageHandler{}
    27  	case coreutils.Yarn:
    28  		handler = &YarnPackageHandler{}
    29  	case coreutils.Pip:
    30  		handler = &PythonPackageHandler{pipRequirementsFile: details.PipRequirementsFile}
    31  	case coreutils.Maven:
    32  		handler = NewMavenPackageHandler(details)
    33  	case coreutils.Nuget:
    34  		handler = &NugetPackageHandler{}
    35  	case coreutils.Gradle:
    36  		handler = &GradlePackageHandler{}
    37  	default:
    38  		handler = &UnsupportedPackageHandler{}
    39  	}
    40  	return
    41  }
    42  
    43  type CommonPackageHandler struct{}
    44  
    45  // UpdateDependency updates the impacted package to the fixed version
    46  func (cph *CommonPackageHandler) UpdateDependency(vulnDetails *utils.VulnerabilityDetails, installationCommand string, extraArgs ...string) (err error) {
    47  	// Lower the package name to avoid duplicates
    48  	impactedPackage := strings.ToLower(vulnDetails.ImpactedDependencyName)
    49  	commandArgs := []string{installationCommand}
    50  	commandArgs = append(commandArgs, extraArgs...)
    51  	versionOperator := vulnDetails.Technology.GetPackageVersionOperator()
    52  	fixedPackageArgs := getFixedPackage(impactedPackage, versionOperator, vulnDetails.SuggestedFixedVersion)
    53  	commandArgs = append(commandArgs, fixedPackageArgs...)
    54  	return runPackageMangerCommand(vulnDetails.Technology.GetExecCommandName(), vulnDetails.Technology.String(), commandArgs)
    55  }
    56  
    57  func runPackageMangerCommand(commandName string, techName string, commandArgs []string) error {
    58  	fullCommand := commandName + " " + strings.Join(commandArgs, " ")
    59  	log.Debug(fmt.Sprintf("Running '%s'", fullCommand))
    60  	//#nosec G204 -- False positive - the subprocess only runs after the user's approval.
    61  	output, err := exec.Command(commandName, commandArgs...).CombinedOutput()
    62  	if err != nil {
    63  		return fmt.Errorf("failed to update %s dependency: '%s' command failed: %s\n%s", techName, fullCommand, err.Error(), output)
    64  	}
    65  	return nil
    66  }
    67  
    68  // Returns the updated package and version as it should be run in the update command:
    69  // If the package manager expects a single string (example: <packName>@<version>) it returns []string{<packName>@<version>}
    70  // If the command args suppose to be seperated by spaces (example: <packName> -v <version>) it returns []string{<packName>, "-v", <version>}
    71  func getFixedPackage(impactedPackage string, versionOperator string, suggestedFixedVersion string) (fixedPackageArgs []string) {
    72  	fixedPackageString := strings.TrimSpace(impactedPackage) + versionOperator + strings.TrimSpace(suggestedFixedVersion)
    73  	fixedPackageArgs = strings.Split(fixedPackageString, " ")
    74  	return
    75  }