OpenSCA-cli/analyzer/javascript/package_json.go

166 lines
3.8 KiB
Go

/*
* @Description: 解析package.json文件
* @Date: 2022-01-07 17:00:41
*/
package javascript
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sort"
"util/bar"
"util/cache"
"util/enum/language"
"util/logs"
"util/model"
"github.com/Masterminds/semver/v3"
)
// package.json 文件结构
type PkgJson struct {
Name string `json:"name"`
Version string `json:"version"`
License string `json:"license"`
DevDeps map[string]string `json:"devDependencies"`
Deps map[string]string `json:"dependencies"`
HomePage string `json:"homepage"`
Repository map[string]string `json:"repository,omitempty"`
}
// npm下载文件结构
type NpmJson struct {
Time map[string]string `json:"time"`
Versions map[string]PkgJson `json:"versions"`
}
// parsePackage 解析package.json
func parsePackage(root *model.DepTree, file *model.FileInfo, simulation bool) (direct []string) {
direct = []string{}
pkg := PkgJson{}
if err := json.Unmarshal(file.Data, &pkg); err != nil {
logs.Error(err)
}
if pkg.Name != "" {
root.Name = pkg.Name
}
if pkg.Version != "" {
root.Version = model.NewVersion(pkg.Version)
}
root.AddLicense(pkg.License)
root.HomePage = pkg.HomePage
// 依赖列表map[name]version
depMap := map[string]string{}
for name, version := range pkg.DevDeps {
depMap[name] = version
}
for name, version := range pkg.Deps {
depMap[name] = version
}
// 组件名排序后添加到deps
for name := range depMap {
direct = append(direct, name)
}
sort.Strings(direct)
if !simulation {
return
}
for _, name := range direct {
version := depMap[name]
dep := model.NewDepTree(root)
dep.Name = name
dep.Version = model.NewVersion(version)
}
// 记录出现过的组件
exist := map[string]struct{}{}
// 搜索子依赖
q := model.NewQueue()
exist[root.Name] = struct{}{}
for _, child := range root.Children {
exist[child.Name] = struct{}{}
q.Push(child)
}
for !q.Empty() {
node := q.Pop().(*model.DepTree)
for _, sub := range npmSimulation(node, exist) {
if _, ok := exist[sub.Name]; !ok {
bar.Npm.Add(1)
exist[sub.Name] = struct{}{}
q.Push(sub)
}
}
}
return
}
// npmSimulation 模拟npm获取详细依赖信息
func npmSimulation(dep *model.DepTree, exist map[string]struct{}) (subDeps []*model.DepTree) {
subDeps = []*model.DepTree{}
dep.Language = language.JavaScript
// 获取依赖数据
data := cache.LoadCache(dep.Dependency)
if len(data) == 0 {
url := fmt.Sprintf(`https://r.cnpmjs.org/%s`, dep.Name)
if rep, err := http.Get(url); err != nil {
logs.Error(err)
return
} else {
if data, err = ioutil.ReadAll(rep.Body); err != nil {
logs.Error(err)
} else {
cache.SaveCache(dep.Dependency, data)
}
rep.Body.Close()
}
}
// 解析依赖信息
npm := NpmJson{}
json.Unmarshal(data, &npm)
// 查找符合范围内的最大版本
latestVersion := ""
// 当前最大版本
var max *semver.Version
if dep.Version.Org == "" || dep.Version.Org == "latest" {
dep.Version.Org = "x"
}
if constraint, err := semver.NewConstraint(dep.Version.Org); err == nil {
for ver := range npm.Time {
if v, err := semver.NewVersion(ver); err == nil {
if constraint.Check(v) {
if max == nil || max.LessThan(v) {
max = v
latestVersion = ver
}
}
}
}
} else {
logs.Warn(err)
}
if latestVersion == "" {
return
}
info := npm.Versions[latestVersion]
dep.Version = model.NewVersion(latestVersion)
dep.AddLicense(info.License)
// 解析子依赖
names := []string{}
for name := range info.Deps {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
if _, ok := exist[name]; ok {
continue
}
sub := model.NewDepTree(dep)
sub.Name = name
sub.Version = model.NewVersion(info.Deps[name])
subDeps = append(subDeps, sub)
}
return
}