Skip to content

Commit 5df0afb

Browse files
author
luotianqi
committed
add cargo.lock support
1 parent f3ebcb8 commit 5df0afb

File tree

8 files changed

+174
-4
lines changed

8 files changed

+174
-4
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module opensca
33
go 1.16
44

55
require (
6+
github.com/BurntSushi/toml v1.0.0
67
github.com/Masterminds/semver/v3 v3.1.1
78
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
89
github.com/dsnet/compress v0.0.1 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
2+
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
13
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
24
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
35
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=

internal/analyzer/rust/analyzer.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package rust
2+
3+
import (
4+
"opensca/internal/enum/language"
5+
"opensca/internal/filter"
6+
"opensca/internal/srt"
7+
)
8+
9+
type Analyzer struct{}
10+
11+
func New() Analyzer {
12+
return Analyzer{}
13+
}
14+
15+
/**
16+
* @description: Get language of Analyzer
17+
* @return {language.Type} language type
18+
*/
19+
func (a Analyzer) GetLanguage() language.Type {
20+
return language.Rust
21+
}
22+
23+
/**
24+
* @description: Check if it is a parsable file
25+
* @param {string} filename file name
26+
* @return {bool} is a parseable file returns true
27+
*/
28+
func (a Analyzer) CheckFile(filename string) bool {
29+
return filter.RustCargoLock(filename)
30+
}
31+
32+
/**
33+
* @description: filters the files that the current parser needs to parse
34+
* @param {*srt.DirTree} dirRoot directory tree node
35+
* @param {*srt.DepTree} depRoot Dependency tree node
36+
* @return {[]*srt.FileData} List of files to parse
37+
*/
38+
func (a Analyzer) FilterFile(dirRoot *srt.DirTree, depRoot *srt.DepTree) []*srt.FileData {
39+
files := []*srt.FileData{}
40+
for _, f := range dirRoot.Files {
41+
if a.CheckFile(f.Name) {
42+
files = append(files, f)
43+
}
44+
}
45+
return files
46+
}
47+
48+
/**
49+
* @description: Parse the file
50+
* @param {*srt.DirTree} dirRoot directory tree node
51+
* @param {*srt.DepTree} depRoot Dependency tree node
52+
* @param {*srt.FileData} file data to parse
53+
* @return {[]*srt.DepTree} parsed dependency list
54+
*/
55+
func (a Analyzer) ParseFile(dirRoot *srt.DirTree, depRoot *srt.DepTree, file *srt.FileData) []*srt.DepTree {
56+
if filter.RustCargoLock(file.Name) {
57+
return parseCargoLock(dirRoot, depRoot, file)
58+
}
59+
return []*srt.DepTree{}
60+
}

internal/analyzer/rust/cargo.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package rust
2+
3+
import (
4+
"opensca/internal/logs"
5+
"opensca/internal/srt"
6+
"sort"
7+
"strings"
8+
9+
"github.com/BurntSushi/toml"
10+
)
11+
12+
type cargoPkg struct {
13+
Name string `toml:"name"`
14+
Version string `toml:"version"`
15+
DepStr []string `toml:"dependencies"`
16+
Dependencies []struct {
17+
Name string
18+
Version string
19+
} `toml:"-"`
20+
}
21+
22+
func parseCargoLock(dirRoot *srt.DirTree, depRoot *srt.DepTree, file *srt.FileData) []*srt.DepTree {
23+
cargo := struct {
24+
Pkgs []*cargoPkg `toml:"package"`
25+
}{}
26+
cdepMap := map[string]*cargoPkg{}
27+
depMap := map[string]*srt.DepTree{}
28+
directMap := map[string]*srt.DepTree{}
29+
if err := toml.Unmarshal(file.Data, &cargo); err != nil {
30+
logs.Warn(err)
31+
}
32+
for _, pkg := range cargo.Pkgs {
33+
dep := srt.NewDepTree(nil)
34+
dep.Name = pkg.Name
35+
dep.Version = srt.NewVersion(pkg.Version)
36+
pkg.Dependencies = make([]struct {
37+
Name string
38+
Version string
39+
}, len(pkg.DepStr))
40+
for i, str := range pkg.DepStr {
41+
name, version := str, ""
42+
index := strings.Index(str, " ")
43+
if index > -1 {
44+
name, version = str[:index], str[index+1:]
45+
}
46+
pkg.Dependencies[i] = struct {
47+
Name string
48+
Version string
49+
}{Name: name, Version: version}
50+
}
51+
depMap[dep.Name] = dep
52+
directMap[dep.Name] = dep
53+
cdepMap[dep.Name] = pkg
54+
}
55+
// 找出未被依赖的作为直接依赖
56+
for _, pkg := range cargo.Pkgs {
57+
for _, d := range pkg.Dependencies {
58+
delete(directMap, d.Name)
59+
}
60+
}
61+
directDeps := []*srt.DepTree{}
62+
for _, v := range directMap {
63+
directDeps = append(directDeps, v)
64+
}
65+
sort.Slice(directDeps, func(i, j int) bool {
66+
return directDeps[i].Name < directDeps[j].Name
67+
})
68+
for _, d := range directDeps {
69+
d.Parent = depRoot
70+
depRoot.Children = append(depRoot.Children, d)
71+
}
72+
// 从顶层开始构建
73+
q := make([]*srt.DepTree, len(directDeps))
74+
copy(q, directDeps)
75+
exist := map[string]struct{}{}
76+
for len(q) > 0 {
77+
n := q[0]
78+
exist[n.Name] = struct{}{}
79+
if cdep, ok := cdepMap[n.Name]; ok {
80+
for _, d := range cdep.Dependencies {
81+
if _, ok := exist[d.Name]; !ok {
82+
exist[d.Name] = struct{}{}
83+
if sub, ok := depMap[d.Name]; ok {
84+
sub.Parent = n
85+
n.Children = append(n.Children, sub)
86+
}
87+
}
88+
}
89+
}
90+
q = append(q[1:], n.Children...)
91+
}
92+
return directDeps
93+
}

internal/engine/engine.go

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"opensca/internal/analyzer/javascript"
1515
"opensca/internal/analyzer/php"
1616
"opensca/internal/analyzer/ruby"
17+
"opensca/internal/analyzer/rust"
1718
"opensca/internal/args"
1819
"opensca/internal/enum/language"
1920
"opensca/internal/filter"
@@ -44,6 +45,7 @@ func NewEngine() Engine {
4445
php.New(),
4546
ruby.New(),
4647
golang.New(),
48+
rust.New(),
4749
},
4850
}
4951
}

internal/enum/language/language.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const (
1919
Php
2020
Ruby
2121
Golang
22+
Rust
2223
)
2324

2425
/**
@@ -39,6 +40,8 @@ func (l Type) String() string {
3940
return "Ruby"
4041
case Golang:
4142
return "Golang"
43+
case Rust:
44+
return "Rust"
4245
default:
4346
return "None"
4447
}
@@ -62,6 +65,8 @@ func (l Type) Vuln() string {
6265
return "ruby"
6366
case Golang:
6467
return "golang"
68+
case Rust:
69+
return "rust"
6570
default:
6671
return ""
6772
}
@@ -77,7 +82,8 @@ func init() {
7782
lm[JavaScript] = []string{"js", "node", "nodejs", "javascript", "npm", "vue", "react"}
7883
lm[Php] = []string{"php", "composer"}
7984
lm[Ruby] = []string{"ruby"}
80-
lm[Golang] = []string{"golang", "go"}
85+
lm[Golang] = []string{"golang", "go", "gomod"}
86+
lm[Rust] = []string{"rust", "cargo"}
8187
for t, ls := range lm {
8288
for _, l := range ls {
8389
lanMap[l] = t

internal/filter/file.go

+5
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,8 @@ var (
7575
GoMod = filterFunc(strings.HasSuffix, "go.mod")
7676
GoSum = filterFunc(strings.HasSuffix, "go.sum")
7777
)
78+
79+
// rust
80+
var (
81+
RustCargoLock = filterFunc(strings.HasSuffix, "Cargo.lock")
82+
)

internal/vuln/local.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ func loadVulnDB() {
4646
}
4747
// 将漏洞信息存到vulnDB中
4848
name := strings.ToLower(info.Product)
49-
if _, ok := vulnDB[info.Language]; !ok {
50-
vulnDB[info.Language] = map[string][]vulnInfo{}
49+
language := strings.ToLower(info.Language)
50+
if _, ok := vulnDB[language]; !ok {
51+
vulnDB[language] = map[string][]vulnInfo{}
5152
}
52-
vulns := vulnDB[info.Language]
53+
vulns := vulnDB[language]
5354
vulns[name] = append(vulns[name], info)
5455
}
5556
}

0 commit comments

Comments
 (0)