☰
Bandit CSV 报告格式器解析:将 Python 安全扫描结果导出为机器可读的 CSV 文件
2026/9/25 17:51:07 网站建设 项目流程
  • SAST
  • 应用安全

【免费下载链接】bandit

Bandit is a tool designed to find common security issues in Python code.

项目地址:https://gitcode.com/gh_mirrors/ba/bandit
点击查看免费下载

Bandit 内置的 CSV 格式器(csvformatter)负责把静态安全扫描发现的问题以逗号分隔值(CSV)格式输出,是自动化集成、结果入库和报表统计场景的关键环节。本文围绕 CSV 格式器的输出规范展开,结合 格式器源码、CLI 入口 与 单元测试,完整讲清它的字段结构、调用链路、版本演进以及实际的命令行用法,读完后可直接在 CI 流水线中生成可被 pandas、Excel 或任何 CSV 解析器直接消费的安全报告。

一、格式器注册:bandit -f csv从何而来

Bandit 的所有报告格式器都以插件形式加载。CSV 格式器在 setup.cfg 的[entry_points]段中通过bandit.formatters组注册:

[entry_points] bandit.formatters = csv = bandit.formatters.csv:report json = bandit.formatters.json:report txt = bandit.formatters.text:report xml = bandit.formatters.xml:report html = bandit.formatters.html:report sarif = bandit.formatters.sarif:report screen = bandit.formatters.screen:report yaml = bandit.formatters.yaml:report custom = bandit.formatters.custom:report

注册键名csv就是命令行--format(-f)可选的取值。在 bandit/cli/main.py 中,-f/--format参数的可选值并非硬编码,而是动态取自已加载的扩展管理器:

parser.add_argument( "-f", "--format", dest="output_format", action="store", default=output_format, help="specify output format", choices=sorted(extension_mgr.formatter_names), )

因此只要第三方包按同样方式注册bandit.formatters入口点,就能扩展出新的输出格式,-f的可选列表会自动包含它。

需要说明的是,输出格式有一个默认值逻辑:当标准输出是终端(TTY)、未设置NO_COLOR环境变量且TERM != "dumb"时默认用screen(带彩色高亮);重定向到文件或管道时默认回退为txt。也就是说bandit examples/ > report.txt与bandit -f txt examples/效果一致,而要用 CSV 必须显式指定-f csv。

二、命令行实战用法

结合 bandit/cli/main.py 的参数定义,CSV 输出的核心用法如下:

# 对示例目录扫描,输出 CSV 到标准输出 bandit -r examples/ -f csv # 将 CSV 报告写入文件(-o 指定输出文件) bandit -r examples/ -f csv -o report.csv # 只报告 MEDIUM 及以上严重级别、MEDIUM 及以上置信度的问题 bandit -r examples/ -f csv -ll -ii -o report.csv # 或使用字符串形式指定阈值 bandit -r examples/ -f csv --severity-level medium --confidence-level high

几个与 CSV 输出直接相关的 CLI 细节(均出自 bandit/cli/main.py 源码):

  • -o/--output:以 UTF-8 编码打开的文件对象,默认是sys.stdout。CSV 格式器内部会判断输出目标是否为 stdout,若是文件则记录一条CSV output written to file: ...的日志,方便在 CI 日志中确认落盘位置。
  • -l/--level与-i/--confidence:使用action="count",-l对应 LOW、-ll对应 MEDIUM、-lll对应 HIGH;字符串形式--severity-level all/low/medium/high会被映射为相同的数值等级。这两个值最终作为sev_level/conf_level传入格式器,决定了 CSV 中会写入哪些问题。
  • 退出码约定:扫描发现满足过滤条件的问题时进程以1退出(--exit-zero可强制返回 0)。在 CI 中若只是生成 CSV 报告而不希望其阻塞流水线,需要加--exit-zero或在 shell 中容忍非零退出码。
  • --baseline限制:基线对比只接受 JSON 格式的基线文件,且要求输出格式是支持基线的格式器;CSV 不在--baseline的可用格式列表中,因此不能把 CSV 报告本身再用作基线。

三、输出字段规范:12 列的完整结构

CSV 报告的列由 bandit/formatters/csv.py 中的fieldnames列表唯一确定,且表头始终写在第一行:

fieldnames = [ "filename", "test_name", "test_id", "issue_severity", "issue_confidence", "issue_cwe", "issue_text", "line_number", "col_offset", "end_col_offset", "line_range", "more_info", ]

各列含义如下:

列名含义取值示例
filename产生问题的源文件路径examples/yaml_load.py
test_name检测规则(插件或黑名单项)的名称blacklist_calls
test_id规则 IDB301
issue_severity严重级别,LOW / MEDIUM / HIGHMEDIUM
issue_confidence置信级别,LOW / MEDIUM / HIGHHIGH
issue_cwe关联的 CWE 条目Mitre 链接(无 CWE 则为空)https://cwe.mitre.org/data/definitions/502.html
issue_text问题描述文本Use of unsafe yaml load. ...
line_number问题所在起始行号9
col_offset问题所在起始列偏移4
end_col_offset问题结束列偏移20
line_range问题覆盖的行号列表(Python list 字符串表示)[9]
more_info该规则在 Bandit 官方文档中的页面 URLhttps://bandit.readthedocs.io/en/latest/

官方文档给出的示例输出(来自 doc/source/formatters/csv.rst 所引用的模块 docstring)大致如下:

filename,test_name,test_id,issue_severity,issue_confidence,issue_cwe, issue_text,line_number,line_range,more_info examples/yaml_load.py,blacklist_calls,B301,MEDIUM,HIGH, https://cwe.mitre.org/data/definitions/20.html,"Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load(). ",5,[5],https://bandit.readthedocs.io/en/latest/

这里有一个值得注意的细节:该文档示例是历史版本留下的,比当前实现少了col_offset和end_col_offset两列。从 bandit/formatters/csv.py 的fieldnames定义可以确认,当前版本的 CSV 输出固定为 12 列。撰写解析脚本时应以源码中的字段列表为准,而不是以旧版示例的列数为准。

另外,CSV 输出不包含问题代码上下文:格式器调用result.as_dict(with_code=False),即Issue.as_dict()中的with_code=True分支(会附带code字段,由 bandit/core/issue.py 的get_code()生成)在 CSV 路径上被显式关闭,这也是extrasaction="ignore"参数的意义之一——即使字典里多出code键,DictWriter也会安全地忽略它。

四、实现链路剖析

4.1report()函数与结果过滤

CSV 格式器的入口函数签名为:

def report(manager, fileobj, sev_level, conf_level, lines=-1): results = manager.get_issue_list( sev_level=sev_level, conf_level=conf_level ) with fileobj: writer = csv.DictWriter(fileobj, fieldnames=fieldnames, extrasaction="ignore") writer.writeheader() for result in results: r = result.as_dict(with_code=False) r["issue_cwe"] = r["issue_cwe"]["link"] r["more_info"] = docs_utils.get_url(r["test_id"]) writer.writerow(r)

(以上为基于 bandit/formatters/csv.py#L41-L82 的整理)调用链如下:

  1. report()接收 Bandit 管理器对象,通过 bandit/core/manager.py 的get_issue_list()取问题列表,后者直接委托给filter_results():先按sev_level/conf_level过滤每个Issue.filter()结果,若加载了基线还会做基线比对。严重度/置信度的可比顺序由 bandit/core/constants.py 中的RANKING = ["UNDEFINED", "LOW", "MEDIUM", "HIGH"]定义,因此-ll(MEDIUM)会保留 MEDIUM 与 HIGH 两级的问题。
  2. 遍历结果时,Issue.as_dict()(见 bandit/core/issue.py)把每个问题转为与上表列名基本一致的字典,其中issue_cwe初始是一个{"id": ..., "link": ...}字典。
  3. CSV 格式器把issue_cwe从字典降维为纯链接字符串(无 CWE 时为空串,由Cwe.link()的NOTSET分支决定),保证 CSV 单元格是扁平的标量值。
  4. more_info一列由 bandit/core/docs_utils.py 的get_url()生成:它以https://bandit.readthedocs.io/en/{当前版本号}/为基址,先查插件注册表,命中则拼出plugins/{test_id}_{插件名}.html页面地址;未命中插件则查黑名单表,按blacklists/blacklist_calls.html或blacklist_imports.html的规则条目锚点拼接(对B304/B305、B313–B320这类合并页面做了特判);两者都查不到时回退为文档首页地址。这意味着 CSV 中每条问题都能一键跳转到对应规则的详细说明页,适合直接分发给非安全团队的读者。

4.2 文件对象管理与一个历史遗留细节

两个实现细节解释了源码中看似“多余”的代码:

  • with fileobj::report()会关闭传入的文件对象。CLI 路径下-o打开的文件由此处统一关闭;而sys.stdout本身支持上下文管理器,直接传入也安全。
  • Python 2 时代的导入说明:bandit/formatters/csv.py 顶部有一段注释,解释本模块命名为csv后与标准库csv同名,在 Python 2 下以from bandit.formatters import csv方式导入会遮蔽标准库;模块内部因此使用绝对导入import csv。项目当前只支持 Python 3(见 setup.cfg 的Programming Language :: Python :: 3分类器),这段注释属于历史兼容说明,但阅读源码时看到模块名与导入名不一致并不矛盾。
  • 模块末尾if fileobj.name != sys.stdout.name: LOG.info(...)依赖fileobj.name属性,即输出目标必须是带name的常规文件对象(-o或测试中的临时文件);单元测试 tests/unit/formatters/test_csv.py 正是通过tempfile.mkstemp()创建真实文件来覆盖这条日志分支的。

五、字段演进史:从 9 列到 12 列

模块 docstring 中保留了 CSV 格式器的完整版本沿革(随 Sphinxautomodule渲染进 文档页):

版本变化
0.11.0CSV 格式器随versionadded引入
1.5.0新增more_info列,输出规则文档链接
1.7.3新增issue_cwe列,输出 CWE Mitre 链接

再加上当前源码中的col_offset/end_col_offset两列(用于精确定位问题代码片段,常见于与编辑器或 LSP 集成时做跳转高亮),形成了如今的 12 列结构。对下游消费方的启示是:解析 CSV 时应以表头行为准动态取列,不要硬编码列数,这样在 Bandit 大版本升级后脚本不会断裂。

六、测试如何验证 CSV 输出

单元测试 tests/unit/formatters/test_csv.py 展示了验证 CSV 格式器的标准姿势,也侧面确认了字段契约:

def test_report(self): with open(self.tmp_fname, "w") as tmp_file: b_csv.report(self.manager, tmp_file, self.issue.severity, self.issue.confidence) with open(self.tmp_fname) as f: reader = csv.DictReader(f) data = next(reader) self.assertEqual(self.issue.severity, data["issue_severity"]) self.assertEqual(self.issue.confidence, data["issue_confidence"]) self.assertEqual(str(self.context["lineno"]), data["line_number"]) self.assertEqual(str(self.context["linerange"]), data["line_range"]) self.assertIsNotNone(data["more_info"]) self.assertEqual(str(self.issue.col_offset), data["col_offset"])

测试构造了一个hardcoded_bind_all_interfaces的Issue(含col_offset=8、end_col_offset=16、linerange=[4]),写入临时文件后用标准库csv.DictReader回读并逐列断言,覆盖了filename、issue_severity、issue_confidence、issue_text、line_number、line_range、test_name、more_info、col_offset、end_col_offset等关键列。这也说明 CSV 输出严格遵循 RFC 风格的引号转义——像issue_text这类可能含逗号、换行的描述字段会被csv模块自动加引号包裹(如文档示例中的"Use of unsafe yaml load. ...")。

七、与其他格式器的定位差异及选型建议

CSV 在 doc/source/formatters/index.rst 所列的格式器家族中定位清晰:

  • screen/txt:面向人类阅读,带彩色/纯文本排版,含代码上下文;
  • json:结构最完整(支持 baseline、嵌套issue_cwe对象),适合程序二次处理与 API 集成;
  • sarif:面向静态分析结果交换标准(SAST 工具生态集成);
  • csv:面向表格化工具与轻量数据流,每行一条扁平化记录,可直接被 Excel、Google Sheets、pandasread_csv、数据库导入工具消费;
  • custom:通过--msg-template自定义消息模板(仅支持{abspath}、{line}、{test_id}等有限标签),适合日志风格输出。

选型建议:

  1. CI 中归档报告:bandit -r <path> -f csv -o report.csv --exit-zero,再把 CSV 作为构建产物上传;注意 CSV 不能用于--baseline,增量扫描仍应以 JSON 报告为基线文件。
  2. 用 pandas 做安全指标统计:直接pd.read_csv("report.csv"),按issue_severity/test_id分组即可得到各规则命中量;line_range列是字符串化的 Python 列表(如[5, 6]),需要时用ast.literal_eval还原。
  3. 多项目汇总入库:CSV 的扁平结构天然适合 append 式入库,filename+test_id+line_number可组合成去重键(与 bandit/core/issue.py 中Issue.__eq__的比对字段思路一致,后者用text/severity/cwe/confidence/fname/test/test_id判断同一问题)。
  4. 链接可达性:more_info与issue_cwe均为外链,内网环境消费 CSV 时应将这两列视为“提示性”数据而非强依赖。

小结

Bandit 的 CSV 格式器是一个“小而稳定”的组件:通过 setup.cfg 的 entry point 注册为csv,由 bandit/formatters/csv.py 的report()函数实现,输出固定 12 列、表头恒定、不含代码上下文的扁平问题清单,issue_cwe与more_info两列分别指向 CWE 定义与 Bandit 规则文档。理解它的字段来源(Issue.as_dict()→ CWE 链接降维 →docs_utils.get_url()补链)与过滤链路(get_issue_list()→filter_results()→RANKING排序),就能在 CI、报表脚本和数据库集成中可靠地消费 Bandit 的 CSV 报告。

  • SAST
  • 应用安全

【免费下载链接】bandit

Bandit is a tool designed to find common security issues in Python code.

项目地址:https://gitcode.com/gh_mirrors/ba/bandit
点击查看免费下载

相关推荐

上一篇:Linux 内核 IRQ 基础概念解读:从设备中断请求到 IRQ 号的完整映射
下一篇:WTF Solidity 代理合约(Proxy Contract)详解:用 delegatecall 实现可升级与省 Gas 的合约架构

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询