← 返回 Day 159 正文

Day 159 · 图解

Day 159 — 安全审计工具:架构与数据流图解

1. 端到端数据流

flowchart LR A[本地目录/文件<br/>只读] --> B[遍历器<br/>黑名单 + 资源上限] B --> C{可安全读取?} C -->|过大/二进制/非法编码/符号链接| S[Coverage.skipped] C -->|读取异常| E[Coverage.errors] C -->|是| D[逐行正则<br/>规则库 13 条] D --> F[命中 → Finding<br/>掩码 + sha256 指纹] F --> G{baseline 命中?} G -->|是| H[suppressed<br/>单独计数, 不删除] G -->|否| I[findings<br/>排序: 优先级降序] H --> J[AuditReport] I --> J S --> J E --> J J --> K[JSON<br/>机器接口] J --> L[Markdown<br/>人类接口] J --> M{门禁<br/>--fail-on} M -->|覆盖不全| N[exit 4] M -->|有命中≥阈值| O[exit 3] M -->|干净| P[exit 0]
flowchart LR
    A[本地目录/文件<br/>只读] --> B[遍历器<br/>黑名单 + 资源上限]
    B --> C{可安全读取?}
    C -->|过大/二进制/非法编码/符号链接| S[Coverage.skipped]
    C -->|读取异常| E[Coverage.errors]
    C -->|是| D[逐行正则<br/>规则库 13 条]
    D --> F[命中 → Finding<br/>掩码 + sha256 指纹]
    F --> G{baseline 命中?}
    G -->|是| H[suppressed<br/>单独计数, 不删除]
    G -->|否| I[findings<br/>排序: 优先级降序]
    H --> J[AuditReport]
    I --> J
    S --> J
    E --> J
    J --> K[JSON<br/>机器接口]
    J --> L[Markdown<br/>人类接口]
    J --> M{门禁<br/>--fail-on}
    M -->|覆盖不全| N[exit 4]
    M -->|有命中≥阈值| O[exit 3]
    M -->|干净| P[exit 0]

关键点:跳过与错误(灰)与命中(黄)汇入同一个报告对象。 只把命中写进报告的工具,会用"0 命中"撒谎;把缺口写进报告的 工具,才有资格说"这次没发现问题,且我知道自己漏了什么"。

2. 五个设计要素与它们的失败模式

Target  ──►  Rule  ──►  Engine  ──►  Finding  ──►  Report
  │           │          │            │             │
  │           │          │            │             └─ 失败模式: 覆盖不声明
  │           │          │            └─ 失败模式: 证据含明文 → 二次泄露
  │           │          └─ 失败模式: 不设上限 → 拖死被审计机器
  │           └─ 失败模式: 没有 why/remediation → 复核人员只能猜
  └─ 失败模式: 默认扫全盘 → 越权 + 噪声淹没

3. triage 矩阵:影响 × 把握

严重度(行,客观影响)× 置信度(列,这条命中是问题的把握):

              confidence=low   medium   high
severity=critical   P0(10)      P0(15)   P0(15)   ← critical 与 high 同档: 宁可多看一眼
severity=high       P1(6)       P1(9)    P0(12)
severity=medium     P2(4)       P2(6)    P1(9)
severity=low        P3(2)       P3(3)    P2(4)    ← 低危+低把握 = 直接沉底
severity=info       P3(1)       P3(2)    P3(3)

公式:priority = (severity_index + 1) × (confidence_index + 1), 分档:≥12 → P0、≥8 → P1、≥4 → P2、其余 P3。

为什么要乘而不是加?因为"确定的低危"和"不确定的高危"处理方式完全不同: 前者排期修,后者必须先去验证。加法会把这两种混成同一个分数。

4. 退出码语义(CI 接口)

       ┌──────────────── 退出码优先级 ────────────────┐
       │ 高                                         低 │
       │ 2 用法/目标错误 → 4 覆盖不全 → 3 命中≥阈值 → 0 通过
       └───────────────────────────────────────────────┘

为什么"覆盖不全"要排在"命中"前面? 因为 CI 门禁的价值在于可信。当扫描只覆盖了 60% 的文件时, "没有高危命中"这句话本身不成立;此时先修工具与权限,再谈修业务代码。

5. 报告的三层使用者

JSON     ──► CI / 工单系统 / 后续 diff 比对(机器只吃结构化数据)
Markdown ──► 复核人员(要位置、证据、为什么、怎么修)
stderr   ──► 操作者(一行摘要 + 退出码解释,不污染报告正文)

一条实用纪律:报告正文里不出现绝对路径和主机名。 报告会被转发出去,/home/niedong/internal-finance-svc/... 这类路径 本身就是情报。末级目录名 + 相对路径足够定位问题。

6. baseline 的生命周期

stateDiagram-v2 [*] --> 命中: 规则匹配 命中 --> 待复核: 无 baseline 命中 --> 已抑制: 在 baseline 中 已抑制 --> 已修复: 排期修复 已抑制 --> 已抑制: 复查日期到期 → 评审 → 延期(需记录理由) 已修复 --> [*] 待复核 --> 已修复: 确认问题 待复核 --> 已抑制: 确认为误报 → 写进 baseline(带原因)
stateDiagram-v2
    [*] --> 命中: 规则匹配
    命中 --> 待复核: 无 baseline
    命中 --> 已抑制: 在 baseline 中
    已抑制 --> 已修复: 排期修复
    已抑制 --> 已抑制: 复查日期到期 → 评审 → 延期(需记录理由)
    已修复 --> [*]
    待复核 --> 已修复: 确认问题
    待复核 --> 已抑制: 确认为误报 → 写进 baseline(带原因)

baseline 是"我们已知并接受的风险清单",不是回收站。 教程 / 注释 / 示例值造成的误报应当进 baseline;真实凭据绝不能进。