OpenSCA-cli/analyzer/python/req.go

183 lines
4.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package python
import (
"fmt"
"os"
"path"
"regexp"
"sort"
"strings"
"util/bar"
"util/ex"
"util/logs"
"util/model"
"util/temp"
)
var reg1 *regexp.Regexp
var regGit *regexp.Regexp
var replacer *strings.Replacer
func init() {
reg1 = regexp.MustCompile(`^\w`)
regGit = regexp.MustCompile(`\/([\w-]+)\.git`)
replacer = strings.NewReplacer("# via","","\r",""," ","","#","")
}
func parseRequirementsin(root *model.DepTree, file *model.FileInfo) {
// 检查python环境
if _, err := ex.CheckPython(ex.Python); err != nil {
return
}
strArry := []string{}
temp.DoInTempDir(func(tempdir string) {
// 安装piptools
if _, err := ex.Do(ex.PipinstallPiptoos, tempdir); err != nil {
logs.Error(err)
return
}
// 删除虚拟环境
defer ex.Do(ex.RemoveVirtualCmd, tempdir)
// 获取输出数据
strArry = getOutData(file, tempdir)
})
// 解析输出数据构建依赖树
parseOutData(root, strArry)
}
// 解析各组件所打印的信息
func parseOutData(root *model.DepTree, strs []string) {
// 直接依赖
directMap := map[string]*model.DepTree{}
childMap := map[*model.DepTree]map[string]struct{}{}
for _, str := range strs {
lines := strings.Split(str, "\n")
for i, line := range lines {
if reg1.MatchString(line) {
lines = lines[i:]
break
}
}
// parentsMap一个组件名对应其所有父组件名
var parentsMap = make(map[string][]string)
cur := model.NewDepTree(nil)
nodes := []string{}
depMap := map[string]*model.DepTree{}
for _, line := range lines {
if strings.Contains(line, "==") {
// 在输出内容"=="符号左右对应名字与版本号
cur = model.NewDepTree(nil)
line = strings.TrimSuffix(line, "\r")
nv := strings.Split(line, `==`)
if len(nv) == 2 {
cur.Name = strings.TrimSpace(nv[0])
cur.Version = model.NewVersion(strings.TrimSpace(nv[1]))
depMap[cur.Name] = cur
m := make(map[string]struct{})
childMap[cur] = m
nodes = append(nodes, cur.Name)
}
} else if strings.Contains(line, "#") {
// "#"符号后有父组件名字信息
line = replacer.Replace(line)
if line == "" {
continue
}
parentsMap[cur.Name] = append(parentsMap[cur.Name], line)
}
}
depMap[cur.Name] = cur
nodes = append(nodes, cur.Name)
for _, name := range nodes {
if _,ok := depMap[name]; !ok {
continue
}
parNames := parentsMap[name]
for _, parName := range parNames {
if len(parNames) == 1 && strings.Contains(parName, "requirements") {
if dep, ok := depMap[name]; ok {
directMap[dep.Name] = dep
}
}
if _,ok := depMap[parName]; !ok {
continue
}
parent := depMap[parName]
dep := depMap[name]
if m,ok := childMap[dep]; ok {
if _,ok := m[dep.Name];ok {
continue
}
m[dep.Name] = struct{}{}
}
parent.Children = append(parent.Children, dep)
dep.Parent = parent
}
}
}
withRoot(root,directMap)
}
// 所有直接依赖连接至root
func withRoot(root *model.DepTree,directMap map[string]*model.DepTree) {
direct := []*model.DepTree{}
for _, n := range directMap {
direct = append(direct, n)
}
sort.Slice(direct, func(i, j int) bool {
return direct[i].Name < direct[j].Name
})
for _, d := range direct {
root.Children = append(root.Children, d)
d.Parent = root
}
}
// 获取打印数据
func getOutData(file *model.FileInfo, dir string) []string {
s := string(file.Data)
strList := []string{}
reqpath := path.Join(dir, `requirements.in`)
out, err := os.Create(reqpath)
if err != nil {
logs.Error(err)
return strList
}
out.Close()
for _, v := range strings.Split(s, "\n") {
// 少部分情况会有git连接
if regGit.MatchString(v) {
res := regGit.FindStringSubmatch(v)
if len(res) == 2 {
bar.PipCompile.Add(1)
strList = append(strList, getSingleModStr(reqpath, res[1]))
continue
}
}
// 一般情况下字母开头的行内容都是组件名
if reg1.MatchString(v) {
bar.PipCompile.Add(1)
strList = append(strList, getSingleModStr(reqpath, v))
}
}
return strList
}
// 将组件名与版本号写入requirements.in文件单独调用pip-compile获取打印数据
func getSingleModStr(reqpath string, elem string) string {
f, err := os.OpenFile(reqpath, os.O_CREATE, 0744)
if err != nil {
return ""
}
f.Seek(0, 0)
f.Truncate(0)
f.WriteString(elem)
f.Close()
if str, err := ex.Do(ex.PipCompilein, path.Dir(reqpath)); err != nil {
logs.Error(err)
logs.Error(fmt.Errorf("get info err:%s", elem))
return ""
} else {
return str
}
}