- 目录
所有的 Scanner 都以 Jar 包 + CLI 的方式来提供服务,使用的框架是:clikt,语言是:Kotlin。
Scanner 执行过程
主要过程上:
- Backend 根据不同上下文,调用不同的 xxxScannerTool。
- xxxScannerTool 下载 jar,再将 jar 复制到目标目录
- 执行对应的 scanner.jar,生成 SQL 数据,并注入到数据库中。
1. Backend 调用 Scanner
在 ArchGuard Backend 中是通过调用 Jar 包的方式,来调用相关的 Scanner。
Backend 是通过 ScannerManager
来寻找系统中的所有 Scanner:
@Component
class ScannerManager(@Autowired private val scanners: List<Scanner>) {
fun execute(context: ScanContext) {
val WORKER_THREAD_POOL = Executors.newFixedThreadPool(4)
val callables: List<Callable<Unit>> = scanners.map { s ->
Callable {
try {
if (s.canScan(context)) {
s.scan(context)
}
} catch (e: Exception) {
log.error("failed to scan {}", s.javaClass.simpleName, e)
}
}
}
WORKER_THREAD_POOL.invokeAll(callables)
}
}
随后,通过构建的 ScanContext
来分析什么情况下使用哪些 Scanner。如 SourceCodeScanner
的条件是:
@Service
class SourceCodeScanner: Scanner {
override fun getScannerName(): String {
return "SourceCodeScanner"
}
override fun canScan(context: ScanContext): Boolean {
return context.language.lowercase() != "jvm"
}
override fun scan(context: ScanContext) {
SourceCodeTool(context.workspace, context.systemId, context.language, context.dbUrl, context.codePath, context.logStream).also { it.analyse() }
}
}
随后,直接调用对应的 ScannerTool。
2. ScannerTool 调用 Jar
Scanner 调用 scanner.jar 时,一般是分为三步的:
fun analyse() {
prepareTool()
val cmd = mutableListOf(
"java",
"-jar",
"-Ddburl=$dbUrl?useSSL=false",
"diff_changes.jar",
"--path=.",
"--branch=$branch",
"--system-id=$systemId",
"--language=${language.lowercase()}"
)
cmd.addAll(this.additionArguments)
scan(cmd)
}
private fun prepareTool() {
val jarExist = checkIfExistInLocal()
if (jarExist) {
copyIntoSystemRoot()
} else {
download()
}
}
PS:其中的 $dbUrl
是从 Spring 的 properties 中读取对应的数据库 URL、用户名和密码生成的,传递给目标 scanner,以向这个数据库表注入数据。
即:
- 准备工具。先检查本地是否有对应的工具,有的话直接复制到目标目录,没有的话 GitHub 下载。
- 生成 Command 命令。
- 执行对应的 Command 命令。
3. Scanner 过程:Sourcecode 示例
如下所示,每个 Scanner 都使用 clikt
框架编写,都以 Runner.kts
作为入口。
执行 Scanner 时,一般会分为这几步:
- 执行对应的分析 Task,分成一个目标数据。
- 构建一个 Repository,如
ContainerRepository
用于将目标数据结构,转换为 SQL 字符串,即调用:SqlGenerator 来生成。 - 随后清理数据,并将数据存入数据库中。
存储过程中下所述:
private fun storeDatabase(tables: Array<String>, systemId: String) {
store.disableForeignCheck()
store.initConnectionPool()
logger.info("========================================================")
val phaser = Phaser(1)
deleteByTables(tables, phaser, systemId)
phaser.arriveAndAwaitAdvance()
logger.info("============ system {} clean db is done ==============", systemId)
saveByTables(tables, phaser)
phaser.arriveAndAwaitAdvance()
logger.info("============ system {} insert db is done ==============", systemId)
updateByTables(tables, phaser)
phaser.arriveAndAwaitAdvance()
logger.info("============ system {} update db is done ==============", systemId)
logger.info("========================================================")
store.enableForeignCheck()
}
Scanner Download
Scanner 下载过程:
- 从 GitHub 的 release 下载对应的版本到应用的运行目录。
- 在本地时,则与后端代码同级(即和 build.gradle.kts 同级)
- 在镜像内,则与 /home/spring/app.jar 同级
- 将 Scanner 从应用目录,拷贝到代码仓库的临时目录
Scanner 调试
Scanner 的调试即可以与 backend 一起,也可以到目标项目的目录中:
- 与 backend 一起。将 jar 包复制到代码中,与
build.gradle.kts
同级,并修改为与xxxScannerTool
相同的版本,即可运行扫描工具进行扫描。 - 目标项目。将 jar 包复制到将要构建的项目(workdir,即从 system_info 表中查询)目录,按不同类型的应用和参数执行。其中 DBUrl 和 systemId 为必填 。
与 backend 放置时示例如下:
├── CHANGELOG.md
├── DesigniteJava.jar
├── LICENSE
├── README.md
├── build.gradle.kts
├── diff_changes-1.4.5-all.jar
├── docker-compose.yml
├── docs
├── install.sh
├── scan_git-1.4.3-all.jar
├── scan_sourcecode-1.4.3-all.jar
├── scan_sourcecode-1.4.5-all.jar
├── settings.gradle.kts
└── src
Scanner Git
CLI 参数:
Options:
--branch TEXT git repository branch
--after TEXT scanner only scan commits after this timestamp
--path TEXT local path
--repo-id TEXT repo id
--system-id TEXT system id
--language TEXT language
--loc TEXT scan loc
CLI 示例:
java "-Ddburl=jdbc:mysql://localhost:3306/archguard?user=root&password=&useSSL=false" -jar scan_git-1.2.4-all.jar --branch=dev --path=. --system-id=5
Scanner SourceCode
CLI 参数:
Options:
--path TEXT local path
--api-only only scan api
--system-id TEXT system id
--language TEXT langauge: Java, Kotlin, TypeScript, CSharp, Python, Golang
CLI 示例:
java "-Ddburl=jdbc:mysql://localhost:3306/archguard?user=root&password=&useSSL=false" -jar scan_sourcecode-1.4.3-all.jar --path=. --language=kotlin --system-id=10
Diff Changes
CLI 参数:
Options:
--branch TEXT git repository branch
--since TEXT since specific revision
--until TEXT util specific revision
--path TEXT local path
--system-id TEXT system id
--language TEXT language
CLI 示例:
java "-Ddburl=jdbc:mysql://localhost:3306/archguard?user=root&password=&useSSL=false" -jar diff_changes-1.4.3-all.jar --since=aa2b5379 --until=fcd00dbd9df71923a9a2dd909dc230b344eea9f2 --system-id=1 --path=.