资讯动态

mitmproxy 自定义命令(Commands)完全指南:编写类型安全的 Addon 命令并绑定到快捷键

发布时间:2026/9/8 22:28:11 来源:尧图企业网站定制
mitmproxy 自定义命令Commands完全指南编写类型安全的 Addon 命令并绑定到快捷键【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy本篇技术指南基于 mitmproxy 官方文档 Custom Commands讲解如何通过command.command装饰器为 mitmproxy 编写带类型标注的自定义命令涵盖命令声明、基于 flow 选择器的批量操作、路径参数处理、完整的支持类型清单并结合 mitmproxy/command.py 与 mitmproxy/types.py 源码剖析命令的注册、参数解析与运行时类型校验机制读完后可独立开发可绑定到 mitmproxy console 快捷键的 addon 命令。命令是什么类型化的用户交互机制Commands 让用户能够主动与 addon 交互——查询 addon 状态、命令其执行动作、让其转换数据。与 Options 类似命令是**类型化typed**的命令的调用和返回值都会在运行时被检查runtime type checked。命令是 mitmproxy 中非常强大的构造物——以 mitmproxy console 为例其中所有用户交互本质上都是把命令绑定到按键上实现的。这意味着你写的每一个自定义命令都可以在 console 命令提示符按:进入中以:命令名 参数...的形式执行获得完整的 Tab 补全和错误提示能力被内置帮助系统按C查看所有命令收录直接绑定为键盘快捷键成为可复用的操作函数。第一个命令声明与执行最简单的例子见官方示例 commands-simple.pyAdd a custom command to mitmproxys command prompt. import logging from mitmproxy import command class MyAddon: def __init__(self): self.num 0 command.command(myaddon.inc) def inc(self) - None: self.num 1 logging.info(fnum {self.num}) addons [MyAddon()]启动加载该 addon 的 mitmproxy console mitmproxy -s ./examples/addons/commands-simple.py确保事件日志Event Log按E切换可见然后在命令提示符输入:开始中执行:myaddon.inc此时事件日志会打印num 1再次执行则打印num 2。注意 Tab 补全是可用的——自定义命令与内置命令享有完全一致的能力。关于这个例子有几点需要说明命令通过command.command装饰器声明。每个命令有唯一名称惯例是「点分隔命名并以 addon 名作前缀」如myaddon.inc。类型标注是强制的包括返回类型本例为None。正是这一点让 mitmproxy 能在整个工具链中支持 addon 命令运行时调用会做类型检查、命令会被内置帮助收录、console 的命令编辑器可以做高级补全和错误检查。源码视角装饰器与注册机制从 mitmproxy/command.py 的实现看command装饰器只是给被装饰函数挂上command_name标记def command(name: str | None None): def decorator(function): functools.wraps(function) def wrapper(*args, **kwargs): verify_arg_signature(function, args, kwargs) return function(*args, **kwargs) wrapper.__dict__[command_name] name or function.__name__.replace(_, .) return wrapper return decorator若不传name默认取函数名并把下划线替换为点my_func→my.func。当 addon 被加载时CommandManager.collect_commands 会遍历 addon 的所有属性凡是带有command_name字符串标记的对象都通过self.add(...)注册进全局命令表self.commands若类型校验失败仅记录 warning 而不中断整个 addon 加载。每个命令被包装为Command对象其构造函数会立即做两件事用inspect.signature(func, eval_strTrue)解析函数签名逐个检查参数标注与返回类型是否都在mitmproxy.types.CommandTypes类型注册表中否则抛出CommandError——这就是文档中类型标注是强制的的底层原因。函数 docstring 会被自动提取并折行后作为命令的help文本显示在console.view.commands按C的命令列表里。与 flows 协作把流量选择器直接变成命令参数由于命令参数是类型化的mitmproxy 可以为某些重要数据类型提供特殊的便捷机制其中最有用的是表示 mitmproxy 流量的flow类。考虑官方示例 commands-flows.pyHandle flows as command arguments. import logging from collections.abc import Sequence from mitmproxy import command from mitmproxy import flow from mitmproxy import http from mitmproxy.log import ALERT class MyAddon: command.command(myaddon.addheader) def addheader(self, flows: Sequence[flow.Flow]) - None: for f in flows: if isinstance(f, http.HTTPFlow): f.request.headers[myheader] value logging.log(ALERT, done) addons [MyAddon()]myaddon.addheader命令本身很简单接收一个 flow 序列给每个请求添加一个 header。真正有意思的地方在于用户如何指定 flows。因为 mitmproxy 能检查函数的类型签名它可以把一个文本形式的 flow 选择器透明地展开成一个 flow 序列。这等于让用户拥有了完整的 flow 过滤器能力。实际操作先加载 addon 并制造一些流量 mitmproxy -s ./examples/addons/commands-flows.py然后以各种方式调用命令。只作用于当前焦点 flow:myaddon.addheader focus作用于全部 flow:myaddon.addheader all或者只处理google.com的 flow:myaddon.addheader ~d google.com更重要的是如果计划频繁使用可以把这些命令直接绑定到 mitmproxy 的键盘快捷键上。flow 选择器加命令的组合极其强大让我们能够构建并暴露对 flows 进行操作的可复用函数。选择器解析的底层链路当命令参数标注为Sequence[flow.Flow]或flow.Flow时解析由 mitmproxy/types.py 中的_FlowsType/_FlowType完成——它们调用内置命令view.flows.resolve把选择器字符串解析为真实的 flow 对象class _FlowsType(_BaseFlowType): typ Sequence[flow.Flow] display flow[] def parse(self, manager, t, s): try: return manager.call_strings(view.flows.resolve, [s]) ...而view.flows.resolve的实现位于 mitmproxy/addons/view.pycommand.command(view.flows.resolve) def resolve(self, flow_spec: str) - Sequence[mitmproxy.flow.Flow]: if flow_spec all: return [i for i in self._store.values()] if flow_spec focus: return [self.focus.flow] if self.focus.flow else [] elif flow_spec shown: return [i for i in self] elif flow_spec hidden: ... elif flow_spec marked: return [i for i in self._store.values() if i.marked] elif flow_spec unmarked: return [i for i in self._store.values() if not i.marked] elif re.match(r[0-9a-f\-,]{36,}, flow_spec): ids flow_spec[1:].split(,) return [i for i in self._store.values() if i.id in ids] else: filt flowfilter.parse(flow_spec) return [i for i in self._store.values() if filt(i)]从这段实现可以确认flow 选择器支持以下形式视图标记allstore 中全部、focus当前焦点、shown当前视图可见、hidden被过滤掉的、marked/unmarked按标记状态flow ID 列表id1,id2,...UUID 形式以逗号分隔完整 flow 过滤器表达式如~d google.com、~u api/、~c 200、~h content-type等运算符清单见 mitmproxy/flowfilter.py 模块头部文档~q请求、~s响应、~h/~hq/~hs头、~b/~bq/~bs正文、~tcontent-type、~d域名、~m方法、~uURL、~c响应码等。此外_FlowType单数flow.Flow解析后会校验选择器必须恰好匹配 1 个 flow否则报错Command requires one flow, specification matched N——所以单 flow 参数与 flow 序列参数的语义差异是明确且被强制执行的。Paths路径参数与任意数量参数命令可以接受任意数量的参数。官方示例 commands-paths.py 在 flow 序列的基础上再加一个路径参数Handle file paths as command arguments. import logging from collections.abc import Sequence from mitmproxy import command from mitmproxy import flow from mitmproxy import http from mitmproxy import types from mitmproxy.log import ALERT class MyAddon: command.command(myaddon.histogram) def histogram( self, flows: Sequence[flow.Flow], path: types.Path, ) - None: totals: dict[str, int] {} for f in flows: if isinstance(f, http.HTTPFlow): totals[f.request.host] totals.setdefault(f.request.host, 0) 1 with open(path, w) as fp: for cnt, dom in sorted((v, k) for (k, v) in totals.items()): fp.write(f{cnt}: {dom}\n) logging.log(ALERT, done) addons [MyAddon()]该命令计算给定 flow 集合中域名直方图并写入命令第二个参数指定的路径。调用方式:myaddon.histogram all /tmp/xxx注意 mitmproxy 对 flow 选择器和路径都提供 Tab 补全。路径补全由 mitmproxy/types.py 的_PathType.completion实现它先用glob展开前缀支持~展开目录项在展示时追加/后缀parse阶段则只做os.path.expanduser把字符串转换为types.Path一个str子类。支持类型清单Supported Types命令参数与返回值只支持以下类型若你需要使用清单之外的类型官方文档建议提交 pull request 扩展类别类型标注说明原始类型str、int、boolbool在命令行上写作true/false见_BoolType.parse序列typing.Sequence[str]命令行上以逗号分隔a, b, c解析为 3 个字符串Flows 及 flow 序列flow.Flow、typing.Sequence[flow.Flow]接受focus、all及任意 flow 过滤器表达式多选字符串types.Choice选项来自另一个返回Sequence[str]的命令补全时动态执行该命令获取元类型types.Cmd、types.Arg用于构造调用其他命令的命令最常见于键绑定场景内置 console 键绑定是丰富示例集数据类型types.CutSpec、types.Datacut 机制目前为 alpha 版提供从 flow 中剪取数据的便捷方式Data不能从字符串解析只能作为命令返回值路径types.Pathstr子类支持~展开与目录 Tab 补全这些类型在 mitmproxy/types.py 末尾统一注册到CommandTypes的TypeManager中CommandTypes TypeManager( _ArgType, _BoolType, _ChoiceType, _CmdType, _CutSpecType, _DataType, _FlowType, _FlowsType, _IntType, _MarkerType, _PathType, _StrType, _StrSeqType, _BytesType, )每个类型对象实现统一的三接口契约_BaseTypeparse(manager, typ, s)把命令行字符串解析为对应 Python 值无效时抛ValueErroris_valid(manager, typ, val)校验一个值是否合法用于该类型命令返回值也要过这道检查completion(manager, t, s)返回 Tab 补全候选项。运行时调用链从字符串参数到函数调用完整执行路径在 mitmproxy/command.py 的Command.call中def call(self, args: Sequence[str]) - Any: bound_args self.prepare_args(args) # 逐参数 parsearg bind ret self.func(*bound_args.args, **bound_args.kwargs) if ret is None and self.return_type is None: return typ mitmproxy.types.CommandTypes.get(self.return_type) if not typ.is_valid(self.manager, typ, ret): raise exceptions.CommandError( f{self.name} returned unexpected data - expected {typ.display} ) return retprepare_args先用inspect.signature.bind校验参数个数与位置再对每个字符串参数调用parsearg内部即对应类型的parse最后执行函数并校验返回值类型。参数个数不符会抛出Command argument mismatch并打印期望签名与实际收到的参数——这正是文档所说调用与返回值都在运行时被检查的具体含义。命令 快捷键把 addon 变成 console 操作文档指出types.Cmd与types.Arg元类型在键绑定中最常用参考内置 mitmproxy console 键绑定即可看到丰富的示例集。mitmproxy/tools/console/defaultkeys.py 就是这种用法的权威样本km.add( b, console.command cut.save focus response.content , [flowlist, flowview], Save response body to file, ) km.add( x, console.choose.cmd Export as... export.formats console.command export.file {choice} focus , [flowlist, flowview], Export this flow to file, ) km.add( z, console.command.confirm Delete all flows view.flows.remove all, [flowlist], Clear flow list, )可以看到三个典型模式命令 flow 选择器cut.save focus response.content——按键直接对焦点 flow 触发命令多行命令序列console.choose.cmd先弹出选项选项来自export.formats命令的返回值再用{choice}占位符把用户选择插入下一条命令export.file {choice} focus带确认的命令console.command.confirm 提示文本 命令 参数在执行破坏性操作如删除全部 flow前要求确认。结合本文前面myaddon.addheader的例子你可以把自己的命令用同样的方式注册进键映射从而获得与内置功能完全一致的操作体验。小结用command.command(addon.name)声明命令所有参数与返回类型都必须标注且必须属于CommandTypes注册表支持的类型参数标注为Sequence[flow.Flow]时用户可在命令行传入focus、all、marked、id,...或任意 flow 过滤器表达式~d、~u、~c等由内置view.flows.resolve统一解析见 mitmproxy/addons/view.pytypes.Path参数获得~展开与目录 Tab 补全Sequence[str]用逗号分隔types.Choice的候选项动态来自另一个命令解析parse、绑定signature.bind、返回值校验is_valid都在运行时强制执行错误以CommandError呈现最终形态是把命令绑定到键盘快捷键参照 defaultkeys.py 的内置键绑定即可构建高频操作。以上路径均可在当前仓库中直接查看文档 docs/src/content/addons/commands.md示例 examples/addons/commands-simple.py、examples/addons/commands-flows.py、examples/addons/commands-paths.py核心实现 mitmproxy/command.py 与 mitmproxy/types.py。【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价