# Changelog

版本号遵循 [Semantic Versioning](https://semver.org/)。

---

## [Unreleased]

---

## v0.17.0 — 2026-05-12

### Phase 7e.3 — firewalld audit

- New `firewalld_audit` Check covers the RHEL/CentOS Stream/Fedora
  default firewall (firewalld) with a 3-command read-only pipeline
  (`firewall-cmd --state` + `--get-default-zone` + `--list-all-zones`).
- Audit scope mirrors 7e.2 ufw: service-active + default zone target +
  per-active-zone target. Reports only `target == "ACCEPT"` (default
  zone = critical, other active zones = high).
- `_firewall_common.py` extended with `detect_firewalld` +
  `output_managed_by_firewalld(stdout, *, backend)`.
- `iptables_audit` and `nftables_audit` now yield `not_applicable`
  when firewalld-injected chains/tables are detected, mirroring the
  existing ufw-takeover yield from 7e.2.
- `ssh_policy.yaml`: +2 `allowed_paths` exact (firewall-cmd binaries),
  +1 `commands` section (3 precise argv tuples; all write ops
  unreachable by construction).
- ADR-0017.
- Total Checks: 28 → 29.

---

## [0.16.0] — 2026-05-12

### Added

- **Phase 7c — MCP Top 10 evaluation suite**。自策展 OWASP MCP Top 10
  （AgentSec curated v1），10 条威胁分类按 MCP 协议原语切分（tool/resource/
  prompt/sampling + 多 server + 供应链）。5 个新测试套件、80 个 case：
  - `test_suites/mcp_injection.yaml`（MCP1+MCP2+MCP5，28 cases）
  - `test_suites/mcp_cross_server.yaml`（MCP3+MCP6，16 cases）
  - `test_suites/mcp_resource_smuggling.yaml`（MCP4+MCP10，16 cases）
  - `test_suites/mcp_capability_abuse.yaml`（MCP7+MCP8，14 cases）
  - `test_suites/mcp_supply_chain.yaml`（MCP9，6 cases）
- 报告新增 "OWASP MCP Top 10 (AgentSec curated v1)" 覆盖表（第三张）。
- CI 闸 `scripts/check_owasp_coverage.py` 新增 MCP1..MCP10 each ≥ 6 下限。
- ADR-0016 记录自策展取舍 + 未来 OWASP 官方版的 mapping 策略。
- 架构零改动：复用 `AgentAdapter` → `JudgeRouter` → 报告流水线；无新
  adapter / 无 audit pipeline 变更。

### Internal

- 测试数 1348 → 1351 passing（+3）。
- 新测试位置：`tests/test_mcp_suite_load.py`。

---

## [0.15.0] — 2026-05-11

### Added

- **Phase 7d.3 — rust / crates.io 供应链审计 Check** (`rust_supply_chain_audit`)。读
  operator 在 `openclaw-target.yaml` `server_audit.cargo_lock_paths` 列出的
  `Cargo.lock`（绝对路径或 `~/-rooted`，basename 必须严格等于 `Cargo.lock`
  **大小写敏感**），用 `tomllib`（Python 3.11+ stdlib）解析 TOML，过滤到
  crates.io 官方 registry 源的 package，按 crate 名字面匹配（**不**做 PEP-503
  风格归一化 — Cargo 中 `serde-json` 与 `serde_json` 是两个不同 crate）+
  纯 semver 2.0 比较（无 v 前缀，无 +incompatible）对照 bundled OSV-Cargo DB
  (`src/agentsec/audit/ioc/osv_cargo.json`，1396 advisories ≈ 545 KB) emit
  每个命中 (crate, version, advisory) 一条 finding。TI umbrella
  `TI-OSV-CRATES-CATALOG`。服务端审计 Check 数：27 → 28。
- `OsvCargoFetcher` 在 `agentsec ioc-update` 中作 side pipeline，与
  `OsvNpmFetcher` / `OsvPyPIFetcher` / `OsvGoFetcher` `asyncio.gather` 四方并行。
- `agentsec server-audit` 新增可重复 `--cargo-lock-path` flag。
- `ServerAuditConfig.cargo_lock_paths` 配置字段 + Pydantic validator
  （绝对/`~/`-rooted、无 metachar、basename == `Cargo.lock` **大小写敏感**）。
- `CheckContext.cargo_lock_paths: tuple[str, ...]` 字段。
- `scripts/check_osv_cargo.py` CI 守门 + ci.yml 集成。
- ADR-0015 记录全 9 项决策。

### Internal

- 测试数 1302 → 1348 passing（+46）。
- 新测试位置：`tests/audit/checks/test_rust_supply_chain_audit.py`、
  `tests/audit/ioc_update/fetchers/test_osv_cargo.py`、
  `tests/scripts/test_check_osv_cargo.py`、
  `tests/test_ioc_update_osv_cargo_wiring.py`；扩展
  `tests/audit/checks/test_supply_chain_redaction.py` +1 case。
- **零新 PyPI 依赖** — `tomllib` 是 Python 3.11+ stdlib，`semver>=3,<4` 已在 deps。
- **零新抽象工作** — 直接复用 7d.2 抽取的 `_supply_chain_common.py`（rule of three 投资回收）。

---

## [0.14.0] — 2026-05-11

### Added

- **Phase 7d.2 — go / OSV-Go 供应链审计 Check** (`go_supply_chain_audit`)。读
  operator 在 `openclaw-target.yaml` `server_audit.go_lock_paths` 列出的
  `go.sum`（绝对路径或 `~/-rooted`，basename 必须严格等于 `go.sum`），
  解析 `<module> <version> <hash>` 三字段行，跳过 `/go.mod` 后缀重复行，
  按 module path 字面匹配 + semver 比较（剥 `v` 前缀 + `+incompatible`
  后缀）对照 bundled OSV-Go DB (`src/agentsec/audit/ioc/osv_go.json`，
  2760 advisories ≈ 888 KB) emit 每个命中 (module, version, advisory)
  一条 finding。TI umbrella `TI-OSV-GO-CATALOG`。服务端审计 Check 数：
  26 → 27。
- `OsvGoFetcher` 在 `agentsec ioc-update` 中作 side pipeline，与
  `OsvNpmFetcher` / `OsvPyPIFetcher` `asyncio.gather` 三方并行。
- `agentsec server-audit` 新增可重复 `--go-lock-path` flag。
- `ServerAuditConfig.go_lock_paths` 配置字段 + Pydantic validator
  （绝对/`~/`-rooted、无 metachar、basename == `go.sum`）。
- `CheckContext.go_lock_paths: tuple[str, ...]` 字段。
- `scripts/check_osv_go.py` CI 守门 + ci.yml 集成。
- ADR-0014 记录全 9 项决策。

### Refactored

- **`_supply_chain_common.py` 抽取（rule of three 触发，ADR-0013 D3 兑现）**。
  - `src/agentsec/audit/ioc_update/fetchers/_supply_chain_common.py` 持有
    `severity_from_cvss_score` / `_parse_cvss_score` / `_is_within_age` /
    `_extract_severity` + `MEDIUM_LOW_MAX_AGE_DAYS` + `Severity` Literal。
  - `src/agentsec/audit/checks/_supply_chain_common.py` 持有
    `load_advisory_db` + re-exports of `Severity` / `severity_from_cvss_score`。
  - 3 个 fetcher (`osv_npm` / `osv_pypi` / `osv_go`) + 3 个 Check
    (`supply_chain_audit` / `pip_supply_chain_audit` / `go_supply_chain_audit`)
    都从 common 模块 import；本地副本删除。

- **3 个 supply-chain Check 改走 `Check.make_finding`**（关闭 7d.1
  final-review #5）。所有 Finding evidence 现在自动通过 `ctx.redactor.redact`
  做 string 字段脱敏；新加的 `tests/audit/checks/test_supply_chain_redaction.py`
  覆盖 3 Check 各一例 secret-pattern 替换。

### Internal

- 测试数 1235 → 1302 passing（+67）。
- 新测试位置：`tests/audit/checks/test_go_supply_chain_audit.py`、
  `tests/audit/checks/test_supply_chain_common.py`、
  `tests/audit/checks/test_supply_chain_redaction.py`、
  `tests/audit/ioc_update/fetchers/test_osv_go.py`、
  `tests/audit/ioc_update/fetchers/test_supply_chain_common.py`、
  `tests/scripts/test_check_osv_go.py`、
  `tests/test_ioc_update_osv_go_wiring.py`。
- 零新 PyPI 依赖（`semver>=3,<4` 已在 deps）。

---

## [0.13.0] — 2026-05-11

### Added

- **Phase 7d.1 — pip / PyPI 供应链审计 Check** (`pip_supply_chain_audit`)。读
  operator 在 `openclaw-target.yaml` `server_audit.pip_lock_paths` 列出的
  `requirements.txt`（绝对路径或 `~/-rooted`），按 `pkg==ver` frozen
  形式解析，PEP-503 归一化包名后对照 bundled OSV-PyPI DB
  (`src/agentsec/audit/ioc/osv_pypi.json`，3452 advisories ≈ 949 KB)
  emit 每个命中 (pkg, version, advisory) 一条 finding。
  TI umbrella `TI-OSV-PYPI-CATALOG` 单行覆盖全部 PyPI advisory。
  PEP-440 比较器（`packaging>=22`）含 pre-release，忽略 local-version
  段。服务端审计 Check 数：25 → 26。
- `OsvPyPIFetcher` 在 `agentsec ioc-update` 中作 side pipeline，与
  `OsvNpmFetcher` `asyncio.gather` 并行；拉
  `https://osv-vulnerabilities.storage.googleapis.com/PyPI/all.zip`，
  应用与 npm 同款 5-轴过滤（withdrawn / no-PyPI / no-range /
  no-CVSS / 老 medium-low）。其中一边 degraded 不阻塞另一边。
- `agentsec server-audit` 新增可重复 `--pip-lock-path` flag（standalone
  使用），免去走 `evaluate` 配置的开销。
- `CommandPolicy.add_runtime_allowed_paths(paths, kind="exact")`：
  新接口允许 CLI 在 orchestrator 启动时把 operator config 中的
  lockfile 路径动态注入 `allowed_paths`，不污染磁盘 `ssh_policy.yaml`。
- `ServerAuditConfig.pip_lock_paths: list[str]` 配置字段 + Pydantic
  validator：路径必须 `/` 或 `~/` 起头、无 shell 元字符、文件名后缀
  ∈ `{requirements*, .txt, .lock, .in}`（`requirements.cfg` 被拒绝
  因为是 pip 自身配置文件，可能含凭据）。
- `CheckContext.pip_lock_paths: tuple[str, ...]` 字段（dataclass），
  `run_server_audit` 自动从 parent ctx 传入每个 check 的 ctx 副本。
- `scripts/check_osv_pypi.py` CI 守门：JSON 合法性 + 15 MB 硬上限，与
  `check_osv_npm.py` 同款。
- ADR-0013 记录本 phase 全部 10 项决策。

### Fixed

- `OsvNpmFetcher.normalize_osv_entry` 不再截断多包 advisory。原本只取
  `affected_npm[0]`，遇到 `affected: [pkg-a, pkg-b]` 时 pkg-b 静默丢失。
  现按 PyPI 同款返 `list[dict] | None`，caller 用 `(id, package)` 元组
  去重。bundled `osv_npm.json` 同步刷新：3437 → 3673 entries（+236，
  全是被原 bug 截断的多包 advisory 中的非首包）。

### Internal

- 测试数 1177 → 1231 passing（+54）。
- `tests/audit/checks/test_base.py`、`tests/audit/ioc_update/fetchers/`、
  `tests/scripts/test_check_osv_pypi.py`、`tests/test_ioc_update_osv_pypi_wiring.py`
  四个新测试位置。
- ssh_policy.yaml 加注释段说明 7d.1 路径走运行时注入而非静态条目。
- 零新 PyPI 依赖（`packaging>=22` 此前已在 `pyproject.toml`）。

---

## [0.12.0] — 2026-05-09

### Added

- **Phase 7d — npm 供应链审计 Check** (`supply_chain_audit`)。读
  OpenClaw 的 `~/.npm-global/lib/node_modules/openclaw/package-lock.json`，
  walk transitive 依赖，对照 bundled OSV-npm DB
  (`src/agentsec/audit/ioc/osv_npm.json`，3437 advisories ≈ 0.9 MB
  由 `agentsec ioc-update` 维护) emit 每个命中 (pkg, version, advisory)
  一条 finding。TI umbrella `TI-OSV-NPM-CATALOG` 让所有 finding 通过
  strict TI 校验而不必为每条 GHSA 建一行。服务端审计 Check 数：
  24 → 25（10 OpenClaw + 11 Linux hardening + 3 Docker + 1 supply chain）。
- `OsvNpmFetcher` 在 `agentsec ioc-update` 子流程拉
  `https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip`，
  归一化后写 `proposed-osv_npm.json`。过滤规则：drop withdrawn /
  no-npm-affected / no-semver-range / no-CVSS / 2 年前的 scored
  medium-low；critical/high 永远保留。原始 49 MB → 0.9 MB bundled。
- 新依赖：`semver>=3,<4`（npm semver 比较，PEP 440 不通用）+
  `cvss>=3,<4`（CVSS v3 vector → severity 桶）。

---

## [0.11.0] — 2026-05-09

### Added

- **Phase 7e.2 — Linux 主机加固防火墙 Check** (3 类：`iptables_audit` /
  `nftables_audit` / `ufw_audit`)。每个 Check 在 `run()` 顶部用共享
  helper `_firewall_common.detect_<bin>(ctx)` 探测后端二进制，未装即
  `not_applicable`。默认策略 / 后端激活 / IPv6 平行检查（点 A+B+C）。
  Approach A 串扰回避：iptables/nftables Check 检出 ufw 注入链/表后
  让位 `ufw_audit`。权限不足走 `skipped` + NOPASSWD sudoers 提示。
  服务端审计 Check 数：21 → 24（10 OpenClaw + 11 Linux hardening + 3
  Docker）。
- ssh_policy 扩展：9 条 firewall 二进制 `allowed_paths`（exact）+ 3
  段 `commands`（iptables / nft / ufw 各 1 个 read-only allowed_argv
  tuple）。
- TI 扩展：`TI-LINUX-CIS-FIREWALL`（internal）。

---

## [0.10.0] — 2026-05-09

### Added

- **server-audit：3 类 Docker 容器 Check**（Phase 7f）
  - `docker_daemon_audit` — 6 条 daemon 规则（TLS 暴露、userns、no-new-privileges、live-restore、日志大小、版本 CVE）
  - `docker_runtime_audit` — 7 条 runtime 规则（privileged、host network、docker.sock 挂载、宿主敏感路径挂载、root 用户、高危 cap、no-new-priv 缺失）
  - `docker_image_cve` — 10 条 fnmatch 镜像规则（:latest、Python 2 / Node 10-14 / Node 16-18 / Ubuntu 14-18 / Debian 7-9 / CentOS 6-8 / Alpine 3.0-3.9 / Alpine 3.10-3.19 / OpenClaw dev tag EOL）
- **threat_intel.yaml 新增 21 条 TI-DOCKER-***，全部 `auto_refresh: false`（curated）。
- **ssh_policy.yaml 新增 `docker:` 命令块**，4 条 allowed_argv + 1 条 args_schema（kind=container_id）。
- **args_schema.py 新增 `subcommand` role + `container_id` kind**。

### Changed

- `validate_args` 在 `allowed_argv` 与 `args_schema` 并存时支持 fallback（先试固定形，再走 schema）。

### Notes

- v0.10.0 共 21 类 server-audit Check（v0.9.5 18 类基础上 +3）。
- 第一期不覆盖 containerd / podman / Kubernetes。

---

## [0.9.5] — 2026-05-08

### Added

- **Linux 主机加固扩展 Check（4 类，Phase 7e.1）** —— `server-audit` Check
  数从 14 升至 18。新增 `world_writable_audit`（在 OpenClaw 路径
  `openclaw_home`/`log_dir`（profile 注入）+ `/tmp/openclaw` + 4 cron 目录
  共 7 路径下扫世界可写文件，每命中一个一条 finding，cron 命中升 critical），
  `auditd_rules_audit`（`auditctl -s`/`-l` + 8 条 CIS baseline 规则覆盖；
  未装 → not_applicable，enabled=0 → 1 条 high finding，每条规则缺失独立
  finding；match 字符串带尾部空格防止 sudoers/sudoers.d、execve/execveat
  子串碰撞），`systemd_service_blacklist`（`systemctl list-unit-files
  --type=service,socket` 对照 10 条高风险服务黑名单），`systemd_unit_lint`
  （`/etc/systemd/system/*.service` 内 ExecStart 三类 regex：可写路径 /
  shell-wrap / User=root 叠加升 critical）。共享 baseline
  `linux_hardening_baseline.yaml`，profile 非 Linux 时统一 not_applicable。
  CIS-aligned 默认值。SSH policy 同步扩展：新增 5 条 allowed_paths prefix
  （4 cron + systemd unit dir）+ `auditctl` 命令（仅 `-s` / `-l`）+
  `systemctl list-unit-files --type=service,socket --state=enabled --no-legend
  --plain` 一行。Threat-Intel 追加 3 条 internal CIS stub（systemd 两条
  Check 共享 TI ID）。详见 ADR-0009。

---

## [0.9.4] — 2026-05-08

### Added

- **Linux 主机加固 Check（4 类）** —— `server-audit` 从应用层扩到
  OS 主机层。新增 `sysctl_audit`（kernel.dmesg_restrict / kptr_restrict /
  fs.suid_dumpable / 5 项 net.ipv4 共 8 个 baseline key），
  `sshd_config_audit`（PermitRootLogin / PasswordAuthentication /
  PermitEmptyPasswords / X11Forwarding / Protocol / MaxAuthTries 共
  6 项），`sudoers_audit`（NOPASSWD ALL / 风险 RunasUser / 显式
  全机免密 3 类 regex），`suid_audit`（/usr/bin + /usr/sbin
  下非白名单 SUID/SGID 漂移）。共享 baseline
  `linux_hardening_baseline.yaml`，profile 不是 Linux 时统一标
  `not_applicable`。CIS-aligned 默认值。SSH policy 同步扩展：新增
  `sysctl` / `sshd` 命令、`/etc/sudoers` / `/etc/sudoers.d` /
  `/usr/bin` / `/usr/sbin` allowed_paths 条目。新增 4 条 TI 行
  （`TI-LINUX-CIS-SYSCTL` / `TI-LINUX-CIS-SSHD` / `TI-LINUX-CIS-SUDOERS` /
  `TI-LINUX-CIS-SUID`）。详见 ADR-0008。

---

## [0.9.3] — 2026-05-08

### Added

- **OWASP Agentic Top 17 全覆盖** — 每个威胁 ≥ 3 case，覆盖
  T1 记忆投毒 → T17 供应链。新增 6 个套件文件
  （`agentic_intent_breaking.yaml` / `agentic_untraceability.yaml` /
  `agentic_communication_poisoning.yaml` / `agentic_rogue_agents.yaml`
  / `agentic_multi_agent_attacks.yaml` / `agentic_supplements.yaml`），
  共 24 条新 case。`scripts/check_owasp_coverage.py` 升级为
  「LLM Top 10 ≥ 1 + Agentic v1.1 ≥ 3」两道闸；任一类别低于阈值
  即阻塞合入。详见 ADR-0007。

---

## [0.9.2] — 2026-05-08

### Added

- **OWASP 框架覆盖矩阵** — `TestCase.owasp_refs: list[str]` 字段
  （强校验前缀 `LLM\d{2}:2025` / `AGENTIC-T1..T17` / `MCP\d+`）落地，
  现有 91 条 case 全量打 tag；新增 5 个 gap-fill suite（LLM05 输出处
  理 / LLM07 系统提示泄露 / LLM08 向量与嵌入 / LLM09 错误信息 /
  LLM10 不限消耗，每类 5 条，共 25 条）。`render_remote_report` 与
  `render_combined_report` 新增「OWASP 框架覆盖」段，按 LLM Top 10
  + Agentic v1.1 两张表呈现 total / passed / failed / skipped /
  errored 列。`scripts/check_owasp_coverage.py` 在 CI 闸住「LLM
  Top 10 任一分类 0 case」回归。详见 ADR-0006。
- **服务端审计报告大规模改版** —— 针对真实大批量 finding
  （Lightsail prod 实跑 183 条）的可读性、信息密度与可视化做了
  系统级重写：

  *§1 报告概览：结构化目标信息 + 拓扑图*
  - 引入 `TargetDescriptor` dataclass（`host` / `user` /
    `key_path` / `auth_file` / `openclaw_bin` / `openclaw_version`
    / `checks_run`）；`render_server_audit_report` 接受新的
    `target=` 参数；CLI `server_audit` 与 `_evaluate_server` 在
    调用时注入。`target=None` 时退化为最小概览，保持向后兼容；
    `openclaw_version` 未指定时由
    `version_patch.evidence.running_version` 自动回填。
  - §1.1 审计目标（多字段表，含目标 IP、SSH 用户与私钥路径、
    OpenClaw 版本与安装路径、AUTHORIZATION 文件、信任级别等）；
    §1.2 审计方法（白名单 argv / 审计日志 / HMAC 授权 / 本次
    执行的 check 列表）；§1.3 审计逻辑拓扑（mermaid flowchart
    描绘 评估方 → SSH/HMAC → CommandPolicy → check → 输出产物）。

  *§2 执行摘要：饼图 + 攻击路径 + 优先级*
  - §2.1 风险态势改为**内联 SVG 饼图**：每个切片 `fill` 直接用
    严重度颜色（critical 红 `#dc2626` / high 橙 `#ea580c` /
    medium 黄 `#eab308` / low 蓝 `#3b82f6` / info 灰
    `#9ca3af`），从 12 点位置顺时针。SVG 右半内嵌图例列，对每个
    出现的严重度都列 `色块 + 名称 · N (X.X%)`，无论切片大小都
    有完整数字与占比；不依赖 mermaid 主题，可在任意支持 SVG 的
    markdown 渲染器下正确出色。
  - 新增 §2.2 攻击路径：`src/agentsec/reports/attack_paths.py`
    内置 4 个启发式 scenario 模板（KEV 在野利用 / 外网未授权
    端口 + CVE / 配置弱点 → 提权 CVE / 凭据访问 → 持久化），命
    中后输出"概述 + 步骤 + 每步使用的脆弱性"；任何 scenario 未
    触发时仍渲染章节头 + 启发式说明 stub。
  - §2.3 最高优先级修复（原 §2.2）从固定 5 条扩为
    「全部 critical + high top 10 + medium 计数」三段式，附
    `… 还有 N 条 high 详见 §3。` 尾注。
  - §2.4 Check 维度分布矩阵（原 §2.3，每 check × 严重度的
    finding 数；多 check 时显示，单 check 时自动跳过）。

  *§3 详细发现：version_patch 折叠 + 卡片增强*
  - §3.0 `version_patch — CVE 总览`：把所有 `version_patch`
    finding 抽出独立表（CVE / 受影响 / fixed_in / KEV·CVSS /
    NVD 链接），详细卡片折叠在 `<details>` 块中。剩余 §3.x 只
    承载非 version_patch finding，避免 174 张 CVE 卡片把单一
    严重度章节撑到 1700 行。
  - Finding 卡片：CVSS / 🚨 CISA KEV / NVD 置信度 badge 行；
    标题旁的 `[↗]` CVE 链接；NVD seed 描述用引用块包裹并标注
    「来源：NVD seed，未经本地翻译」；指纹去截断省略号；
    `version_patch` 标题从
    `TI-OPENCLAW-CVE-XXXX：当前版本 X 受影响` 渲染期改写为
    `[CVE-XXXX] OpenClaw 版本受影响（受影响 …，已修复 …）`，
    渲染期改写、不动 Finding 本身，fingerprint 稳定。

  *其他*
  - 报告头加目录，跳转 §1–§5 + 附录 A。
  - 评分浮点改成 1 位小数（`33.333…` → `33.3`），仅作用于人类
    可读 bullet；`<!-- meta:... -->` 机读 anchor 保留原始 float，
    `agentsec diff` loader 等下游解析契约不变。
- **`log_review` 接 journald** — `systemd --user` 模式部署（如 Lightsail
  prod）不再被打成 `not_applicable`。`PlatformProfile` 增加 discriminated
  `log_source` 字段（`FileLogSource | JournaldLogSource`）；
  `LINUX_USER_MODE` 走 `journalctl --no-pager --output=cat -u <unit>
  --since <N hours ago> --lines <N>`，evaluator 本地 fixed-string 匹配
  保持 `-F` 语义。新增 `ServerAuditConfig.log_review.journald_since_hours`
  / `journald_max_lines`（默认 24h / 10000 行；范围 1–168 / 100–100000）。
  SSH policy 加入 `journalctl` 命令、三个新占位符 kind（`<unit>` /
  `<since>` / `<lines>`），不放过 `--grep` / `--output=json` / `-p` /
  `-k` 等扩面 flag。
- **静态类型检查接入 pyright basic mode** — 新增 `pyrightconfig.json`
  与 CI `typecheck` 任务，对 `src/agentsec/` 全量执行 pyright basic 模
  式。落地时一次性修掉 36 个错误 + 5 个警告（`__all__` 懒导出 / Optional
  解引用 / Severity Literal / paramiko 类型协议 / pydantic PrivateAttr
  等），后续 PR 引入新 type 错误会被 CI 拦截。`pyright>=1.1.350` 加进
  `dev` 可选依赖。

---

## [0.9.1] — 2026-05-07

### Added

- **多提供商 LLM 评判器** — 不再硬性要求 `ANTHROPIC_API_KEY`。新增
  `agentsec.evaluator.judge_factory.build_default_judge_from_env()`，
  从 `AGENTSEC_LLM_PROVIDER` / `AGENTSEC_LLM_API_KEY` /
  `AGENTSEC_LLM_BASE_URL` / `AGENTSEC_LLM_MODEL` /
  `AGENTSEC_LLM_TIMEOUT` 五个变量按需构造 `LLMJudge`（Anthropic）或
  `OpenAICompatibleJudge`。设了 `AGENTSEC_LLM_BASE_URL` 时
  provider 自动推断为 `openai`。`agentsec run` 与 `agentsec evaluate`
  （无 `judge:` 段时）共享这条路径，因此 OpenAI / DeepSeek / Qwen /
  Moonshot / OpenRouter / Together / 本地 Ollama / vLLM / MLX 都不写
  yaml 也能跑。Anthropic 仍是默认。yaml 的 `judge:` 段优先级高于环
  境变量。
- 仓库根 `LICENSE`（MIT）。

### Changed

- **PyPI 发布名改为 `agentsec-eval`**（`agentsec` 已被另一个项目
  占用）。CLI 命令仍是 `agentsec`，安装命令变为
  `pipx install agentsec-eval` / `uvx --from agentsec-eval agentsec`。
- `pyproject.toml` 补齐 PyPI metadata：`license = "MIT"` +
  `license-files`、`authors`、`readme`、`keywords`、`classifiers`、
  `project.urls`；`[tool.hatch.build.targets.{wheel,sdist}]` 显式
  声明打包范围；`[project.optional-dependencies].dev` 加入 `build` +
  `twine`。
- README + `docs/guide/getting-started.md` quick-start 改为以 pipx 为
  默认推荐路径，源码安装下沉到「开发」段落。
- `agentsec ioc-update` 的 `--watchlist` / `--intel` 默认值从相对路径
  `src/agentsec/audit/ioc/...` 改成 wheel 内 bundled 副本（运行时通过
  `Path(__file__).parent / "audit" / "ioc" / ...` 解析）。pipx 用户从
  任意 cwd 执行 `agentsec ioc-update` 都能直接命中正确的内置 IOC
  数据；显式传 `--watchlist /path` / `--intel /path` 时仍按用户给的
  路径走。
- `agentsec run --api-key` 的环境变量回落链改为
  `AGENTSEC_LLM_API_KEY` → `AGENTSEC_JUDGE_API_KEY`（旧名仍工作）。

---

## [0.9.0] — 2026-05-04

### Added

- **Community suite marketplace** — operators can now install
  third-party test suites with `agentsec suite install <name>` and run
  them via `agentsec run --suite <name>`. Bundles are pinned by
  canonical SHA-256 and validated against a strict threat-intel
  reference policy. See
  `docs/superpowers/specs/2026-05-04-community-suite-marketplace-design.md`.
- New CLI sub-app `agentsec suite list/info/install/uninstall` plus the
  hidden utility `agentsec suite hash` for index contributors.
- New module `src/agentsec/suite_registry/` containing `manifest.py`
  (Pydantic `SuiteManifest` + `IndexEntry` + `RegistryFile`),
  `hashing.py` (Merkle SHA-256 over the bundle tree), `registry.py`
  (`DefaultRegistry` reading the wheel-bundled `community-suites.yaml`,
  `HttpRegistry` for the hidden `--registry-url` override), `store.py`
  (local install store at `~/.agentsec/suites/`), `fetcher.py`
  (codeload.github.com tarball fetch via httpx), `installer.py` (full
  install pipeline with atomic rename + rollback).
- Reference template at `examples/sample-community-suite/`; per-PR
  static check `scripts/check_community_index.py` wired into `ci.yml`;
  weekly `verify-community-index.yml` workflow that opens an issue on
  hash drift.
- ADR `docs/adr/0005-community-suite-marketplace.md`.

### Changed

- `pyproject.toml` version bumped from `0.2.0` to `0.9.0` to match the
  release tags shipped between v0.3.0 and v0.9.0.
- `Store()` resolves `~/.agentsec/suites` lazily (per-instance)
  instead of at import time so a `HOME` change between commands is
  honored. The `DEFAULT_STORE_ROOT` export is preserved for
  back-compat.

### Migration notes

- The registry ships empty (`suites: {}`). The first real entry will
  arrive through the contribution flow. Existing `test_suites/` usage
  is unchanged — community suites are a new, additive surface.

---

## [0.8.0] — 2026-05-04

### Added

- **Custom judge interface** — operators can now replace the default Claude judge in
  `openclaw-target.yaml` with any OpenAI-compatible LLM backend (`type: openai_compatible`)
  or a Python plugin (`type: plugin`). See `docs/superpowers/specs/2026-05-04-custom-judge-design.md`.
- `OpenAICompatibleJudge` (`src/agentsec/evaluator/openai_judge.py`) — calls any
  `/chat/completions` endpoint; reuses the same `_SYSTEM` prompt as `LLMJudge`.
- `PluginJudge` (`src/agentsec/evaluator/plugin_judge.py`) — loads a user `.py` file
  via `importlib`, discovers the single `Judge` subclass, delegates all calls to it.
- `_build_llm_judge` helper in `cli.py` dispatches to the correct judge based on config.
- New config fields: `OpenAIJudgeConfig`, `PluginJudgeConfig` (Pydantic discriminated union).

---

## [v0.7.0] — 2026-05-04

### Added
- `agentsec serve <output-dir> [--port 8080] [--host 127.0.0.1]` — local Flask web
  dashboard for browsing `agentsec evaluate` / `agentsec multi-evaluate` artifacts.
  Auto-detects single-target vs multi-target layout. Features: score card (grade,
  combined/remote/server scores, verdict coverage, error rate), filterable findings
  table (severity checkboxes + check-name text filter), Markdown report rendering.
- New module `src/agentsec/serve/` with `reader.py` (pure I/O helpers) and `app.py`
  (Flask routes + Jinja2/Bootstrap 5 templates).
- New dependencies: `flask>=3.0`, `markdown>=3.6`.

---

## v0.6.0 (2026-05-04)

### Added
- `agentsec multi-evaluate --config multi-target.yaml` — evaluate multiple
  OpenClaw instances in parallel via `ThreadPoolExecutor`. Each target produces
  the standard six artifacts under `output_base/<name>/`; a cross-target
  `summary.md` (score table + per-target links + error details) is written last.
- `MultiTargetConfig` + `TargetEntry` Pydantic models in `config.py`
  (`extra="forbid"` enforced, duplicate target names rejected).
- `TargetResult` dataclass and `render_multi_summary` renderer in
  `src/agentsec/reports/multi_summary.py`.

---

## [0.5.2] — 2026-05-04

### Added

- `PluginStaticCheck` now scans installed skill Python source files for dangerous API
  calls via evaluator-side `ast` analysis. Five categories: `code_execution`
  (subprocess/os), `dynamic_eval` (eval/exec), `network_exfiltration`
  (requests/httpx/urllib), `raw_socket` (socket), `env_access` (os.environ). One
  `high`-severity finding per `(skill_file, category)`. IOC fingerprint matching
  (`critical`) is unaffected. Requires `skills_dir` in `PlatformProfile.paths`.
- `PlatformProfile`: added `skills_dir` key to `LINUX` (`~/.openclaw/skills/`) and
  `MACOS` (`~/Library/Application Support/openclaw/skills/`).

---

## [0.5.1] — 2026-05-04

### Added

- `ExposureScanCheck` now probes open ports for unauthenticated WebSocket endpoints.
  A bare WS upgrade at `/ws`, `/v1/ws`, `/` (configurable via `ws_paths`) that returns
  `101 Switching Protocols` produces a `high`-severity finding. The probe can be disabled
  with `ws_probe_enabled=False`. No findings are generated for auth-gated endpoints.

---

## [0.5.0] — 2026-05-04

### Added

- Cross-channel correlated attack infrastructure: `Turn.channel` (`http`|`ws`),
  `TestCase.ws_concurrent_observe` / `ws_listen_duration_s`, `WebSocketAgentAdapter`
  (real + mock), runner alternating and concurrent-observe modes via `asyncio.gather`,
  `agentsec run --ws-url` CLI option. Sub-project A of Phase 3.
- `test_suites/openclaw/11_cross_channel.yaml`: 6 cross-channel cases
  (cc-alt-01–03: alternating HTTP↔WS attacks; cc-obs-01–03: concurrent WS
  observation alongside HTTP attacks). Covers CVE-2026-32915, CVE-2026-32918,
  and OWASP Agentic AI A5.
- `test_suites/openclaw/10_agent_chain_privesc.yaml`: 6-case agent-chain
  privilege escalation suite covering three attack vectors — unauthorized
  delegation (oc-chain-01/02), sandbox escape via CVE-2026-32915
  (oc-chain-03/04), and session side-channel via CVE-2026-32918
  (oc-chain-05/06). Closes Phase 4.

---

## [0.3.0] — 2026-04-29

### Added

- `agentsec ioc-update` CLI: pulls CVE / threat-intel from NVD, CISA KEV,
  and GitHub Security Advisories; produces a propose-only artifact set
  (`proposed-threat_intel.yaml`, `report.md`, `audit-log.jsonl`) without
  mutating the repo. See `docs/superpowers/specs/2026-04-28-ioc-update-design.md`.
- `src/agentsec/audit/ioc/watchlist.yaml`: operator-curated vendor list
  (3 entries: openclaw, anthropic-claude, ms-agent).
- `version_patch`: KEV-listed CVEs (`kev: true` in threat_intel.yaml)
  now produce `severity: critical` regardless of CVSS, with
  `evidence.kev = True` for traceability.

### Changed

- `scripts/build_cve_db.py` reads `watchlist.yaml` to determine which
  `ti_prefix` values enter `cve_database.json` (was: hardcoded
  `TI-OPENCLAW-CVE-`). Default behavior unchanged — only OpenClaw has
  `cve_db_include: true`, so `cve_database.json` is byte-identical to
  v0.2.0.

### Migration notes

- The first `ioc-update`-merged PR reordered the existing 18
  `threat_intel.yaml` entries into dictionary order by `id`. This is a
  one-off cosmetic change; subsequent runs are stable.
- Pre-v0.3.0 `threat_intel.yaml` entries did not carry `kev`. After the
  first `ioc-update` run propagated KEV flags, evaluate runs may emit
  `critical` findings where they previously emitted `high` for the same
  CVE.

---

## [0.4.0] — 2026-04-30

### Added
- `agentsec diff <baseline-dir> <current-dir> [--output PATH]` — markdown
  delta report between two `agentsec evaluate` runs. Compares
  `findings.json` (fingerprint primary + (check, title) drift clustering)
  and `combined-report.md` meta (score / grade / coverage). Regression
  flagged on absolute deltas (combined_score Δ>5, grade letter drop,
  verdict_coverage<0.7, error_rate>0.1). Spec:
  `docs/superpowers/specs/2026-04-30-agentsec-diff-design.md`.
- Reference sample diff at `docs/samples/diff-2026-04-30-self/diff.md`
  (sample-vs-itself; useful as a renderer-output reference).

---

## [0.2.0] — 2026-04-28

### Stage G — Integration regression + docs sync (2026-04-28)

- Added `tests/integration/test_openclaw_mock.py`: an end-to-end run of
  `agentsec evaluate` against an in-process OpenClaw mock
  (`httpx.MockTransport` injected into the real `OpenClawGatewayAdapter`)
  plus a stubbed `SSHExecutor`. Asserts on dedupe, the six artifacts, the
  `Grade:` line in `combined-report.md`, and that `LOW_COVERAGE` is **not**
  emitted when every case is scored.
- Added `docs/samples/report-2026-04-28/`: committed copies of the six
  `agentsec evaluate` artifacts (`remote-report.md`,
  `server-audit-report.md`, `combined-report.md`, `findings.json`,
  `threat-intel-snapshot.yaml`, `audit-log.jsonl`) plus a `README.md` with
  regeneration instructions. A drift-guard test asserts the committed
  copies stay byte-equal to the pipeline output (refresh with
  `AGENTSEC_REFRESH_SAMPLE=1`). Markdown sample reports carry
  `**Date**: <stripped>` so the drift guard is stable across wallclock-
  minute boundaries.
- `ROADMAP.md`: ticked 阶段 G; Phase 3 retitled to "OpenClaw v1.x 后续"
  (IOC auto-feed, cross-channel attacks, Web viewer); ticked the two
  Phase 5 items Stage F shipped; renumbered the existing Phase 3/4/5 to
  4/5/6.
- `README.md`: updated Phase numbering to match the renumbered ROADMAP;
  flipped Phase 2 from 🚧 to ✅.
- `CLAUDE.md`: added sections for `agentsec evaluate` orchestrator, the
  `agentsec.scoring` module, and the three-renderer Markdown split.
- `docs/guide/writing-test-cases.md`: added `isolation:` and
  `threat_refs:` field references with YAML examples using only real
  `TI-*` IDs from `threat_intel.yaml`.
- `docs/guide/writing-adapters.md`: tightened the registry note to cover
  all three helpers (`register` / `get` / `available`).
- `src/agentsec/reports/__init__.py`: re-exports the three Markdown
  renderers + two JSON writers that Stage F added; previously only the
  deprecated shim was reachable via `from agentsec.reports import ...`.
- `scripts/check_threat_refs.py`: scoped the regex to skip namespace-glob
  references like `TI-OPENCLAW-CVE-*` (writing-test-cases.md introduces
  these to denote prefixes; possessive `++` + negative lookahead prevents
  Python regex backtracking from emitting truncated false matches).
- `pyproject.toml`: bumped version `0.1.0` → `0.2.0`.

**Audit findings closed by Stage G:** OE-AUD-014 (docs sync executed).

### Stage F — `agentsec evaluate` + scoring + reports (2026-04-28)

- New CLI subcommand: `agentsec evaluate --config openclaw-target.yaml`. Orchestrates the remote-test pipeline + server-audit pipeline + spec §9 scorer and writes the six artifacts from spec §9.3 (`remote-report.md`, `server-audit-report.md`, `combined-report.md`, `findings.json`, `threat-intel-snapshot.yaml`, `audit-log.jsonl`). Either half is optional — `cfg.remote = None` skips the remote suite, `cfg.server_audit = None` skips the SSH connection.
- Added `OpenClawTargetConfig` Pydantic model (`src/agentsec/config.py`) for `openclaw-target.yaml`. `extra="forbid"` at every nesting level rejects typos at load time. Both `remote` and `server_audit` are optional; at least one must be set.
- Added `agentsec.scoring` module implementing spec §9.1–9.3: `category_score`, `remote_score`, `dedupe_findings`, `server_score`, `coverage_triple`, `is_low_coverage`, `combined_score`, `grade_letter`, top-level `score_evaluation`. Coverage triple uses `planned` (load-time count) as denominator, not `executed` — closes OE-AUD3-006.
- `combined_score` reweights to 100% when one of remote/server is unavailable; both unavailable yields `None` rather than a fake `0` — closes OE-AUD-009.
- Identical findings (matching `Finding.fingerprint`) collapse before scoring and before writing `findings.json` — closes OE-AUD2-011.
- `verdict_coverage < 0.7` or `error_rate > 0.1` → grade is `INSUFFICIENT_COVERAGE` (not A/B/C/D/F), and the combined report carries a `LOW_COVERAGE` banner.
- Split `agentsec/reports/markdown.py` into three renderers: `render_remote_report`, `render_server_audit_report` (lifted from inline `cli.py:server_audit` builder), `render_combined_report`. The legacy `render_markdown_report(name, results)` is kept as a deprecated shim for backward compatibility (removes in v0.3).
- Added `agentsec/reports/json_report.py` with `write_findings_json` (sorted-keys, stable diffs) and `write_threat_intel_snapshot` (projects only the TI rows referenced by this run).
- Closed Stage C M4 carryover: replaced the inline `_UnreachableLLM` sentinel in `cli.py:run` with a real `NoOpJudge` subclass under `agentsec/evaluator/no_op_judge.py`. The new judge subclasses the `Judge` ABC and raises `NoOpJudgeInvoked` (typed `RuntimeError` subclass) if dispatched to.
- Pydantic models added by Stage F (`CoverageTriple`, `CombinedScore`, `EvaluationScore`) are `frozen=True` so report renderers receive immutable snapshots.

**Audit findings closed by Stage F:** OE-AUD-009 (combined-score reweight when one half is None), OE-AUD2-011 (server-side fingerprint dedupe in scorer + zero-denom category handling + severity-factor aggregation), OE-AUD3-006 (coverage denom = `planned`).

### Stage E — server-audit complete (2026-04-27)

- Added `PlatformProfile` (`src/agentsec/audit/platform_profile.py`) with `LINUX` and `MACOS` instances; the orchestrator now detects platform via `uname -s` and feeds the profile into every `CheckContext`. Stage D checks were refactored to consume `ctx.profile.paths` — no hard-coded `/var/log/openclaw/` or `~/.openclaw/` strings remain in `src/agentsec/audit/checks/`.
- Implemented remote-`$HOME` resolution at orchestrator startup (ADR-0004): `resolve_remote_home(user, profile)` + `materialize_paths(profile, remote_home)` rewrite `~/...` profile paths to absolute paths before any check runs, and `PathMatcher` accepts a `remote_home` so `allowed_paths` matching uses the same home as the remote shell. Closes a silent-failure mode where `find '~/.openclaw/'` returned exit=1 because `shlex.quote` blocked tilde expansion.
- Replaced `tunnel.py` Stage D stub with a real paramiko `direct-tcpip` local-forward; `--skip-active-test` removed from the `agentsec server-audit` CLI; `active_test` always registers and skips cleanly when the tunnel fails.
- Six new checks: `exposure_scan`, `filesystem`, `process_forensics`, `credential_audit`, `plugin_static`, `log_review`. Total audit coverage now 10 checks (Stage D's 4 + Stage E's 6).
- IOC layer (`src/agentsec/audit/ioc/`):
  - `cve_database.json` — projection of `TI-OPENCLAW-CVE-*` entries from `threat_intel.yaml`, generated by `scripts/build_cve_db.py`. CI gates on `--check` to keep the file in sync.
  - `clawhavoc_skills.json` — 5 hand-seeded ClawHavoc-family skill fingerprints; consumed by `plugin_static`.
  - `attack_signatures.yaml` — 5 hand-seeded grep patterns; consumed by `log_review`. Patterns must not contain shell metachars (the policy rejects the resulting grep argv otherwise).
- `version_patch` Findings now include `cve_db_url` / `cve_db_confidence` from `cve_database.json` for traceability.
- macOS CI job added (`test-macos` in `.github/workflows/ci.yml`); Linux + macOS matrix both green.
- Hygiene: orchestrator now records exception type in `report.errors` (e.g. `RuntimeError: ...`); CLI report adds `<!-- meta:errors count:N -->` header; `_REQUIRED_PATHS` guard refactored from per-check inlined for-loops into `Check.check_required_paths()` base method.

**Deferred to vNext (intentional v0.2.0 scope cut):**
- AST-grep danger-pattern detection in `plugin_static`.
- WebSocket endpoint probing in `exposure_scan`.
- `agentsec ioc-update` (auto-feed pull) — committed static files only in v0.2.0.
- Non-standard remote home directories — `resolve_remote_home` uses convention table (Linux: `/home/<user>`/`/root`; macOS: `/Users/<user>`/`/var/root`); deployments using `nsswitch`/LDAP/`ChrootDirectory` need vNext's `getent passwd` policy entry.

### Added (Stage D)
- `src/agentsec/audit/` server-audit package: `SSHExecutor` (paramiko + policy enforcement), `CommandPolicy` + `path_matcher` + `metachar_guard` + `args_schema` (spec §6.1), `Authorization` with seven-step HMAC-SHA256 validate (spec §6.4), canonical `Finding` with stable fingerprint, `SnapshotCollector` (module-only; runner wiring deferred to Stage E), `Redactor`-fronted audit-log.jsonl, `tunnel.py` stub (real local-forward in Stage E), `server_audit` orchestrator with per-pass fingerprint dedupe.
- Four P0 audit checks: `native_audit` (consumes `openclaw security audit --json`), `config_audit` (against `config_baseline.yaml`), `version_patch` (PEP-440 specifier match against `threat_intel.yaml`), `active_test` (canary suite through SSH local-forward).
- New CLI subcommand: `agentsec server-audit` — host / user / key / consent-file / output flags, AUTHORIZATION.txt gate, low-assurance banner.

### Fixed (Stage D post-review)
Found by ultrareview on PR #4 (10 findings, all addressed before merge).

- **bug_009** (`pyproject.toml`): declared `packaging>=22` as a runtime dep. `version_patch` imported it but it was only present transitively via pytest in `[dev]`, so wheel installs hard-failed at `agentsec --help` with ImportError.
- **bug_001** (`authorization.py` / `cli.py`): path-normalize `report_output_path_prefix` via `Path()` equality so the documented `--output ./report-2026-04-27/` invocation matches the example AUTHORIZATION.txt prefix on first try (Typer parses `--output` as `Path()`, which strips both `./` and the trailing slash; the previous byte-exact compare rejected the documented form).
- **bug_007** (`authorization.py`): reject naive datetimes in `Authorization.load()` with an explicit `AuthorizationError` rather than letting `validate()` raise a bare `TypeError` that the CLI doesn't catch — preserves the seven-step gate's "predictable AuthorizationError" contract.
- **bug_006** (`native_audit.py`): pipe `title`, `description`, `remediation` through `ctx.redactor.redact()` symmetrically with `evidence`. Vulnerability scanners typically embed offending values in human-readable text; those fields land verbatim in `server-audit-report.md`, so leaving them un-redacted defeated the global-Redactor guarantee.
- **bug_011** (`ssh.py`): replace `stdout.read()` / `stderr.read()` with a chunked `_read_capped` loop bounded at `max_output_bytes`, applied symmetrically to stderr (which previously had no cap at all). Bounds in-memory buffering against compromised targets.
- **bug_002** (`args_schema.py` / `ssh_policy.yaml`): add `value_taking` to the flags role and consume the next argv token after each value-taking flag. `head -n 5 /file`, `tail -n 100 /file`, `stat -c %y /file` — all dormant in Stage D's four checks but Stage E's `log_review` / `exposure_scan` would have hit it on first use.
- **bug_004** (`ssh_policy.yaml`): drop dead `enforce_max_results` / `forbidden_subcommands` keys (declared but never read). Mirror in spec §6.1.
- **bug_003** (`ssh.py`): omit `argv` from audit-log.jsonl on success (it carries the same parameter content the SHA-256 design was meant to keep out of the log); keep `argv` on rejection lines for triage. Update docstring + CLAUDE.md.
- **bug_010** (`config_baseline.yaml`): correct inverted header comment ("FORBIDDEN value" → "REQUIRED value"). The check emits a Finding when `actual != must_equal`, so must_equal describes the secure state.
- **bug_012** (`active_test.py`): move `active-test-canary.yaml` into the package at `src/agentsec/audit/checks/data/` so wheel installs ship it; replace the `parents[4]` walk with `Path(__file__).parent / "data" / …`. Verified end-to-end against a built wheel.

### Added (Stage C)
- Discriminated `Assertion` union under `src/agentsec/evaluator/assertions/` covering the seven types from spec §5.4 (`response_not_contains_pattern`, `response_status_in`, `json_path_equals`, `outbound_request_not_to`, `file_not_created`, `config_key_not_changed`, `tool_event_not_invoked`).
- `DeterministicJudge` and `HybridJudge` implementing spec §5.1 tri-state aggregation (`hard_fail` / `hard_pass` / `inconclusive`); `HybridJudge` honors `always_consult_llm` without letting the LLM flip a hard-pass verdict (OE-AUD2-007).
- `JudgeRouter` dispatching per-test on `TestCase.judge_type`; CLI builds a router so deterministic-only suites no longer require `AGENTSEC_JUDGE_API_KEY`.
- Seven P0 category suites under `test_suites/openclaw/` (≥5 cases each): direct PI, indirect PI (with HTML/text fixtures), memory poisoning, tool abuse, consent bypass, auth/authz bypass, SSRF.
- Stage C end-to-end smoke `tests/integration/test_openclaw_stage_c_smoke.py` running every category suite through the full JudgeRouter pipeline against a mocked OpenClaw.

### Changed (Stage C)
- `TestCase.assertions` is now `list[Assertion] | None` (was `list[dict[str, Any]]`); unknown `type` strings and extra fields are rejected at load time.
- `runner.py` distinguishes adapter-side errors (`error="adapter: ..."`) from judge-side errors (`error="judge: ..."`) and fixture/runtime errors (`error="runtime: ..."`) so reports surface failure causes more clearly.
- New runtime dependency: `jsonpath-ng>=1.6` for `json_path_equals`.

### Fixed (Stage C post-review)
- **C1**: `AgentResponse.tool_events` (new field) now carries adapter-extracted tool invocations through to `Observation.tool_events`. `OpenClawGatewayAdapter` extracts OpenAI-style `choices[0].message.tool_calls[*]` into `[{"tool": fn.name, "args": fn.arguments}, ...]`. Pre-fix, 4 cases in `04_tool_abuse.yaml` and 2 in `05_consent_bypass.yaml` silently `hard_pass` against an always-empty list. `HttpAgentAdapter._parse_tool_events` is the override hook for non-OpenAI adapters.
- **I1**: Rewrote `06_auth_authz_bypass.yaml` from `judge_type=deterministic` + `response_status_in: [401,403]` (incorrect: `/v1/chat/completions` always returns 200) to `judge_type=hybrid` + `response_not_contains_pattern` per case + `judge_hint`, so the deterministic side flags content-shaped leaks and HybridJudge consults the LLM to disambiguate refusals that quote the prompt.
- **I2**: Tightened `tests/integration/test_openclaw_stage_c_smoke.py` to additionally assert `verdict.judge_type == case.judge_type` and that `verdict.passed` / `verdict.reasoning` are well-formed; added a parametrized regression that runs `04_tool_abuse.yaml` and `05_consent_bypass.yaml` through a tool-emitting stub adapter to prove `tool_event_not_invoked` actually fires now (prevents silent C1-style breakage).
- **I3**: Documented the `oc-ssrf-04` redirect-chain operational prereq (controlled-domain → loopback resolver mapping) inline in `07_ssrf.yaml` and in `docs/guide/writing-test-cases.md` so the suite operator knows the fixture is a no-op without name resolution / hosts override.

### Added (Stage 0/A/B)
- threat-intel source table (`src/agentsec/audit/ioc/threat_intel.yaml`) and `scripts/check_threat_refs.py` CI gate per spec §2.
- `.env.example` documents `OPENCLAW_API_KEY` and `AGENTSEC_AUTH_SIGNING_KEY`.
- Adapter registry (`agentsec.adapters.registry`) with `register/get/available`; CLI `--adapter` (default `http`) resolves through it.
- `OpenClawGatewayAdapter` posting to `/v1/chat/completions` with OpenClaw body shape (spec §5.2).
- `Fixture` model + `ServeVia` literal on `TestCase`.
- `NetworkObserverConfig` + `FixtureOnlyObserver`; CLI `--observer-mode`, `--controlled-domain`, `--same-host`. Loader rejects `outbound_request_not_to.target` outside `controlled_domains` in `fixture_only` mode (spec §5.4 / OE-AUD2-006).
- Three v1 fixture topologies in `agentsec.observability.fixture_server`: `same-host-loopback`, `reachable-url`, `ssh-port-forward` (spec §7.2; `target-local-http` removed per OE-AUD3-002).
- `FixtureRuntime` + `{{fixture_url}}` substitution + `serve_via: auto` resolution.
- Multi-turn replay loop in `runner.py` honoring `Turn.judge_after` / `sleep_ms`; per-turn observer snapshots in `Observation.outbound_requests` / `fixture_events`.
- 5-case smoke suite for OpenClaw (`test_suites/openclaw/_stage_b_smoke.yaml`) plus end-to-end integration test against a mocked `/v1/chat/completions`.
- CLI `--token-env` injects `Authorization: Bearer …` and never echoes the token to stdout.

### Changed (Stage 0/A/B)
- `HttpAgentAdapter` now sends adapter-level headers per request (instead of via the `httpx.AsyncClient` constructor), so test-injected client factories see the same headers without touching httpx internals.
- `evaluator/__init__.py` lazy-loads `LLMJudge` via `__getattr__` to break the `tests.models` ↔ `evaluator.judge` import cycle that was blocking new `observability` modules from being imported in isolation.
- `load_test_suite()` accepts an optional `observer_config` (backward compatible).

### Dependencies (Stage 0/A/B)
- Added: `aiohttp>=3.9` (fixture HTTP server), `paramiko>=3.4` (ssh-port-forward fixture + Stage D SSH).

## [0.1.0] — 2026-04-24

### Added
- 项目初始框架：`AgentAdapter`、`LLMJudge`、`runner`、Markdown 报告渲染、CLI
- 内置测试用例：prompt injection（4条）、data leakage（4条）、tool abuse（3条）
- `HttpAgentAdapter` 通用 HTTP 适配器，支持覆盖 `_build_request` / `_parse_response`

### Fixed
- `HttpAgentAdapter.send` 中缺失的 `await`，修复并发时阻塞事件循环的问题
