# ============================================================================= # 企微IT智能服务台 — 火绒集成数据模型 # ============================================================================= # 说明:火绒API请求/响应的 Pydantic 数据模型 # 包含:终端信息、漏洞信息、病毒事件、任务下发等 # ============================================================================= from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field, model_validator # ========================================================================== # 通用响应模型 # ========================================================================== class HuorongApiResponse(BaseModel): """火绒API统一响应模型。 火绒所有API返回格式一致(官方API文档 v1): 成功时: { "errno": 0, "errmsg": "", "data": { ... } } 失败时: { "errno": 1, "errmsg": "Authentication failed" } 官方错误码定义: - errno=0: 成功 - errno=1: 认证失败 - errno=2: 参数错误 - errno=3: 服务器内部错误 - errno=4: API未授权 注意:火绒API始终使用 errno(不是 errcode)。 使用 model_validator 在验证前将 errno 归一化为 errcode, 保持内部代码统一使用 errcode 字段。 Attributes: errcode: 错误码,0表示成功(从 errno 归一化而来) errmsg: 错误描述(成功时为空字符串) data: 业务数据(成功时非None) """ @model_validator(mode='before') @classmethod def normalize_error_fields(cls, data: Any) -> Any: """将火绒API返回的 errno 字段归一化为 errcode。 火绒API在认证失败等错误场景下返回 errno 而非 errcode, 此验证器在 Pydantic 字段校验前将 errno 转换为 errcode, 统一后续处理逻辑。 Args: data: 原始输入数据(通常为dict) Returns: 归一化后的数据 """ if isinstance(data, dict) and 'errno' in data and 'errcode' not in data: data['errcode'] = data.pop('errno') return data errcode: int = Field(..., description="错误码,0=成功") errmsg: str = Field(default="ok", description="错误描述") data: Optional[Any] = Field(default=None, description="业务数据") # ========================================================================== # 终端基本信息 — /api/clnts/_list 返回 # ========================================================================== class TerminalBasicInfo(BaseModel): """终端基本信息(_list 接口返回的每条记录)。 字段名严格按照火绒API文档实际返回值定义。 注意:API返回的字段名与之前猜测不同,已根据官方文档修正。 Attributes: id: 内部数据库ID client_id: 终端唯一ID(40位十六进制字符串,用于所有任务下发) client_name: 客户端名称 computer_name: 计算机名 local_ip: 本地IP connect_ip: 连接IP(客户端连接控制中心使用的IP) mac: MAC地址 group_id: 分组ID os_version: 操作系统版本 version: 火绒客户端版本 definitions: 病毒库更新时间 is_online: 在线状态 last_connect_time: 最后连接时间(Unix时间戳) last_seen_time: 最后可见时间(Unix时间戳) first_appear_time: 首次出现时间(Unix时间戳) """ id: Optional[int] = Field(default=None, description="内部数据库ID") client_id: str = Field(..., description="终端唯一ID") client_name: str = Field(default="", description="客户端名称") computer_name: str = Field(default="", description="计算机名") local_ip: str = Field(default="", description="本地IP") connect_ip: str = Field(default="", description="连接IP") mac: str = Field(default="", description="MAC地址") group_id: Optional[Any] = Field(default=None, description="分组ID(int或str)") os_version: str = Field(default="", description="操作系统版本") version: str = Field(default="", description="火绒客户端版本") definitions: str = Field(default="", description="病毒库更新时间") is_online: bool = Field(default=False, description="在线状态") last_connect_time: Optional[int] = Field(default=None, description="最后连接时间") last_seen_time: Optional[int] = Field(default=None, description="最后可见时间") first_appear_time: Optional[int] = Field(default=None, description="首次出现时间") class TerminalListRequest(BaseModel): """终端列表查询请求。 Attributes: group_id: 分组ID(可选,不传则查全部分组) page: 页码(从1开始) per_page: 每页条数 """ group_id: Optional[str] = Field(default=None, description="分组ID") page: int = Field(default=1, ge=1, description="页码") per_page: int = Field(default=20, ge=1, le=100, description="每页条数") # ========================================================================== # 终端详细信息v2 — /api/clnts/_info2 返回 # ========================================================================== class HardwareInfo(BaseModel): """终端硬件信息。 Attributes: cpu: CPU信息 memory: 内存信息 disk: 磁盘信息 motherboard: 主板信息 network_card: 网卡信息 """ cpu: str = Field(default="", description="CPU信息") memory: str = Field(default="", description="内存信息") disk: str = Field(default="", description="磁盘信息") motherboard: str = Field(default="", description="主板信息") network_card: str = Field(default="", description="网卡信息") class SoftwareInfo(BaseModel): """已安装软件条目。 Attributes: name: 软件名称 version: 版本号 publisher: 发布者 """ name: str = Field(default="", description="软件名称") version: str = Field(default="", description="版本号") publisher: str = Field(default="", description="发布者") class AssetInfo(BaseModel): """资产信息。 Attributes: asset_tag: 资产标签 serial_number: 序列号 """ asset_tag: str = Field(default="", description="资产标签") serial_number: str = Field(default="", description="序列号") class NetworkConfig(BaseModel): """网络配置信息。 Attributes: ip: IP地址 gateway: 网关 dns: DNS服务器 adapter_info: 网卡适配器信息 """ ip: str = Field(default="", description="IP地址") gateway: str = Field(default="", description="网关") dns: str = Field(default="", description="DNS服务器") adapter_info: str = Field(default="", description="网卡适配器信息") class TerminalDetailV2(BaseModel): """终端详细信息v2(_info2 接口返回)。 通过 optional_fields 参数指定需要返回的信息块: - hardware: 硬件信息 - software: 已安装软件 - assets: 资产信息 - netconf: 网络配置 Attributes: client_id: 终端唯一ID computer_name: 计算机名 hardware: 硬件信息(可选) software: 已安装软件列表(可选) assets: 资产信息(可选) netconf: 网络配置(可选) """ client_id: str = Field(..., description="终端唯一ID") computer_name: str = Field(default="", description="计算机名") hardware: Optional[HardwareInfo] = Field(default=None, description="硬件信息") software: Optional[List[SoftwareInfo]] = Field(default=None, description="已安装软件") assets: Optional[AssetInfo] = Field(default=None, description="资产信息") netconf: Optional[NetworkConfig] = Field(default=None, description="网络配置") class TerminalDetailRequest(BaseModel): """终端详细信息查询请求。 Attributes: client_id: 终端唯一ID optional_fields: 需要返回的可选信息块列表 """ client_id: str = Field(..., description="终端唯一ID") optional_fields: List[str] = Field( default_factory=lambda: ["hardware", "software", "assets", "netconf"], description="可选信息块: hardware/software/assets/netconf", ) # ========================================================================== # 漏洞信息 — /api/clnts/_leak 返回 # 说明:_leak 接口返回的是"存在高危漏洞未修复的终端列表", # 每条记录是终端信息(非漏洞详情),API不返回具体漏洞CVE列表。 # 外层还有 all_client(终端总数)和 risk_client(高危终端数)统计。 # ========================================================================== class TerminalLeakInfo(BaseModel): """存在高危漏洞的终端信息(_leak 接口返回的每条记录)。 注意:_leak 返回的是终端维度数据,不是漏洞维度。 字段名严格按照火绒API文档实际返回值定义。 与 _list 接口的字段名不同! Attributes: cid: 终端唯一ID(_leak 中叫 cid,_list 中叫 client_id) hostname: 计算机名(_leak 中叫 hostname,_list 中叫 computer_name) client_name: 终端名称 group_name: 分组名称 group_id: 分组ID ip_addr: 本地IP(_leak 中叫 ip_addr,_list 中叫 local_ip) call_ip: 连接IP(_leak 中叫 call_ip,_list 中叫 connect_ip) mac: MAC地址 osver: 操作系统版本(_leak 中叫 osver,_list 中叫 os_version) os_type: 终端类型(如 Windows) prodver: 火绒客户端版本(_leak 中叫 prodver,_list 中叫 version) virdb: 病毒库版本(Unix时间戳,_leak 中叫 virdb,_list 中叫 definitions) stat: 在线状态码(1=离线, 2=在线, 3=异常,_list 中是 is_online 布尔值) """ cid: str = Field(..., description="终端唯一ID") hostname: str = Field(default="", description="计算机名") client_name: str = Field(default="", description="终端名称") group_name: str = Field(default="", description="分组名称") group_id: Optional[Any] = Field(default=None, description="分组ID") ip_addr: str = Field(default="", description="本地IP") call_ip: str = Field(default="", description="连接IP") mac: str = Field(default="", description="MAC地址") osver: str = Field(default="", description="操作系统版本") os_type: str = Field(default="", description="终端类型") prodver: str = Field(default="", description="火绒客户端版本") virdb: Optional[Any] = Field(default=None, description="病毒库版本(Unix时间戳)") stat: int = Field(default=1, description="在线状态码: 1=离线 2=在线 3=异常") # ========================================================================== # 病毒事件 — /api/clnts/_virus_events 返回 # 说明:_virus_events 返回终端维度的病毒日志统计, # 含总数(count)和4种处理结果(result)的明细。 # 请求需指定 type: 0=按client_id查, 1=按group_id查, 2=查全部 # ========================================================================== class VirusHandleResult(BaseModel): """病毒事件处理结果统计。 Attributes: success: 处理成功数 fail: 处理失败数 ignored: 暂不处理数 trusted: 已信任数 """ success: int = Field(default=0, description="处理成功数") fail: int = Field(default=0, description="处理失败数") ignored: int = Field(default=0, description="暂不处理数") trusted: int = Field(default=0, description="已信任数") class VirusEventStats(BaseModel): """终端病毒事件统计(_virus_events 接口返回的每条记录)。 字段名严格按照火绒API文档实际返回值定义。 与 _list 接口的字段名基本一致。 Attributes: group_id: 分组ID client_id: 终端唯一ID client_name: 终端名称 computer_name: 计算机名 local_ip: 本地IP connect_ip: 连接IP mac: MAC地址 count: 病毒日志总数 result: 处理结果统计(success/fail/ignored/trusted) """ group_id: Optional[Any] = Field(default=None, description="分组ID") client_id: str = Field(..., description="终端唯一ID") client_name: str = Field(default="", description="终端名称") computer_name: str = Field(default="", description="计算机名") local_ip: str = Field(default="", description="本地IP") connect_ip: str = Field(default="", description="连接IP") mac: str = Field(default="", description="MAC地址") count: int = Field(default=0, description="病毒日志总数") result: Optional[VirusHandleResult] = Field(default=None, description="处理结果统计") # ========================================================================== # 终端任务 — /api/task/_create # ========================================================================== class TaskCreateRequest(BaseModel): """终端任务创建请求。 支持的任务类型: - quick_scan: 快速扫描 - full_scan: 全盘扫描 - custom_scan: 自定义扫描 - netctrl: 终端隔离/解除 - message: 发送通知 Attributes: task_type: 任务类型 client_ids: 目标终端ID列表 net_isolation: 是否隔离(仅 netctrl 类型有效) message_content: 通知内容(仅 message 类型有效) """ task_type: str = Field(..., description="任务类型: quick_scan/full_scan/custom_scan/netctrl/message") client_ids: List[str] = Field(..., min_length=1, description="目标终端ID列表") net_isolation: Optional[bool] = Field(default=None, description="是否隔离(仅netctrl类型)") message_content: Optional[str] = Field(default=None, description="通知内容(仅message类型)") # ========================================================================== # 终端安全画像(聚合模型,供前端直接使用) # ========================================================================== class TerminalSecurityProfile(BaseModel): """终端安全画像(聚合模型)。 将终端基本信息+安全状态聚合成一个模型,供坐席端直接展示。 Attributes: client_id: 终端唯一ID computer_name: 计算机名 ip: 本地IP mac: MAC地址 os_version: 操作系统版本 is_online: 在线状态 group_name: 分组名称 hardware: 硬件概要 high_risk_leaks: 高危漏洞数 uncleaned_virus: 未处理病毒事件数 security_score: 安全评分(0-100,综合漏洞+病毒+在线状态) """ client_id: str = Field(..., description="终端唯一ID") computer_name: str = Field(default="", description="计算机名") ip: str = Field(default="", description="本地IP") mac: str = Field(default="", description="MAC地址") os_version: str = Field(default="", description="操作系统版本") is_online: bool = Field(default=False, description="在线状态") group_name: str = Field(default="", description="分组名称") hardware: Optional[HardwareInfo] = Field(default=None, description="硬件概要") high_risk_leaks: int = Field(default=0, description="高危漏洞数") uncleaned_virus: int = Field(default=0, description="未处理病毒事件数") security_score: int = Field(default=100, description="安全评分(0-100)")