FR-057 手測驗收棒 — 換 session 交接(2026-07-29)

項目 內容
緣由 FR-057 八張子任務卡全部完成(修正待驗證),三 repo 程式碼已就位、單元測試綠、主 session 已做獨立抽查;尚未經 user 手測驗收。本棒接手做端到端手測與問題修正
Branch BE feature/instance-agent(10 commits 未 push)/FE feature/instance-agent(2 commits 未 push)/agent repo(4 commits,working tree 乾淨)
本棒角色 手測驗收 + 修問題,不做收尾(spec/SUMMARY/進版一律等 user 下令)
接手前必讀 本文 §0 讀序
預估時間 討論 + 升 image 約 1h;端到端手測視問題量

🧭 原始需求 / WHY

FR-057 = 在 FR-056 檢測工具整合平台上接入第二個掃描工具 OpenSCAP(CIS/STIG 組態合規稽核),並開出 connector 第三種連線型態 SSH。

大圖:FR-056(已隨 v1.11.0 上線)讓租戶設定檢測工具 → 任務按「開始執行」→ 客戶端 Agent(evidence-agent,Docker)心跳領派工 → 掃描 → 報告自動回收成任務證據。第一版只有 OpenVAS(API 型)。本案價值:①客戶多了組態合規稽核能力(不只弱點掃描)②connector 架構證明可擴(SSH 型開出後,未來 Lynis 等 CLI/SSH 工具同軌)。

關鍵設計事實(討論中 user 反覆確認過的):

  • OpenSCAP 是純 CLI 無 API、無 daemon。組態稽核必須以登入身分在目標 OS 上執行——物理限制,全業界一致。目標主機需一次性裝 openscap-scanner + SCAP content(SSG),這是官方正規用法(Red Hat Satellite 同模式),不是我們的妥協。Agent 維持 Docker 形態不變,只當掃描發起端。
  • 憑證=租戶層一組共用稽核帳號(Tenable 業界標準),金鑰/密碼二擇一推薦金鑰 + use_sudo,沿用 FR-056 既有 Fernet 加密鏈與 D9 派工下發。
  • SCAP content 用目標主機自帶的 SSG(D3),不由平台推送 → 免維護版本矩陣。
  • 證據=每台一份 HTML 原樣上傳(D4/D5)→ 牽動 result 回收鏈由單檔擴多檔(FR-057.3 的由來)。
  • D3 實作澄清(2026-07-29,commit 3a67e938:官方 oscap-ssh 腳本語意是「本機 content 推送到遠端」,與 D3 矛盾 → 定案 connector 自組 SSH 指令(paramiko)直接在目標上執行 oscap 引用目標本地 content。D3 核心價值不變。

§0 接手讀序

  1. 🔒 先懂需求 gate:本文「🧭 原始需求」全讀 + docs/features/FR-057-2607-openscap-ssh-connector/design.md §1–§2(背景 + D1–D7 + 變更紀錄的 D3 澄清)全讀
  2. 本文 §1(現況)→ §2(connector 實際行為)→ §3(本棒工作順序)
  3. 需要細節才開:design.md §4(詳細設計)/~/Projects/Billows/Audit-Manager/evidence-agent/core/task_executor_connectors/openscap.py(458 行,含大量設計註解)
  4. Notion 母案:https://app.notion.com/p/3ab346da4cd081af9cfac78397f7acfb(data source collection://23c346da-4cd0-8041-955e-000bb6976dd2

冷接自檢:①FR-057 解決什麼使用者問題?②為什麼不用官方 oscap-ssh 而自組 SSH 指令?③為什麼每台一份 HTML 而不是合併單檔?④本棒該做什麼、不該做什麼?——答不出回去讀 §0.1。

§1 現況:八張卡全完成(修正待驗證),未手測

子需求 卡號 / 子任務 Repo commit
FR-057.1 BE/FE 地基 CM-942 T-1.1 migration seed BE 768aa289 + 3e30d99d(required 語意修正)+ de979c30(setup_guide 落地鏈路)
CM-943 T-1.2 FE 設定頁欄位型態 FE d3dc91a
CM-944 T-1.3 FE 任務參數 select_or_text FE e77d303
FR-057.2 Agent connector CM-945 T-2.1 骨架 agent 2ec3c5b
CM-946 T-2.2 run() agent 21e157b
CM-947 T-2.3 probe() agent b628b1c
FR-057.3 多檔回收鏈 CM-948 T-3.1 agent 多檔上傳 agent a14ab28
CM-949 T-3.2 BE 多檔取回 BE c20b22a2

母案 CM-938 與三張子需求卡 CM-939/940/941 仍 Not started——收線時才動(等 user 下令)。

主 session 2026-07-29 獨立抽查結果(已實查,非 runner 自報):

  • agent repo working tree 乾淨,四個 commit 都在
  • 測試綠:cd ~/Projects/Billows/Audit-Manager/evidence-agent && PYTHONPATH=. poetry run pytest test/ -q122 passed, 1 skipped
  • factory core/task_executor_connectors/__init__.py_TOOL_ID_TO_CODE 已加 4: "openscap",且 .2 沒動 core/task_executor.py(檔案邊界正確)
  • 實作有落實 D3 裁決:自組 SSH 指令(paramiko)非 oscap-ssh、exit code (0,2) 視為成功、遠端 mktemp 暫存 + finally 清理且等 recv_exit_status()shlex.quote 全參數、私鑰 /dev/shm 暫存
  • ⚠️ 已知小問題(不擋事,列 follow-up):agent repo 缺 pytest.ini/conftest.py,裸跑 pytest test/ModuleNotFoundError,必須帶 PYTHONPATH=.

§2 connector 實際行為(讀 openscap.py 後寫實,供討論掃描指令用)

實際送到目標主機的掃描指令(_scan_one_host,所有參數過 shlex.quote):

sudo /usr/bin/oscap xccdf eval --profile '<profile id>' --report '<遠端 mktemp .html>' '<content 路徑>'
  • sudo 前綴只在 use_sudo=true(預設 true)時加;/usr/bin/oscap 是寫死常數,對齊 setup_guide 腳本的 sudoers 白名單 NOPASSWD: /usr/bin/oscap
  • 報告先在目標主機 mktemp --suffix=.html 建暫存(由登入帳號建立,oscap 以 root 開檔寫入不改擁有者,故登入者可讀回);掃完 SFTP stat 顯式驗可讀性(讀不到給「sudo 情境下可能是暫存檔擁有者問題」明確診斷)→ 讀 bytes → finally 一律 rm -frecv_exit_status() 等清理跑完才關連線
  • content 路徑解析_resolve_content_path):路徑以 .xml 結尾 → 直接用;否則視為目錄(含預設 /usr/share/xml/scap/ssg/content/)→ 遠端 cat /etc/os-releaseID/VERSION_ID → 組 <目錄>ssg-<id><ver>-ds.xml。版號格式:ubuntu 去點(22.042204),debian/rhel/centos/rocky/almalinux/ol/fedora 只取主版號。ID 不在表內或缺 VERSION_ID → 明確 raise 叫使用者直接填完整檔案路徑,不靜默猜檔名
  • exit code0(全 pass)/2(有 fail 但掃描成功)視為成功;其餘 raise 並附 stdout 末 500 字
  • 取消/逾時_exec_with_cancel):邊跑邊排空 stdout/stderr 避免 paramiko 管線死鎖(stderr 讀完丟棄不解析),每 0.5s 輪詢 cancel_event 與 deadline,命中則 channel.close() 並 raise ScanCancelledError/TimeoutError
  • summary_parse_stdout_summary):先把 \r 去掉再 splitlines(否則 Result\r\tpass 會被拆行破壞比對),取 Result 開頭行的值,pass/fail/notapplicable 各自計數、其餘(unknown/error/notchecked/…)一律進 error 桶;解析失敗回全零不拖垮派工。每筆 ScanResult 只放該台統計findings=failhosts_scanned=1),多台加總由 task_executor._merge_summaries() 做——connector 不可自己加總,否則 N 倍虛報
  • 多台策略:hosts 以 [,;\s]+ 正規化;單台失敗(連線/認證/執行/逾時)記 warning 續掃下一台,只有 ScanCancelledError 中止整個 run();全部失敗才整體 raise(不靜默回空)
  • 檔名OpenSCAP掃描報告_<host>_<YYYYMMDD>.html
  • 私鑰tempfile.mkstemp 寫進 /dev/shm(不存在才 fallback 系統 tmp)+ 顯式 chmod 0600 + try/finally 刪;paramiko look_for_keys=False/allow_agent=False(不撿本機其他身分)、AutoAddPolicy(第一版接受首次連線風險)
  • probe():需 params.host單數,只測一台)→ ①②連線+認證(AuthenticationException → 「認證失敗」;SSHException/OSError → 「無法連線」)③ command -v /usr/bin/oscap 空 → 「未安裝 openscap-scanner」④ test -s <解析後 content> 非 OK → 「缺少 SCAP content」。四段任一失敗即 raise 不續探

§3 本棒工作順序

  1. 與 user 討論掃描指令細節與預期問題(user 明確要求的下一個議題)——素材見 §2,重點可能落在:profile id 是否對得上目標機實際 SSG 版本、content_path 用預設目錄推斷 vs 直接填檔案、sudo 白名單只涵蓋 /usr/bin/oscap 的邊界、逾時 3600s 是否合理
  2. 升級 192.168.50.123 上的 agent image(現行部署 0.2.8 不含 openscap connector)→ 見 §5,版號 bump 與升級步驟未定,需與 user 確認
  3. 端到端手測:設定頁填憑證 → 測試連線(probe)→ 建任務選 OpenSCAP + hosts + profile → 開始執行 → 驗證每台一份 HTML 報告進證據池
  4. 發現問題 → 開 Notion case 修 → 全綠後才走收尾(spec/SUMMARY/進版),等 user 下令

§4 測試環境(user 2026-07-29 已備妥)

角色 主機
管理機 / Agent 主機 192.168.50.123(agent 跑在該機 Docker)
目標主機 ×5 192.168.50.123(agent 宿主機自己)、.151.188(DEV/STG DB)、.171.189(POC DB)
  • 各台已建 audit-scan 帳號 + 佈公鑰 + sudoers 白名單(NOPASSWD: /usr/bin/oscap)+ 裝 openscap-scanner 與 SSG content
  • 部署腳本:123 上的 ~/deploy-openscap.sh(自包含、冪等可重跑;原始檔也在 user Mac 桌面)
  • 稽核金鑰:123 上 ~/.ssh/openscap-audit-key(私鑰,要貼進 Guidant 設定頁)/.pub(已佈五台)
  • ⚠️ 已知環境事實.151/.188/.171/.189 四台共用同一組 SSH host key 指紋(同一 VM 映像克隆未重產)。connector 用 AutoAddPolicy 不會因此擋連線,但 CIS/STIG benchmark 會掃成 finding——屬預期,不是 bug

§5 Pre-flight(可直接複製)

# ① BE:branch / 未 push commits / 服務
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git rev-parse --abbrev-ref HEAD          # 應為 feature/instance-agent
git log --oneline origin/feature/instance-agent..HEAD | cat   # 應 10 筆
lsof -i :8000 | head                      # BE 是否在跑(沒跑:python main_app.py)

# ② agent repo:測試(必須帶 PYTHONPATH=.,repo 缺 conftest.py)
cd ~/Projects/Billows/Audit-Manager/evidence-agent
git status --short && PYTHONPATH=. poetry run pytest test/ -q

# ③ DEV DB 查 openscap seed(密碼查 .env DB_SECRET,勿寫進任何檔案)
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
  -c "SELECT id,code,connection_type,status FROM config.detection_tools ORDER BY id"
# 預期:4 | openscap | SSH | available(id 必須是 4,agent factory 固定映射)

psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
  -c "SELECT dt.code, s.version, s.is_current FROM config.detection_tool_param_schemas s
      JOIN config.detection_tools dt ON dt.id = s.detection_tool_id WHERE dt.code='openscap'"

# ④ 目標主機就緒驗證(在 123 上跑,逐台換 IP)
for h in 192.168.50.123 192.168.50.151 192.168.50.188 192.168.50.171 192.168.50.189; do
  echo "=== $h ==="
  ssh -i ~/.ssh/openscap-audit-key -o StrictHostKeyChecking=no audit-scan@$h \
    'sudo /usr/bin/oscap --version | head -2; ls /usr/share/xml/scap/ssg/content/ | head -5; grep -E "^(ID|VERSION_ID)=" /etc/os-release'
done

# ⑤ agent 版本 / 容器狀態(在 123 上)
docker ps --filter name=agent --format '{{.Names}}\t{{.Image}}\t{{.Status}}'

§6 Agent image 升級(待確認,以下為推測步驟

現行 123 部署的是 guidant-ai-agent:0.2.8不含 openscap connector(connector 是 0.2.8 之後才進的四個 commit)。推測路徑(開工前先與 user 確認版號與出貨方式):

# 開發機 build(依賴 .secrets/nexus.env,帳密查部署文件,不入版控)
cd ~/Projects/Billows/Audit-Manager/evidence-agent
IMAGE=guidant-ai-agent:0.2.9 ./rebuild.sh

# 出貨到 agent 主機
docker save guidant-ai-agent:0.2.9 | ssh <user>@192.168.50.123 docker load

# 123 上改 deploy/.env 的 AGENT_IMAGE 後起服務(必指定 service 名,避免 recreate 同 compose 的 DB)
docker compose up -d file-agent

待確認事項:①是否 bump 到 0.2.9(版號要同步五處:pyproject.tomldeploy/docker-compose.yml ×2、config/config.py 預設值——前次 e59c9fc 就是補這個漏)②123 是否能直接從開發機 docker save | ssh ③agent 心跳間隔吃部署機 agent.json 快取,改環境變數可能無效(memory reference_agent_heartbeat_interval_cache)。

§7 行為規範提醒

  • 不切 branch(三個 repo 都已在 feature/instance-agent,branch 不對停下問 user)
  • 不自動 push(BE 10 / FE 2 commits 未 push,等 user 明示)
  • 顯式 git add <檔名>,禁用 -am
  • 憑證絕不進版控:私鑰、DB 密碼、Nexus 帳密一律「查 .env / 部署文件」
  • 改 agent code 要 rebuild image 才生效;改 BE service code 要重啟 BE(Claude 負責重啟,FE 由 user 自理)
  • BE 出錯先看 logtail -200 log/app.log | grep -A 30 -i 'openscap\|detection\|Traceback\|ERROR',不先問 user
  • 收尾等命令:手測全綠後不自動寫 spec/SUMMARY/改 Notion 母案狀態/進版

§8 不在本棒 scope

  • install.sh 安裝精靈(user 明示最後再做)
  • Nessus / SonarQube connector、ARF findings 結構化解析、content 平台管理、host inventory 表、Push 模式(D7 明列第一版不做)
  • FR-057 全案收尾文件(spec / SUMMARY / 進版)——手測全綠 + user 下令才做
  • agent repo 補 pytest.ini/conftest.py(列 follow-up,不在本棒動)

§9 未 push commits 清單

BE feature/instance-agent(10 筆)3a67e938 D3 實作澄清/c20b22a2 T-3.2 多檔取回/3e30d99d T-1.1 required 語意修正/7e93be34 前一棒 handoff/de979c30 setup_guide 落地鏈路/92b28070 big-feature-workflow skill/768aa289 T-1.1 migration seed/8c17aefa+81301ee1 mermaid 修復/3e39607e design 初版

FE feature/instance-agent(2 筆)e77d303 T-1.3 select_or_text/d3dc91a T-1.2 設定頁欄位型態

agent(4 筆,working tree 乾淨)b628b1c T-2.3 probe/21e157b T-2.2 run/2ec3c5b T-2.1 骨架/a14ab28 T-3.1 多檔上傳

§10 給 fresh session 的超短 prompt

請讀 ~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-057-2607-openscap-ssh-connector/handoff/2026-07-29-fr057-manual-test-handoff.md,
先過「🧭 原始需求」與 §0 冷接自檢(答不出先回去讀 design.md §1-§2),
再讀 §2(connector 實際掃描指令行為)與 §5 pre-flight。
你是手測驗收棒:先跟我討論掃描指令細節與預期問題,再升 123 上的 agent image,然後端到端手測。
不做收尾(spec/SUMMARY/進版/Notion 母案)除非我明確下令;不 push、不切 branch。