标题: 静态分析依赖组件 分类: 工具 创建: 2023-01-04 22:31 修改: 链接: http://0x2531.tech/tools/202301042231.txt -------------------------------------------------------------------------------- 现有一个创建组件库的需求,需从 Java 项目代码中提取出依赖的组件,用于组件漏洞管理。 经分析,Java 项目使用 Maven 或 Gradle 方式构建,对应的配置文件分别为 pom.xml 和 build.gradle 文件,其中包含项目依赖组件。 xml 属于结构化数据,比较容易解析,而且有现成的 Python 包。gradle 属于脚本为非结构化数据,通 过静态分析比较麻烦,可行的方案是使用正则表达式。 此外,组件版本可能使用变量动态配置,因此需要获取变量指向的真正的版本。 下面 Python 代码包含3个函数:get_dependency_from_pomxml 、get_dependency_from_buildgradle和find_files。通过这3个函数,我们可以从代码仓库中分析 出各项目依赖的组件,进而创建组件库。 ========== # -*- coding: utf-8 -*- import os import re from xml.etree import ElementTree def get_dependency_from_pomxml(pom_file): dependencies = [] ns = {'xmlns' : 'http://maven.apache.org/POM/4.0.0'} try: root = ElementTree.parse(pom_file).getroot() deps = root.findall(".//xmlns:dependency", namespaces=ns) for d in deps: groupId = d.find("xmlns:groupId", namespaces=ns) artifactId = d.find("xmlns:artifactId", namespaces=ns) version = d.find("xmlns:version", namespaces=ns) if version is not None and '$' in version.text: version_id = version.text[2:-1] version = root.find(".//xmlns:{}".format(version_id), namespaces=ns) dependency = { "groupId" : groupId.text, "artifactId" : artifactId.text, "version" : version.text if version is not None else "", } dependencies.append(dependency) except ElementTree.ParseError: pass return dependencies def get_dependency_from_buildgradle(gradle_file): dependencies = [] with open(gradle_file, 'r') as f: gradle_str = f.read() deps_tmp = re.findall( r"\"[0-9a-zA-Z\.-]{1,}:[0-9a-zA-Z\.-]{1,}.*\"|'[0-9a-zA-Z\.-]{1,}:[0-9a- zA-Z\.-]{1,}.*'", gradle_str) for tmp in deps_tmp: dep_tmp = tmp[1:-1].split(":") version = "" if len(dep_tmp) == 3: if '$' in dep_tmp[2]: pattern = re.compile(r'{}\s*=\s*(\'|\")(.*)(\'|\")'.format(dep_tmp[2][2:- 1])) match = pattern.search(gradle_str) if match: version = match.group(2) else: version = "oops" else: version = dep_tmp[2] dependency = { "groupId" : dep_tmp[0], "artifactId" : dep_tmp[1], "version" : version, } dependencies.append(dependency) return dependencies def find_files(filename, dirpath): """ filename: 搜索文件名 dirpath: 搜索目录 """ target_files = [] for dir_path, subdirs, files in os.walk(dirpath): for file_name in files: if file_name == filename: target_files.append(os.path.join(dir_path, file_name)) return target_files ========== 正则表达式和递归搜索文件部分代码借助 ChatGPT 生成,实践证明这3个函数足够健壮,成功分析出近 50 万的组件数据。 最后,附上 build.gradle 配置样例: ========== plugins { id 'java' } group 'com.abc.acms.ttop' version 'unspecified' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' def mobBootVersion = '1.2.7' def mobBootCacheVersion = '1.3.1' compile ("javax.validation:validation-api:2.0.1.Final") compile 'org.springframework.boot:spring-boot-starter-redis:1.2.3.RELEASE' compile ("com.abc.mob:mob-boot-rest:${mobBootVersion}") compile ("com.abc.mob:mob-boot-jdbc:${mobBootVersion}") compile ("com.abc.mob:mob-boot-web:${mobBootVersion}") compile("com.abc.mob:mob-boot-cache:${mobBootCacheVersion}") compile 'com.alibaba:fastjson:1.2.75' compile 'org.projectlombok:lombok:1.16.16' compile ("com.abc.mob:mob-search:1.4.35-SNAPSHOT") { exclude group: 'com.abc.mob' } compile "cn.hutool:hutool-all:5.7.15" } ==========