- SAST
- 应用安全
【免费下载链接】bandit
Bandit is a tool designed to find common security issues in Python code.
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 | 规则 ID | B301 |
issue_severity | 严重级别,LOW / MEDIUM / HIGH | MEDIUM |
issue_confidence | 置信级别,LOW / MEDIUM / HIGH | HIGH |
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 官方文档中的页面 URL | https://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 的整理)调用链如下:
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 两级的问题。- 遍历结果时,
Issue.as_dict()(见 bandit/core/issue.py)把每个问题转为与上表列名基本一致的字典,其中issue_cwe初始是一个{"id": ..., "link": ...}字典。 - CSV 格式器把
issue_cwe从字典降维为纯链接字符串(无 CWE 时为空串,由Cwe.link()的NOTSET分支决定),保证 CSV 单元格是扁平的标量值。 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.0 | CSV 格式器随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}等有限标签),适合日志风格输出。
选型建议:
- CI 中归档报告:
bandit -r <path> -f csv -o report.csv --exit-zero,再把 CSV 作为构建产物上传;注意 CSV 不能用于--baseline,增量扫描仍应以 JSON 报告为基线文件。 - 用 pandas 做安全指标统计:直接
pd.read_csv("report.csv"),按issue_severity/test_id分组即可得到各规则命中量;line_range列是字符串化的 Python 列表(如[5, 6]),需要时用ast.literal_eval还原。 - 多项目汇总入库:CSV 的扁平结构天然适合 append 式入库,
filename+test_id+line_number可组合成去重键(与 bandit/core/issue.py 中Issue.__eq__的比对字段思路一致,后者用text/severity/cwe/confidence/fname/test/test_id判断同一问题)。 - 链接可达性:
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.
相关推荐
Gobuster结果导出为CSV:电子表格分析扫描数据
Gobuster结果导出为CSV:电子表格分析扫描数据 痛点与解决方案 你是否还在为Gobuster扫描结果难以统计分析而烦恼?当目录扫描返回数百条结果时,手动
网络安全渗透测试零代码体验 OmniVoice 语音克隆 TTS:Colab、HuggingFace Space 与本地 Web Demo 三种方式全解析
零代码体验 OmniVoice 语音克隆 TTS:Colab、HuggingFace Space 与本地 Web Demo 三种方式全解析 OmniVoice
人工智能语音音频预训练免费船舶设计软件FREE!ship Plus:从零开始设计专业船型的完整指南
免费船舶设计软件FREE!ship Plus:从零开始设计专业船型的完整指南 你是否梦想设计自己的船舶,但被昂贵的专业软件所困扰?FREE!ship Plus为
桌面应用科学计算科研
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考