源码集成代码分析工具

初识TCA任务执行机制

  1. TCA server在接收到开启分析的请求后根据所选规则生成对应的task_request,每个task_request对应一个工具的任务
  2. TCA server将task_request分发到能够执行该工具的机器
  3. TCA client在收到task_request后提取出本次任务的工具名也就是其中的task_name字段,字段对应于工具的name字段
  4. TCA client按照task_name在client中的tool目录查找对应python启动脚本
  5. 执行python启动脚本中的内容

添加分析工具(以 tca_ql_php 为例)

根据上述的任务机制添加工具需要做到以下几点

  1. 让server知道存在tca_ql_php工具及其所含的规则
  2. 让server知道哪些客户端可以执行tca_ql_php工具
  3. client下载/找到工具所在目录及需要的环境
  4. 让client知道tca_ql_php对应的启动脚本是什么

如何让Server知道存在相应工具

  1. 找到server/projects/main/apps/scan_conf/management/commands/open_source目录

  2. 创建工具json文件,json文件名尽量对应工具名称方便查看

  3. json文件内容为(以 tca_ql_php 为例)

[
    {
        "name": "tca_ql_php",
        "display_name": "Hades_PHP(展示名称用于前端展示使用)",
        "description": "工具描述",
        "license": "工具license",
        "libscheme_set": [], # 暂时不需要
        "task_processes": [
            "analyze",
            "datahandle",
            "compile"
        ],  # 工具进程,包含compile编译, analyze分析, datahandle数据处理
        "scan_app": "codelint",  # 代码分析统一为codelint
        "scm_url": "", # 暂时为空
        "run_cmd": "",
        "envs": null, # 是否需要特殊环境,这里无需填写
        "build_flag": false, # 是否需要编译命令才能运行
        "checkrule_set": [  # 工具包含的规则
            {
                "real_name": "deser",  # 规则名
                "display_name": "反序列化漏洞",  # 规则前端展示,考虑各工具规则名可能晦涩难懂,设置展示名称方便查找
                "severity": "error",  # 规则等级 从上到下分为 fatal, error, warning, info 四个等级
                "category": "security",  # 规则类别。correctness 功能 security安全 performance性能 usability可用性 accessibility无障碍化 i18n国际化 convention代码风格 other其他
                "rule_title": "反序列化漏洞",  # 一句话概括规则简介
                "rule_params": null,  # 规则参数
                "languages": [  # 支持语言
                    "php"
                ],
                "solution": "",  # 建议的解决方法
                "owner": "",
                "labels": [],
                "description": "",  # 规则详细介绍
            }
        ]
    }
]
  1. server/projects/main/目录执行python manage.py loadcheckers --dir open_source tca_ql_php 加载工具进入数据库

让server知道哪些客户端可以执行tca_ql_php工具

  1. 进入节点管理页面

节点管理

  1. 选择其中一台机器 工具进程配置,勾选其工具进程

工具进程

client下载/找到工具所在目录及需要的环境

  1. 找到puppy-tool-config若没有额外配置则为默认代码库https://github.com/TCATools/puppy-tools-config.git在新窗口打开
  2. 修改其中的 ini 配置文件,每个操作系统对应一个ini
  3. 以 tca_ql_php 为例需要做以下修改
; env_path 主要填写存放工具文件所在的相对目录,一般都存放/拉取在tools下,会在工具执行前加载到环境变量中提供使用
[env_path]
ZEUS_HOME   : Zeus
HADES_HOME  : Hades

; toolz_url
[tool_url] 主要填写工具的git仓库,这里因为 tca_ql_php 直接使用tools下的目录所以不用再进行额外拉取也无需再写
CPPCHECK    : ${base_value:git_url}/linux-cppcheck-1.78

; 各工具配置 以 tca_ql_php 为例
; env_path 填写上面需要加载的环境变量
; env_value 通用环境变量,一般无需填写如果有需求需要现在 [env_value] 中定义好再填写
; path 工具所在目录填写上面的定义
; tool_url 工具git仓库,使用本地相对目录故为空
[tca_ql_php]
env_path  : ZEUS_HOME;HADES_HOME
env_value :
path      : ${env_path:ZEUS_HOME};${env_path:HADES_HOME}
tool_url  : 

让client知道tca_ql_php对应的启动脚本是什么

  1. 以上述步骤在client/tool目录添加脚本tca_ql_php.py作为启动脚本 注:启动脚本必须与工具名称相同

  2. 编写脚本

脚本编写规范

tca_ql_php为例


from task.codelintmodel import CodeLintModel
from util.logutil import LogPrinter
from util.subprocc import SubProcController

logger = LogPrinter()


class TcaQlPHP(CodeLintModel):
    # 代码分析工具集成基类CodeLintModel
    def __init__(self, params):
        logger.info("找到工具了Q_Q")
        super().__init__(params)

    def compile(self, params):
        logger.info("开始编译了Q_Q")
        build_cmd = params.get('build_cmd', None)  # 从params中获取编译命令, params内容可以在最后附录查看
        lang = "php"
        do_some_things()

    def analyze(self, params):
        logger.info("开始分析了Q_Q")
        lang = "php"
        HADES_HOME = envs.get("HADES_HOME", None)
        output_json = "result.json"
        sp = SubProcController(
            command=["Hades", "analyze", "test.php", "-o", output_json],
            cwd=HADES_HOME,
            stdout_line_callback=subprocc_log,
            stderr_line_callback=subprocc_log,
        )
        sp.wait()  # 执行工具分析命令
        issues = []
        # 工具结果输出到output_json,具体工具可能有所不同
        if os.path.exists(output_json):
            with open(output_json, "r") as result_reader:
                result = json.load(result_reader)
                issues.extend(result)
        return issues

tool = TcaQlPHP  # 必须,必须包含tool变量并且为该工具的类
  1. 脚本必须包含analyze方法,如果有配置编译进程也需要相应的compile方法来做编译相关工作,datahandle函数不用自定义基类方法已经够用了。方法执行顺序为 compile -> analyze -> datahandle
  2. params参数为task_request中的task_params字段,具体字段将在最后附录进行说明
  3. anlyze方法必须有返回值,返回值为issue列表,issue格式为
{
    "path": "文件相对路径",
    "line": "行号,int类型",
    "column": "列号, int类型,如果工具没有输出列号信息,可以用0代替",
    "msg": "提示信息",
    "rule": "规则名称,可以根据需要输出不同的规则名",
    "refs": [
        {
            "line": "回溯行号", 
            "msg": "提示信息", 
            "tag": "用一个词简要标记该行信息,比如uninit_member,member_decl等,如果没有也可以都写成一样的", 
            "path": "回溯行所在文件绝对路径"
        },
        ...
    ]
}
说明:
    refs:可选,记录问题回溯路径信息。比如当前文件的回溯路径其他的3行代码,可以将这三行的路径及提示信息,按顺序添加到refs数组中。

PR

如果有意公开您添加的工具欢迎发起PR

注:别忘了puppy-tool-config 也需要PR

附录

params 表格

字段说明类型
scan_languages语言字符串列表如 ["python", "php"]
pre_cmd编译前置命令字符串
build_cmd编译命令字符串
envs额外环境变量字符串
scm_last_revision上次成功分析的代码版本,增量使用字符串
incr_scan是否为增量分析bool
rules规则名称列表,只有规则名字符串列表
rule_list详细的规则列表包含规则名和规则参数等字典列表
checktool工具详细信息,执行一般用不到字典
path_filters过滤路径字典
scm_url代码库url字符串
source_dir代码库本地目录字符串
work_dir本次任务的work_dir目录字符串
project_id分析项目idint
repo_id仓库idint
task_id任务idint
job_id本次分析的idint