-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathpythonpackagehandler.go
97 lines (86 loc) · 3.76 KB
/
pythonpackagehandler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package packagehandlers
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/jfrog/frogbot/v2/utils"
"github.com/jfrog/jfrog-cli-security/utils/techutils"
)
const (
// Package names are case-insensitive with this prefix
PythonPackageRegexPrefix = "(?i)"
// Match all possible operators and versions syntax
PythonPackageRegexSuffix = "\\s*(([\\=\\<\\>\\~]=)|([\\>\\<]))\\s*(\\.|\\d)*(\\d|(\\.\\*))(\\,\\s*(([\\=\\<\\>\\~]=)|([\\>\\<])).*\\s*(\\.|\\d)*(\\d|(\\.\\*)))?"
)
// PythonPackageHandler Handles all the python package mangers as they share behavior
type PythonPackageHandler struct {
pipRequirementsFile string
CommonPackageHandler
}
func (py *PythonPackageHandler) UpdateDependency(vulnDetails *utils.VulnerabilityDetails) error {
if vulnDetails.IsDirectDependency {
return py.updateDirectDependency(vulnDetails)
}
return &utils.ErrUnsupportedFix{
PackageName: vulnDetails.ImpactedDependencyName,
FixedVersion: vulnDetails.SuggestedFixedVersion,
ErrorType: utils.IndirectDependencyFixNotSupported,
}
}
func (py *PythonPackageHandler) updateDirectDependency(vulnDetails *utils.VulnerabilityDetails) (err error) {
switch vulnDetails.Technology {
case techutils.Poetry:
return py.handlePoetry(vulnDetails)
case techutils.Pip:
return py.handlePip(vulnDetails)
case techutils.Pipenv:
return py.CommonPackageHandler.UpdateDependency(vulnDetails, vulnDetails.Technology.GetPackageInstallationCommand())
default:
return errors.New("unknown python package manger: " + vulnDetails.Technology.GetPackageType())
}
}
func (py *PythonPackageHandler) handlePoetry(vulnDetails *utils.VulnerabilityDetails) (err error) {
// Install the desired fixed version
if err = py.CommonPackageHandler.UpdateDependency(vulnDetails, vulnDetails.Technology.GetPackageInstallationCommand()); err != nil {
return
}
// Update Poetry lock file as well
return runPackageMangerCommand(techutils.Poetry.GetExecCommandName(), techutils.Poetry.String(), []string{"update"})
}
func (py *PythonPackageHandler) handlePip(vulnDetails *utils.VulnerabilityDetails) (err error) {
var fixedFile string
// This function assumes that the version of the dependencies is statically pinned in the requirements file or inside the 'install_requires' array in the setup.py file
fixedPackage := vulnDetails.ImpactedDependencyName + "==" + vulnDetails.SuggestedFixedVersion
if py.pipRequirementsFile == "" {
py.pipRequirementsFile = "setup.py"
}
wd, err := os.Getwd()
if err != nil {
return
}
fullPath := filepath.Join(wd, py.pipRequirementsFile)
if !strings.HasPrefix(filepath.Clean(fullPath), wd) {
return errors.New("wrong requirements file input")
}
data, err := os.ReadFile(filepath.Clean(py.pipRequirementsFile))
if err != nil {
return errors.New("an error occurred while attempting to read the requirements file:\n" + err.Error())
}
currentFile := string(data)
// Check both original and lowered package name and replace to only one lowered result
// This regex will match the impactedPackage with it's pinned version e.py. PyJWT==1.7.1
re := regexp.MustCompile(PythonPackageRegexPrefix + "(" + vulnDetails.ImpactedDependencyName + "|" + strings.ToLower(vulnDetails.ImpactedDependencyName) + ")" + PythonPackageRegexSuffix)
if packageToReplace := re.FindString(currentFile); packageToReplace != "" {
fixedFile = strings.Replace(currentFile, packageToReplace, strings.ToLower(fixedPackage), 1)
}
if fixedFile == "" {
return fmt.Errorf("impacted package %s not found, fix failed", vulnDetails.ImpactedDependencyName)
}
if err = os.WriteFile(py.pipRequirementsFile, []byte(fixedFile), 0600); err != nil {
err = fmt.Errorf("an error occured while writing the fixed version of %s to the requirements file:\n%s", vulnDetails.SuggestedFixedVersion, err.Error())
}
return
}