- 静态分析
- 开发工具
- 代码质量
【免费下载链接】pyre-check
Performant type-checking for python.
导读
Pysa Fuzzer 是 pyre-check 仓库中一套基于随机化的污点分析回归测试工具:它自动生成含有一条合法污点流(taint flow)的 Python 代码,再用 Pysa(Pyre 的污点分析引擎)对生成代码执行analyze,最后比对"预期能报错"与"实际报错"的文件,从而定位 Pysa 的漏报(false negative)。读完本文,你将掌握它的三种核心动作(all/find-undetected/clean)、全部可选参数(文件数、语句数、生成器版本、随机种子、已知漏报开关),并能从源码层面理解两类代码生成器(正向生成器与变异生成器)的内部机制,以及自动生成.pyre_configuration、sources_sinks.pysa、taint.config的完整逻辑。
工具定位:为什么需要"随机生成 + 反向验证"
传统测试通常用人工构造的正例/反例来验证分析器。但静态分析器面对的是无穷尽的代码形态,人工用例难以覆盖控制流、数据结构、函数间接调用等复杂组合。Pysa Fuzzer 的思路与之相反:
- 先保证污染一定存在:生成的每个文件都以
input()作为 Taint Source,以print(...)作为 Taint Sink(见 scripts/pysa_fuzzer/run.py 生成的sources_sinks.pysa),并在中间随机插入各种数据变换与控制流语句; - 再验证 Pysa 是否找到这条流:只有 Pysa 报出对应规则(rule code
9999)的文件才算"检测成功",未报出的文件即为漏报候选; - 统计漏报比例:脚本会给出"未检测到流"的文件清单与百分比,作为分析器质量与回归的量化指标。
从源码结构看,这套工具服务于 Pyre/Pysa 团队的持续集成与回归测试,定位明确:它是针对污点分析的漏报挖掘器,而不是通用代码生成器。
前置条件
README 中明确要求两项准备:
- Python:运行
run.py及其两个生成器模块; - pyre-check:本地安装并配置好
pyre命令行工具(run_pysa会通过 subprocess 调用pyre,见 scripts/pysa_fuzzer/run.py)。
另外,从 scripts/pysa_fuzzer/run.py 的入口校验可知:必须在pysa_fuzzer目录内执行脚本,否则会直接报错退出:
cd scripts/pysa_fuzzer三种动作与两个可选参数
动作(Actions)
| 动作 | 作用 | 备注 |
|---|---|---|
all | 生成 Python 文件 → 配置分析环境 → 运行 Pysa 分析 → 输出未检测到流的文件 | 完整流水线,内部依次调用generate_python_files、configure_and_analyze、run_pysa、find_undetected_files |
analyze/find-undetected | 仅分析已生成的文件,找出漏报 | 注意:README 写作analyze,但 run.py 中 argparse 的合法取值实际是["all", "find-undetected", "clean"],两者均指向find_undetected_files,请以源码为准 |
clean | 清理配置与临时文件 | 内部执行shutil.rmtree(output_dir)(见 run.py),会整体删除generated_files目录 |
可选参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--num-files | 100 | 生成的文件数量,生成结果写入generated_files/tests/test_1.py~test_N.py |
--num-statements | 20 | 每个文件包含的语句数量(正向生成器使用;至少为 2,因为必须包含 source 与 sink) |
README 中的经典示例:
# 生成 50 个文件、每个 10 条语句,然后配置环境并运行分析 python run.py all --num-files 50 --num-statements 10源码中还有两个 README 未展开的扩展参数
打开 scripts/pysa_fuzzer/run.py 可以看到argparse额外支持:
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
--generator-version | int,取值 1/2 | 1 | 选择代码生成器:1为正向生成器(ForwardCodeGenerator),2为变异生成器(MutationBasedCodeGenerator) |
--seed | int | 无(随机) | 为随机数生成器设定种子,保证生成过程可复现 |
--enable-known-false-negatives | flag | 关闭 | 启用"已知漏报"变异(见下文"变异生成器"章节) |
完整用法示例:
python run.py all --num-files 50 --num-statements 10 --generator-version 2 --seed 42 --enable-known-false-negatives完整流水线:四个步骤逐一拆解
README 的 Detailed Steps 给出了手工分段执行流程,结合源码可拆解为如下四步:
步骤 1:生成文件、配置环境并运行分析
python run.py all --num-files 50 --num-statements 10该命令会一次性完成三件事(对应main()中action == "all"的分支,见 run.py):
generate_python_files(...):按生成器版本生成N个文件到generated_files/tests/,并把文件名列表序列化到generated_files/filenames.json(供后续比对使用,见 run.py);configure_and_analyze(...):写出分析所需的三份配置(下节详述);run_pysa(...):在generated_files目录下执行pyre -n analyze --rule 9999 --no-verify,stdout 存为generated_files/analysis_output.json(见 run.py),随后调用find_undetected_files打印结果。
其中-n表示非交互/无守护进程模式,--rule 9999限定只关注编号为9999的自定义规则,--no-verify跳过结果校验。
步骤 2:进入生成目录(如需要手工分析)
cd generated_filesgenerated_files目录内包含:tests/(生成的测试源码)、.pyre_configuration、sources_sinks.pysa、taint.config、filenames.json,分析后还会出现analysis_output.json。
步骤 3:分析生成文件、定位漏报
python3 ../run.py find-undetected(README 中写为python3 ../run.py analyze,源码中的合法动作名为find-undetected。)
find_undetected_files的实现逻辑(见 run.py)非常直观:
- 读取
filenames.json得到全部生成文件集合; - 读取
analysis_output.json,取其中每条记录entry["path"]的 basename 组成"已检测集合"; - 两者做差集得到未检测文件,按编号排序后逐条打印;
- 最后输出漏报百分比:
Flow has not been detected in X.XX% of the files。
步骤 4:清理
cd .. python3 run.py cleanclean直接删除整个generated_files目录(run.py),以便下一轮 fuzzing 从干净状态开始。
自动生成的配置:污染规则如何定义
configure_and_analyze(run.py)一次性生成三份关键文件,这是理解"为什么生成的代码必然包含合法污点流"的核心:
1..pyre_configuration
{ "site_package_search_strategy": "pep561", "source_directories": ["./tests"], "taint_models_path": [".", "../../../stubs/taint"], "typeshed": "../../../stubs/typeshed/typeshed" }source_directories:把./tests(生成的测试文件)纳入分析范围;taint_models_path:加载当前目录(含生成的sources_sinks.pysa)与仓库内置的 stubs/taint 模型(如common/builtin_functions.pysa、core_privacy_security/general.pysa等,它们构成生产级污点模型库,可作为自定义模型的参考样例);typeshed:指向仓库 vendored 的 stubs/typeshed/typeshed。
若在 Meta 内部 fbcode 环境下(is_in_fbcode()检测当前路径祖辈目录是否含fbcode),还会追加stable_client、unstable_client、binary三个字段,普通开源用户可忽略。这些字段与 client/configuration/configuration.py 中source_directories、taint_models_path的解析逻辑一一对应。
2.sources_sinks.pysa—— 最小污点模型
def input() -> TaintSource[TestSource]: ... def print(*__args: TaintSink[TestSink], **__kwargs): ...这里用两行 Pysa 模型把内置函数input声明为TestSource污点源、print声明为TestSink污点汇,所以生成代码中的x = input()与print(x)天然构成一条合法污点流。
3.taint.config—— 自定义规则
{ "sources": [ {"name": "TestSource", "comment": "test source"} ], "sinks": [ {"name": "TestSink", "comment": "test sink"} ], "features": [], "rules": [ { "name": "Test issue", "code": 9999, "sources": ["TestSource"], "sinks": ["TestSink"], "message_format": "test source flowing into test sink" } ] }规则编号9999即模块顶部的常量RULE_CODE(run.py),与pyre -n analyze --rule 9999中的过滤条件呼应——Pysa 只针对这条测试规则报告问题,避免生产规则干扰 fuzzing 结果。相比仓库内置的 stubs/taint/common/taint.config(预置 Demo/Test 等概念),这里的配置是独立的最小可运行集合,适合作为你自定义 fuzzing 规则时的起点模板。
源码纵深:两类代码生成器的内部机制
--generator-version选择的两条生成路径(见 run.py)代表了两种不同的随机代码构造策略。
正向生成器(version 1):组合随机语句
文件 scripts/pysa_fuzzer/forward_code_generator.py 中CodeGenerator.generate_statements(L326-L384)的核心流程:
- 先写入
import random\nimport math; - 生成 source:
x = input(); - 从 14 个语句生成器(
generate_addition、generate_for_loop、generate_while_loop、generate_list、generate_dictionary、generate_set、字符串拼接/切片/f-string、元组解包、break/continue 循环、if/elif/else、嵌套循环、random.shuffle数据结构、try/except 等)中允许重复地随机抽样num_statements - 2次,串联成中间变换; - 最后生成 sink:
print(上一变量)。
值得注意的实现细节:污点始终在变量链上传递——每个语句生成器都基于get_last_variable()读取上一个变量、再用generate_new_variable()产出新变量,因此无论中间插入多少变换,source 的污染都能沿数据依赖到达 sink。变量名从a~z、aa~zz的序列中分配,并剔除as/in/if/is/or等关键字(L24-L34)。
变异生成器(version 2):基于模板的增量变异
文件 scripts/pysa_fuzzer/mutation_based_code_generator.py 的策略完全不同:每个文件从"初始 source/sink"出发,用apply_mutation(L51-L70)随机挑选一个source_mutation_*或sink_mutation_*方法执行,逐步给污点流"加包装"。
- source 侧(共 44 个
source_mutation_1~source_mutation_44):包括函数包装、if/else分支、装饰器、闭包、类/静态方法、lambda、生成器、map、functools.partial、动态type(...)创建类、迭代器协议,以及大量"加解密/变换"式模板(Base64、ROT13、Vigenère、XOR、Rail Fence、Playfair、仿射密码等,见 L973-L1483)——这些复杂变换专门用于考验分析器跨复杂变换追踪污点的能力; - sink 侧(共 24 个
sink_mutation_1~sink_mutation_24):把 sink 包进函数、装饰器、类属性、map(lambda ...)、zip、编码、shuffle 等包装中。
已知漏报开关(--enable-known-false-negatives)
变异生成器中有一个硬编码的已知漏报集合(L52-L56):
known_false_negatives = { "source_mutation_43", # T201163025: False negative with d[i] += s "sink_mutation_5", # T172546800: False negative when storing functions as values "sink_mutation_6", # T201159288: False negative with map(lambda x: sink(x), parameter) }默认情况下apply_mutation会跳过这三个已确认触发漏报的变异;只有传入--enable-known-false-negatives才启用它们。这解释了该参数的存在意义:在普通回归轮次避免必然失败用例,在专项验证时复现已知缺陷。
结果解读与实战建议
- 输出位置:
generated_files/analysis_output.json是 Pysa 的 JSON 输出,filenames.json是生成清单,两者是漏报比对的输入; - 判定逻辑:某生成文件只要在
analysis_output.json中出现(即 Pysa 对该文件报告了 rule 9999 的问题),即视为"检测成功";未出现的文件进入漏报清单; - 百分比输出:
Flow has not been detected in X.XX% of the files可直接作为该轮 fuzzing 的漏报率指标; - 复现与回归:遇到疑似漏报时,用
--seed固定随机种子重新生成,即可稳定复现同一批文件;再把触发漏报的最小用例抽出来,可作为 bug 报告或新增测试用例; - 规模化:
--num-files默认 100、--num-statements默认 20;run_pysa的日志提示"Please wait a minute or two for Pysa to Run!",说明单轮分析以分钟计,建议从较小规模(如 50×10)开始验证流程,再逐步放大。
注意事项与已知差异
- 必须从
pysa_fuzzer目录运行:main()会校验os.getcwd()的目录名,否则直接sys.exit(1)(run.py); - 动作命名差异:README 中
analyze与源码 argparse 的find-undetected存在不一致,本文以源码为准,两者在main()中执行的是同一函数; clean是破坏性操作:会删除整个generated_files目录,若需保留分析产物请先移出;- fbcode 环境差异:在 Meta 内部环境会额外写入客户端与 binary 字段,并要求设置
PYRE_BINARY环境变量(run.py),开源用户不受影响; - 模型依赖:
taint_models_path引用了仓库内 stubs/taint 目录,请保持仓库目录结构完整后再运行。
延伸阅读
- scripts/pysa_fuzzer/run.py:入口脚本,动作分发、配置生成与漏报比对
- scripts/pysa_fuzzer/forward_code_generator.py:正向组合式代码生成器
- scripts/pysa_fuzzer/mutation_based_code_generator.py:变异式代码生成器与已知漏报集合
- stubs/taint:仓库内置的 Pysa 污点模型库,可作
taint_models_path配置参考 - client/configuration/configuration.py:
source_directories、taint_models_path等配置项的前端解析与校验逻辑
- 静态分析
- 开发工具
- 代码质量
【免费下载链接】pyre-check
Performant type-checking for python.
相关推荐
Pyre 污点丢失调试实战:用 Pysa Model Explorer 定位假阴性(False Negative)
Pyre 污点丢失调试实战:用 Pysa Model Explorer 定位假阴性(False Negative) 导读 Pysa(Python Static
静态分析开发工具代码质量使用 Pysa Sanitizer 消除污点分析误报:Pyre 教程 Exercise 3 实战指南
使用 Pysa Sanitizer 消除污点分析误报:Pyre 教程 Exercise 3 实战指南 本指南围绕 Pyre 仓库中 Pysa 污点分析教程的第三
静态分析开发工具代码质量Pysa 快速上手指南:用 pyre-check 的静态污点分析定位 Python Web 服务安全漏洞
Pysa 快速上手指南:用 pyre check 的静态污点分析定位 Python Web 服务安全漏洞 Pysa(Python Static Analyzer
静态分析开发工具代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考