diff --git a/docs/signal_audit_full.md b/docs/signal_audit_full.md new file mode 100644 index 00000000..baecbc53 --- /dev/null +++ b/docs/signal_audit_full.md @@ -0,0 +1,146 @@ +# 全面代码与安全审计报告 + +## 1. 摘要 (Executive Summary) +- 审计输入来源:本地代码(pg_engine.py, pg_formatter.py, history.py, ui.py, engine_v2.py, signals/__init__.py, bot/app.py);未提供变更说明/配置/IaC。 +- 规范审查结论:⚠️ 无法裁决方向合规性(方向变量未提供)。 +- 总体安全风险:中(SQL 注入高危面 + 推送并发模型问题)。 +- Risk Scorecard:Critical 0 / High 1 / Medium 2 / Low 1。 +- 关键发现 Top 5: + 1) PG 查询符号拼接导致 SQL 注入面。 + 2) PG 推送跨线程创建事件循环,易 DoS/丢消息。 + 3) 历史库默认写入仓库目录且无权限/清理控制。 + 4) 历史保存异常分支未关闭连接,潜在锁泄漏。 + 5) 其余 8 个审计域未见阻断级风险,但缺少方向变量无法做规范裁决。 + +## 2. 审计范围、假设与方向变量 +### 2.1 审计范围与假设 +- 范围:`services/telegram-service/src/signals/pg_engine.py`, `pg_formatter.py`, `history.py`, `ui.py`, `engine_v2.py`, `signals/__init__.py`, `services/telegram-service/src/bot/app.py`。 +- 假设:部署未披露;TimescaleDB 用户权限未知;SYMBOLS_GROUPS 可能来自配置/环境可被外部控制;同进程多线程运行。 +- 不确定性:无 DB 角色、文件权限、Bot 限速数据 → 结论偏保守。 + +### 2.2 方向变量与对齐总览 +- 未提供方向变量 → 全部“无法判定”,仅给出风险陈述。 + +## 3. 变更解析与威胁建模 +- 结构化变更:新增 PG 格式化/历史模块;双引擎统一保存历史;Bot 同时启动 PG+SQLite;新增历史查询 UI 回调。 +- 攻击面:新增 TimescaleDB 读取、历史 SQLite 文件、回调 `sig_hist_*` 与 `signal_history`。 +- 权限/数据流:SYMBOLS_GROUPS → PG 查询 → history.save → signal_history.db → Bot 推送。 + +## 4. 详细审计发现 (按 12 大类) + +### 1) 控制流 (Control Flow) +- 无阻断级发现。 + +### 2) 执行模型 (Execution Model) +**发现 A** +- 规范严重级别:S2;安全严重级别:Medium;置信度:中。 +- 位置/证据:`bot/app.py:6788-6816` 每条 PG 信号回调创建新事件循环并在子线程调用 `application.bot.send_message`。 +- 反模式:Cross-thread event loop creation;隐式并发。 +- 规范对齐:未提供方向变量;多事件循环导致行为分裂(不同线程/loop 调度)。 +- 安全影响:高频信号可耗尽线程/loop 造成推送漏发或阻塞,形成 DoS 面。 +- 攻击路径:伪造/诱发高频 PG 信号 → 大量 loop 创建 → Bot 被限流/崩溃。 +- 修复:复用主事件循环,用 `asyncio.run_coroutine_threadsafe` 投递;添加速率限制与队列;记录推送失败指标。 +- 验证:压测 1000 信号/分钟,确认 loop 数不增长、推送完整率 100%;单元测试覆盖线程投递。 +- 标准映射:CWE-667, CAPEC-551。 +- 追问:生产 Bot 是否暴露主 loop 句柄?Telegram 速率限制参数? + +### 3) 状态 (State) +- 未新增共享可变状态竞争;cooldown dict 线程内使用,风险低。 + +### 4) 时间与顺序 (Time & Ordering) +- 未发现 TOCTOU;发现 A 已涵盖时序阻塞风险。 + +### 5) 错误与失败 (Error & Failure) +**发现 B** +- 规范严重级别:S1;安全严重级别:Low;置信度:中。 +- 位置/证据:`signals/history.py:60-111` 异常时直接 `return -1`,未关闭连接。 +- 反模式:资源未清理;隐式失败路径。 +- 安全影响:在高频写入/异常下锁泄漏导致 “database is locked”,信号记录缺失。 +- 修复:使用 context manager / try-finally 关闭连接;可加重试与错误计数。 +- 验证:故障注入(磁盘满/权限错误)并发写入,确认无锁残留。 +- 标准映射:CWE-404。 + +### 6) 输入与前置条件 (Input & Preconditions) +**发现 C** +- 规范严重级别:S3;安全严重级别:High;置信度:高。 +- 位置/证据:`pg_engine.py:404-418`, `448-462` 将 `self.symbols` 直接拼接到 SQL IN 子句。 +- 反模式:SQL 拼接;配置驱动多世界。 +- 规范对齐:未提供方向变量;此实现显著增加语义分裂风险。 +- 安全影响:若 SYMBOLS_GROUPS 可被外部控制,可执行任意 SQL(DROP/SELECT 任意表)。 +- 攻击路径:控制 env 配置 → 注入 `BTCUSDT');DROP TABLE ...--` → TimescaleDB 被破坏/泄露。 +- 修复:参数化 IN(psycopg2.sql 或 `symbol = ANY(%s)`);符号白名单校验 `^[A-Z0-9_]{2,}$`;限制列表长度。 +- 验证:单测注入字符串应被拒绝;集成测试校验查询结果与 SQL 日志。 +- 标准映射:CWE-89 / OWASP A03:2021。 +- 追问:SYMBOLS_GROUPS 是否来自用户输入?TimescaleDB 账户是否只读? + +### 7) 数据建模 (Data Model) +- 单表结构清晰;未见 IDOR/Mass Assignment。 + +### 8) 依赖与耦合 (Dependency & Coupling) +- 依赖未新增;需确认 requirements 中 psycopg2 版本锁定(未审)。 + +### 9) 配置与变体 (Configuration & Variability) +**发现 D** +- 规范严重级别:S2;安全严重级别:Medium;置信度:中。 +- 位置/证据:`history.py:14-46` 导入即创建 `signal_history.db` 于仓库路径,未设权限/配置。 +- 反模式:启动即侧写、不可配置、缺省权限。 +- 安全影响:文件可能 0644 暴露用户信号;无限增长耗尽磁盘导致拒绝服务。 +- 修复:路径配置化(如 `SIGNAL_HISTORY_DB_PATH`);创建后 chmod 600;启动定期 cleanup,设置最大保留天数/大小。 +- 验证:启动后检查权限;压测写入并触发 cleanup。 +- 标准映射:CWE-732, CWE-400。 + +### 10) 结构与架构 (Structure & Architecture) +- 双引擎并行 + 统一历史库边界清晰;未见信任边界跨越。 + +### 11) 性能与资源 (Performance & Resources) +- 未见 ReDoS;发现 D 已覆盖磁盘膨胀风险。 + +### 12) 可观测性与运维 (Observability & Ops) +- PG/历史写入缺少指标与告警;建议暴露 Prometheus 指标并在 Bot 日志中计数失败/限流事件。 + +## 5. 方向变量冲突与不可满足性 +- 方向变量缺失,无法判定冲突。 + +## 6. 不可判定点与最坏情况推断 +- 未提供 DB 权限:最坏假设 DB 账号有写/DDL → 注入可致毁表。 +- 未提供 Bot 限速:最坏假设无速率限制 → 高频信号可致推送失败。 +- 未提供文件权限策略:最坏假设 umask 022 → signal_history.db 可被同机他人读取。 + +## 7. 回归风险与安全测试计划 +- 回归点:PG 查询链路、推送线程模型、历史写入。 +- 必测用例: + 1) SQL 注入拒绝:符号含引号/分号应被拒绝。 + 2) 高频信号 1000/min:推送完整率保持 100%,事件循环数量不增长。 + 3) 历史写入故障:磁盘满/权限拒绝不阻断推送,错误计数可观测。 + 4) 历史清理:40 天数据后 cleanup 删除过期记录。 +- 自动化:CI 增 pytest 覆盖上述用例;接入 SAST(SQL 注入规则);运行时暴露 metrics(signal_save_errors, pg_query_errors)。 + +## 8. 依赖、配置与基础设施审计 +- 依赖:确认 `psycopg2(-binary)` 版本锁定;无新增第三方依赖。 +- 配置:新增 `SIGNAL_HISTORY_DB_PATH`(建议);确保 `DATABASE_URL` 仅可由可信配置写入。 +- IaC/CI:未提供,建议验证镜像/容器不打包历史 DB,CI 中增加依赖漏洞扫描。 + +## 9. 需要补充的信息清单 +- P0:SYMBOLS_GROUPS 来源与可控性;TimescaleDB 账号权限(只读/DDL)。 +- P1:Bot 主事件循环获取方式;Telegram 速率限制配置。 +- P2:运行节点 umask/文件权限;历史数据保留策略/容量上限。 + +## 10. 行动项 (Action Items) +ID | 发现标题 | 负责人 | 优先级 | 类型 | 工作量 +-|-|-|-|-|- +A | PG 查询参数化防注入 | 后端 | 高 | 安全修复 | M +B | 推送复用主 loop + 限流 | 后端 | 中 | 安全修复 | M +C | 历史库路径/权限/清理 | 运维/后端 | 中 | 规范收敛 | M +D | save 异常关连接+重试 | 后端 | 低 | 安全修复 | S +E | CI 加 SAST/指标监控 | 平台/后端 | 中 | 规范收敛 | M + +- 立即行动(24h):A +- 短期(1–2 周):B, C, D +- 中长期(1–2 月):E + +## 11. 总结与长期建议 +- 反模式:SQL 拼接;跨线程事件循环;缺省文件权限/清理。 +- 长期改进: + - 统一 DB 访问层强制参数化与符号白名单。 + - 信号链路增加速率限制、队列、可观测指标。 + - 持久化路径全部配置化并启动权限/容量校验;CI 集成 SAST & 依赖扫描。 diff --git a/docs/signal_audit_pg.md b/docs/signal_audit_pg.md new file mode 100644 index 00000000..67196222 --- /dev/null +++ b/docs/signal_audit_pg.md @@ -0,0 +1,52 @@ +# 规范与安全双重审计 - PG/SQLite 信号集成 + +## 0. 范围 +- 代码:signals/pg_engine.py, pg_formatter.py, history.py, ui.py, engine_v2.py, signals/__init__.py, bot/app.py。 +- 配置/IaC/部署:未提供,按最坏情况推断。 +- 方向变量:未提供,规范合规性仅做客观风险陈述。 + +## 1. 结论 +- 规范合规:⚠️ 无法裁决(缺少方向变量)。 +- 安全风险:中。 +- Risk Scorecard:Critical 0 / High 1 / Medium 2 / Low 1。 +- 需立即关注:PG 查询注入面;PG 推送跨线程事件循环。 + +## 2. 关键发现 +1) **SQL 注入面**(High,S3) + - 位置:pg_engine.py 查询字符串直接拼接 symbols。 + - 风险:SYMBOLS_GROUPS 可控时可执行任意 SQL;影响 TimescaleDB。 + - 修复:参数化 IN(psycopg2.sql 或 =ANY(%s)),并对白名单正则校验符号。 + +2) **推送并发模型不安全**(Medium,S2) + - 位置:bot/app.py on_pg_signal 为每条信号创建新事件循环并跨线程 send_message。 + - 风险:高频信号可耗尽线程/loop,导致推送丢失或 DoS。 + - 修复:复用主事件循环,使用 asyncio.run_coroutine_threadsafe;加速率限制。 + +3) **历史库默认写入+无权限/清理**(Medium,S2) + - 位置:history.py 导入即创建 signal_history.db,未设权限、路径不可配置。 + - 风险:文件 0644 泄露用户信号;无限增长占满磁盘。 + - 修复:创建时 chmod 600,路径配置化(如 SIGNAL_HISTORY_DB_PATH),启动定期 cleanup。 + +4) **异常分支未关连接**(Low,S1) + - 位置:history.py save 异常直接返回 -1,连接未显式关闭。 + - 风险:潜在锁/句柄泄漏,导致写入失败。 + - 修复:使用 context manager / finally 关闭;可加重试。 + +## 3. 建议行动 +- 24h:修复 SQL 参数化 + 符号白名单(Action A)。 +- 1–2 周: + - PG 推送改主 loop 投递并加速率限制(Action B)。 + - 历史库路径配置化、默认 600、启动清理任务(Action C)。 + - save 异常关闭连接并重试(Action D)。 +- 1–2 月:在 CI 加 SAST/限流/指标监控,覆盖信号链路(Action E)。 + +## 4. 必测用例 +- SQL 注入拒绝:符号含引号应被拒绝/转义。 +- 高频信号 1000/min:推送完整率与线程/loop 数保持常量。 +- 历史写入故障:磁盘满/权限拒绝不应阻断推送。 +- 历史清理:插入 40 天数据后 cleanup 删除过期记录。 + +## 5. 待补充信息 +- P0:生产 SYMBOLS_GROUPS 来源及可控性;TimescaleDB 账号权限。 +- P1:Bot 主事件循环获取方式、Telegram 速率限制配置。 +- P2:运行节点 umask/文件权限策略;历史数据保留/容量上限。 diff --git a/messages.mo b/messages.mo index 34dccf86..723fecfe 100644 Binary files a/messages.mo and b/messages.mo differ diff --git a/services-preview/vis-service/src/templates/registry.py b/services-preview/vis-service/src/templates/registry.py index aed91e36..769346a2 100644 --- a/services-preview/vis-service/src/templates/registry.py +++ b/services-preview/vis-service/src/templates/registry.py @@ -443,188 +443,6 @@ def render_market_vpvr_heat(params: Dict, output: str) -> Tuple[object, str]: fig.tight_layout() return _fig_to_png(fig), "image/png" - -def render_vpvr_zone_dot(params: Dict, output: str) -> Tuple[object, str]: - """ - VPVR 价值区点阵图。 - - 使用聚合后的价值区上下沿与控制点,绘制横向带(VAH–VAL)+ 控制点标记。 - - 必填: - - data: [{symbol, value_area_low, value_area_high, poc}] - - 可选: - - coverage: 覆盖率/占比,用于颜色深浅 - - title: 图表标题 - """ - - data = params.get("data") - if not data or not isinstance(data, list): - raise ValueError("缺少 data 列表") - - records = [] - for item in data: - try: - symbol = item["symbol"] - val_low = float(item.get("value_area_low")) - val_high = float(item.get("value_area_high")) - poc = float(item.get("poc")) - except Exception: - continue - coverage = item.get("coverage") - try: - coverage = float(coverage) if coverage is not None else None - except Exception: - coverage = None - if val_high <= val_low: - continue - records.append((symbol, val_low, val_high, poc, coverage)) - - if not records: - raise ValueError("无有效 VPVR 数据") - - df = pd.DataFrame(records, columns=["symbol", "val_low", "val_high", "poc", "coverage"]) - df.sort_values("poc", inplace=True) - y_positions = range(len(df)) - - if output == "json": - return ( - { - "title": params.get("title", "VPVR Value Area"), - "points": [ - { - "symbol": row.symbol, - "value_area_low": row.val_low, - "value_area_high": row.val_high, - "poc": row.poc, - "coverage": row.coverage, - "y": idx, - } - for idx, row in df.reset_index(drop=True).itertuples() - ], - }, - "application/json", - ) - - sns.set_theme(style="white") - fig, ax = plt.subplots(figsize=(12, max(4, len(df) * 0.35))) - cmap = sns.color_palette("viridis", as_cmap=True) - - for y, row in zip(y_positions, df.itertuples()): - width = row.val_high - row.val_low - ax.broken_barh([(row.val_low, width)], (y - 0.25, 0.5), facecolors="lightblue", alpha=0.6) - color = cmap(row.coverage) if row.coverage is not None else "#1d4ed8" - ax.scatter(row.poc, y, color=color, s=30, zorder=3) - ax.text(row.val_high, y, f" POC {row.poc:.4g}", va="center", fontsize=8, color="#111") - - ax.set_xlabel("Price") - ax.set_yticks(list(y_positions)) - ax.set_yticklabels(df["symbol"]) - ax.set_title(params.get("title", "VPVR Value Area & POC")) - ax.grid(True, axis="x", linestyle="--", alpha=0.3) - fig.tight_layout() - return _fig_to_png(fig), "image/png" - - -def render_vpvr_zone_grid(params: Dict, output: str) -> Tuple[object, str]: - """ - VPVR 价值区小卡片网格(横向多卡片)。 - - 每个币种一张卡:背景矩形 = 价值区 (VAL→VAH),点 = POC,卡片标题标注币种与覆盖率。 - - 必填: - - data: [{symbol, value_area_low, value_area_high, poc, coverage?}] - - 可选: - - cols: 每行卡片数,默认 3 - - title: 总标题 - """ - - data = params.get("data") - if not data or not isinstance(data, list): - raise ValueError("缺少 data 列表") - - cards = [] - for item in data: - try: - symbol = item["symbol"] - val_low = float(item.get("value_area_low")) - val_high = float(item.get("value_area_high")) - poc = float(item.get("poc")) - except Exception: - continue - if val_high <= val_low: - continue - cov = item.get("coverage") - try: - cov = float(cov) if cov is not None else None - except Exception: - cov = None - cards.append((symbol, val_low, val_high, poc, cov)) - - if not cards: - raise ValueError("无有效 VPVR 数据") - - cols = max(1, int(params.get("cols", 3))) - rows = math.ceil(len(cards) / cols) - width = 4 * cols - height = max(2.8 * rows, 3) - - sns.set_theme(style="white") - fig, axes = plt.subplots(rows, cols, figsize=(width, height), squeeze=False) - cmap = sns.color_palette("viridis", as_cmap=True) - - for idx, (symbol, val_low, val_high, poc, cov) in enumerate(cards): - r, c = divmod(idx, cols) - ax = axes[r][c] - band_width = val_high - val_low - ax.broken_barh([(val_low, band_width)], (0.2, 0.6), facecolors="#bfdbfe", alpha=0.7) - color = cmap(cov) if cov is not None else "#1d4ed8" - ax.scatter(poc, 0.5, color=color, s=30, zorder=3) - ax.text( - poc, - 0.65, - f"POC {poc:.4g}", - ha="center", - va="bottom", - fontsize=8, - color="#0f172a", - ) - subtitle = f"{symbol}" + (f" cov {cov:.0%}" if cov is not None else "") - ax.set_title(subtitle, fontsize=10, pad=6) - ax.set_yticks([]) - ax.set_ylim(0, 1) - ax.grid(True, axis="x", linestyle="--", alpha=0.3) - # 隐藏空白轴 - for idx in range(len(cards), rows * cols): - r, c = divmod(idx, cols) - axes[r][c].axis("off") - - fig.suptitle(params.get("title", "VPVR 价值区卡片"), fontsize=12) - fig.tight_layout(rect=[0, 0, 1, 0.95]) - - if output == "json": - return ( - { - "title": params.get("title", "VPVR 价值区卡片"), - "cards": [ - { - "symbol": symbol, - "value_area_low": val_low, - "value_area_high": val_high, - "poc": poc, - "coverage": cov, - } - for (symbol, val_low, val_high, poc, cov) in cards - ], - "cols": cols, - }, - "application/json", - ) - - return _fig_to_png(fig), "image/png" - - def render_vpvr_zone_strip(params: Dict, output: str) -> Tuple[object, str]: """ VPVR 价值区分布图。 @@ -1736,56 +1554,6 @@ def register_defaults() -> TemplateRegistry: ), render_market_vpvr_heat, ) - registry.register( - TemplateMeta( - template_id="vpvr-zone-dot", - name="VPVR 价值区点阵", - description="使用价值区上下沿 + 控制点绘制横向带和点阵,适合全市场对比", - outputs=["png", "json"], - params=[ - "data(list[{symbol, value_area_low, value_area_high, poc, coverage?}])", - "title?", - ], - sample={ - "template_id": "vpvr-zone-dot", - "output": "png", - "params": { - "data": [ - {"symbol": "BTCUSDT", "value_area_low": 100, "value_area_high": 105, "poc": 102.5, "coverage": 0.68}, - {"symbol": "ETHUSDT", "value_area_low": 50, "value_area_high": 52, "poc": 51.2, "coverage": 0.63}, - ] - }, - }, - ), - render_vpvr_zone_dot, - ) - registry.register( - TemplateMeta( - template_id="vpvr-zone-grid", - name="VPVR 价值区卡片", - description="横向多卡片:每个币种一条价值区带 + POC 点,方便快速扫描", - outputs=["png", "json"], - params=[ - "data(list[{symbol, value_area_low, value_area_high, poc, coverage?}])", - "cols?(int, default 3)", - "title?", - ], - sample={ - "template_id": "vpvr-zone-grid", - "output": "png", - "params": { - "cols": 3, - "data": [ - {"symbol": "BTCUSDT", "value_area_low": 100, "value_area_high": 105, "poc": 102.5, "coverage": 0.68}, - {"symbol": "ETHUSDT", "value_area_low": 50, "value_area_high": 52, "poc": 51.2, "coverage": 0.63}, - {"symbol": "SOLUSDT", "value_area_low": 8.3, "value_area_high": 9.1, "poc": 8.7, "coverage": 0.7}, - {"symbol": "XRPUSDT", "value_area_low": 0.5, "value_area_high": 0.56, "poc": 0.53, "coverage": 0.6}, - ], - }, - }, - ), - render_vpvr_zone_grid, - ) registry.register( TemplateMeta( template_id="vpvr-zone-strip", diff --git a/services/telegram-service/locales/en/LC_MESSAGES/bot.mo b/services/telegram-service/locales/en/LC_MESSAGES/bot.mo index 34dccf86..723fecfe 100644 Binary files a/services/telegram-service/locales/en/LC_MESSAGES/bot.mo and b/services/telegram-service/locales/en/LC_MESSAGES/bot.mo differ diff --git a/services/telegram-service/locales/zh_CN/LC_MESSAGES/bot.mo b/services/telegram-service/locales/zh_CN/LC_MESSAGES/bot.mo index 7ec9ba71..258f1cca 100644 Binary files a/services/telegram-service/locales/zh_CN/LC_MESSAGES/bot.mo and b/services/telegram-service/locales/zh_CN/LC_MESSAGES/bot.mo differ diff --git a/spec_security_dual_audit_template.md b/spec_security_dual_audit_template.md new file mode 100644 index 00000000..5fa964b6 --- /dev/null +++ b/spec_security_dual_audit_template.md @@ -0,0 +1,402 @@ +# 规范与安全双重审计提示词模板 + +## 1. 核心角色与心智模型 (Core Role & Mental Model) + +你将扮演“首席规范与安全架构师 (Principal Spec-Alignment & Security Architect)”。 + +你的心智模型必须融合: +* **一名资深 Staff Engineer 的视角**:关注代码的长期结构、可维护性、确定性,以及“多世界”问题的根源。 +* **一名首席产品安全工程师的视角**:默认悲观,以攻击者思维審视每一处变更,深挖潜在的利用链条。 + +你的双重职责是: +1. **规范对齐审查 (Spec-Alignment Audit)**:对输入代码进行覆盖全部 12 个软件工程大类的“多世界”分析,并将每一大类的发现逐条对齐到用户给定的“规范方向变量”做裁决。 +2. **深度安全审计 (In-depth Security Audit)**:基于代码本身及可选的“变更说明”,识别代码中引入、修复或回归的安全风险,并生成一份**可直接用于工程排期和风险决策**的《全面代码与安全审计报告》。 + +--- + +## 2. 核心概念、常量与工作原则 (Core Concepts, Constants & Principles) + +### 2.1 规范对齐部分 (常量) +* **12 大类审查模块**:你的分析必须完整覆盖这 12 个模块,缺一不可。 +* **统一严重级别 (S0-S3)**:用于衡量“多世界/不唯一性”的结构性风险。 +* **统一反模式标签词典**:用于归因代码结构问题。 +* **方向变量对齐工作方式**:所有规范层面的发现都必须对齐到用户提供的方向变量。 + +### 2.2 安全审计部分 (常量) +* **以“变更影响面”为核心**:分析必须关注变更点(如果有提供)带来的攻击面变化。 +* **覆盖核心安全领域**:鉴权、注入、SSRF、供应链、业务逻辑漏洞等。 +* **结构化发现 (Finding) 格式**:每个发现都必须包含标题、双重严重级别、置信度、攻击路径、修复建议等。 +* **统一输出报告格式**:最终产出必须严格遵循指定的报告结构,不得增删章节。 + +### 2.3 核心工作原则 (必须严格遵守) +* **默认悲观原则 (Pessimistic by Default)**:对于任何不确定点(如外部依赖行为、框架内部机制),必须做最坏情况推断。 +* **零信任,需验证 (Zero Trust, Must Verify)**:不轻信注释或方法命名,一切以代码的实际执行逻辑为准。 +* **上下文至上 (Context is King)**:紧密结合用户提供的业务场景、技术栈和变更说明来判断风险的实际影响。 +* **结论驱动证据 (Conclusion Driven by Evidence)**:任何结论都必须有明确的代码片段、逻辑路径或配置项作为支撑。 + +--- + +## 3. 输入 (User Will Provide) + +### 3.1 必须输入 +* **要审计的代码**:完整的代码片段、文件、目录结构或 Git 仓库链接。 +* **期望的代码规范方向 (方向变量)**:一组目标/约束/性质,例如“唯一生产逻辑、唯一执行语义、唯一失败路径、唯一解、幂等性”等。(可能为空、可能多条、可能冲突) + +### 3.2 可选输入 (缺失也必须继续输出报告,并明确指出缺失带来的不确定性) +* **AI 总结的输出修改说明 / PR Diff / Commit Message**:用于聚焦变更引入的风险。 +* **技术栈与版本**:语言 / 框架 / 关键依赖库及版本号。 +* **基础设施与部署**:部署环境(本地/容器/K8s/云服务)、运行权限、网络策略、Terraform/IaC 文件、CI/CD pipeline 定义。 +* **业务上下文**:业务场景、关键资产、数据流图、接口文档。 +* **现有安全基线**:鉴权方式、网关策略、WAF、SAST/DAST、密钥管理/KMS、审计日志策略。 + +### 3.3 交互式输入 +* 在分析过程中,若关键信息缺失导致无法做出高置信度判断,你可以在报告的“追问点”部分明确提出需要用户补充的具体信息。 + +--- + +## 4. 工作流程 (Execution Workflow) + +### A) 抽取、确认与初始化 (Extraction, Confirmation & Initialization) +1. **抽取**: 原样列出用户提供的所有输入,特别是“方向变量”和“修改说明”。 +2. **确认理解**: 用一句话总结你的任务目标,确认你已正确理解用户的意图。 +3. **初始化**: 若方向变量为空,则标注“方向变量为空,将输出客观多世界风险与安全风险,但无法判定是否违反用户目标”。若修改说明为空,则进行全面审计而非变更审计。 + +### B) 威胁建模与多世界展开 (Threat Modeling & Multi-World Expansion) +1. **威胁建模 (STRIDE 心智模型)**:基于代码和变更点,系统性地分析新增或扩大的攻击面、变化的权限模型和数据流。识别出所有可能的威胁入口和受影响资产。 +2. **多世界展开**: 识别所有可能导致“可观察行为分裂”的点,包括但不限于:显式分支(if/else)、隐式分支(多态)、运行时分支(配置/Feature Toggle)、并发分支(async/await)、环境分支 (dev/staging/prod)、依赖行为分支 (SDK 内部重试)。 + +### C) 12 大类逐项双重审查 (Dual Audit Across 12 Domains) +对每一大类,你必须同时进行“规范对齐”和“安全风险”两个维度的检查。 + +* **结论**: 通过 / 失败 / 无法对齐方向 +* **发现列表 (Findings)**: 或“无发现”。 + +**每条发现必须是规范和安全的结合体,强制包含以下所有字段:** +1. **标题与元数据**: + * **发现标题**: 清晰描述问题。 + * **规范严重级别**: S0 / S1 / S2 / S3。 + * **安全严重级别**: Critical / High / Medium / Low / Info。 + * **置信度**: 高 / 中 / 低 (必须说明原因)。 +2. **定位与归因**: + * **位置/范围**: 文件路径:行号 / 函数 / 类 / 语句形态。 + * **证据代码片段**: 引用 1-3 行最关键的源码。 + * **规范反模式标签**: 来自词典。 + * **关联变更点**: 引用修改说明原句。 +3. **规范对齐分析**: + * **对齐到方向变量**: 影响了哪些用户目标。 + * **偏离解释**: 它如何导致多世界/不唯一/语义漂移等。 +4. **安全风险分析**: + * **影响范围**: 资产 / 接口 / 模块 / 用户群。 + * **攻击路径 (Attack Scenario)**: 分步骤描述一个可行的攻击或利用思路 (PoC 思路)。 + * **风险评估**: Impact + Likelihood + 综合等级。 +5. **修复与验证**: + * **最小收敛建议 (规范层)**: 只允许“收敛到用户方向变量”的动作 (删除/固定/单一化)。 + * **安全修复建议 (安全层)**: 包含代码层、配置层、架构层的具体建议。 + * **验证方案 (Verification Plan)**: 提供具体的安全测试用例、单元测试或集成测试思路来验证修复的有效性。 +6. **标准映射与追问**: + * **标准映射**: CWE / OWASP Top 10 (2021) / CAPEC (如可匹配)。 + * **追问点 (Follow-up Questions)**: 如果需要更多信息来确认此发现,在此处明确提问。 + +### D) 汇总裁决与风险排序 (Verdict Formulation & Risk Prioritization) +* **规范层面**: 若方向变量非空且存在 S0/S1 发现,则规范审查“❌ 不合格”。若为空,则“⚠️ 无法裁决方向合规性”。 +* **安全层面**: 根据发现的最高安全严重级别,给出总体风险判断(高/中/低)。 +* **风险矩阵**: 内部构建一个规范严重性 vs 安全严重性的矩阵,用于最终行动项的优先级排序。 + +--- + +## 5. 12 大类审查模块 (必须全部覆盖,双重检查) + +#### 1) 控制流 (Control Flow) +* **规范**: 路径分裂、串行兜底。 **安全**: 业务逻辑绕过、状态机不一致、不安全的重定向。 + +#### 2) 执行模型 (Execution Model) +* **规范**: 并发模型不一致、隐式并发。 **安全**: 竞态条件、资源竞争型 DoS。 + +#### 3) 状态 (State) +* **规范**: 隐式状态、共享可变状态。 **安全**: 会话固定/劫持、权限缓存污染、信息泄露。 + +#### 4) 时间与顺序 (Time & Ordering) +* **规范**: 时间/顺序敏感。 **安全**: TOCTOU 漏洞、加密随机性不足、重放攻击。 + +#### 5) 错误与失败 (Error & Failure) +* **规范**: 软失败、错误语义合并。 **安全**: 敏感信息泄露(堆栈、配置)、不安全的异常处理、失败时打开过多权限。 + +#### 6) 输入与前置条件 (Input & Preconditions) +* **规范**: 容错解析导致多世界。 **安全**: 各类注入、XSS、CSRF、SSRF、反序列化、路径遍历。 + +#### 7) 数据建模 (Data Model) +* **规范**: 数据多义、Optional 回退。 **安全**: Mass Assignment、不安全的对象引用 (IDOR)、敏感数据模型未加密/脱敏。 + +#### 8) 依赖与耦合 (Dependency & Coupling) +* **规范**: SDK 隐式容错。 **安全**: 供应链攻击 (已知 CVE)、依赖混淆、日志库引入的格式化字符串漏洞。 + +#### 9) 配置与变体 (Configuration & Variability) +* **规范**: 配置驱动分支。 **安全**: 调试端点暴露、硬编码密钥、不安全的默认配置、权限配置过宽。 + +#### 10) 结构与架构 (Structure & Architecture) +* **规范**: 动态派发、插件化。 **安全**: 信任边界不清、认证授权逻辑分散、不安全的 IPC。 + +#### 11) 性能与资源 (Performance & Resources) +* **规范**: 资源条件改变语义。 **安全**: ReDoS、非对称资源消耗型 DoS、限流逻辑绕过。 + +#### 12) 可观测性与运维 (Observability & Ops) +* **规范**: 运行模式分裂。 **安全**: 日志注入、敏感信息泄露到日志、监控端点未鉴权、副作用的观测 hook。 + +--- + +## 6. 输出格式 (强制,必须严格按此结构输出) + +# 全面代码与安全审计报告 + +## 1. 摘要 (Executive Summary) +* **审计输入来源**: 代码 / (是否包含)变更说明 / (是否包含)补充信息。 +* **规范审查结论**: ✅ 合格 / ❌ 不合格 / ⚠️ 无法裁决方向合规性 (附理由)。 +* **总体安全风险**: {高/中/低} (附理由)。 +* **风险计分卡 (Risk Scorecard)**: (表格形式展示 Critical/High/Medium/Low 风险数量)。 +* **关键发现 Top 5 (规范+安全)**: (要点列出)。 +* **需要立即确认/回滚的点**: (如有)。 + +## 2. 审计范围、假设与方向变量 +### 2.1 审计范围与假设 +* 从代码和修改说明中提取的模块/接口/组件清单。 +* 因缺少 {源码/PR diff/配置/IaC} 导致的不确定点及其对风险判断的影响。 +### 2.2 方向变量与对齐总览 +* 逐条列出用户方向变量。 +* 对每条给出:通过 / 失败 / 无法判定 + 最关键证据(1 句)。 + +## 3. 变更解析与威胁建模 (若提供了修改说明) +* 结构化变更点表格 (ID, 模块, 类型, 安全相关性)。 +* 分析新增/扩大的攻击面、权限模型变化、数据流变化。 + +## 4. 详细审计发现 (Findings) +* **严格按照 12 大类审查模块组织**。 +* 在每个大类下,**按安全严重级别从高到低**罗列所有“发现”。 +* 每条发现必须使用 **第 4 部分 C) 小节** 定义的完整格式。 + +## 5. 方向变量冲突与不可满足性分析 +* 检查用户方向变量之间是否逻辑冲突,并指出代码中触发冲突的点。 + +## 6. 不可判定点与最坏情况推断 +* 列出依赖/运行时/框架导致的不确定点,并给出最坏情况下的规范和安全风险推断。 + +## 7. 回归风险与安全测试计划 +* 回归风险清单 (按模块/接口)。 +* 必测用例 (覆盖鉴权、注入、逻辑漏洞等)。 +* 自动化与监控建议 (SAST/DAST/IAST/RASP)。 + +## 8. 依赖、配置与基础设施审计 (Supply Chain, Config & IaC) +* 新增/升级依赖风险 (SBOM 视角)。 +* 密钥、配置、HTTP 安全头、CORS、限流策略检查。 +* **基础设施即代码 (IaC) 安全检查**: Terraform/CloudFormation 中的过宽权限、公开暴露资源 (S3/OSS)、网络安全组规则。 +* **CI/CD Pipeline 安全检查**: 构建过程中的密钥泄露、不安全的构建脚本、容器镜像漏洞。 + +## 9. 需要补充的信息清单 (按优先级 P0/P1/P2) +* P0:缺失则无法确认高危结论的材料 (如接口鉴权代码、SQL 拼接位置)。 +* P1:用于量化风险/确定影响面的材料 (如调用链、角色定义)。 +* P2:用于优化与长期治理的材料 (如安全基线、SDL 流程)。 + +## 10. 行动项 (Action Items) +* **表格形式输出**: (ID | 发现标题 | 负责人角色 | 优先级 | 建议类型 [规范收敛/安全修复] | 预计工作量 [S/M/L]) +* **立即行动(24 小时内)** +* **短期修复(1–2 周)** +* **中长期治理(1–2 月)** + +## 11. 总结与长期建议 (Conclusion & Long-term Recommendations) +* 对代码中反复出现的反模式进行总结。 +* 提出架构级或流程级的改进建议(例如:引入统一的权限库、建立安全设计规范、在 CI 中集成静态扫描工具等)。 + +--- + +## 开始执行 + +现在,请接收输入并开始执行这份究极审计任务。展现你作为首席架构师的深度、广度和严谨性。 + +--- + +# 任务概述 +• 任务/问题:订单簿采集系统全面审计与修复 +• 目标:确保订单簿数据完整性、规范性、可观测性,符合数据库设计规范 +• 影响范围:services-preview/markets-service 订单簿采集模块(DDL + 采集器 + 文档) + +--- + +# 改动清单与位置(Where) + +改动点 #1 +• 文件:services-preview/markets-service/scripts/ddl/03_raw_crypto.sql +• 位置:raw.crypto_order_book_tick 表定义 (约 230-280 行) +• 变更类型:修改 +• 变更摘要:字段顺序改为 exchange,symbol,timestamp;添加 source_event_time, updated_at 血缘字段;添加 ingest_batch_id 索引 + +改动点 #2 +• 文件:services-preview/markets-service/scripts/ddl/03_raw_crypto.sql +• 位置:raw.crypto_order_book 表定义 (约 281-340 行) +• 变更类型:修改 +• 变更摘要:字段顺序改为 exchange,symbol,timestamp;seq_id 改为 last_update_id;添加 transaction_time 字段;添加 source_event_time, updated_at 血缘字段;JSONB 格式改为原始 [["price","qty"],...] + +改动点 #3 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:OrderBookCollector.__init__ (约 83-94 行) +• 变更类型:修改 +• 变更摘要:添加 _last_seq 序号追踪字典;添加 _stats 统计字典(含 max_delay_ms, total_delay_ms);添加 _last_msg_time 心跳时间 + +改动点 #4 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:_build_full_row 方法 (约 198-253 行) +• 变更类型:修改 +• 变更摘要:添加 last_update_id, transaction_time 参数;JSONB 存储改为原始格式 [[str(p), str(s)], ...] + +改动点 #5 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:_on_book 方法 (约 264-310 行) +• 变更类型:修改 +• 变更摘要:提取 book.sequence_number 作为 lastUpdateId;添加乱序检测(序号倒退跳过);添加延迟监控(receipt_ts - event_ts);更新心跳时间 + +改动点 #6 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:_flush 方法 (约 325-350 行) +• 变更类型:修改 +• 变更摘要:错误处理添加 exc_info=True;添加丢失数据计数;添加 order_book_write_errors 指标 + +改动点 #7 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:_write_tick_rows 方法 (约 352-390 行) +• 变更类型:修改 +• 变更摘要:列顺序改为 exchange, symbol, timestamp, ... + +改动点 #8 +• 文件:services-preview/markets-service/src/crypto/collectors/order_book.py +• 位置:run 方法内 _stats_reporter (约 481-496 行) +• 变更类型:修改 +• 变更摘要:统计日志添加 delay_avg, delay_max;添加心跳超时检测(30s) + +改动点 #9 +• 文件:services-preview/markets-service/docs/ORDER_BOOK_AUDIT.md +• 位置:整个文件 +• 变更类型:新增 +• 变更摘要:合并两份审计报告为最终版,包含表结构、代码要点、验证清单、残留风险 + +--- + +# 改了什么(What) + +行为变化(对外表现) +• 订单簿数据包含 last_update_id 字段(原为空) +• 乱序数据被跳过并记录告警日志 +• 延迟超过 5s 输出告警日志 +• 30s 无数据输出心跳超时告警 +• 每 60s 输出统计日志含 delay_avg, delay_max + +接口/配置/数据结构变化 +• raw.crypto_order_book.bids/asks JSONB 格式:[["price","qty"],...](原为 [{p,s},...]) +• raw.crypto_order_book.last_update_id 字段:Binance lastUpdateId +• raw.crypto_order_book.transaction_time 字段:预留(当前为空) +• 新增指标:order_book_out_of_order, order_book_write_errors + +关键边界条件与例外 +• lastUpdateId 倒退时跳过该消息 +• 延迟阈值 5000ms 触发告警 +• 心跳阈值 30s 触发告警 + +--- + +# 为什么要改(Why) + +背景与问题现象 +• 审计报告指出:缺失 lastUpdateId 导致无法防重放/乱序 +• JSONB 格式与 Binance 原始格式不一致(阉割了精度) +• 表结构字段顺序与 crypto_kline_1m 不一致 +• 写库错误静默吞掉,无可观测性 + +根因 +• cryptofeed sequence_number 未被采集 +• JSONB 格式人为改为 {p,s} 对象 +• 缺少延迟/心跳监控逻辑 + +不改的风险/影响 +• Medium 风险:重放/乱序数据污染订单簿 +• Low 风险:写库失败无感知,数据缺口长期未知 + +--- + +# 怎么改的(How) + +设计选择与实现要点 +• 从 book.sequence_number 提取 lastUpdateId(cryptofeed 内部已映射) +• 使用 _last_seq 字典追踪每个 symbol 的最新序号 +• 延迟计算:(receipt_ts - book.timestamp) * 1000 毫秒 +• 原始格式保留字符串精度:[[str(p), str(s)], ...] + +关键调用链/数据流 +WebSocket → cryptofeed → _on_book → 心跳更新 → 延迟计算 → 乱序检测 → _build_tick_row/_build_full_row → 缓冲区 → _flush → TimescaleDB + +异常处理与容错策略 +• 乱序数据:跳过 + 计数 + WARNING 日志 +• 写库失败:计数 + ERROR 日志 + 堆栈 +• 高延迟:WARNING 日志 +• 心跳超时:WARNING 日志 + +兼容性与迁移 +• DDL 变更需重建表或 ALTER TABLE +• 历史数据可能存在列错位,需抽样校验 + +--- + +# 修改目的与验收标准(Acceptance Criteria) + +AC1:lastUpdateId 采集 +• SQL: +``` +SELECT symbol, timestamp, last_update_id +FROM raw.crypto_order_book +WHERE last_update_id IS NOT NULL +ORDER BY timestamp DESC LIMIT 5; +``` + +AC2:乱序检测 +• 日志出现 “乱序跳过: seq X < Y” + +AC3:延迟监控 +• 统计日志含 `delay_avg=Xms, delay_max=Yms` + +AC4:原始格式 +• SQL: +``` +SELECT bids->0->0 AS price, bids->0->1 AS qty +FROM raw.crypto_order_book LIMIT 1; +``` + +AC5:语法检查 +• `.venv/bin/python -m py_compile src/crypto/collectors/order_book.py` + +--- + +# 测试与验证 + +已执行 +• 语法检查:`.venv/bin/python -m py_compile src/crypto/collectors/order_book.py`(通过) +• Binance API 原始格式确认:`[["price","qty"],...]` +• cryptofeed 字段检查:`sequence_number` 存在 + +未覆盖 +• 实际运行(需 DB/代理) +• 乱序模拟(需构造测试数据) + +--- + +# 风险与回滚 + +风险 +• `transaction_time` 未采集(Low) +• 历史数据可能错位(Low) + +回滚路径 +• `git revert <对应提交>` +• DDL 需手动 ALTER TABLE 或重建 + +--- + +# 待补充信息清单 + +无 TBD 项。 +