diff --git a/analyzer/engine/engine.go b/analyzer/engine/engine.go index 4f8ef070253980249013929e9e1f7f5115f4a3ed..45201f7066545281db4ab276d0c629480f6f28ef 100644 --- a/analyzer/engine/engine.go +++ b/analyzer/engine/engine.go @@ -19,6 +19,7 @@ import ( "analyzer/analyzer" "analyzer/erlang" "analyzer/golang" + "analyzer/groovy" "analyzer/java" "analyzer/javascript" "analyzer/php" @@ -58,7 +59,9 @@ func (e Engine) ParseFile(filepath string) (*model.DepTree, error) { // 目录 dirRoot = e.opendir(filepath) // 尝试解析mvn依赖 - depRoot = java.MvnDepTree(filepath) + java.MvnDepTree(filepath, depRoot) + // 尝试解析gradle依赖 + groovy.GradleDepTree(filepath, depRoot) } else if filter.AllPkg(filepath) { // 压缩包 dirRoot = e.unArchiveFile(filepath) diff --git a/analyzer/groovy/analyzer.go b/analyzer/groovy/analyzer.go new file mode 100644 index 0000000000000000000000000000000000000000..4e7d15a5288a13017d9b3744851eaddb883e9cb3 --- /dev/null +++ b/analyzer/groovy/analyzer.go @@ -0,0 +1,39 @@ +package groovy + +import ( + "util/enum/language" + "util/filter" + "util/model" +) + +type Analyzer struct{} + +func New() Analyzer { + return Analyzer{} +} + +// GetLanguage get language of analyzer +func (a Analyzer) GetLanguage() language.Type { + return language.Groovy +} + +// CheckFile check parsable file +func (a Analyzer) CheckFile(filename string) bool { + return false + // groovy 文件无法解析依赖层级,暂不处理 + // return filter.GroovyFile(filename) +} + +// ParseFiles parse dependency from file +func (a Analyzer) ParseFiles(files []*model.FileInfo) []*model.DepTree { + deps := []*model.DepTree{} + for _, f := range files { + dep := model.NewDepTree(nil) + dep.Path = f.Name + if filter.GroovyFile(f.Name) { + parseGroovyFile(dep, f) + } + deps = append(deps, dep) + } + return deps +} diff --git a/analyzer/groovy/gradle.go b/analyzer/groovy/gradle.go new file mode 100644 index 0000000000000000000000000000000000000000..1d1ae4be65725fb974a7c79c3856e891f59a70fa --- /dev/null +++ b/analyzer/groovy/gradle.go @@ -0,0 +1,76 @@ +package groovy + +import ( + "bytes" + _ "embed" + "encoding/json" + "os" + "os/exec" + "util/enum/language" + "util/logs" + "util/model" +) + +//go:embed oss.gradle +var ossGradle []byte + +// gradle 脚本输出的依赖结构 +type gradleDep struct { + GroupId string `json:"groupId"` + ArtifactId string `json:"artifactId"` + Version string `json:"version"` + Children []*gradleDep `json:"children"` + // 对应的DepTree + MapDep *model.DepTree `json:"-"` +} + +// GradleDepTree 尝试获取 gradle 依赖树 +func GradleDepTree(dirpath string, root *model.DepTree) { + pwd, err := os.Getwd() + if err != nil { + logs.Error(err) + return + } + os.Chdir(dirpath) + // 复制 oss.gradle + if err = os.WriteFile("oss.gradle", ossGradle, 0444); err != nil { + logs.Warn(err) + return + } + cmd := exec.Command("gradle", "--I", "oss.gradle", "oss") + out, _ := cmd.CombinedOutput() + // 删除 oss.gradle + os.Remove("oss.gradle") + os.Chdir(pwd) + // 获取 gradle 解析内容 + startTag := `ossDepStart` + endTag := `ossDepEnd` + for { + startIndex, endIndex := bytes.Index(out, []byte(startTag)), bytes.Index(out, []byte(endTag)) + if startIndex > -1 && endIndex > -1 { + data := out[startIndex+len(startTag) : endIndex] + out = out[endIndex+1:] + gdep := &gradleDep{MapDep: model.NewDepTree(root)} + err = json.Unmarshal(data, &gdep.Children) + if err != nil { + logs.Warn(err) + } + q := []*gradleDep{gdep} + for len(q) > 0 { + n := q[0] + d := n.MapDep + d.Vendor = n.GroupId + d.Name = n.ArtifactId + d.Version = model.NewVersion(n.Version) + d.Language = language.Groovy + for _, c := range n.Children { + c.MapDep = model.NewDepTree(d) + } + q = append(q[1:], n.Children...) + } + } else { + break + } + } + return +} diff --git a/analyzer/groovy/grape.go b/analyzer/groovy/grape.go new file mode 100644 index 0000000000000000000000000000000000000000..abc0cccb686670383ee206cf35386a2f30341f22 --- /dev/null +++ b/analyzer/groovy/grape.go @@ -0,0 +1,25 @@ +package groovy + +import ( + "regexp" + "util/model" +) + +// parseGroovyFile parse deps in groovy file +func parseGroovyFile(root *model.DepTree, file *model.FileInfo) { + // repo: @GrabResolver(name='mvnRepository', root='http://central.maven.org/maven2/') + regs := []*regexp.Regexp{ + // @Grab('org.springframework:spring-orm:3.2.5.RELEASE') + // @Grab('org.neo4j:neo4j-cypher:2.1.4;transitive=false') + regexp.MustCompile(``), + // @Grab(group='org.restlet', module='org.restlet', version='1.1.6') + // @Grab(group='org.restlet', module='org.restlet', version='1.1.6', classifier='jdk15') + regexp.MustCompile(``), + // Grape.grab(group:'org.slf4j', module:'slf4j-api', version:'1.7.25') + // Grape.grab(groupId:'com.jidesoft', artifactId:'jide-oss', version:'[2.2.1,2.3)', classLoader:loader) + } + for _, reg := range regs { + _ = reg + // match := reg.FindAllStringSubmatch(string(file.Data), -1) + } +} diff --git a/analyzer/groovy/oss.gradle b/analyzer/groovy/oss.gradle new file mode 100644 index 0000000000000000000000000000000000000000..900f4b6289f2417fc55d051db985260fa033ab23 --- /dev/null +++ b/analyzer/groovy/oss.gradle @@ -0,0 +1,74 @@ +import groovy.json.JsonOutput + +class DepTree{ + def children + def dep + def dict + DepTree(dep){ + this.dict = [:] + if (dep){ + this.dict.groupId = dep.moduleGroup + this.dict.artifactId = dep.moduleName + this.dict.version = dep.moduleVersion + this.dep = dep + } + this.dict.children = [] + } + def key(){ + return "${this.dict.("groupId")}:${this.dict.get("artifactId")}" + } +} + +def getDepsList(deps){ + def nodes = [] + deps.each{ dep -> + nodes += new DepTree(dep) + } + return nodes +} + +def getDepsTree(deps){ + def result = [] + deps.each{ ds -> + def exist = [:] + def q = getDepsList(ds) + q.each{ d -> + result += d.dict + } + while (!q.isEmpty()){ + def node = q.get(0) + q.remove(0) + if (!exist.get(node.key())){ + exist.put(node.key(), true) + def children = getDepsList(node.dep.children) + q += children + children.each{ d -> + node.dict.children += d.dict + } + } + node.dep = null + } + } + return result +} + +allprojects { + task oss { + doLast { + def deps = [] + rootProject.allprojects.each{ proj-> + def ds = [] + proj.configurations.each({ conf -> + try { + ds += conf.resolvedConfiguration.firstLevelModuleDependencies + } catch (Exception ex) { + } + }) + deps.add(ds) + } + println("ossDepStart") + println(JsonOutput.toJson(getDepsTree(deps))) + println("ossDepEnd") + } + } +} \ No newline at end of file diff --git a/analyzer/java/ext.go b/analyzer/java/ext.go index e643088263ebddcbcefdf53f20c32f0100b97ed4..eb381a40bf3830d0225701c7fd64287cc8dc90e7 100644 --- a/analyzer/java/ext.go +++ b/analyzer/java/ext.go @@ -23,8 +23,7 @@ import ( ) // MvnDepTree 调用mvn解析项目获取依赖树 -func MvnDepTree(path string) (root *model.DepTree) { - root = model.NewDepTree(nil) +func MvnDepTree(path string, root *model.DepTree) { pwd, err := os.Getwd() if err != nil { logs.Error(err) diff --git a/util/enum/language/language.go b/util/enum/language/language.go index 15b301cb8e6dbcd90b53223454dae84ced6fbbcd..a4d96eb6fd68f277a4e13d33f0709df1bd1154fe 100644 --- a/util/enum/language/language.go +++ b/util/enum/language/language.go @@ -21,6 +21,7 @@ const ( Golang Rust Erlang + Groovy ) // String 语言类型 @@ -42,6 +43,8 @@ func (l Type) String() string { return "Rust" case Erlang: return "Erlang" + case Groovy: + return "Groovy" default: return "None" } @@ -52,7 +55,7 @@ func (l Type) Vuln() string { switch l { case None: return "" - case Java: + case Java, Groovy: return "java" case JavaScript: return "js" @@ -77,13 +80,14 @@ var ( func init() { lm := map[Type][]string{} - lm[Java] = []string{"java", "maven", "mvn"} - lm[JavaScript] = []string{"js", "node", "bower", "nodejs", "javascript", "npm", "vue", "react"} + lm[Java] = []string{"java", "maven"} + lm[JavaScript] = []string{"js", "node", "nodejs", "javascript", "npm", "vue", "react"} lm[Php] = []string{"php", "composer"} - lm[Ruby] = []string{"ruby", "gem"} - lm[Golang] = []string{"golang", "go", "gomod", "govendor", "gosum"} + lm[Ruby] = []string{"ruby"} + lm[Golang] = []string{"golang", "go", "gomod"} lm[Rust] = []string{"rust", "cargo"} lm[Erlang] = []string{"erlang", "rebar"} + lm[Groovy] = []string{"groovy", "gradle"} for t, ls := range lm { for _, l := range ls { lanMap[l] = t @@ -91,7 +95,7 @@ func init() { } } -// NewLanguage 获取最相似的语言 +// Type 获取最相似的语言 func NewLanguage(language string) Type { if language == "" { return None diff --git a/util/filter/file.go b/util/filter/file.go index b21d8ee9f24bc345d6550da4ba96327395f4d86b..331c1a94b8830207a2e6ee7a2edfced2e29664eb 100644 --- a/util/filter/file.go +++ b/util/filter/file.go @@ -79,3 +79,8 @@ var ( var ( ErlangRebarLock = filterFunc(strings.HasSuffix, "rebar.lock") ) + +// groovy +var ( + GroovyFile = filterFunc(strings.HasSuffix, ".groovy") +)