Skip to content

Latest commit

 

History

History
719 lines (587 loc) · 35.6 KB

README.zh-TW.md

File metadata and controls

719 lines (587 loc) · 35.6 KB

QA Touchstone

CI License: ISC Desktop: Tauri 2 Frontend: React 19 Language: TypeScript Build: Vite Tests: Vitest Performance: k6 Data: local-first

English

QA Touchstone 是一個本機優先、為 CI 做 API 安全測試的桌面工具: 對真實 API 執行身分 × 端點的 RBAC 矩陣、BOLA/IDOR 物件授權測試與速率限制 濫用檢查,跨執行管理 findings 並與 baseline 比對,再匯出 SARIF / JUnit / HTML / JSON 產物供 pipeline 把關。圍繞這個核心,它也提供完整的支援工作台 ——Postman 相容 API client(REST 與 GraphQL)、collection 真實執行、背景 monitors、k6 效能測試、AI 測試案例產生與分流、可匯出的 API 文件——整合在 同一個 Tauri app 裡。

畫面截圖

本機優先、相容 Postman 的桌面 API QA 工作台 — API client、collection runner、 安全矩陣、monitors、效能測試與可匯出文件,全部整合在一個 app。

QA Touchstone

安全矩陣(RBAC) AI 測試產生 API client
安全矩陣 AI 測試產生 API client
產生的 API 文件 效能 / 負載測試 Realtime(WebSocket / SSE)
API 文件 效能測試 Realtime

重生指令:開著 dev server 時執行 node scripts/capture-screenshots.mjs(用系統 Chrome 透過 DevTools Protocol 驅動;介面截圖預設英文,要中文介面設 LOCALE=zh-TW)。

文件

目前範圍

目前公開版聚焦在 API 測試:

  • Generic API environments:local、staging、production,以及自訂目標
  • 透過 Tauri desktop backend 執行本機 request
  • Browser/dev fallback,方便測試穩定與快速 UI 迭代
  • Runtime data 與 credentials 儲存在本機
  • 不包含公司內部服務、內部連結,或已淘汰的非 API workflow 文件

功能

  • API client:建立 HTTP 與 GraphQL requests、切換 environments、檢視 responses、查看 call history,並把 responses 與 history 匯出成 HTML、JSON 或 CSV 報告。
  • Import/export:匯入 Postman v2.1 collections 與 OpenAPI/Swagger JSON; 也可匯出為 Postman JSON。
  • Authentication:No Auth、Bearer Token、OAuth 2.0(authorization-code、 client-credentials、password grants)、API Key、Basic Auth、AWS SigV4。
  • Variables and cookies:解析 global、collection、environment、local variables,以及動態值({{$timestamp}}、{{$guid}}、{{$randomInt}}); 透過本機 cookie jar replay 符合 domain/path 的 cookies。
  • Collection Runner:批次執行選定 requests,支援 CSV/JSON data iteration,並用 live responses 計算 assertions。
  • 安全測試(Security testing):執行身分 × 端點的 RBAC 矩陣——同一批已存 requests 用多個身分各送一次——可逐格設定 allow/deny 預期、可調的拒絕狀態碼集合, 並以 response oracles 標示敏感資料外洩與 schema 偏移。矩陣 oracle 會看 body: 回 200 但內文實際拒絕({"error":"Access denied"})會被判定為 denied,而不是 誤報的 vuln。物件層級授權(BOLA/IDOR) 測試在不同身分間替換物件 id,支援自動偵測 id 位置、可重用的跨租戶 presets,以及 避免誤判的 negative control(物件 id 回顯只在「像身分」的 key 上才算數,且 control 改用獨立的結構 oracle 評分)。速率限制 / 濫用測試在確認關卡後送出有上限的 request bursts,並依「第一個 429 出現前放行了幾個 request」把防護分級為 none / weak / strong。 單一 執行完整安全掃描(Run full security suite) 會把六個引擎當成一次已記錄的 執行依序跑完。速率限制刻意排最後一個上場:它的測法就是對 API 狂發請求, 讓最吵的測試先跑,會弄髒矩陣與 BOLA 引擎的量測結果。 其中三個引擎——輸入 fuzzing(偵測 5xx、stack-trace 洩漏、payload 反射)、 自動推導的 BFLA(OWASP API5)掃描,與 JSON-Schema/OpenAPI conformance 驗證——以純函式、完整單元測試的模組形式出貨,SARIF 規則中繼資料已備妥於報告層。 fuzzing 與 BFLA 已同時接入桌面版安全套件與 headless scan 指令;conformance 目前僅在桌面版執行(尚無 Rust port,故 CLI 不會跑它)。各表面的支援範圍見 docs/capability-matrix.md。
  • AI 安全分流(AI security triage):把整批掃描(矩陣 + 物件授權 + 速率限制) 濃縮成一份簡短、依優先序分類的清單——先看哪幾個、哪些像真的問題、哪些可能是誤判 ——僅供參考,不會更動底層 findings。
  • 發現生命週期(Findings lifecycle):抑制誤判、覆寫嚴重度、指派負責人/狀態/備註, 並將每次掃描與釘選的基準(baseline)比對——新增/延續/已消失標記,加上新增高/嚴重計數。
  • 安全報告 / CI 產物(Security reports / CI artifacts):將一次完整的 suite 執行 匯出為 JSON 產物、HTML 主管報告、JUnit XML(CI 測試檢查)或 SARIF(GitHub code scanning)——以「新增的高/嚴重發現」作為 gate,並提供三段遮蔽等級給離開本機的產物: strict 完全不含佐證、redacted 保留短遮罩值、evidence 再附上一份結構保留、 預設遮掩(mask-by-default) 的 request/response 摘要以定位每筆發現——除了發現本身那一個 葉節點外,其餘值一律型別化,確保 token、cookie、PII 永不外洩。該佐證摘要為即時生成, 僅在明確選擇時才落地進該次執行。SARIF 輸出已可直接餵 code scanning:每條 rule 帶名稱、完整描述、helpUri、CWE/OWASP tags 與 GitHub security-severity 分數,每筆 result 附 physicalLocation。
  • Monitors:可手動觸發真實 collection checks;啟用後也會在 app 執行期間依照設定的 cadence 自動執行。
  • Performance testing:產生並執行 k6 performance、load、stress tests, 提供 live metrics、SLO scoring、history 與可匯出的 reports。
  • AI assistance:從 BDD、OpenAPI、PRD 或類 PDF 文字產生分類好的測試案例; 也能針對單筆 API response 對照既有 assertions 做 review;並可隨時掃描單筆 response 找出敏感資料外洩(PII、secrets、內部或 debug 欄位)。
  • 可設定的 AI 供應商(Configurable AI provider):所有 AI 功能(測試產生、 response review、敏感資料掃描、安全分流)都跑在你選的供應商上——OpenAI, 或自訂 / 企業自管(on-prem)的 OpenAI 相容 endpoint——憑證只留在本機。 AI 功能是可選的:未設定供應商時,測試產生會 fallback 到內建的 heuristic 引擎,其餘功能完全可用。(「內建 Claude 免 key」供應商只在 UI 跑在 claude.ai Artifacts 沙箱內時可用——桌面版安裝檔中無法使用;設定頁會 即時顯示其可用狀態。)
  • AI 隱私模式(AI privacy mode):所有 AI 呼叫都經過單一 egress chokepoint, 且預設遮蔽。三段模式——full context、redacted(預設)、local only ——決定哪些資料離開裝置。redacted 在送出前於本機遮蔽 URL(去 host)、回應內容 (結構保留、值轉型別 token、保留 key 名)、headers 與識別碼(email、token、UUID、 IP、Luhn 卡號、SSN),並把 OpenAPI 規格縮成 path shape(不送真實 host、不送 example 值);local only 封鎖雲端供應商、只允許自管端點(loopback/私網/已聲明)。送出前 會顯示完整 prompt 預覽,且 CI/組織 lockdown(env QA_ALLOW_EXTERNAL_AI)可強制 關閉外部 AI。
  • Docs and codegen:產生 API docs、獨立 HTML 匯出,以及 request code snippets(cURL、Python、JavaScript、HTTPie)。
  • Realtime testing:測試 WebSocket 與 Server-Sent Events streams。
  • 雙語、可換主題的介面(Bilingual, themeable UI):完整的英文與繁體中文 介面,可在設定切換;深色 UI 提供多組 accent 配色。

使用者故事

下面用一條完整旅程把上面每一項功能串起來:你剛接手團隊的訂房服務 API, 從第一次匯入一路走到把整套檢查交給 CI。想知道 findings、指紋與 baseline 在底層怎麼運作,見資料模型(白話版)的三天示範故事。

接手——匯入而不是重建。前人留下一份 Postman collection 和一份 OpenAPI 規格。兩份檔案直接匯入,requests、資料夾與變數原樣進到 workspace;之後整理乾淨了,也隨時能匯出成 Postman JSON 還給團隊。

打通第一發。把 environment 切到 staging,設好 OAuth 2.0 (client-credentials),送出第一個 GET /bookings——回應、耗時與 call history 都留在本機。同事問怎麼呼叫,直接產生 cURL 或 Python snippet 貼給他;PM 要文件,就把整份 API docs 匯出成獨立 HTML。

把測試資料變活。動態值幫你造資料——{{$guid}} 當訂位代碼、 {{$randomInt}} 當人數——cookie jar 自動 replay 登入後的 session cookie。報表端點是 GraphQL?同一個 client 切過去就能測。

批次回歸。把冒煙測試存成 collection,交給 Collection Runner 搭配 CSV 做資料迭代,assertions 對著真實回應評分——綠燈是真的綠燈, 不是罐頭資料。

讓 AI 補位,資料不出門。把 PRD 或 OpenAPI 丟給 AI 產生分類好的 測試案例、對可疑回應做 review、掃一輪敏感資料外洩。供應商由你指定 (OpenAI 或公司自管的相容 endpoint),而且預設走 AI 隱私模式的 redacted:URL 去 host、值轉成型別 token,送出前還能看完整 prompt 預覽。

一鍵安全體檢。按下「執行完整安全掃描」,六個引擎依序上場—— RBAC 矩陣、BOLA/IDOR、fuzzing、BFLA、conformance,最吵的速率限制 壓軸——整趟記成一次執行。掃完先讓 AI 安全分流把結果濃縮成 「先看哪幾個」的優先清單。

人的判斷留下來。真問題指派給後端同事、誤判抑制掉、嚴重度該調就調; 這些決定跟著指紋走,重掃不會被洗掉。把這次結果釘成 baseline,之後 只需要盯「新增的高/嚴重」。

上線前的壓力與串流。用 k6 模擬連假訂房尖峰,live metrics 與 SLO 評分即時告訴你撐不撐得住;訂位狀態推播走 WebSocket,開 Realtime testing 盯著 stream 驗證事件順序。

交給 CI,放著讓它顧。headless CLI 在 pipeline 裡重跑 collection 與安全掃描,JUnit 進測試報告、SARIF 進 GitHub code scanning,「新增高/嚴重發現」就是 merge gate;上線後 Monitors 依 cadence 自動巡檢。而這一切——從匯入第一份 collection 到現在——都在 同一個雙語、可換主題、資料完全留在本機的桌面 app 裡完成。

架構

QA Touchstone 動態架構圖

  • Frontend:React 19 + Vite,100% TypeScript(strict)。Workspace、 request/send、monitor 狀態都放在型別化的 React context provider (src/qa/state/);共用領域型別在 src/qa/types.ts。
  • Desktop shell:Tauri 2
  • Backend commands:Rust——request execution(reqwest + 手動跟隨 redirect、 AWS SigV4,並內建 SSRF 防護:擋下對雲端 metadata 位址的簽名請求)、k6 子程序 執行器、temp-file 輔助、本機 config/data 持久化、OS keychain 機密儲存 (keyring),以及由原生儲存對話框餵路徑的文字檔儲存指令。暴露給 renderer 的指令面刻意維持最小(無 shell、無任意網路存取);停用 TLS 驗證需要 renderer 明確確認,且會留下稽核紀錄。
  • Security engine(單一事實來源):RBAC 矩陣掃描在共用的 Rust core (src-tauri/core)中透過 IPC 執行(run_security_matrix)——與 headless CLI 同一套引擎;TypeScript 引擎(src/qa/*.ts)則作為經 parity gate 把關的瀏覽器 fallback。其餘引擎(BOLA、rate-limit、BFLA、fuzz)目前在桌面版仍以 TypeScript 執行。
  • Storage:單一版本化儲存層(src/qa/storage.ts,讀取時自動 migrate 舊資料 形狀)透過 Rust 後端把關鍵 工作區資料鏡像到磁碟;cookie jar 套用完整 Public Suffix List(src/qa/psl.ts)。
  • Performance engine:k6,會 materialize 到 src-tauri/resources/
  • Tests + checks:Vitest + Testing Library 與 Rust unit tests,CI 連同 tsc --noEmit、ESLint、npm audit、cargo audit 一起把關;workflow actions 全數釘在 commit SHA。

資料模型(白話版)

findings、指紋、baseline、雙引擎與 AI 怎麼兜在一起——不用術語也能懂,附三天示範故事

所有問題回報單長得一模一樣。六個安全引擎(矩陣、BOLA、BFLA、rate-limit、 conformance、fuzz)各自找不同種類的問題,但找到的每個問題都會改寫成同一張 標準回報單——UnionFinding(src/qa/types.ts),欄位固定:哪個引擎、多嚴重、 哪條規則、在哪裡、證據是什麼。因為所有引擎講同一種格式,畫面、報告、跨執行 比對只要寫一次就能服務全部六個引擎——之後新增引擎也完全不用動它們。

每個問題都有指紋。app 會把問題的「身分」(規則+位置)雜湊成指紋 (fp),並刻意忽略每次執行會變動的部分(例如文字描述與證據);那些細節另外 用第二個雜湊(dfp)記錄。這樣就能分辨三種情況:全新的問題、同一個問題再次 出現、同一個問題但細節變了。規則改名時走別名表(src/qa/findings.ts), 所以改名後的規則永遠保有原本的指紋。

兩個存檔位,誠實比對。app 只保留兩份快照:釘選的 baseline(你認可的 狀態)與 lastRun(最新一次)。比對指紋就得出「新增/延續/已消失」標記。 每份快照還存了一個「這次測了什麼」的雜湊(scopeHash);如果這次涵蓋的端點 或身分和 baseline 不同,報告會在「已消失」標記旁邊同時亮出「範圍變了」的 警告,免得測試範圍縮水被誤當成真的修好了。

人工判讀永不遺失。分流決定——抑制誤判、覆寫嚴重度、負責人、備註——存在 自己的表裡,以指紋為 key、不屬於任何單次執行,所以重新掃描不會洗掉它們。 格式升級時,舊的分流資料會被隔離到 legacy 欄位而不是刪除;至於重掃一次就能 重生的快照,直接重置即可。

兩個引擎、一個真相。矩陣引擎存在兩份——Rust core(src-tauri/core, 單一事實來源,桌面 IPC 與 headless CLI 都用它)與 TypeScript(fallback: 瀏覽器裡由它掃描,桌面版上核心還沒接手的少數部分也暫時由它負責)。兩個 機制防止它們走鐘:其一,Rust 端只要看到 config JSON 裡有不認得的欄位就 整份拒收;其二,CI 上的 parity gate——用凍結的時鐘和固定的亂數執行 TypeScript 引擎,產出提交進 repo 的「標準答案卷」,Rust 測試必須重現一模 一樣的答案,答案過期 CI 直接失敗。secrets 只能以環境變數引用寫進 config(變數不存在是硬錯誤,不會靜默變成空字串),而 core 經 IPC 回傳的所有 內容——findings 和錯誤訊息——都會先過同一道遮蔽關卡,因為連「連線被拒」 的錯誤訊息都可能夾帶含 API key 的 URL。

AI 是乘客,不是駕駛。上面整條 finding pipeline 是完全確定性的——可選的 AI 功能只坐在它的邊緣。資料要送往 LLM 只有一扇門(src/qa/llm.ts),組 prompt 之前先遮蔽;回來的答案採防禦性解析,格式不對就收斂成空結果、絕不 重試。AI 分流與 review 的建議寫進自己的型別,永遠不碰 findings、快照或分流 記錄。唯一刻意留的例外是隨選的 AI 敏感資料掃描:它的結果會以真正的 finding 進入 pipeline,但只在你按下按鈕時發生,而且一律標記 source: 'llm'。加上 指紋本來就忽略文字描述與證據——恰好是 AI 能影響的欄位——所以 AI 的非確定 性永遠改變不了一個 finding 跨執行的身分。

示範故事・第一天——問題被抓到。你經營一個線上商店網站,後台有一個 「查看所有訂單」的功能(網址是 GET /admin/orders)。你準備了兩種測試 身分:訪客(沒登入的人)和管理員。接著在一張表格裡寫下你的期望: 訪客用這個功能應該被擋下、管理員應該可以通過。這張「身分 × 功能」的期望 表格就叫矩陣;矩陣引擎則是一個自動測試程式,它替表格裡的每一格 真的發出一次請求,再把實際結果拿來跟你的期望對答案。這次對答案發現: 管理員看得到訂單(符合期望),但訪客竟然也看得到全部訂單(跟期望不符) ——引擎就開出一張回報單,上面固定記著:哪個引擎抓到的、違反哪條 規則、發生在哪裡、多嚴重、證據是什麼(證據會先打上馬賽克)。app 還會替 每張回報單算出一個指紋——像身分證號碼,從「哪條規則+發生在哪裡」 計算出來、跟描述文字無關,所以同一個問題以後再被抓到,號碼一定相同。 同一次檢查還發現回應裡露出顧客的 email,所以總共兩張回報單。這整批結果 存成一份快照(這次檢查的存檔,並附上一個「這次檢查了哪些項目」的 範圍代碼 scopeHash)。你把這份存檔釘選成基準(baseline,以後每次 檢查都跟它比),然後做兩個人工決定:訂單問題指派給後端同事;email 那張 其實是測試用的假資料,標成「誤判、以後不用再報」。這兩個決定記在一本 獨立的小本子上,用各張回報單的指紋當索引。

示範故事・第二天——靠指紋比對。後端同事修了一半:訪客現在看到的 畫面不一樣了,但其實還是拿得到訂單資料。再檢查一次,app 把新結果拿去跟 基準比對:訂單問題的指紋跟昨天一樣(同一條規則、同一個位置),所以標成 「延續」——是老問題,不是新問題;而記錄細節的第二個號碼變了,所以 app 還能告訴你「同一個問題,只是長相變了」。email 那張又被抓到,但小本 子裡它的指紋已經標了「誤判」,所以自動保持靜音,不用重新判斷一次。另外 這次冒出一個真正的新問題:網站沒有速率限制(不會擋下短時間內狂發 請求的人)。它的指紋在基準裡找不到,所以標成「新增」。如果新問題夠 嚴重,團隊的自動把關系統(CI)就會亮紅燈,擋下這次的程式碼變更。

示範故事・第三天——防止自欺欺人。同事為了讓紅燈變綠,乾脆把「查看 所有訂單」從檢查清單裡拿掉。再檢查一次,訂單問題當然「消失」了,甚至還會 被標成「已消失」——它的指紋確實不在這次的結果裡。但 app 不會讓這筆帳就 這樣過關:這次的範圍代碼跟基準對不上,報告會在旁邊直接標明 「檢查範圍變了」,提醒大家問題消失是因為沒檢查,不是修好了。 最後你把結果匯出成標準格式的安全報告(SARIF,GitHub 可以直接顯示在 安全頁面上):報告層把快照和小本子上的人工決定合併起來,證據依你選的 遮蔽等級輸出。這就是整套資料設計從頭到尾的運作——從一次網頁請求,到 團隊把關系統的紅綠燈。

專案狀態

持續維護中。前端已全面 strict TypeScript;每次 push 都會跑 ESLint、 tsc --noEmit、Vitest 測試、npm audit 與 Rust unit tests,且 release 流程在這些未通過前不會 build 安裝檔。逐版歷史見 CHANGELOG.md(近期工作:安全矩陣引擎改由 Rust core 透過 IPC 執行、OS keychain 憑證儲存、強化 RBAC/BOLA/rate-limit oracles、三個新 安全引擎——conformance、fuzzing、BFLA——更完整的 SARIF,以及 React 19 升級)。

需求

  • Node.js 20 或更新版本(CI 使用 22)
  • npm
  • Rust toolchain,用於 Tauri commands 與 desktop builds
  • k6 由下方 setup scripts 處理

開發

常用指令

安裝 dependencies:

npm install

啟動 frontend dev server:

npm run dev

以 development mode 啟動 Tauri desktop app:

npm run tauri:dev

執行測試:

npm test

還有幾個值得認識的測試指令:

  • npm run test:gui — 跨引擎 UI 測試(Playwright:WebKit ≈ macOS WKWebView、Chromium ≈ Windows WebView2),對 production bundle 執行, 含 4 種解析度的版面矩陣。詳見 uitests/README.md。
  • npm run test:install:mac — macOS 安裝鏈冒煙測試:建置 DMG、驗證 bundle、安裝到暫存目錄、以隔離的 $HOME 啟動(隔離 Rust 側的 config/data 路徑;WebView 內部儲存由 WebKit 自行管理)並確認 app 正常起來(僅限 macOS)。

型別檢查、lint 與格式化:

npm run typecheck
npm run lint
npm run format

建置 frontend:

npm run build

建置 desktop app:

npm run tauri:build

Headless CI Runner

不開桌面 UI,在 CI 裡執行 QA Touchstone

qa-touchstone-ci 是獨立的 headless runner,專門給 CI 使用。它不連結 Tauri 或 OS keychain layer,所以可以跑在一般 GitHub Actions、GitLab CI、Jenkins 或 container job。tag release 會在桌面版安裝檔旁邊附上這些 CLI 產物:

  • qa-touchstone-ci-linux-x64.tar.gz
  • qa-touchstone-ci-macos-<arch>.tar.gz
  • qa-touchstone-ci-windows-x64.zip
  • 對應的 .sha256 checksum 檔

從包含 npm/action wrappers 的 release 開始,可以用這兩種方式安裝:

# npm package version 不含開頭的 "v"。
npx qa-touchstone-ci@<version> --version

或在 GitHub Actions 裡:

- uses: asdfghj1237890/qa-touchstone/setup-ci@vX.Y.Z
  with:
    version: vX.Y.Z

兩個 wrapper 都會下載對應的 GitHub Release asset、驗 .sha256、cache 或安裝 binary,最後執行同一個 qa-touchstone-ci。受限較嚴格的 CI 環境仍可使用手動 artifact 下載方式。release workflow 只有在 repository variable PUBLISH_NPM 為 true 且有 NPM_TOKEN 時,才會 publish npm launcher。

CI 可用的功能就是下面這組 qa-touchstone-ci 指令。它刻意比桌面 app 小: CI job 會從檔案與環境變數執行可重現的 API、安全與效能檢查,不啟動 Tauri UI, 也不使用 OS keychain。

CI 可跑的功能 指令 需要連網 需要 runner 有 k6 常見 CI 輸出
原始 smoke probe ping 是 否 human status line
Postman/OpenAPI 轉換 import 否 否 qa.json scaffold
單一 API request + assertions send 是 否 JSON result
Collection/API smoke suite run 是 否 JSON + optional JUnit XML
k6-backed performance check perf 是 是 JSON + optional k6 summary export
Security scan/report gate scan 是 否 JSON、HTML、JUnit XML、SARIF、baseline updates
BOLA config candidate helper bola-suggest 否 否 human 或 JSON candidate list

Headless CLI 不會 bundle k6。API checks、collection runs、BOLA suggestions、imports 與 security scans 只需要 qa-touchstone-ci binary; perf 指令要求 CI runner 上已安裝 k6,或用 --k6-bin 明確指定 k6 路徑。

桌面專用功能不包含在 headless artifact:沒有互動式 UI、沒有背景 monitor scheduler、沒有 OS keychain storage、沒有桌面版 bundle 的 k6 resource,也沒有 AI-assisted generation flow。CI 裡請把 secrets 放在 runner 的 secrets manager, 並在 config 用 { "env": "API_TOKEN" } 這類 environment-backed value 引用。

從本 repo 本機建置:

cargo build --manifest-path src-tauri/Cargo.toml -p qa-touchstone-ci --release
./src-tauri/target/release/qa-touchstone-ci --version

最小 CI config 範例:

{
  "version": 1,
  "environments": [{ "name": "staging", "variables": { "baseUrl": "https://api.example.com" } }],
  "identities": [
    { "id": "anon", "auth": { "type": "none" } },
    { "id": "api", "auth": { "type": "bearer", "token": { "env": "API_TOKEN" } } }
  ],
  "requests": [
    {
      "id": "health",
      "method": "GET",
      "url": "{{baseUrl}}/health",
      "assertions": [{ "type": "status", "op": "eq", "value": 200 }]
    },
    {
      "id": "admin-users",
      "method": "GET",
      "url": "{{baseUrl}}/admin/users",
      "privileged": true
    }
  ],
  "collections": [{ "id": "smoke", "requests": ["health"] }],
  "security": {
    "matrix": {
      "endpoints": ["admin-users"],
      "expect": { "admin-users": { "anon": "deny", "api": "allow" } }
    }
  }
}

逐步呼叫:

# 需要時,把 Postman / OpenAPI JSON 轉成 qa.json。
qa-touchstone-ci import --input postman.json --base-url https://api.example.com --out qa.generated.json

# 送出單一 request,輸出 machine-readable JSON。
API_TOKEN="$API_TOKEN" qa-touchstone-ci send \
  --config qa.json \
  --request health \
  --identity api \
  --env staging \
  --json

# 跑 collection,並寫出 JUnit 給 CI test results 使用。
API_TOKEN="$API_TOKEN" qa-touchstone-ci run \
  --config qa.json \
  --collection smoke \
  --identity api \
  --env staging \
  --junit reports/qa-run.xml \
  --json > reports/qa-run.json

# 跑 k6 performance check。需要 PATH 上有 k6,或傳 --k6-bin /path/to/k6。
k6 version
API_TOKEN="$API_TOKEN" qa-touchstone-ci perf \
  --config qa.json \
  --request health \
  --identity api \
  --env staging \
  --stage 30s:5 \
  --stage 1m:10 \
  --summary-out reports/k6-summary.json \
  --json > reports/k6-run.json

# 跑 security suite,並輸出 CI artifacts。
API_TOKEN="$API_TOKEN" qa-touchstone-ci scan \
  --config qa.json \
  --env staging \
  --json \
  --out reports/security.json \
  --html reports/security.html \
  --junit reports/security-junit.xml \
  --sarif reports/security.sarif \
  --fail-on high

Exit codes 固定:0 通過、1 runtime/network error、2 輸入或 config 錯誤、3 有高於或等於 --fail-on 的 security findings、4 assertions 失敗或 perf 收到 k6 非 0 結果。若要把目前掃描結果採納成 baseline,可執行 scan --baseline .qa/security-baseline.json --update-baseline;後續掃描會與該 檔案比對,只針對新增 findings gate。

perf --script-out 會把產生的 k6 script 留下來方便 review/debug。該 script 可能包含 auth headers 或 tokens;除非你的 secrets policy 允許,否則不要把它 上傳成 CI artifact。一般未指定 --script-out 時,臨時 script 會在 k6 結束後刪除。

在另一個 repo 的 GitHub Actions 使用 release artifact:

name: qa-touchstone

on:
  pull_request:
  push:
    branches: [main]

jobs:
  api-security:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    env:
      QA_TOUCHSTONE_VERSION: v0.23.0
      API_TOKEN: ${{ secrets.API_TOKEN }}
    steps:
      - uses: actions/checkout@v4

      - name: Install QA Touchstone CLI
        uses: asdfghj1237890/qa-touchstone/setup-ci@vX.Y.Z
        with:
          version: ${{ env.QA_TOUCHSTONE_VERSION }}

      - name: Run API smoke collection
        run: |
          mkdir -p reports
          qa-touchstone-ci run \
            --config qa-touchstone.json \
            --collection smoke \
            --identity api \
            --env staging \
            --junit reports/qa-run.xml \
            --json > reports/qa-run.json

      - name: Verify k6 is available
        run: k6 version

      - name: Run k6 performance check
        run: |
          qa-touchstone-ci perf \
            --config qa-touchstone.json \
            --request health \
            --identity api \
            --env staging \
            --stage 30s:5 \
            --summary-out reports/k6-summary.json \
            --json > reports/k6-run.json

      - name: Run security scan
        id: scan
        run: |
          set +e
          qa-touchstone-ci scan \
            --config qa-touchstone.json \
            --env staging \
            --json \
            --out reports/security.json \
            --html reports/security.html \
            --junit reports/security-junit.xml \
            --sarif reports/security.sarif \
            --fail-on high
          code=$?
          echo "exit_code=$code" >> "$GITHUB_OUTPUT"
          exit 0

      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: reports/security.sarif

      - name: Upload QA artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: qa-touchstone-reports
          path: reports/

      - name: Fail on QA Touchstone gate
        if: steps.scan.outputs.exit_code != '0'
        run: exit ${{ steps.scan.outputs.exit_code }}

正式環境若有嚴格 supply-chain 要求,建議把範例中的 Actions 版本改成固定 commit SHA。

k6 Binary

k6 setup 與 release 驗證

Performance page 會透過 k6 執行真實 load tests。k6 binary 體積較大且與平台相依, 所以不會 commit 到 repo。

Setup script 會把 k6 materialize 到 src-tauri/resources/:

  • Dev:npm run setup:k6 如果已存在 k6 就不動;否則從 PATH 複製 k6, 或下載 pinned release。
  • Release:npm run setup:k6:release 會下載符合 OS/arch 的官方 artifact, 使用 scripts/setup-k6.mjs 內建的 SHA256 checksums 驗證,並確認 k6 version 後才 bundle。

手動執行:

npm run setup:k6
npm run setup:k6:release

若要更新 K6_VERSION=<x.y.z>,需要把該 release 的 checksums 加到 scripts/setup-k6.mjs 的 CHECKSUMS table。Release build 在 checksum 缺失或不一致時會 fail closed。

本機資料與 Secrets

儲存檔案與 secret 處理

Runtime configuration 儲存在本機。常見產生檔案包含:

  • config.json:application settings
  • user_data.json:關鍵工作區資料的磁碟鏡像(安全 findings 生命週期、 baseline、效能歷史、monitors)。所有讀寫經過單一版本化儲存層 (src/qa/storage.ts),webview 快取被清掉也能還原,舊資料形狀會在讀取時 自動 migrate,寫入失敗會浮出 提示而非無聲吞掉。機密(LLM API key)刻意排除在鏡像之外。

手動輸入的 AWS secret access key 不會存進 config.json:它存放在 OS keychain (Windows Credential Manager/macOS Keychain/Linux Secret Service),以 credential profile id 為 key。舊設定中的 inline secret 讀取時仍然有效,方便 平順遷移。

LLM settings 儲存在 browser localStorage。AI 隱私模式預設遮蔽:prompt 會在本機 遮蔽、並於送出前顯示預覽,才送到你選擇的 provider;local only 模式只允許自管 端點,CI/組織 lockdown(QA_ALLOW_EXTERNAL_AI)可完全停用外部 AI。 請不要 commit credentials、generated cache files、local tokens 或 machine-specific paths。

安全執行記錄只會持久化一份壓縮、已遮蔽的 snapshot——絕不儲存 request/response body。 較完整的「已遮蔽佐證摘要」僅在當前工作階段保留在記憶體,唯有你明確選擇時才會寫入 已儲存的執行(或釘選的 baseline)。

Packaging Notes

macOS、Windows 與 Gatekeeper notes

macOS build 會在 src-tauri/target/release/bundle/dmg/ 產生 .dmg。 Windows build 會同時產生 NSIS installer(-x64-setup.exe)與一個免安裝的 portable ZIP(-x64-portable.zip,內含執行檔與旁邊的 resources/k6.exe)。 本機要組 portable ZIP 可執行 npm run package:portable,輸出到 dist/ (與 release workflow 同一套步驟)。 macOS k6 binary 會 bundle 到 app 的 Contents/Resources/resources/k6,所以 Performance testing 不需要額外安裝 system k6。

目前 macOS build 尚未使用 Apple Developer ID sign/notarize。第一次啟動時, Gatekeeper 可能會擋下。可以右鍵 app 選 Open,或清除 quarantine:

xattr -dr com.apple.quarantine "/Applications/QA Touchstone.app"

若要更廣泛發佈,請 sign 並 notarize build。

Credits

  • 效能測試由 Grafana k6 (grafana/k6)提供,採 AGPL-3.0 授權。官方 k6 binary 會經下載、SHA256 驗證後 bundle 到 app 的 resources/ 目錄(見 k6 Binary 與 Packaging Notes)。它以獨立執行檔 的方式被 app 呼叫,並未連結進 QA Touchstone 本體。感謝 Grafana Labs 團隊與 k6 contributors。
  • 以 Tauri 與 React 打造。

各商標與專案名稱歸其各自所有者所有。

License

ISC