feat(security): 產生高價值配置 owner packet 草案 [skip ci]
This commit is contained in:
@@ -1,3 +1,43 @@
|
||||
## 2026-06-11|IwoooS 高價值配置 Owner Packet 草案
|
||||
|
||||
**背景**:P0.2 已建立高價值配置變更 Gate,但若只有分類結果,owner 仍需要人工整理欄位。本階段把分類結果轉成 owner response packet 草案,並對齊 S4.9 canonical owner response envelope,避免高價值配置流程和 S4.9 欄位分裂。
|
||||
|
||||
**同步狀態:**
|
||||
- 開工前 `gitea/main` 先前進到 `edbb1194 chore(cd): deploy 6bae94f [skip ci]`,只改 `k8s/awoooi-prod/kustomization.yaml`。
|
||||
- 隨後遠端前進到 `ccf87213 chore(cd): trigger Alertmanager context repair deploy`;本地已 fast-forward 對齊後才開始 P0.3。
|
||||
- 驗證前遠端再前進到 `8c11af7c feat(governance): 定義 Agent 主動溝通學習契約`;本地以 stash → fast-forward → stash pop 合併,保留雙方 `docs/LOGBOOK.md` 內容。
|
||||
|
||||
**完成內容:**
|
||||
- 新增 `scripts/security/high-value-config-owner-packet.py`,讀取 `high-value-config-change-gate` JSON,為 impacted categories 產生 owner response packet 草案。
|
||||
- 新增 `docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md`,固定 canonical 欄位、allowed decisions、packet 狀態、禁止事項與完成度。
|
||||
- 新增 `docs/security/high-value-config-owner-packet.snapshot.json`,本階段 `packet_count=1`、`c0_packet_count=0`、`c1_packet_count=0`、`request_sent_count=0`、`received_response_count=0`、`accepted_response_count=0`、`runtime_gate_count=0`。
|
||||
- 更新 `scripts/security/high-value-config-change-gate.py`,將 `owner_role_team` 對齊為 S4.9 canonical `owner_role_or_team`,並保留 `owner_role_team`、`owner_role`、`owner_team`、`responsible_team` 等 alias。
|
||||
- 更新 `docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md` 與 `docs/security/high-value-config-change-gate.snapshot.json`,同步 canonical 欄位與 P0.3 變更檔分類;目前仍只命中 C3 security evidence / tooling。
|
||||
- 更新 `docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md`,將 Gate → owner response packet 草案完成度列為 `100%`,canonical owner 欄位對齊列為 `100%`,owner response request / received / accepted 仍列為 `0%`。
|
||||
|
||||
**本地驗證:**
|
||||
- `python3 -m py_compile scripts/security/high-value-config-change-gate.py scripts/security/high-value-config-owner-packet.py` 通過。
|
||||
- `python3 -m json.tool docs/security/high-value-config-change-gate.snapshot.json` 通過。
|
||||
- `python3 -m json.tool docs/security/high-value-config-owner-packet.snapshot.json` 通過。
|
||||
- `python3 scripts/security/high-value-config-change-gate.py --root . ... --output docs/security/high-value-config-change-gate.snapshot.json` 通過,`changed_file_count=8`、`impacted_c0_category_count=0`、`impacted_c1_category_count=0`。
|
||||
- `python3 scripts/security/high-value-config-owner-packet.py --root . --gate-report docs/security/high-value-config-change-gate.snapshot.json --output docs/security/high-value-config-owner-packet.snapshot.json` 通過,`packets=1`、`c0=0`、`c1=0`、`runtime_gate=0`。
|
||||
- C0 Nginx 範例 owner packet smoke 通過:`infra/ansible/roles/nginx/templates/188-all-sites.conf.j2` 產出 `nginx_public_gateway` packet,`c0_packet_count=1`、`runtime_gate_count=0`。
|
||||
- `python3 scripts/security/security-mirror-progress-guard.py --root .` 通過。
|
||||
- `python3 scripts/security/source-control-owner-response-guard.py --root .` 通過。
|
||||
- `node scripts/ci/check-gitea-step-env-secrets.js` 通過。
|
||||
- `python3 scripts/ops/doc-secrets-sanity-check.py docs .gitea` 通過,`scanned_files=646`。
|
||||
- P0 高風險字串掃描通過:關閉 SSH host key 驗證的逐字參數、舊 Gitea token、Grafana 密碼常值、舊 MinIO credential、舊 MinIO token、Prometheus inline bearer token 均未命中。
|
||||
- `git diff --check` 通過。
|
||||
|
||||
**完成度與邊界:**
|
||||
- Gate → owner response packet 草案:`100%`。
|
||||
- Canonical 欄位對齊:`100%`。
|
||||
- owner response request sent / received / accepted:`0%`。
|
||||
- runtime gate:`0%`。
|
||||
- 本階段沒有前端變更,production desktop / mobile 頁面驗證不適用。
|
||||
- 未修改 workflow、未 SSH、未讀 live Nginx、未執行 `nginx -t` / reload / restart、未 DNS / TLS / ArgoCD / kubectl / host write / secret rotation / active scan / agent-bounty runtime。
|
||||
- IwoooS 整體仍維持 `64%`;active runtime gate 仍為 `0`;owner response received / accepted 仍為 `0 / false`。
|
||||
|
||||
## 2026-06-11|IwoooS 高價值配置變更 Gate 第一波
|
||||
|
||||
**背景**:接續「所有重要配置都要被資安控管」要求,前一波已建立高價值配置清冊與 Nginx 只讀漂移偵測器。本階段補上可重跑的分類型 Gate,讓 reviewer 不必只靠人工記憶判斷變更是否碰到 Nginx、DNS / TLS、K8s、secret、workflow、runner、backup、monitoring、host service、network、AI provider 或 agent-bounty-protocol runtime 邊界。
|
||||
|
||||
@@ -78,7 +78,7 @@ python3 scripts/security/high-value-config-change-gate.py \
|
||||
|
||||
所有 C0 / C1 高價值配置變更至少要補:
|
||||
|
||||
1. `owner_role_team`
|
||||
1. `owner_role_or_team`
|
||||
2. `decision`
|
||||
3. `decision_reason`
|
||||
4. `affected_scope`
|
||||
|
||||
85
docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md
Normal file
85
docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# IwoooS 高價值配置 Owner Response Packet
|
||||
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 日期 | 2026-06-11 |
|
||||
| 狀態 | `owner_packet_draft_ready` |
|
||||
| 工具 | `scripts/security/high-value-config-owner-packet.py` |
|
||||
| 輸入 | `docs/security/high-value-config-change-gate.snapshot.json` |
|
||||
| Snapshot | `docs/security/high-value-config-owner-packet.snapshot.json` |
|
||||
| runtime gate | `0` |
|
||||
|
||||
## 1. 目的
|
||||
|
||||
P0.2 已能將變更分類成 C0 / C1 / C2 / C3。本文件與工具負責下一步:把分類結果轉成 owner response packet 草案,讓 Nginx、DNS / TLS、K8s、secret、workflow、runner、backup、monitoring、host service、network、AI provider、agent-bounty-protocol 等高價值配置變更有一致補件欄位。
|
||||
|
||||
本階段仍是只讀草案,不送 request、不收 owner response、不標記 received / accepted、不建立 action button、不接 blocking CI、不執行 runtime。
|
||||
|
||||
## 2. Canonical 欄位
|
||||
|
||||
高價值配置 packet 對齊 S4.9 canonical owner response envelope,使用以下欄位:
|
||||
|
||||
1. `owner_role_or_team`
|
||||
2. `decision`
|
||||
3. `decision_reason`
|
||||
4. `affected_scope`
|
||||
5. `redacted_evidence_refs`
|
||||
6. `followup_owner`
|
||||
7. `rollback_owner`
|
||||
8. `maintenance_window`
|
||||
9. `validation_plan`
|
||||
|
||||
`decision` 只能是 `confirm`、`defer`、`reject`、`request_more_evidence`。`confirm` 只代表 owner 對該分類補件方向確認,不代表 reload、deploy、sync、scan、payout 或 host write。
|
||||
|
||||
## 3. 指令
|
||||
|
||||
使用 committed gate snapshot 產生 owner packet:
|
||||
|
||||
```bash
|
||||
python3 scripts/security/high-value-config-owner-packet.py \
|
||||
--root . \
|
||||
--gate-report docs/security/high-value-config-change-gate.snapshot.json \
|
||||
--output docs/security/high-value-config-owner-packet.snapshot.json
|
||||
```
|
||||
|
||||
查看即時分類結果的 packet:
|
||||
|
||||
```bash
|
||||
python3 scripts/security/high-value-config-change-gate.py \
|
||||
--root . \
|
||||
--changed-file infra/ansible/roles/nginx/templates/188-all-sites.conf.j2 \
|
||||
--output /tmp/high-value-config-gate-nginx.json
|
||||
|
||||
python3 scripts/security/high-value-config-owner-packet.py \
|
||||
--root . \
|
||||
--gate-report /tmp/high-value-config-gate-nginx.json
|
||||
```
|
||||
|
||||
## 4. Packet 狀態
|
||||
|
||||
| 狀態 | 意義 | Gate 影響 |
|
||||
|------|------|-----------|
|
||||
| `draft_waiting_owner_response` | 只是草案,尚未送件或收件 | request / received / accepted 全部維持 0 |
|
||||
| `request_more_evidence` | owner 欄位缺漏或 scope 不清 | 不增加 accepted |
|
||||
| `quarantine_sensitive_payload` | 疑似含 secret、token、cookie、private key、未脫敏 evidence | 不保存 raw payload |
|
||||
| `reject_execution_request` | 夾帶 reload、deploy、sync、host write、scan、payout 等執行要求 | 不建立 action button |
|
||||
| `ready_for_reviewer_validation` | 欄位完整且無敏感 payload / 執行要求 | 只進 reviewer checklist,仍非 accepted |
|
||||
|
||||
## 5. 絕對禁止
|
||||
|
||||
1. 不把 packet 草案當成 request sent。
|
||||
2. 不把 owner 口頭同意當成 response received。
|
||||
3. 不把 `confirm` 當成 runtime reload / deploy / scan / payout 授權。
|
||||
4. 不收 secret value、partial token、private key、cookie、session、authorization header、runner token 或 webhook secret。
|
||||
5. 不把內部工作視窗對話、抱怨或 Session 指令放入前端產品文案。
|
||||
6. 不用 packet 產生器修改 `.gitea/workflows`、K8s、Nginx、DNS、TLS、主機或 agent-bounty runtime。
|
||||
|
||||
## 6. 完成度
|
||||
|
||||
| 工作 | 完成度 | 說明 |
|
||||
|------|--------|------|
|
||||
| Gate → owner packet 草案 | `100%` | 可從 gate JSON 產生 impacted category packet |
|
||||
| Canonical 欄位對齊 | `100%` | 已對齊 S4.9 `owner_role_or_team` 等欄位,P0.2 gate 也接受 alias |
|
||||
| owner response 收件 | `0%` | 尚未送 request、尚未收到 owner response |
|
||||
| reviewer accepted | `0%` | 尚未進 reviewer checklist |
|
||||
| runtime gate | `0%` | 未授權且未開啟 |
|
||||
@@ -130,6 +130,9 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公
|
||||
| repo-only Nginx drift detector | `100%` | 已新增 `scripts/security/nginx-config-drift-detector.py` 與 repo source-of-truth snapshot |
|
||||
| 高價值配置變更分類 Gate | `100%` | 已新增 `scripts/security/high-value-config-change-gate.py`,可用 git diff 或手動檔案分類 C0/C1/C2/C3 並列出 owner / rollback / evidence / 驗證欄位 |
|
||||
| owner response evidence JSON 欄位檢查 | `70%` | Gate 可檢查必要欄位與 false flags;尚未接正式收件 API 或 AwoooP queue |
|
||||
| Gate → owner response packet 草案 | `100%` | 已新增 `scripts/security/high-value-config-owner-packet.py`,可將 impacted category 轉成 canonical owner response packet 草案 |
|
||||
| canonical owner 欄位對齊 | `100%` | 高價值配置 Gate 已對齊 S4.9 `owner_role_or_team`,並保留 `owner_role_team` 等 alias 支援 |
|
||||
| owner response request / received / accepted | `0%` | Packet 只是草案;尚未送件、尚未收件、尚未 reviewer accepted |
|
||||
| CI blocking / workflow gate | `0%` | 本階段刻意不修改 `.gitea/workflows`,避免初期資安流程摩擦過大 |
|
||||
| owner-provided live Nginx file compare | `70%` | 工具可吃 owner 匯出的 live conf 檔比較;本階段不主動 SSH 取得 |
|
||||
| live Nginx evidence collection | `0%` | 尚未 SSH / Ansible check-mode / live hash;需 owner 與維護窗口規則 |
|
||||
@@ -139,7 +142,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公
|
||||
|
||||
## 7. 下一階段優先順序
|
||||
|
||||
1. P0:把高價值配置 Gate 的分類結果接成 owner response packet 草案;先只讀,不接 blocking CI。
|
||||
1. P0:將 owner response packet 草案接入 IwoooS / AwoooP 只讀狀態,顯示 request / received / accepted 仍為 0。
|
||||
2. P0:由 owner 提供脫敏 live Nginx conf 匯出檔,重跑 compare mode;不自動覆寫、不 reload。
|
||||
3. P0:補 DNS / TLS / certbot domain inventory,先只讀,不 renew、不 reload。
|
||||
4. P0:把 workflow / runner / secret name owner response 與高價值配置 C0 gate 串成同一個 IwoooS 狀態。
|
||||
|
||||
@@ -42,6 +42,27 @@
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"category_id": "security_evidence_tooling",
|
||||
"control_tier": "C3",
|
||||
"label": "Security evidence / snapshot / guard tooling",
|
||||
"priority": "P3",
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_validation": [
|
||||
"snapshot_parse",
|
||||
"guard_smoke",
|
||||
"doc_secret_sanity",
|
||||
"no_runtime_gate_increase"
|
||||
]
|
||||
}
|
||||
],
|
||||
"matched": true,
|
||||
"path": "docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md",
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
@@ -84,6 +105,27 @@
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"category_id": "security_evidence_tooling",
|
||||
"control_tier": "C3",
|
||||
"label": "Security evidence / snapshot / guard tooling",
|
||||
"priority": "P3",
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_validation": [
|
||||
"snapshot_parse",
|
||||
"guard_smoke",
|
||||
"doc_secret_sanity",
|
||||
"no_runtime_gate_increase"
|
||||
]
|
||||
}
|
||||
],
|
||||
"matched": true,
|
||||
"path": "docs/security/high-value-config-owner-packet.snapshot.json",
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
@@ -104,6 +146,27 @@
|
||||
"path": "scripts/security/high-value-config-change-gate.py",
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"category_id": "security_evidence_tooling",
|
||||
"control_tier": "C3",
|
||||
"label": "Security evidence / snapshot / guard tooling",
|
||||
"priority": "P3",
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_validation": [
|
||||
"snapshot_parse",
|
||||
"guard_smoke",
|
||||
"doc_secret_sanity",
|
||||
"no_runtime_gate_increase"
|
||||
]
|
||||
}
|
||||
],
|
||||
"matched": true,
|
||||
"path": "scripts/security/high-value-config-owner-packet.py",
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
}
|
||||
],
|
||||
"control_category_inventory": [
|
||||
@@ -120,7 +183,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "public_gateway_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -156,7 +219,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "domain_tls_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -188,7 +251,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "gitops_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -223,7 +286,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "secret_metadata_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -258,7 +321,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "workflow_source_control_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -292,7 +355,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "public_runtime_config_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -324,7 +387,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "backup_restore_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -355,7 +418,7 @@
|
||||
"priority": "P0",
|
||||
"required_gate": "agent_bounty_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -389,7 +452,7 @@
|
||||
"priority": "P1",
|
||||
"required_gate": "monitoring_observability_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -423,7 +486,7 @@
|
||||
"priority": "P1",
|
||||
"required_gate": "host_service_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -459,7 +522,7 @@
|
||||
"priority": "P1",
|
||||
"required_gate": "network_access_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -492,7 +555,7 @@
|
||||
"priority": "P1",
|
||||
"required_gate": "ai_provider_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -523,7 +586,7 @@
|
||||
"priority": "P2",
|
||||
"required_gate": "product_surface_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -553,7 +616,7 @@
|
||||
"priority": "P3",
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -573,7 +636,7 @@
|
||||
],
|
||||
"diff": {
|
||||
"base": null,
|
||||
"changed_file_count": 5,
|
||||
"changed_file_count": 8,
|
||||
"head": "HEAD"
|
||||
},
|
||||
"execution_boundaries": {
|
||||
@@ -587,8 +650,8 @@
|
||||
"ssh_executed": false,
|
||||
"workflow_modified": false
|
||||
},
|
||||
"generated_at": "2026-06-11T12:30:00+08:00",
|
||||
"git_commit": "e1cacdf3",
|
||||
"generated_at": "2026-06-11T13:00:00+08:00",
|
||||
"git_commit": "ccf87213",
|
||||
"impacted_categories": [
|
||||
{
|
||||
"category_id": "security_evidence_tooling",
|
||||
@@ -614,7 +677,7 @@
|
||||
"complete": false,
|
||||
"invalid_false_flags": [],
|
||||
"missing_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -639,7 +702,7 @@
|
||||
"action_buttons_allowed"
|
||||
],
|
||||
"required_owner_fields": [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -651,11 +714,11 @@
|
||||
],
|
||||
"schema_version": "high_value_config_change_gate_v1",
|
||||
"summary": {
|
||||
"changed_file_count": 5,
|
||||
"changed_file_count": 8,
|
||||
"impacted_c0_category_count": 0,
|
||||
"impacted_c1_category_count": 0,
|
||||
"impacted_category_count": 1,
|
||||
"matched_high_value_file_count": 5,
|
||||
"matched_high_value_file_count": 8,
|
||||
"owner_evidence_complete": false,
|
||||
"owner_evidence_provided": false,
|
||||
"runtime_execution_authorized": false,
|
||||
|
||||
729
docs/security/high-value-config-owner-packet.snapshot.json
Normal file
729
docs/security/high-value-config-owner-packet.snapshot.json
Normal file
@@ -0,0 +1,729 @@
|
||||
{
|
||||
"allowed_decisions": [
|
||||
"confirm",
|
||||
"defer",
|
||||
"reject",
|
||||
"request_more_evidence"
|
||||
],
|
||||
"canonical_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"control_category_inventory": [
|
||||
{
|
||||
"category_id": "nginx_public_gateway",
|
||||
"control_tier": "C0",
|
||||
"label": "Nginx / reverse proxy / public route",
|
||||
"path_patterns": [
|
||||
"infra/ansible/roles/nginx/templates/*.j2",
|
||||
"infra/ansible/playbooks/nginx-sync.yml",
|
||||
"ops/nginx/**",
|
||||
"docs/runbooks/disaster-recovery/DR-Nginx.md"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "public_gateway_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"rendered_diff",
|
||||
"nginx_t",
|
||||
"affected_route_smoke",
|
||||
"admin_route_smoke_if_affected",
|
||||
"acme_path_smoke_if_affected",
|
||||
"rollback_ref"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "dns_tls_certbot",
|
||||
"control_tier": "C0",
|
||||
"label": "DNS / TLS / certbot / certificate path",
|
||||
"path_patterns": [
|
||||
"docs/runbooks/REGISTRY-CERTBOT-188.md",
|
||||
"docs/runbooks/**/*CERTBOT*.md",
|
||||
"docs/runbooks/**/*TLS*.md",
|
||||
"ops/**/*cert*",
|
||||
"ops/**/*tls*",
|
||||
"infra/**/*cert*",
|
||||
"infra/**/*tls*",
|
||||
"k8s/**/*tls*"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "domain_tls_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"domain_inventory",
|
||||
"certificate_path_check",
|
||||
"renewal_window",
|
||||
"acme_path_smoke",
|
||||
"public_https_smoke",
|
||||
"rollback_ref"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "k8s_production_gitops",
|
||||
"control_tier": "C0",
|
||||
"label": "K8s / ArgoCD / production manifests",
|
||||
"path_patterns": [
|
||||
"k8s/awoooi-prod/**",
|
||||
"k8s/argocd/**",
|
||||
"k8s/velero/**",
|
||||
"k8s/monitoring/**"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "gitops_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"gitops_diff",
|
||||
"argocd_health_readback",
|
||||
"sync_authorization_check",
|
||||
"rollback_revision",
|
||||
"post_deploy_health_if_executed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "secret_metadata",
|
||||
"control_tier": "C0",
|
||||
"label": "Secret metadata / injection / redaction",
|
||||
"path_patterns": [
|
||||
"k8s/**/*secret*",
|
||||
"k8s/**/*Secret*",
|
||||
".gitea/workflows/*.yml",
|
||||
".gitea/workflows/*.yaml",
|
||||
".github/workflows/*.yml",
|
||||
".github/workflows/*.yaml",
|
||||
"docs/runbooks/SECRETS-MANAGEMENT.md",
|
||||
"docs/security/SECRETS_REFERENCE.md"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "secret_metadata_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"secret_name_parity",
|
||||
"metadata_only_check",
|
||||
"no_secret_value_check",
|
||||
"rotation_owner",
|
||||
"injection_readback_if_deployed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "gitea_workflow_runner_source_control",
|
||||
"control_tier": "C0",
|
||||
"label": "Gitea workflow / runner / deploy key / webhook / branch protection",
|
||||
"path_patterns": [
|
||||
".gitea/workflows/**",
|
||||
".github/workflows/**",
|
||||
"ops/runner/**",
|
||||
"scripts/setup-runner*.sh",
|
||||
"scripts/**/*runner*",
|
||||
"docs/security/SOURCE-CONTROL-*",
|
||||
"docs/security/GITEA-*",
|
||||
"docs/security/GITHUB-*"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "workflow_source_control_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"workflow_diff",
|
||||
"runner_label_owner",
|
||||
"deploy_key_metadata_only",
|
||||
"webhook_metadata_only",
|
||||
"branch_protection_metadata",
|
||||
"no_token_value_check"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "public_admin_api_runtime_config",
|
||||
"control_tier": "C0",
|
||||
"label": "Public / admin / API / frontend runtime config",
|
||||
"path_patterns": [
|
||||
"apps/web/next.config.*",
|
||||
"apps/web/src/lib/config.*",
|
||||
"apps/api/src/core/config.py",
|
||||
"apps/api/src/api/v1/monitoring.py",
|
||||
"apps/api/src/middleware/**",
|
||||
"apps/web/src/middleware.*"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "public_runtime_config_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"public_url_check",
|
||||
"frontend_internal_ip_ban",
|
||||
"cors_boundary_check",
|
||||
"admin_auth_boundary_check",
|
||||
"desktop_mobile_smoke_if_frontend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "backup_restore_credential",
|
||||
"control_tier": "C0",
|
||||
"label": "Backup / restore / escrow / retention",
|
||||
"path_patterns": [
|
||||
"scripts/backup/**",
|
||||
"k8s/velero/**",
|
||||
"docs/runbooks/disaster-recovery/**",
|
||||
"docs/runbooks/**/*RESTORE*.md",
|
||||
"docs/runbooks/**/*BACKUP*.md"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "backup_restore_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"credential_absence_check",
|
||||
"restore_drill_gate",
|
||||
"retention_policy",
|
||||
"escrow_owner",
|
||||
"rollback_ref"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "agent_bounty_protocol_runtime",
|
||||
"control_tier": "C0",
|
||||
"label": "agent-bounty-protocol runtime / MCP / A2A / treasury boundary",
|
||||
"path_patterns": [
|
||||
"docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md",
|
||||
"docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json",
|
||||
"docs/schemas/agent_bounty_iwooos_onboarding_handoff_v1.schema.json",
|
||||
"agent-bounty-protocol/**"
|
||||
],
|
||||
"priority": "P0",
|
||||
"required_gate": "agent_bounty_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"repo_owner_scope",
|
||||
"runtime_gate_false",
|
||||
"no_payout_or_treasury_execution",
|
||||
"no_mcp_a2a_runtime_execution",
|
||||
"redacted_evidence_refs_only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "monitoring_alerting_observability",
|
||||
"control_tier": "C1",
|
||||
"label": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse",
|
||||
"path_patterns": [
|
||||
"ops/monitoring/**",
|
||||
"ops/alertmanager/**",
|
||||
"ops/grafana/**",
|
||||
"ops/signoz/**",
|
||||
"ops/sentry-self-hosted/**",
|
||||
"infra/langfuse/**",
|
||||
"k8s/monitoring/**"
|
||||
],
|
||||
"priority": "P1",
|
||||
"required_gate": "monitoring_observability_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"rule_diff",
|
||||
"receiver_diff",
|
||||
"reload_gate",
|
||||
"failure_notification_policy",
|
||||
"public_route_smoke_if_affected"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "docker_compose_systemd_host_config",
|
||||
"control_tier": "C1",
|
||||
"label": "Docker Compose / systemd / host service config",
|
||||
"path_patterns": [
|
||||
"docker-compose*.yml",
|
||||
"docker-compose*.yaml",
|
||||
"ops/**/docker-compose*.yml",
|
||||
"ops/**/docker-compose*.yaml",
|
||||
"scripts/reboot-recovery/**",
|
||||
"scripts/**/*.service",
|
||||
"ops/**/*.service"
|
||||
],
|
||||
"priority": "P1",
|
||||
"required_gate": "host_service_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"port_conflict_check",
|
||||
"volume_diff",
|
||||
"env_name_diff",
|
||||
"restart_window",
|
||||
"rollback_owner"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "ssh_firewall_network_access",
|
||||
"control_tier": "C1",
|
||||
"label": "SSH / sudoers / known_hosts / firewall / WireGuard / NodePort",
|
||||
"path_patterns": [
|
||||
"infra/ansible/inventory/**",
|
||||
"infra/ansible/**/*known_hosts*",
|
||||
"infra/ansible/**/*ssh*",
|
||||
"scripts/**/*ssh*",
|
||||
"scripts/**/*known_hosts*",
|
||||
"ops/**/*wireguard*",
|
||||
"ops/**/*firewall*",
|
||||
"k8s/**/*network*",
|
||||
"k8s/**/*Network*"
|
||||
],
|
||||
"priority": "P1",
|
||||
"required_gate": "network_access_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"target_whitelist",
|
||||
"host_key_policy",
|
||||
"ingress_egress_matrix",
|
||||
"rollback_owner",
|
||||
"maintenance_window"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "ai_provider_model_routing",
|
||||
"control_tier": "C1",
|
||||
"label": "AI provider / model routing / Ollama proxy / cost and privacy",
|
||||
"path_patterns": [
|
||||
"apps/api/src/services/ai_providers/**",
|
||||
"apps/api/src/services/**/*model*",
|
||||
"apps/api/src/services/**/*provider*",
|
||||
"infra/ansible/roles/nginx/templates/110-ollama-proxy.conf.j2",
|
||||
"docs/ai/**",
|
||||
"docs/**/*Ollama*"
|
||||
],
|
||||
"priority": "P1",
|
||||
"required_gate": "ai_provider_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"dry_run",
|
||||
"benchmark",
|
||||
"cost_review",
|
||||
"privacy_review",
|
||||
"fallback_order_check"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "product_surface_runtime_routes",
|
||||
"control_tier": "C2",
|
||||
"label": "AWOOOI / AwoooP / IwoooS / VibeWork / other product runtime routes",
|
||||
"path_patterns": [
|
||||
"apps/web/src/app/**",
|
||||
"apps/web/messages/*.json",
|
||||
"docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md",
|
||||
"docs/security/vibework-iwooos-onboarding-handoff.snapshot.json"
|
||||
],
|
||||
"priority": "P2",
|
||||
"required_gate": "product_surface_owner_response_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"product_boundary_check",
|
||||
"i18n_traditional_chinese_check",
|
||||
"no_internal_transcript_check",
|
||||
"desktop_mobile_smoke_if_frontend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category_id": "security_evidence_tooling",
|
||||
"control_tier": "C3",
|
||||
"label": "Security evidence / snapshot / guard tooling",
|
||||
"path_patterns": [
|
||||
"docs/security/**",
|
||||
"docs/schemas/**",
|
||||
"scripts/security/**",
|
||||
"docs/LOGBOOK.md"
|
||||
],
|
||||
"priority": "P3",
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_owner_fields": [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan"
|
||||
],
|
||||
"required_validation": [
|
||||
"snapshot_parse",
|
||||
"guard_smoke",
|
||||
"doc_secret_sanity",
|
||||
"no_runtime_gate_increase"
|
||||
]
|
||||
}
|
||||
],
|
||||
"execution_boundaries": {
|
||||
"action_buttons_allowed": false,
|
||||
"host_write_authorized": false,
|
||||
"request_sent": false,
|
||||
"response_accepted": false,
|
||||
"response_received": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"secret_value_collected": false
|
||||
},
|
||||
"generated_at": "2026-06-11T13:00:00+08:00",
|
||||
"git_commit": "ccf87213",
|
||||
"next_steps": [
|
||||
"若 packet_count > 0,將 packet 交給 owner 補 canonical 欄位;不得把草案視為已送件。",
|
||||
"若 owner 回覆含 secret 或執行要求,先 quarantine 或 reject_execution_request。",
|
||||
"只有 reviewer checklist 完成後才可進 accepted;accepted 仍不開 runtime gate。"
|
||||
],
|
||||
"packets": [
|
||||
{
|
||||
"affected_files": [
|
||||
"docs/LOGBOOK.md",
|
||||
"docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md",
|
||||
"docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md",
|
||||
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
|
||||
"docs/security/high-value-config-change-gate.snapshot.json",
|
||||
"docs/security/high-value-config-owner-packet.snapshot.json",
|
||||
"scripts/security/high-value-config-change-gate.py",
|
||||
"scripts/security/high-value-config-owner-packet.py"
|
||||
],
|
||||
"allowed_decisions": [
|
||||
"confirm",
|
||||
"defer",
|
||||
"reject",
|
||||
"request_more_evidence"
|
||||
],
|
||||
"blocked_requests": [
|
||||
"repo_create",
|
||||
"visibility_change",
|
||||
"refs_sync",
|
||||
"refs_delete",
|
||||
"force_push",
|
||||
"workflow_modify",
|
||||
"runner_enable",
|
||||
"secret_value_submit",
|
||||
"ssh_host_modify",
|
||||
"nginx_reload",
|
||||
"dns_tls_modify",
|
||||
"argocd_sync",
|
||||
"kubectl_apply",
|
||||
"active_scan",
|
||||
"agent_bounty_runtime_execute",
|
||||
"payout_or_withdrawal"
|
||||
],
|
||||
"category_id": "security_evidence_tooling",
|
||||
"control_tier": "C3",
|
||||
"false_flags": {
|
||||
"action_buttons_allowed": false,
|
||||
"active_scan_authorized": false,
|
||||
"dns_tls_change_authorized": false,
|
||||
"force_push_authorized": false,
|
||||
"host_write_authorized": false,
|
||||
"nginx_reload_authorized": false,
|
||||
"refs_sync_authorized": false,
|
||||
"request_sent": false,
|
||||
"response_accepted": false,
|
||||
"response_received": false,
|
||||
"runner_change_authorized": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"secret_value_collection_allowed": false,
|
||||
"workflow_modification_authorized": false
|
||||
},
|
||||
"field_templates": [
|
||||
{
|
||||
"field": "owner_role_or_team",
|
||||
"instruction": "填角色或團隊,不填私人帳號、密碼、token 或私人聯絡資訊。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "decision",
|
||||
"instruction": "只能填 confirm、defer、reject、request_more_evidence;不得附帶 runtime 執行批准。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "decision_reason",
|
||||
"instruction": "填脫敏短理由;不可貼 raw log、raw API body、未脫敏截圖或內部對話。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "affected_scope",
|
||||
"instruction": "填受影響 repo、host、domain、namespace、route、service、secret name 或產品邊界。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "redacted_evidence_refs",
|
||||
"instruction": "只填文件路徑、snapshot id、ticket id、commit、hash 或脫敏 metadata pointer。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "followup_owner",
|
||||
"instruction": "填後續補證、審查或決策負責角色 / 團隊。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "rollback_owner",
|
||||
"instruction": "填回滾負責角色 / 團隊;不是直接執行授權。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "maintenance_window",
|
||||
"instruction": "填維護窗口或明確寫 deferred / not scheduled;不得用口頭同意代替。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "validation_plan",
|
||||
"instruction": "填 preflight、post-check、rollback check;若只讀文件變更,寫 guard / json / doc secret sanity。",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"label": "Security evidence / snapshot / guard tooling",
|
||||
"outcome_lanes": [
|
||||
"keep_waiting_owner_response",
|
||||
"request_more_evidence",
|
||||
"quarantine_sensitive_payload",
|
||||
"reject_execution_request",
|
||||
"ready_for_reviewer_validation"
|
||||
],
|
||||
"packet_id": "high_value_config_owner_packet:security_evidence_tooling",
|
||||
"priority": "P3",
|
||||
"redaction_rules": [
|
||||
"只收 redacted evidence refs,不收 secret value。",
|
||||
"疑似 token、cookie、authorization header、private key、runner token 或 webhook secret 一律 quarantine。",
|
||||
"內部工作視窗對話、抱怨、口頭同意不得進產品文案或 LOGBOOK raw text。"
|
||||
],
|
||||
"required_gate": "security_evidence_owner_review_required",
|
||||
"required_validation": [
|
||||
"snapshot_parse",
|
||||
"guard_smoke",
|
||||
"doc_secret_sanity",
|
||||
"no_runtime_gate_increase"
|
||||
],
|
||||
"reviewer_checklist": [
|
||||
"canonical owner fields 全部存在。",
|
||||
"decision 只使用允許值。",
|
||||
"affected scope 可映射到 repo / host / domain / route / service / secret name。",
|
||||
"redacted evidence refs 不含 raw payload。",
|
||||
"沒有夾帶執行要求。",
|
||||
"C0 / C1 若要進 runtime,需獨立人工批准與維護窗口。"
|
||||
],
|
||||
"status": "draft_waiting_owner_response"
|
||||
}
|
||||
],
|
||||
"schema_version": "high_value_config_owner_packet_v1",
|
||||
"source_gate_schema_version": "high_value_config_change_gate_v1",
|
||||
"source_gate_summary": {
|
||||
"changed_file_count": 8,
|
||||
"impacted_c0_category_count": 0,
|
||||
"impacted_c1_category_count": 0,
|
||||
"impacted_category_count": 1,
|
||||
"matched_high_value_file_count": 8,
|
||||
"owner_evidence_complete": false,
|
||||
"owner_evidence_provided": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"strongest_priority": "P3",
|
||||
"strongest_tier": "C3"
|
||||
},
|
||||
"status": "draft_waiting_owner_response",
|
||||
"summary": {
|
||||
"accepted_response_count": 0,
|
||||
"c0_packet_count": 0,
|
||||
"c1_packet_count": 0,
|
||||
"packet_count": 1,
|
||||
"received_response_count": 0,
|
||||
"request_sent_count": 0,
|
||||
"runtime_gate_count": 0
|
||||
},
|
||||
"universal_owner_response_template": {
|
||||
"allowed_decisions": [
|
||||
"confirm",
|
||||
"defer",
|
||||
"reject",
|
||||
"request_more_evidence"
|
||||
],
|
||||
"false_flags": {
|
||||
"action_buttons_allowed": false,
|
||||
"active_scan_authorized": false,
|
||||
"dns_tls_change_authorized": false,
|
||||
"force_push_authorized": false,
|
||||
"host_write_authorized": false,
|
||||
"nginx_reload_authorized": false,
|
||||
"refs_sync_authorized": false,
|
||||
"request_sent": false,
|
||||
"response_accepted": false,
|
||||
"response_received": false,
|
||||
"runner_change_authorized": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"secret_value_collection_allowed": false,
|
||||
"workflow_modification_authorized": false
|
||||
},
|
||||
"field_templates": [
|
||||
{
|
||||
"field": "owner_role_or_team",
|
||||
"instruction": "填角色或團隊,不填私人帳號、密碼、token 或私人聯絡資訊。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "decision",
|
||||
"instruction": "只能填 confirm、defer、reject、request_more_evidence;不得附帶 runtime 執行批准。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "decision_reason",
|
||||
"instruction": "填脫敏短理由;不可貼 raw log、raw API body、未脫敏截圖或內部對話。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "affected_scope",
|
||||
"instruction": "填受影響 repo、host、domain、namespace、route、service、secret name 或產品邊界。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "redacted_evidence_refs",
|
||||
"instruction": "只填文件路徑、snapshot id、ticket id、commit、hash 或脫敏 metadata pointer。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "followup_owner",
|
||||
"instruction": "填後續補證、審查或決策負責角色 / 團隊。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "rollback_owner",
|
||||
"instruction": "填回滾負責角色 / 團隊;不是直接執行授權。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "maintenance_window",
|
||||
"instruction": "填維護窗口或明確寫 deferred / not scheduled;不得用口頭同意代替。",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"field": "validation_plan",
|
||||
"instruction": "填 preflight、post-check、rollback check;若只讀文件變更,寫 guard / json / doc secret sanity。",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ TIER_ORDER = {"C0": 0, "C1": 1, "C2": 2, "C3": 3}
|
||||
PRIORITY_ORDER = {"P0": 0, "P1": 1, "P2": 2, "P3": 3}
|
||||
|
||||
REQUIRED_OWNER_FIELDS = [
|
||||
"owner_role_team",
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
@@ -37,6 +37,18 @@ REQUIRED_OWNER_FIELDS = [
|
||||
"validation_plan",
|
||||
]
|
||||
|
||||
OWNER_FIELD_ALIASES = {
|
||||
"owner_role_or_team": ["owner_role_or_team", "owner_role_team", "owner_role", "owner_team", "responsible_team"],
|
||||
"decision": ["decision", "owner_decision", "scope_decision", "disposition"],
|
||||
"decision_reason": ["decision_reason", "reason", "decision_summary", "owner_note", "rationale"],
|
||||
"affected_scope": ["affected_scope", "affected_sources", "affected_repos", "host_scope", "target_scope"],
|
||||
"redacted_evidence_refs": ["redacted_evidence_refs", "evidence_refs", "redacted_refs", "source_refs"],
|
||||
"followup_owner": ["followup_owner", "next_owner", "followup_team", "resolution_owner"],
|
||||
"rollback_owner": ["rollback_owner", "rollback_team"],
|
||||
"maintenance_window": ["maintenance_window", "change_window"],
|
||||
"validation_plan": ["validation_plan", "validation_checks", "verification_plan"],
|
||||
}
|
||||
|
||||
REQUIRED_FALSE_FLAGS = [
|
||||
"runtime_execution_authorized",
|
||||
"host_write_authorized",
|
||||
@@ -433,6 +445,11 @@ def load_evidence(path: Path | None) -> dict[str, Any] | None:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def has_owner_field(evidence: dict[str, Any], field: str) -> bool:
|
||||
aliases = OWNER_FIELD_ALIASES.get(field, [field])
|
||||
return any(bool(evidence.get(alias)) for alias in aliases)
|
||||
|
||||
|
||||
def validate_evidence(evidence: dict[str, Any] | None) -> dict[str, Any]:
|
||||
if evidence is None:
|
||||
return {
|
||||
@@ -443,7 +460,7 @@ def validate_evidence(evidence: dict[str, Any] | None) -> dict[str, Any]:
|
||||
"note": "未提供 owner response evidence;本階段只能分類,不得執行 runtime 變更。",
|
||||
}
|
||||
|
||||
missing = [field for field in REQUIRED_OWNER_FIELDS if not evidence.get(field)]
|
||||
missing = [field for field in REQUIRED_OWNER_FIELDS if not has_owner_field(evidence, field)]
|
||||
false_flags = evidence.get("false_flags", {})
|
||||
invalid_false_flags = [
|
||||
flag for flag in REQUIRED_FALSE_FLAGS if false_flags.get(flag) is not False
|
||||
|
||||
262
scripts/security/high-value-config-owner-packet.py
Normal file
262
scripts/security/high-value-config-owner-packet.py
Normal file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
IwoooS 高價值配置 owner response packet 產生器。
|
||||
|
||||
本工具讀取 high-value-config-change-gate 的 JSON 報告,為 impacted
|
||||
categories 產出 owner response packet 草案。它只產生欄位、補件規則與
|
||||
禁止事項,不送件、不收回覆、不建立 action button、不執行 runtime。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
TAIPEI = timezone(timedelta(hours=8))
|
||||
|
||||
CANONICAL_OWNER_FIELDS = [
|
||||
"owner_role_or_team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"maintenance_window",
|
||||
"validation_plan",
|
||||
]
|
||||
|
||||
ALLOWED_DECISIONS = ["confirm", "defer", "reject", "request_more_evidence"]
|
||||
|
||||
FIELD_INSTRUCTIONS = {
|
||||
"owner_role_or_team": "填角色或團隊,不填私人帳號、密碼、token 或私人聯絡資訊。",
|
||||
"decision": "只能填 confirm、defer、reject、request_more_evidence;不得附帶 runtime 執行批准。",
|
||||
"decision_reason": "填脫敏短理由;不可貼 raw log、raw API body、未脫敏截圖或內部對話。",
|
||||
"affected_scope": "填受影響 repo、host、domain、namespace、route、service、secret name 或產品邊界。",
|
||||
"redacted_evidence_refs": "只填文件路徑、snapshot id、ticket id、commit、hash 或脫敏 metadata pointer。",
|
||||
"followup_owner": "填後續補證、審查或決策負責角色 / 團隊。",
|
||||
"rollback_owner": "填回滾負責角色 / 團隊;不是直接執行授權。",
|
||||
"maintenance_window": "填維護窗口或明確寫 deferred / not scheduled;不得用口頭同意代替。",
|
||||
"validation_plan": "填 preflight、post-check、rollback check;若只讀文件變更,寫 guard / json / doc secret sanity。",
|
||||
}
|
||||
|
||||
FALSE_FLAGS = [
|
||||
"request_sent",
|
||||
"response_received",
|
||||
"response_accepted",
|
||||
"runtime_execution_authorized",
|
||||
"host_write_authorized",
|
||||
"nginx_reload_authorized",
|
||||
"dns_tls_change_authorized",
|
||||
"workflow_modification_authorized",
|
||||
"runner_change_authorized",
|
||||
"refs_sync_authorized",
|
||||
"force_push_authorized",
|
||||
"secret_value_collection_allowed",
|
||||
"active_scan_authorized",
|
||||
"action_buttons_allowed",
|
||||
]
|
||||
|
||||
BLOCKED_REQUESTS = [
|
||||
"repo_create",
|
||||
"visibility_change",
|
||||
"refs_sync",
|
||||
"refs_delete",
|
||||
"force_push",
|
||||
"workflow_modify",
|
||||
"runner_enable",
|
||||
"secret_value_submit",
|
||||
"ssh_host_modify",
|
||||
"nginx_reload",
|
||||
"dns_tls_modify",
|
||||
"argocd_sync",
|
||||
"kubectl_apply",
|
||||
"active_scan",
|
||||
"agent_bounty_runtime_execute",
|
||||
"payout_or_withdrawal",
|
||||
]
|
||||
|
||||
OUTCOME_LANES = [
|
||||
"keep_waiting_owner_response",
|
||||
"request_more_evidence",
|
||||
"quarantine_sensitive_payload",
|
||||
"reject_execution_request",
|
||||
"ready_for_reviewer_validation",
|
||||
]
|
||||
|
||||
|
||||
def git_short_sha(root: Path) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--short", "HEAD"],
|
||||
cwd=root,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
return "unknown"
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def field_templates() -> list[dict[str, Any]]:
|
||||
return [
|
||||
{
|
||||
"field": field,
|
||||
"required": True,
|
||||
"instruction": FIELD_INSTRUCTIONS[field],
|
||||
}
|
||||
for field in CANONICAL_OWNER_FIELDS
|
||||
]
|
||||
|
||||
|
||||
def false_flag_map() -> dict[str, bool]:
|
||||
return {flag: False for flag in FALSE_FLAGS}
|
||||
|
||||
|
||||
def files_for_category(gate_report: dict[str, Any], category_id: str) -> list[str]:
|
||||
paths: list[str] = []
|
||||
for item in gate_report.get("changed_files", []):
|
||||
if any(category.get("category_id") == category_id for category in item.get("categories", [])):
|
||||
paths.append(item["path"])
|
||||
return sorted(paths)
|
||||
|
||||
|
||||
def category_inventory_by_id(gate_report: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
return {
|
||||
item["category_id"]: item
|
||||
for item in gate_report.get("control_category_inventory", [])
|
||||
}
|
||||
|
||||
|
||||
def packet_for_category(gate_report: dict[str, Any], category: dict[str, Any]) -> dict[str, Any]:
|
||||
inventory = category_inventory_by_id(gate_report).get(category["category_id"], {})
|
||||
affected_files = files_for_category(gate_report, category["category_id"])
|
||||
return {
|
||||
"packet_id": f"high_value_config_owner_packet:{category['category_id']}",
|
||||
"status": "draft_waiting_owner_response",
|
||||
"category_id": category["category_id"],
|
||||
"label": category["label"],
|
||||
"priority": category["priority"],
|
||||
"control_tier": category["control_tier"],
|
||||
"required_gate": category["required_gate"],
|
||||
"affected_files": affected_files,
|
||||
"allowed_decisions": ALLOWED_DECISIONS,
|
||||
"field_templates": field_templates(),
|
||||
"required_validation": category.get("required_validation") or inventory.get("required_validation", []),
|
||||
"redaction_rules": [
|
||||
"只收 redacted evidence refs,不收 secret value。",
|
||||
"疑似 token、cookie、authorization header、private key、runner token 或 webhook secret 一律 quarantine。",
|
||||
"內部工作視窗對話、抱怨、口頭同意不得進產品文案或 LOGBOOK raw text。",
|
||||
],
|
||||
"blocked_requests": BLOCKED_REQUESTS,
|
||||
"outcome_lanes": OUTCOME_LANES,
|
||||
"false_flags": false_flag_map(),
|
||||
"reviewer_checklist": [
|
||||
"canonical owner fields 全部存在。",
|
||||
"decision 只使用允許值。",
|
||||
"affected scope 可映射到 repo / host / domain / route / service / secret name。",
|
||||
"redacted evidence refs 不含 raw payload。",
|
||||
"沒有夾帶執行要求。",
|
||||
"C0 / C1 若要進 runtime,需獨立人工批准與維護窗口。",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def build_report(root: Path, gate_report: dict[str, Any], generated_at: str | None) -> dict[str, Any]:
|
||||
report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
|
||||
impacted_categories = gate_report.get("impacted_categories", [])
|
||||
packets = [packet_for_category(gate_report, category) for category in impacted_categories]
|
||||
c0_packets = [packet for packet in packets if packet["control_tier"] == "C0"]
|
||||
c1_packets = [packet for packet in packets if packet["control_tier"] == "C1"]
|
||||
|
||||
return {
|
||||
"schema_version": "high_value_config_owner_packet_v1",
|
||||
"generated_at": report_time,
|
||||
"git_commit": git_short_sha(root),
|
||||
"source_gate_schema_version": gate_report.get("schema_version"),
|
||||
"source_gate_summary": gate_report.get("summary", {}),
|
||||
"status": "draft_waiting_owner_response",
|
||||
"canonical_owner_fields": CANONICAL_OWNER_FIELDS,
|
||||
"allowed_decisions": ALLOWED_DECISIONS,
|
||||
"execution_boundaries": {
|
||||
"request_sent": False,
|
||||
"response_received": False,
|
||||
"response_accepted": False,
|
||||
"runtime_execution_authorized": False,
|
||||
"host_write_authorized": False,
|
||||
"secret_value_collected": False,
|
||||
"action_buttons_allowed": False,
|
||||
},
|
||||
"summary": {
|
||||
"packet_count": len(packets),
|
||||
"c0_packet_count": len(c0_packets),
|
||||
"c1_packet_count": len(c1_packets),
|
||||
"request_sent_count": 0,
|
||||
"received_response_count": 0,
|
||||
"accepted_response_count": 0,
|
||||
"runtime_gate_count": 0,
|
||||
},
|
||||
"universal_owner_response_template": {
|
||||
"allowed_decisions": ALLOWED_DECISIONS,
|
||||
"field_templates": field_templates(),
|
||||
"false_flags": false_flag_map(),
|
||||
},
|
||||
"packets": packets,
|
||||
"control_category_inventory": gate_report.get("control_category_inventory", []),
|
||||
"next_steps": [
|
||||
"若 packet_count > 0,將 packet 交給 owner 補 canonical 欄位;不得把草案視為已送件。",
|
||||
"若 owner 回覆含 secret 或執行要求,先 quarantine 或 reject_execution_request。",
|
||||
"只有 reviewer checklist 完成後才可進 accepted;accepted 仍不開 runtime gate。",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="IwoooS 高價值配置 owner response packet 產生器")
|
||||
parser.add_argument("--root", default=".", help="repo root")
|
||||
parser.add_argument(
|
||||
"--gate-report",
|
||||
default="docs/security/high-value-config-change-gate.snapshot.json",
|
||||
help="high-value-config-change-gate.py 輸出的 JSON",
|
||||
)
|
||||
parser.add_argument("--output", help="寫出 JSON 報告")
|
||||
parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用")
|
||||
args = parser.parse_args()
|
||||
|
||||
root = Path(args.root).resolve()
|
||||
gate_report = load_json(root / args.gate_report)
|
||||
report = build_report(root, gate_report, args.generated_at)
|
||||
payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
|
||||
|
||||
if args.output:
|
||||
output = Path(args.output)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(payload + "\n", encoding="utf-8")
|
||||
else:
|
||||
print(payload)
|
||||
|
||||
summary = report["summary"]
|
||||
print(
|
||||
"HIGH_VALUE_CONFIG_OWNER_PACKET_OK "
|
||||
f"packets={summary['packet_count']} "
|
||||
f"c0={summary['c0_packet_count']} "
|
||||
f"c1={summary['c1_packet_count']} "
|
||||
f"runtime_gate={summary['runtime_gate_count']}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user