harden released pages and archive openspec change

This commit is contained in:
egg
2026-02-23 17:48:32 +08:00
parent 6e2ff9813e
commit e5d7700b36
47 changed files with 2126 additions and 141 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-23

View File

@@ -0,0 +1,90 @@
## Context
Released 頁面已直接使用於生產,且現行部署為單層對外服務(無反向代理)。現況存在多個交叉風險:
- JSON 解析錯誤可能透過全域 exception handler 回落為 500。
- 部分高成本查詢端點缺乏批量輸入與查詢筆數上限。
- rate-limit client key 可能受 `X-Forwarded-For` spoofing 影響。
- 設定載入在缺漏時存在偏寬鬆預設(含 API 可見性、環境模式)。
- 記錄連線 URL 時可能暴露敏感資訊。
- 前端仍有 inline handler 字串插值路徑。
本變更屬跨模組 hardeningroutes/core/config/frontend/tests且要求在不破壞 Released 正常流程下補齊安全與穩定性基線。
## Goals / Non-Goals
**Goals:**
- 將 Released 高風險端點的輸入錯誤語義固定為可預期 4xx。
- 對 batch / detail 查詢導入可設定的硬上限與拒絕策略。
- 在無 proxy 預設下建立正確的 rate-limit 信任邊界。
- 將生產安全設定調整為 fail-safe 預設並加入啟動檢查。
- 移除已知前端 inline 插值風險點並補強測試,確保無回歸。
**Non-Goals:**
- 不重寫 Released 頁面的商業邏輯或資料模型。
- 不改動 Oracle schema 或新增外部服務。
- 不一次性移除全站所有 legacy inline script以風險最高路徑優先
## Decisions
### Decision 1: 建立一致的 JSON 輸入驗證邊界,將解析失敗明確轉為 4xx
- 選擇:在 Released 相關 JSON routes 採一致的 request parsing helper含 content-type 與 malformed JSON 驗證),回傳 400/415僅真正未預期例外才走 500。
- 理由:修正「客戶端錯誤被誤判為服務端錯誤」並提升可觀測性。
- 替代方案:維持各 route 自行 `get_json()` + 全域 handler。
- 未採用原因:行為不一致且易再次回歸 500。
### Decision 2: 以設定驅動的輸入預算input budget治理高成本端點
- 選擇:新增集中化上限設定(例如 `QUERY_TOOL_MAX_CONTAINER_IDS``RESOURCE_DETAIL_MAX_LIMIT``MAX_JSON_BODY_BYTES`route 先驗證再呼叫 service。
- 理由:避免 hardcode 分散、便於環境調優與壓測。
- 替代方案:在 service 層被動截斷或依 DB timeout 自然保護。
- 未採用原因:無法在入口即時拒絕,仍浪費應用資源。
### Decision 3: 以「預設不信任 proxy headers」實作 rate-limit identity
- 選擇:新增 `TRUST_PROXY_HEADERS=false` 預設;只有顯式開啟且來源符合 trusted proxy 條件時才使用 `X-Forwarded-For`
- 理由:符合當前無反向代理部署現況,避免 IP spoofing 使限流失效。
- 替代方案:永遠信任 XFF。
- 未採用原因:對外直連部署下可被任意偽造。
### Decision 4: 生產安全設定 fail-safe 與敏感資訊遮罩
- 選擇:`api_public` 缺值或配置錯誤時預設 false`SECRET_KEY` 等關鍵安全變數缺失時拒絕啟動或進入明確受限模式;所有 URL 型密鑰資訊在 log 遮罩。
- 理由:把「配置失誤」從安全事件轉為可診斷的啟動錯誤。
- 替代方案:保留寬鬆 fallback例如預設公開 API
- 未採用原因:與生產最小暴露原則衝突。
### Decision 5: 前端高風險 inline handler 先行替換為安全事件綁定
- 選擇:針對 Released 且已觀察到風險的 job-query 動作欄位,改為 data attribute + addEventListener避免 raw 字串 `onclick` 插值。
- 理由:以最小變更降低 XSS/斷裂風險且不影響 UX。
- 替代方案:一次性重構所有頁面事件綁定。
- 未採用原因:變更面過大,不利快速風險收斂。
### Decision 6: 以「負向測試 + 既有契約測試」雙軌防回歸
- 選擇:新增 hardening 專屬負向測試invalid JSON、超量輸入、限流來源、secret redaction並保留既有 released route 正向契約測試,兩者皆納入 CI gate。
- 理由:確保防護生效且既有功能不被破壞。
- 替代方案:僅補單元測試或手動驗證。
- 未採用原因:無法長期防止行為漂移。
## Risks / Trade-offs
- [Risk] 新增 4xx 驗證可能影響少量既有錯誤處理流程 → Mitigation: 僅對 JSON-only endpoint 啟用,並以契約測試固定成功路徑。
- [Risk] 輸入上限過低可能影響查詢體驗 → Mitigation: 上限參數化並透過壓測/實際流量校準。
- [Risk] fail-safe 設定可能在配置不完整時阻擋啟動 → Mitigation: 發布前檢查清單與啟動時清楚錯誤訊息。
- [Risk] 前端事件綁定改動造成局部互動差異 → Mitigation: 補 UI 行為測試與手動 smoke 驗證。
## Migration Plan
1. 新增設定鍵與預設值輸入上限、proxy trust、安全啟動檢查保留清楚註解與環境文件。
2. 先改 route 層 JSON 驗證與批量上限檢查,再補 service 防線(雙層保護)。
3. 更新 rate-limit client identity resolver預設走 `remote_addr`
4. 加入 Redis URL log redaction 與 page registry fail-safe 預設。
5. 調整 job-query 前端事件綁定,移除高風險 inline 插值。
6. 補齊測試:負向 API、限流信任邊界、設定 fail-safe、log redaction、既有 released route 契約。
7. CI 全綠後部署;若出現非預期拒絕,僅允許透過設定值調整上限,不回退安全語義。
Rollback Strategy:
- 若發生突發相容性問題,優先調整上限配置與 trusted proxy 配置;
- 嚴禁回退到「信任任意 XFF」或「invalid JSON 回 500」行為
- 必要時暫時放寬單一端點上限,但保留防護機制本身。
## Open Questions
- `container_ids``resource detail limit` 的正式預設值是否以現網 P95 請求分佈定版(例如 200 / 500
- trusted proxy 是否需要 CIDR allowlist而非單純 bool以支援未來拓樸演進

View File

@@ -0,0 +1,37 @@
## Why
Released 頁面目前直接套用到生產環境,且部署型態為無反向代理的單層對外服務;現況在 API 輸入驗證、流量防護、設定安全預設、與錯誤處理上仍有可導致 500、資源耗盡或安全邊界被繞過的風險。需要以一次性治理方式補齊基線並建立可重複執行的無回歸驗證避免修正後再次退化。
## What Changes
- 統一 Released 頁面相關高成本 API 的輸入驗證與錯誤語義:非 JSON 或格式錯誤請求回覆 4xx不再落入 500。
- 為 query-tool 與 resource 等批次/明細查詢加入明確上限(批量 ID、limit、payload size與拒絕策略降低 DoS 與慢查風險。
- 強化 rate-limit 客戶端識別信任邊界:在無 trusted proxy 情境下不可直接信任 `X-Forwarded-For`
- 對生產安全設定採 fail-safe 預設:`api_public``FLASK_ENV``SECRET_KEY`、Redis URL log masking 等。
- 收斂前端可注入風險(如 inline handler 字串插值)與 CSP 風險設定,降低 XSS 面。
- 建立 Released 頁面專屬無回歸驗證矩陣(正向、負向、壓力邊界、契約),納入 CI gate。
## Capabilities
### New Capabilities
- `released-pages-production-hardening`: 定義 Released 頁面在生產環境的輸入驗證、資源保護、信任邊界、安全預設與回歸防線要求。
### Modified Capabilities
- None.
## Impact
- Affected code:
- `src/mes_dashboard/routes/job_query_routes.py`
- `src/mes_dashboard/routes/query_tool_routes.py`
- `src/mes_dashboard/routes/resource_routes.py`
- `src/mes_dashboard/routes/hold_routes.py`
- `src/mes_dashboard/routes/wip_routes.py`
- `src/mes_dashboard/core/rate_limit.py`
- `src/mes_dashboard/core/redis_client.py`
- `src/mes_dashboard/config/settings.py`
- `src/mes_dashboard/app.py`
- `frontend/src/job-query/main.js`
- `data/page_status.json`
- APIs/routes: Released route 對應 API包含 `/api/query-tool/*`, `/api/job-query/*`, `/api/resource/*` 等)會新增/明確化 4xx 與 429 邊界行為。
- Tests/quality gates: 新增與擴充 Released 頁面 API 的負向驗證、限流、上限邊界與模板整合回歸測試CI 需納入通過條件。

View File

@@ -0,0 +1,81 @@
## ADDED Requirements
### Requirement: Released Query APIs SHALL Return 4xx for Invalid JSON Inputs
Released 頁面對應的 JSON API 在收到非 JSON、Malformed JSON、或型別不符 payload 時MUST 回覆可預期的 4xx 錯誤,且 MUST NOT 因 JSON 解析失敗回落為 500。
#### Scenario: Non-JSON request to JSON-only endpoint
- **WHEN** client 以 `Content-Type: text/plain` 或缺少 JSON body 呼叫 JSON-only endpoint例如 `/api/query-tool/*``/api/job-query/*``/api/resource/detail`
- **THEN** endpoint MUST 回覆 400 或 415並提供一致的錯誤訊息
- **THEN** service layer MUST NOT 執行高成本查詢
#### Scenario: Malformed JSON payload
- **WHEN** client 送出無法解析的 JSON 內容
- **THEN** endpoint MUST 回覆 400
- **THEN** response MUST 指出 payload 格式錯誤,而非 generic 500
### Requirement: High-Cost Batch Inputs SHALL Enforce Hard Upper Bounds
Released 頁面高成本查詢端點 MUST 對批量輸入與查詢筆數上限施加硬限制,避免單次請求造成過量資料讀取或計算。
#### Scenario: Query-tool batch container IDs exceed limit
- **WHEN** `container_ids` 數量超過設定上限
- **THEN** endpoint MUST 回覆 400 或 413且 MUST 附帶可操作的上限資訊
- **THEN** backend MUST NOT 執行 Oracle/Redis 高成本查詢流程
#### Scenario: Resource detail limit exceeds limit
- **WHEN** `/api/resource/detail``limit` 超過設定上限
- **THEN** endpoint MUST 拒絕請求或安全夾制至上限,並在契約中明確定義行為
- **THEN** response 行為 MUST 於測試中固定化,避免版本漂移
### Requirement: Rate-Limit Client Identity SHALL Respect Trust Boundary
Rate limiting 的 client identity 解析 MUST 依部署信任邊界運作,未啟用 trusted proxy 時 MUST NOT 直接信任 `X-Forwarded-For`
#### Scenario: Direct internet deployment without reverse proxy
- **WHEN** 服務直接對外且未啟用 trusted proxy 模式
- **THEN** rate-limit key MUST 使用 `remote_addr`(或等價來源)
- **THEN** 來自 request header 的 `X-Forwarded-For` MUST 被忽略
#### Scenario: Deployment with trusted reverse proxy enabled
- **WHEN** 系統明確配置 trusted proxy 名單或模式
- **THEN** rate-limit key MAY 使用 `X-Forwarded-For` 的可信 client IP
- **THEN** 非可信來源 MUST 回退至 `remote_addr`
### Requirement: Production Security Defaults SHALL Fail Safe
生產設定在缺漏或格式錯誤時 MUST 採 fail-safe 預設,避免 API 無意外暴露或低安全模式啟動。
#### Scenario: page status config missing or invalid
- **WHEN** `page_status.json` 缺失、破損或缺少 `api_public` 設定
- **THEN** runtime MUST 預設為 API 非公開(`api_public=false`
- **THEN** 需要明確配置才可開啟公開 API 行為
#### Scenario: runtime environment variables incomplete
- **WHEN** 生產啟動缺少關鍵安全變數(例如 `SECRET_KEY`
- **THEN** 系統 MUST 以安全方式拒絕啟動或進入受限模式,且輸出可診斷訊息
### Requirement: Sensitive Configuration Values SHALL Be Redacted in Logs
任何含憑證的連線字串(例如 Redis URL在 log 輸出時 MUST 進行遮罩,避免密碼外洩。
#### Scenario: Redis URL includes password
- **WHEN** 應用程式記錄 Redis 連線設定
- **THEN** log 中的 URL MUST 隱藏密碼(例如 `redis://***@host:port/db`
- **THEN** 原始明文密碼 MUST NOT 出現在任何應用層日誌
### Requirement: Released Frontend Views SHALL Avoid Unsafe Inline Interpolation
Released 頁面前端 MUST 避免將不受信資料直接插入 inline JavaScript 或 HTML 屬性字串,降低 XSS 與 handler 斷裂風險。
#### Scenario: Rendering action controls with user-derived values
- **WHEN** 前端渲染按鈕或互動控制(例如 job-query 操作欄)且內容含資料列值
- **THEN** MUST 透過安全資料綁定data-* attribute 或事件監聽)實作
- **THEN** MUST NOT 依賴 raw string `onclick="...${value}..."` 拼接
### Requirement: Released Hardening SHALL Be Protected by Regression Gates
本次 hardening 的行為 MUST 由自動化測試固定,並納入 CI gate避免日後回歸。
#### Scenario: Negative-path regression suite execution
- **WHEN** CI 執行 Released 頁面 API 測試
- **THEN** MUST 覆蓋 invalid JSON、超量輸入、rate-limit、security default、與 log redaction 斷言
- **THEN** 任一關鍵斷言失敗 MUST 阻擋合併
#### Scenario: Existing released behavior parity
- **WHEN** hardening 變更部署後執行既有 Released route 測試
- **THEN** 成功路徑與既有回應契約 MUST 維持相容
- **THEN** 僅新增已定義的防護錯誤路徑4xx/429

View File

@@ -0,0 +1,34 @@
## 1. Config and Core Safety Baseline
- [x] 1.1 Add centralized hardening config keys (`TRUST_PROXY_HEADERS`, trusted proxy source config, JSON/body/input limits) with production-safe defaults.
- [x] 1.2 Change page registry fallback behavior so `api_public` defaults to false when config is missing/invalid.
- [x] 1.3 Implement secret redaction utility for connection-string logging and apply it to Redis URL logs.
- [x] 1.4 Enforce startup validation for required production security variables (including `SECRET_KEY`) with actionable diagnostics.
- [x] 1.5 Update environment documentation (`.env.example`/README/deploy docs) to match new hardening settings.
## 2. Released API Input Validation and Budget Guards
- [x] 2.1 Introduce a shared JSON request parsing/validation helper and adopt it in released JSON-only endpoints (`query-tool`, `job-query`, `resource` related routes).
- [x] 2.2 Ensure invalid/malformed/non-JSON payloads return deterministic 400/415 and do not fall through to generic 500 handlers.
- [x] 2.3 Add configurable hard caps for query-tool batch inputs (including `container_ids`) and reject overflow requests before service execution.
- [x] 2.4 Add configurable `limit` bounds for `/api/resource/detail` and normalize/reject invalid pagination limits consistently.
- [x] 2.5 Fix released route numeric query parsing edge cases to avoid `TypeError`/500 regressions.
## 3. Rate-Limit Trust Boundary Hardening
- [x] 3.1 Refactor rate-limit client identity resolution to ignore `X-Forwarded-For` by default and use `remote_addr` in direct-exposure deployments.
- [x] 3.2 Add trusted-proxy mode behavior so forwarded IP is used only when explicit trust configuration is enabled.
- [x] 3.3 Add tests for spoofed header attempts, direct mode behavior, and trusted-proxy behavior.
## 4. Frontend Injection-Surface Reduction
- [x] 4.1 Refactor `job-query` action rendering to remove raw inline `onclick` interpolation and use safe event binding/data attributes.
- [x] 4.2 Review and tighten applicable CSP/script-safety configuration for released routes without breaking current module/fallback loading.
- [x] 4.3 Add frontend/template tests to lock down safe rendering behavior for quoted/special-character data.
## 5. Regression Gates and Verification
- [x] 5.1 Add negative-path tests for invalid JSON, oversized batch input, bounded `limit`, and no-service-call-on-reject behavior.
- [x] 5.2 Add config hardening tests for `api_public` fail-safe fallback, production env validation, and Redis URL redaction.
- [x] 5.3 Run released-route focused pytest suite and update/repair affected contract tests to reflect explicit new 4xx/429 boundaries only.
- [x] 5.4 Ensure CI requires the new hardening test set to pass before merge.