test(iwooos): 新增供應鏈 owner policy gate [skip ci]

This commit is contained in:
Your Name
2026-06-15 07:01:58 +08:00
parent f40b83456e
commit c35f064d2a
8 changed files with 703 additions and 13 deletions

View File

@@ -193,7 +193,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公
| P2 | AWOOOI / AwoooP / IwoooS frontend runtime config | `apps/web/next.config.js``apps/web/src/lib/config.ts`、i18n | web owner | NEXT_PUBLIC public-domain only、no internal transcript, desktop/mobile smoke |
| P2 | VibeWork product boundary | VibeWork owner docs / future evidence refs | VibeWork owner | independent product boundary、repo / deploy / admin / backup scope |
| P2 | StockPlatform / Tsenyang / Bitan / VTuber routes | Nginx templates、product runbooks | product owner | domain / admin / API / backup / owner matrix |
| P2 | Package / supply-chain baselines | `pnpm-lock.yaml``package.json``pyproject.toml``requirements.txt`、Dockerfiles、docker-compose、`docs/security/PACKAGE-SUPPLY-CHAIN-BASELINE.md``docs/security/package-supply-chain-baseline.snapshot.json` | repo / registry owner | package manager policy、lockfile owner、Python lock policy、CVE / license / SBOM window、image digest evidence、registry owner、rollback owner |
| P2 | Package / supply-chain baselines | `pnpm-lock.yaml``package.json``pyproject.toml``requirements.txt`、Dockerfiles、docker-compose、`docs/security/PACKAGE-SUPPLY-CHAIN-BASELINE.md``docs/security/package-supply-chain-baseline.snapshot.json``docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md``docs/security/package-supply-chain-owner-policy-gate.snapshot.json` | repo / registry owner | package manager policy、lockfile owner、Python lock policy、requirements pinning policy、Docker digest pinning policy、compose image digest policy、CVE / license / SBOM window、registry owner、rollback owner |
| P3 | Runbook / endpoint docs / snapshots | `docs/reference/*``docs/runbooks/*``docs/security/*.snapshot.json` | doc owner | no secret value, stale endpoint flag, owner-reviewed evidence refs |
2026-06-14 P0-20 已新增 `docs/security/K8S-ARGOCD-MANIFEST-INVENTORY.md``docs/security/k8s-argocd-manifest-inventory.snapshot.json`,把 K8s / ArgoCD / Velero / monitoring repo source 固定為 `files=49``c0=36``yaml=45``unique_kinds=20``blocked_actions=13` 的只讀清冊。P0-21 再新增 `docs/security/K8S-ARGOCD-OWNER-REQUEST-DRAFT.md``docs/security/k8s-argocd-owner-request-draft.snapshot.json`,將四個 scan group 轉成 `drafts=4``c0=3``owner_fields=11` 的 owner request draft。2026-06-15 P0-25 再新增 `docs/security/K8S-ARGOCD-OWNER-RESPONSE-ACCEPTANCE.md``docs/security/k8s-argocd-owner-response-acceptance.snapshot.json`,固定 `candidates=4``c0=3``owner_fields=11``reviewer_checks=12``outcome_lanes=7``blocked_actions=18` 的 owner response acceptance 只讀帳本。2026-06-15 再新增 `docs/security/K8S-ARGOCD-CHANGE-EVIDENCE-ACCEPTANCE.md``docs/security/k8s-argocd-change-evidence-acceptance.snapshot.json`,固定 `candidates=4``c0=3``write_capable=4``required_evidence_fields=18``reviewer_checks=18``outcome_lanes=8``blocked_actions=28` 的 GitOps 變更證據驗收只讀帳本。這些都不是 live cluster read、ArgoCD API read、ArgoCD sync、kubectl action、Helm upgrade、secret collection、manual pod restart、scale workload、RBAC / NetworkPolicy change、restore backup、production write 或 runtime gate。
@@ -211,6 +211,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公
9. DNS / TLS / certbot owner response 只能收脫敏 metadata ref、coverage basis、expiry metadata、renewal owner、ACME route owner、maintenance window、rollback owner 與 validation plan不得因 owner 回覆而自動做 DNS query、live TLS probe、certbot renew、Nginx reload、route smoke、DNS record 變更、certificate path 變更或 ACME route 變更。
10. Public / admin / API / frontend runtime config 變更必須先通過 affected route、auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、desktop / mobile smoke、sensitive string scan、rollback owner 與 post-check evidence前台不得顯示 raw owner namespace、repo slug、內部狀態碼、內部協作內容或未脫敏截圖。
11. 高價值配置控管必須能由 `scripts/security/iwooos-config-control-guard.py` 集中驗證guard 通過只代表 repo snapshot 基線完整,不代表 owner response、live evidence、reload、restart、workflow / secret / runner 變更、backup / restore、scan、runtime 或 deploy 授權。
12. Package / Docker 供應鏈修復前必須先通過 owner policy gatePython lockfile、requirements pinning、Docker digest pinning、compose digest、CVE / license / SBOM 只能先收脫敏 owner metadata不得因 baseline 或 gate 通過而 install、upgrade、rewrite lockfile、pull / build / push image、登入 registry、修改 workflow、部署或開 runtime gate。
## 5. 需要調整的既有規範
@@ -255,6 +256,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公
| K8s / ArgoCD GitOps 變更證據驗收 | `100%` | 已新增 `k8s_argocd_change_evidence_acceptance_v1`4 個 candidate、3 個 C0、4 個 write-capable、18 個 reviewer checks、8 條 outcome lanes、28 類 blocked action成熟度 `62% -> 64%` |
| Public / Admin / API runtime config 變更證據驗收 | `100%` | 已新增 `public_runtime_config_change_evidence_acceptance_v1`6 個 candidate、5 個 C0、21 個 reviewer checks、8 條 outcome lanes、32 類 blocked action成熟度 `62% -> 64%`raw namespace / repo slug / 內部狀態碼 / 內部協作內容外洩列為拒收或隔離條件 |
| Package / Docker supply-chain repo-only baseline | `100%` | 已新增 `package_supply_chain_baseline_v1`,盤點 `package_json=6``pyproject=4``requirements=2``dockerfiles=2``compose=6``gaps=5`;不 install、不掃 CVE、不改 image、不部署 |
| Package / Docker supply-chain owner policy gate | `100%` | 已新增 `package_supply_chain_owner_policy_gate_v1`6 個 owner policy request、2 個 C0、8 個 owner 欄位、12 個 reviewer checks、20 類 blocked actionrequest_sent / received / accepted / runtime / action 仍為 `0 / false` |
| Backup / restore / escrow owner request draft | `100%` | 已將 38 個 backup / restore / escrow surface 轉成 owner request draftrequest sent / received / accepted、backup run、restore run、offsite sync、remote delete、escrow marker write、retention change 仍為 0 |
| CI blocking / workflow gate | `0%` | 本階段刻意不修改 `.gitea/workflows`,避免初期資安流程摩擦過大 |
| owner-provided live Nginx file compare | `70%` | 工具可吃 owner 匯出的 live conf 檔比較;本階段不主動 SSH 取得 |

View File

@@ -6,6 +6,7 @@
| 狀態 | `repo_only_inventory_ready_needs_owner_policy` |
| 腳本 | `scripts/security/package-supply-chain-baseline.py` |
| Snapshot | `docs/security/package-supply-chain-baseline.snapshot.json` |
| Owner policy gate | `docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md` / `docs/security/package-supply-chain-owner-policy-gate.snapshot.json` |
| Schema | `docs/schemas/package_supply_chain_baseline_v1.schema.json` |
| 模式 | repo snapshot only不 install、不連外、不做 CVE scan、不改 image |
| runtime gate | `0` |
@@ -54,7 +55,22 @@
7. `cve_scan_window`
8. `rollback_owner`
## 5. 指令
## 5. Owner Policy Gate
2026-06-15 已新增 `docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md``docs/security/package-supply-chain-owner-policy-gate.snapshot.json`,把 baseline 缺口轉成六個 owner policy request
| Request | 對應治理項 | 狀態 |
|---------|------------|------|
| package manager / lockfile owner | Node / pnpm lockfile owner 與更新窗口 | waiting owner policy response |
| Python lockfile policy | Python lockfile 缺席 | waiting owner policy response |
| requirements pinning policy | `requirements.txt` 未 pin | waiting owner policy response |
| Docker digest pinning policy | Dockerfile base image 與 `COPY --from` image 未 digest pin | waiting owner policy response |
| compose image digest policy | docker-compose image 未 digest pin | waiting owner policy response |
| CVE / license / SBOM window | 掃描工具、窗口與噪音處理策略未定 | waiting owner policy response |
此 gate 只補「誰能決定、用什麼政策決定、何時驗證、誰負責 rollback」的收件前規範。request_sent、owner_response_received、owner_response_accepted、runtime_gate 與 action_button 仍全部是 `0 / false`
## 6. 指令
```bash
python3 scripts/security/package-supply-chain-baseline.py \
@@ -77,7 +93,19 @@ python3 scripts/security/package-supply-chain-baseline.py \
PACKAGE_SUPPLY_CHAIN_BASELINE_OK package_json=6 pyproject=4 requirements=2 dockerfiles=2 compose=6 gaps=5 runtime_gate=0
```
## 6. 邊界
Owner policy gate 驗證:
```bash
python3 scripts/security/package-supply-chain-owner-policy-guard.py --root .
```
預期輸出:
```text
PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD_OK
```
## 7. 邊界
此 baseline 通過不代表:
@@ -87,13 +115,15 @@ PACKAGE_SUPPLY_CHAIN_BASELINE_OK package_json=6 pyproject=4 requirements=2 docke
- registry login、Harbor policy、image immutability 或 scanner policy 已驗收。
- workflow、runner、secret、production deploy 或 runtime gate 已授權。
## 7. 完成度
## 8. 完成度
| 工作 | 完成度 | 說明 |
|------|--------|------|
| Package / Docker supply-chain repo-only baseline | `100%` | 已新增腳本、snapshot 與人讀文件 |
| Node lockfile 基線 | `80%` | `pnpm-lock.yaml` 存在;仍需 owner policy 確認 lockfile owner / update window |
| Python lock policy | `30%` | 已盤點 pyproject / requirements尚缺 owner policy 與 lockfile 決策 |
| Docker / compose image policy | `35%` | 已盤點 image refs尚缺 digest pinning policyregistry owner、rollback owner |
| CVE / license / SBOM 驗證 | `0%` | 未執行外部掃描;需 owner window 與工具策略 |
| Package / Docker supply-chain owner policy gate | `100%` | 已新增 guard、snapshot 與人讀文件;六個 request 仍 waiting owner policy response |
| Node lockfile 基線 | `80%` | `pnpm-lock.yaml` 存在owner policy gate 已補,但尚未收到 lockfile owner / update window |
| Python lock policy | `45%` | 已盤點 pyproject / requirements 並補 owner policy request尚缺正式 owner response 與 lockfile 決策 |
| requirements pinning policy | `35%` | 已盤點 26 條未 pin entry 並補 owner policy request尚未批准 pinning 或相容性窗口 |
| Docker / compose image policy | `45%` | 已盤點 image refs 並補 C0 owner policy request尚缺 digest pinning policy、registry owner、rollback owner |
| CVE / license / SBOM 驗證 | `15%` | 已補 owner policy request未執行外部掃描需 owner window 與工具策略 |
| runtime gate | `0%` | 未開啟任何執行期閘門 |

View File

@@ -0,0 +1,99 @@
# Package / Docker 供應鏈 Owner Policy Gate
| 項目 | 內容 |
|------|------|
| 日期 | 2026-06-15 |
| 狀態 | `draft_waiting_owner_policy_response` |
| 腳本 | `scripts/security/package-supply-chain-owner-policy-guard.py` |
| Snapshot | `docs/security/package-supply-chain-owner-policy-gate.snapshot.json` |
| Baseline | `docs/security/package-supply-chain-baseline.snapshot.json` |
| 模式 | repo snapshot policy gate不 install、不 upgrade、不 pull image、不 build image、不 push image |
| runtime gate | `0` |
## 1. 目的
此 owner policy gate 把 Package / Docker 供應鏈 baseline 的五個缺口轉成六個 owner policy request 草稿,讓後續處理 Python lockfile、requirements pinning、Docker digest pinning、compose image digest、CVE / license / SBOM 時,都必須先有 owner role / team、policy decision、maintenance window、rollback owner 與 redacted evidence refs。
本 gate 是只讀治理物件不是套件安裝、升級、pinning、CVE 掃描、SBOM 產生、image pull / build / push、registry login、workflow 修改、production deploy 或 runtime gate。
## 2. Owner Policy Request
| Request ID | 分級 | 對應缺口 | 目前狀態 |
|------------|------|----------|----------|
| `supply_chain_owner_policy:package_manager_lockfile_owner` | C1 | Node / pnpm lockfile owner 與更新窗口 | waiting owner policy response |
| `supply_chain_owner_policy:python_lockfile_policy` | C1 | `python_lockfile_absent` | waiting owner policy response |
| `supply_chain_owner_policy:requirements_pin_policy` | C1 | `requirements_unpinned_entries_present` | waiting owner policy response |
| `supply_chain_owner_policy:dockerfile_digest_pin_policy` | C0 | `docker_base_images_not_all_digest_pinned``docker_copy_from_images_not_all_digest_pinned` | waiting owner policy response |
| `supply_chain_owner_policy:compose_image_digest_pin_policy` | C0 | `compose_images_not_all_digest_pinned` | waiting owner policy response |
| `supply_chain_owner_policy:cve_license_sbom_window` | C1 | CVE / license / SBOM tool 與執行窗口未定 | waiting owner policy response |
## 3. Required Owner Fields
1. `package_manager_policy`
2. `lockfile_owner`
3. `python_lock_policy`
4. `docker_base_image_policy`
5. `compose_image_policy`
6. `registry_owner`
7. `cve_scan_window`
8. `rollback_owner`
## 4. Reviewer Checks
1. owner role / team 必須可追溯,不能只填個人暱稱或未脫敏 repo namespace。
2. policy decision 必須明確區分 observe、plan、approve change、reject change。
3. Python lockfile 決策必須說明工具、產生位置與相容性驗證方式。
4. requirements pinning 決策必須說明 pinning 原則、例外條件與 rollback owner。
5. Docker digest pinning 決策必須說明 base image、`COPY --from` image、registry owner 與回復策略。
6. compose image digest 決策必須說明 service 範圍、tag / digest 對照與維護窗口。
7. CVE / license / SBOM 決策必須說明工具、掃描窗口、結果分級與噪音處理方式。
8. evidence refs 只能是脫敏 metadata不得包含 token、cookie、registry password、private key 或 secret value。
9. change evidence 必須能回連到 baseline gap不得只提供口頭批准。
10. runtime gate 必須獨立批准owner policy accepted 不能自動變成 runtime execution authorized。
11. deploy / workflow / registry / image write 必須另有變更證據與 post-check。
12. 前台呈現只能顯示脫敏產品 / 控管狀態,不得顯示 raw namespace、內部協作內容或工作視窗對話。
## 5. Blocked Actions
本 gate 通過前,以下動作全部維持 blocked
1. install package
2. upgrade package
3. rewrite lockfile
4. change requirements pin
5. run external CVE lookup
6. run external license lookup
7. generate SBOM
8. docker pull
9. docker build
10. docker push
11. image tag change
12. image digest pin change
13. registry login
14. workflow modification
15. runner change
16. secret value collection
17. production deploy
18. runtime execution
19. action button enablement
20. treat owner policy as runtime approval
## 6. 不可誤讀
- `package-supply-chain-baseline.snapshot.json` 只是 repo-only baseline。
- `package-supply-chain-owner-policy-gate.snapshot.json` 只是 owner policy gate 草稿。
- 所有 request_sent、owner_response_received、owner_response_accepted、runtime_execution_authorized、action_buttons_allowed 仍是 `0 / false`
- `owner policy gate` 通過只代表文件、snapshot 與 guard 對齊,不代表 Python lockfile、requirements pinning、Docker digest pinning、CVE / license / SBOM 已完成。
- 本輪不 install、不 upgrade、不 pull image、不 build image、不 push image不登入 registry、不改 workflow、不改 secret、不部署。
## 7. 完成度
| 工作 | 完成度 | 說明 |
|------|--------|------|
| Package / Docker supply-chain owner policy gate | `100%` | 已新增人讀文件、snapshot 與 guard並可由總進度 guard 串接 |
| Node lockfile owner policy | `45%` | 已有 pnpm lockfile 與 owner policy request尚未收到 owner response |
| Python lock policy | `45%` | 已有 Python lockfile 缺口與 owner policy request尚未決定工具與 lockfile 策略 |
| requirements pinning policy | `35%` | 已有 26 條未 pin entry 的 policy request尚未批准 pinning 或相容性窗口 |
| Docker / compose image policy | `45%` | 已有 C0 digest pinning policy request尚未批准 digest 變更、registry owner 或 rollback |
| CVE / license / SBOM 驗證 | `15%` | 已定義 owner policy request尚未批准外部掃描窗口或工具 |
| runtime gate | `0%` | 未開啟任何執行期閘門 |

View File

@@ -3,14 +3,14 @@
| 項目 | 內容 |
|------|------|
| 日期 | 2026-06-15 |
| 狀態 | IwoooS 64% 只讀治理推進中;高價值配置集中 guardPackage / Docker 供應鏈 repo-only baseline 已完成CD / Runner / Secret 注入變更證據驗收與 Public / Admin / API runtime config 變更證據驗收只讀帳本已本地完成;端口 / 防火牆變更證據驗收與 K8s / ArgoCD GitOps 變更證據驗收已正式部署驗證S4.9 owner response gate 仍是第一優先 |
| 狀態 | IwoooS 64% 只讀治理推進中;高價值配置集中 guardPackage / Docker 供應鏈 repo-only baseline 與 Package / Docker 供應鏈 owner policy gate 已完成CD / Runner / Secret 注入變更證據驗收與 Public / Admin / API runtime config 變更證據驗收只讀帳本已本地完成;端口 / 防火牆變更證據驗收與 K8s / ArgoCD GitOps 變更證據驗收已正式部署驗證S4.9 owner response gate 仍是第一優先 |
| 本階段完成 | 資安供應鏈 contract manifest + Source Control Approval Board + Draft Reconcile Plan + Ref Detail Diff + Ref Truth Classification + Source Control Ref Truth Owner Response 收件包 + GitHub Primary Readiness Gate + GitHub Primary Rollback ADR + GitHub Target Owner Decision Response 收件包 + Gitea 認證清冊匯出請求 + Gitea 認證清冊匯入驗收契約 + Gitea 清冊覆蓋 Owner Attestation + Gitea Owner Attestation Approval Lane 對齊 + Gitea Owner Attestation Response 收件包 + Workflow / Secret Name Inventory + Workflow / Secret Name Local Evidence + Workflow / Secret Name Redacted Export Request + Workflow / Secret Name Owner Response 收件包 + Source Control Owner Response Validation Rollup + Kali 112 live integration status + Security Finding contract + Kali scan scope approval package + Security Approval Queue + S3 人工批准 Gate + S3 人工決策紀錄 + S3 人工審查封包 + S3 人工決策狀態轉移 + S3 後續 runtime gate 準備契約 + 鏡像 readiness index + 鏡像接收計畫 + 鏡像事件信封 + 鏡像路由矩陣 + 鏡像驗收契約 + 鏡像隔離契約 + 鏡像 dry-run 報告契約 + 鏡像狀態彙整契約 + IwoooS 前端態勢入口 + IwoooS posture projection contract + IwoooS 既有前端資安頁面整合 + IwoooS 覆蓋與邊界矩陣 + IwoooS 只讀資安處理旅程 + IwoooS owner evidence readiness board + IwoooS host coverage view + IwoooS host action gate matrix + IwoooS host evidence readiness board + IwoooS host evidence collection order + IwoooS host evidence intake preflight + IwoooS host evidence review outcome lanes + IwoooS host evidence review handoff packets + IwoooS host evidence reviewer checklist + IwoooS host evidence reviewer outcome lanes + IwoooS host owner decision candidate packets + IwoooS host owner decision review checklist + IwoooS host owner decision review outcome lanes + IwoooS host owner decision record draft packets + IwoooS host owner decision record draft review checklist + IwoooS host owner decision record draft review outcome lanes + IwoooS host owner decision record write-up packets + IwoooS host owner decision record write-up review checklist + IwoooS host owner decision record write-up review outcome lanes + IwoooS host owner decision record formal candidate packets + IwoooS host owner decision record formal candidate review checklist + IwoooS host owner decision record formal candidate review outcome lanes + IwoooS host owner decision record formal record queue packets + IwoooS host owner decision record formal record queue review checklist + IwoooS host owner decision record formal record queue review outcome lanes + IwoooS host owner decision record human handoff readiness packets + IwoooS host owner decision record human handoff readiness review checklist + IwoooS host owner decision record human handoff readiness review outcome lanes + IwoooS host owner decision record human record owner review candidate packets + IwoooS host owner decision record human record owner review candidate checklist + IwoooS host owner decision record human record owner review candidate outcome lanes + IwoooS host owner decision record human record owner review preparation packets + IwoooS host owner decision record human record owner review preparation checklist + IwoooS progress acceleration lanes + IwoooS owner response next-action focus + IwoooS S4.9 owner response preflight + IwoooS S4.9 owner response request templates + IwoooS progress hold movement gates + IwoooS AwoooP read-only landing readiness + IwoooS AwoooP cross-session handoff packets + AwoooP 首頁 IwoooS 資安鏡像候選 + AwoooP 工作鏈路 IwoooS 資安鏡像候選 + AwoooP 審批佇列 IwoooS owner response 只讀焦點 |
| 本階段追加 | AwoooP 合約儀表板 IwoooS 資安契約只讀候選 + AwoooP 租戶管理 IwoooS 資安租戶範圍只讀候選 + AwoooP 執行監控 IwoooS 執行狀態只讀候選 + 既有安全 / 合規頁面 IwoooS 只讀反向橋接 + 告警 / 錯誤 / 授權 / 治理頁面 IwoooS 只讀反向橋接 + 稽核 / 工程審查頁面 IwoooS 深色只讀反向橋接 + IwoooS 前端資安頁面連接狀態板 + IwoooS GitHub 主要來源就緒度只讀狀態板 + AwoooP 工作鏈路 GitHub 主要來源就緒度只讀工作項 + AwoooP 合約儀表板 GitHub 主要來源就緒度合約只讀候選 + AwoooP 審批佇列 GitHub 主要來源就緒度審批邊界 + AwoooP 首頁 GitHub 主要來源就緒度只讀摘要 + AwoooP 租戶管理 GitHub 主要來源就緒度租戶範圍 + AwoooP 執行監控 GitHub 主要來源就緒度執行邊界 + IwoooS / AwoooP 資安可視區塊繁體中文呈現防護檢查 + AwoooP 執行詳情 / 審批詳情繁體中文呈現防護檢查 + AwoooP 首頁負責人回覆驗收總覽 + AwoooP 工作鏈路負責人回覆驗收只讀工作項 + AwoooP 合約儀表板負責人回覆驗收契約只讀候選 + AwoooP 審批佇列負責人回覆驗收只讀審查邊界 + AwoooP 租戶管理負責人回覆驗收租戶範圍 + AwoooP 執行監控負責人回覆驗收執行邊界 + AwoooP 執行詳情負責人回覆驗收詳情邊界 + AwoooP 審批決策負責人回覆驗收審批邊界 + IwoooS AwoooP 資安入口覆蓋狀態板 + IwoooS 階段式資安收斂節奏圖 + IwoooS 下一步人工收件作戰板 + IwoooS 人工回覆安全驗收閘道 + IwoooS 人工回覆審查結果分流 + IwoooS 人工決策準備佇列 + IwoooS 人工決策紀錄草稿防誤用 + IwoooS 人工決策正式紀錄負責人指派確認準備包 + IwoooS 人工決策正式紀錄負責人指派確認清單 + IwoooS 人工決策正式紀錄負責人指派確認結果分流 + IwoooS 人工決策正式紀錄負責人指派決策準備包 + IwoooS 人工決策正式紀錄負責人指派決策檢查清單 + IwoooS S4.9 負責人回覆封套欄位 + IwoooS S4.9 負責人回覆封套送件前檢查 + IwoooS S4.9 負責人回覆封套送件前結果分流 + IwoooS S4.9 負責人回覆送件請求草稿 + IwoooS S4.9 負責人回覆送件鏈路摘要 + IwoooS 低摩擦分階段收斂主控 + IwoooS 低摩擦下一步行動邊界 + IwoooS 64% 進度移動訊號驗收條 + IwoooS 第一個進度解鎖路徑 + IwoooS 第一解鎖證據包 + IwoooS 第一解鎖證據包預檢分流 + IwoooS 第一解鎖證據包補件路徑 + IwoooS 第一解鎖證據包補件送審前檢查 + IwoooS 第一解鎖證據包補件送審結果分流 + IwoooS 第一解鎖證據包 reviewer 指派準備包 + IwoooS 第一解鎖證據包 reviewer 指派前檢查 + IwoooS 第一解鎖證據包 reviewer 指派前檢查結果分流 + IwoooS 正式只讀 landing 與 Kali 112 只讀證據進度重估 |
| 本階段追加補充 | IwoooS 目前具體工作地圖 + IwoooS 目前具體交付清單 + IwoooS 目前阻塞與解除條件 + IwoooS 三軸進度與全產品套用範圍 + IwoooS 全產品分階段套用台帳 + IwoooS 全產品 rollout 波次驗收門檻 + IwoooS 全產品 rollout 驗收結果分流 + IwoooS 全產品證據接線地圖 + IwoooS 全產品證據接線預檢 + IwoooS 全產品證據接線預檢結果分流 + IwoooS 全產品預檢補件回收台帳 + IwoooS 全產品補件重試門檻 + IwoooS 全產品重試結果分流 + IwoooS 全產品人工審查候選準備 + IwoooS 全產品人工審查候選預檢 + IwoooS 全產品人工審查候選預檢結果分流 + IwoooS 全產品人工審查候選預檢補件回收台帳 + IwoooS 全產品人工審查候選預檢補件重試門檻 + IwoooS 全產品只讀套用快照 + P2-145 owner response acceptance gate 正式驗證完成 |
| P0 追加 | IwoooS P0 配置控管優先序前台正式驗證完成Nginx public gateway、DNS / TLS / certbot、K8s / ArgoCD / production manifests、Workflow / runner / secret metadata、Public / admin / API runtime config、agent-bounty runtime / treasury 六類先列為即時風險配置;高價值配置 Gate 已補上 `k8s/nginx/**``scripts/ops/**/*cert*``scripts/ops/**/*tls*`sample 從 `matched=0 / C0=0` 收斂到 `matched=3 / C0=2`Gate 預設工作樹 preflight 已可讀取 staged / unstaged / untracked本地 smoke 對臨時 `k8s/nginx/*` 檔命中 C0Owner Packet snapshot 已同步為 `packets=3 / c0=2`Coverage snapshot 已同步最新 patternsIwoooS / AwoooP 前台 Owner Packet 摘要已正式驗證 `packet=3 / c0=2`feature commit `e999c16b`、deploy marker `16c6b983`、Gitea code-review `2973` / CD `2972` successIwoooS posture projection snapshot / schema / guard 已同步 `packet=3 / c0=2`,不再保留舊 `1 / 0` 口徑;高價值配置 Owner Packet 收件預檢已新增 `checks=9 / lanes=5 / required_fields=27 / blocked_requests=16`;高價值配置 Owner Request 草稿包已新增 `drafts=3 / handoff_fields=11 / forbidden_payloads=12 / sent=0`Public Gateway live conf 匯出請求包已新增 `requests=3 / c0=2 / redaction_rules=8 / received=0`Public Gateway redacted export 收件預檢已新增 `candidates=3 / c0=2 / checks=10 / rejection_guards=12 / received=0 / accepted=0`Public Gateway rendered diff / nginx gate 草稿已新增 `candidates=3 / c0=2 / stages=7 / blocked=14 / rendered_diff=0 / runtime=0`Public Gateway owner response acceptance 只讀帳本已新增 `candidates=3 / c0=2 / fields=12 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`DNS / TLS / certbot Owner Confirmation Request 已新增 `requests=4 / c0=4 / fields=9 / questions=5 / guards=12 / received=0 / accepted=0`K8s / ArgoCD manifest repo-only 清冊已新增 `files=49 / c0=36 / yaml=45 / kinds=20 / blocked=13 / runtime=0`K8s / ArgoCD Owner Request Draft 已新增 `drafts=4 / c0=3 / fields=11 / sent=0 / runtime=0`K8s / ArgoCD owner response acceptance 只讀帳本已新增 `candidates=4 / c0=3 / fields=11 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`K8s / ArgoCD GitOps 變更證據驗收已新增 `candidates=4 / c0=3 / write_capable=4 / evidence_fields=18 / checks=18 / lanes=8 / blocked=28 / accepted=0 / runtime=0`CD / Runner / Secret 注入變更證據驗收已新增 `candidates=5 / c0=4 / write_capable=5 / workflow_files=33 / secret_names=42 / runner_labels=5 / evidence_fields=19 / checks=19 / lanes=8 / blocked=32 / accepted=0 / runtime=0`Public / Admin / API runtime config 變更證據驗收已新增 `candidates=6 / c0=5 / write_capable=6 / source_refs=20 / evidence_fields=21 / checks=21 / lanes=8 / blocked=32 / accepted=0 / runtime=0`,並把 raw namespace、repo slug、內部狀態碼與內部協作內容外洩列為拒收 / 隔離Backup / Restore / Escrow owner response acceptance 只讀帳本已新增 `candidates=38 / write_capable=27 / fields=14 / checks=13 / lanes=7 / blocked=22 / accepted=0 / runtime=0`SSH / Firewall / Network Access owner response acceptance 只讀帳本已新增 `candidates=16 / write_capable=6 / fields=13 / checks=15 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;端口 / 防火牆變更證據驗收只讀帳本已新增 `candidates=14 / write_capable=6 / policy_or_exposure=5 / evidence_fields=16 / checks=16 / lanes=8 / blocked=24 / accepted=0 / runtime=0`owner response / live evidence / runtime gate / action buttons 仍全部為 0 |
| P0 agent-bounty 追加 | agent-bounty-protocol Owner Request Draft 已新增 `drafts=11 / control=4 / surface=7 / write_capable=8 / treasury=4 / mcp_a2a=5 / fields=22 / forbidden_inputs=25 / blocked=28 / sent=0 / runtime=0`;這是 repo / refs、deployment、data classification、MCP / A2A、cron / daemon、admin / treasury、webhook / traffic 的人工送件前草稿,不是 owner response、repo push、refs sync、workflow 修改、secret 收集、deploy、compose restart、DB migration、claim / submit、payout / withdrawal、cron / daemon、external send、host write 或 runtime gate |
| P1 追加 | Docker / systemd / Host Service Owner Request Draft 已新增 `drafts=9 / write_capable=3 / fields=12 / blocked=14 / sent=0 / runtime=0`SSH / Firewall / Network Access Owner Request Draft 已新增 `drafts=16 / write_capable=6 / fields=13 / blocked=16 / sent=0 / runtime=0`Backup / Restore / Escrow Owner Request Draft 已新增 `drafts=38 / write_capable=27 / fields=14 / blocked=18 / sent=0 / runtime=0`Backup / Restore / Escrow Owner Response Acceptance 已新增 `candidates=38 / write_capable=27 / reviewer_checks=13 / lanes=7 / blocked=22 / accepted=0 / runtime=0`Monitoring / Alerting / Observability Owner Request Draft 已新增 `drafts=60 / write_capable=11 / fields=14 / blocked=24 / sent=0 / runtime=0`;上述全部仍是人工送件前草稿或只讀 acceptance 帳本,不是 owner response、live evidence、reload、restart、backup、restore、Telegram send、alert smoke、host write 或 runtime gate |
| P2 供應鏈追加 | Package / Docker 供應鏈 repo-only baseline 已新增 `package_json=6 / pyproject=4 / requirements=2 / dockerfiles=2 / compose=6 / gaps=5 / runtime=0`;缺口為 Python lockfile 缺席、requirements 未 pin、Docker base image 未全數 digest pinning、Docker `COPY --from` 外部 image 未 digest pinning、compose image 未 digest pinning目前尚未列入 36 個正式 AwoooP 消費 contract後續若要前台消費需同步 manifest / readiness / route / rollup / dry-run / posture projection / guard count本輪不 install、不 upgrade、不跑 CVE、不 pull / build / push image、不改 tag、不登入 registry、不部署 |
| P2 供應鏈追加 | Package / Docker 供應鏈 repo-only baseline 已新增 `package_json=6 / pyproject=4 / requirements=2 / dockerfiles=2 / compose=6 / gaps=5 / runtime=0`Package / Docker 供應鏈 owner policy gate 已新增 `requests=6 / c0=2 / fields=8 / checks=12 / blocked=20 / sent=0 / accepted=0 / runtime=0`缺口為 Python lockfile 缺席、requirements 未 pin、Docker base image 未全數 digest pinning、Docker `COPY --from` 外部 image 未 digest pinning、compose image 未 digest pinning,以及 CVE / license / SBOM window 未定;目前尚未列入 36 個正式 AwoooP 消費 contract後續若要前台消費需同步 manifest / readiness / route / rollup / dry-run / posture projection / guard count本輪不 install、不 upgrade、不跑 CVE、不 pull / build / push image、不改 tag、不登入 registry、不部署 |
| 原則 | 低摩擦分階段文件、schema、read-only evidence 優先;不做 runtime enforcement、不切 primary |
| P0 主控板 | `docs/workplans/2026-06-04-iwooos-security-governance-p0.md` |

View File

@@ -0,0 +1,310 @@
{
"schema_version": "package_supply_chain_owner_policy_gate_v1",
"status": "draft_waiting_owner_policy_response",
"mode": "repo_snapshot_policy_gate_no_install_no_network_no_cve_scan",
"generated_at": "2026-06-15T07:05:00+08:00",
"baseline_ref": "docs/security/package-supply-chain-baseline.snapshot.json",
"baseline_git_commit": "1ab85f51",
"summary": {
"baseline_gap_count": 5,
"owner_policy_request_count": 6,
"c0_policy_request_count": 2,
"write_capable_policy_request_count": 6,
"required_owner_field_count": 8,
"reviewer_check_count": 12,
"blocked_action_count": 20,
"owner_policy_request_sent_count": 0,
"owner_response_received_count": 0,
"owner_response_accepted_count": 0,
"owner_response_rejected_count": 0,
"runtime_gate_count": 0,
"action_button_count": 0
},
"baseline_gaps": [
"python_lockfile_absent",
"docker_base_images_not_all_digest_pinned",
"docker_copy_from_images_not_all_digest_pinned",
"compose_images_not_all_digest_pinned",
"requirements_unpinned_entries_present"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"reviewer_checks": [
"owner_role_or_team_present",
"decision_present",
"decision_reason_present",
"affected_scope_present",
"redacted_evidence_refs_present",
"followup_owner_present",
"rollback_owner_present",
"package_manager_policy_consistent",
"python_lock_policy_consistent",
"image_digest_policy_consistent",
"cve_license_sbom_window_metadata_only",
"no_runtime_or_secret_request_embedded"
],
"blocked_actions": [
"install_package",
"upgrade_package",
"downgrade_package",
"rewrite_lockfile",
"pin_requirements_without_owner_policy",
"run_npm_audit",
"run_pip_audit",
"run_external_cve_lookup",
"run_external_license_lookup",
"generate_sbom",
"docker_pull",
"docker_build",
"docker_push",
"change_image_tag",
"pin_image_digest_without_owner_policy",
"registry_login",
"change_workflow",
"change_secret",
"production_deploy",
"open_runtime_gate"
],
"owner_policy_requests": [
{
"request_id": "supply_chain_owner_policy:package_manager_lockfile_owner",
"label": "Node / pnpm package manager 與 lockfile owner policy",
"control_tier": "C1",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"package_manager_policy_required"
],
"evidence_refs": [
"package.json",
"pnpm-lock.yaml",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"owner confirms pnpm lockfile owner",
"owner defines lockfile update window",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
},
{
"request_id": "supply_chain_owner_policy:python_lockfile_policy",
"label": "Python lockfile policy",
"control_tier": "C1",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"python_lockfile_absent"
],
"evidence_refs": [
"apps/api/pyproject.toml",
"packages/lewooogo-brain/pyproject.toml",
"packages/lewooogo-data/pyproject.toml",
"scripts/aider_watch_client/pyproject.toml",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"owner selects poetry.lock / uv.lock / Pipfile.lock policy",
"compatibility owner confirms scope",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
},
{
"request_id": "supply_chain_owner_policy:requirements_pin_policy",
"label": "requirements pinning policy",
"control_tier": "C1",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"requirements_unpinned_entries_present"
],
"evidence_refs": [
"apps/api/requirements.txt",
"apps/sensor/requirements.txt",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"owner defines pinning approach",
"compatibility test owner is assigned",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
},
{
"request_id": "supply_chain_owner_policy:dockerfile_digest_pin_policy",
"label": "Dockerfile base image / COPY --from digest policy",
"control_tier": "C0",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"docker_base_images_not_all_digest_pinned",
"docker_copy_from_images_not_all_digest_pinned"
],
"evidence_refs": [
"apps/api/Dockerfile",
"apps/web/Dockerfile",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"registry owner confirms digest source",
"image compatibility owner confirms scope",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
},
{
"request_id": "supply_chain_owner_policy:compose_image_digest_pin_policy",
"label": "docker-compose image digest policy",
"control_tier": "C0",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"compose_images_not_all_digest_pinned"
],
"evidence_refs": [
"apps/api/docker-compose.test.yml",
"docker-compose.yml",
"infra/langfuse/docker-compose.yml",
"k8s/monitoring/docker-compose-110.yml",
"ops/monitoring/docker-compose.exporters.yaml",
"ops/sentry-self-hosted/docker-compose.yml",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"registry owner confirms digest source",
"compose service owner confirms blast radius",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
},
{
"request_id": "supply_chain_owner_policy:cve_license_sbom_window",
"label": "CVE / license / SBOM 驗證窗口 policy",
"control_tier": "C1",
"status": "draft_waiting_owner_policy_response",
"baseline_gap_refs": [
"cve_license_sbom_not_run"
],
"evidence_refs": [
"docs/security/PACKAGE-SUPPLY-CHAIN-BASELINE.md",
"docs/security/package-supply-chain-baseline.snapshot.json"
],
"required_owner_fields": [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner"
],
"blocked_until": [
"owner selects allowed tools",
"network / cost / runtime boundary is confirmed",
"rollback owner is assigned"
],
"request_sent": false,
"owner_response_received": false,
"owner_response_accepted": false,
"runtime_gate_open": false
}
],
"execution_boundaries": {
"package_installation_allowed": false,
"package_upgrade_allowed": false,
"lockfile_write_allowed": false,
"requirements_pin_change_allowed": false,
"external_cve_lookup_allowed": false,
"external_license_lookup_allowed": false,
"sbom_generation_allowed": false,
"docker_pull_allowed": false,
"docker_build_allowed": false,
"docker_push_allowed": false,
"image_tag_change_allowed": false,
"image_digest_pin_change_allowed": false,
"registry_login_allowed": false,
"workflow_modification_authorized": false,
"production_deploy_authorized": false,
"runtime_execution_authorized": false,
"action_buttons_allowed": false,
"secret_value_collection_allowed": false,
"runtime_gate_count": 0,
"not_authorization": true
},
"operator_interpretation": [
"此 owner policy gate 只把 package / Docker baseline 缺口轉成 owner metadata 收件草案。",
"通過此 gate 不代表可 install、upgrade、pin requirements、寫 lockfile、pull / build / push image、跑 CVE / license / SBOM 或登入 registry。",
"C0 Docker / compose digest policy 仍需 registry owner、rollback owner 與相容性證據,不得自動改 tag 或 digest。",
"owner policy request sent、received、accepted、runtime gate 與 action button 全部維持 0 / false。"
]
}

View File

@@ -169,6 +169,34 @@
"production_deploy"
]
},
{
"step_id": "CHECK_PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD",
"expected_observation": "AwoooP dry-run 必須確認 Package / Docker 供應鏈 baseline 的五個缺口已轉成 owner policy gate六個 request、兩個 C0、八個 owner 欄位、十二個 reviewer checks、二十個 blocked actions 齊備,且 request_sent / received / accepted / runtime / action 全部維持 0 / false。",
"evidence_refs": [
"docs/security/PACKAGE-SUPPLY-CHAIN-BASELINE.md",
"docs/security/package-supply-chain-baseline.snapshot.json",
"docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md",
"docs/security/package-supply-chain-owner-policy-gate.snapshot.json",
"scripts/security/package-supply-chain-owner-policy-guard.py"
],
"pass_condition": "`python3 scripts/security/package-supply-chain-owner-policy-guard.py` 回傳 PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD_OK且不 install、不 upgrade、不跑外部 CVE / license lookup、不產生 SBOM、不 pull / build / push image、不登入 registry。",
"execution_allowed": false,
"blocked_actions": [
"install_package",
"upgrade_package",
"rewrite_lockfile",
"pin_requirements_without_owner_policy",
"run_external_cve_lookup",
"run_external_license_lookup",
"generate_sbom",
"docker_pull",
"docker_build",
"docker_push",
"registry_login",
"production_deploy",
"open_runtime_gate"
]
},
{
"step_id": "CHECK_LOW_NOISE_CHANNEL",
"expected_observation": "Channel Event 初期只發低噪音摘要或人工批准必要事件。",
@@ -208,8 +236,8 @@
"status": "repo_snapshot_guard_pass",
"date": "2026-06-15",
"scope": "repo_snapshot_only",
"command": "python3 scripts/security/security-mirror-progress-guard.py && python3 scripts/security/source-control-owner-response-guard.py && python3 scripts/security/iwooos-config-control-guard.py && python3 scripts/security/iwooos-owner-gate-guard.py",
"result": "SECURITY_MIRROR_PROGRESS_GUARD_OK; SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK; IWOOOS_CONFIG_CONTROL_GUARD_OK; IWOOOS_OWNER_GATE_GUARD_OK",
"command": "python3 scripts/security/security-mirror-progress-guard.py && python3 scripts/security/source-control-owner-response-guard.py && python3 scripts/security/iwooos-config-control-guard.py && python3 scripts/security/iwooos-owner-gate-guard.py && python3 scripts/security/package-supply-chain-owner-policy-guard.py",
"result": "SECURITY_MIRROR_PROGRESS_GUARD_OK; SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK; IWOOOS_CONFIG_CONTROL_GUARD_OK; IWOOOS_OWNER_GATE_GUARD_OK; PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD_OK",
"validated_steps": [
"LOAD_CONTRACT_INDEXES",
"CHECK_ACCEPTANCE_AND_QUARANTINE",
@@ -217,6 +245,7 @@
"CHECK_OWNER_RESPONSE_GUARD",
"CHECK_OWNER_GATE_GUARD",
"CHECK_CONFIG_CONTROL_GUARD",
"CHECK_PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD",
"CONFIRM_NO_RUNTIME_ACTION"
],
"runtime_actions_executed": false,

View File

@@ -0,0 +1,211 @@
#!/usr/bin/env python3
"""驗證 Package / Docker 供應鏈 owner policy gate 維持只讀邊界。
本 guard 只讀取 repo 內的 package / Docker 供應鏈 baseline 與 owner
policy gate snapshot不安裝套件、不連外查 CVE / license、不產生 lockfile、
不 pull / build / push image也不修改 workflow、registry 或 production。
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
EXPECTED_BASELINE_GAPS = [
"python_lockfile_absent",
"docker_base_images_not_all_digest_pinned",
"docker_copy_from_images_not_all_digest_pinned",
"compose_images_not_all_digest_pinned",
"requirements_unpinned_entries_present",
]
EXPECTED_REQUEST_IDS = [
"supply_chain_owner_policy:package_manager_lockfile_owner",
"supply_chain_owner_policy:python_lockfile_policy",
"supply_chain_owner_policy:requirements_pin_policy",
"supply_chain_owner_policy:dockerfile_digest_pin_policy",
"supply_chain_owner_policy:compose_image_digest_pin_policy",
"supply_chain_owner_policy:cve_license_sbom_window",
]
EXPECTED_OWNER_FIELDS = [
"package_manager_policy",
"lockfile_owner",
"python_lock_policy",
"docker_base_image_policy",
"compose_image_policy",
"registry_owner",
"cve_scan_window",
"rollback_owner",
]
FALSE_BOUNDARY_KEYS = [
"package_installation_allowed",
"package_upgrade_allowed",
"lockfile_write_allowed",
"requirements_pin_change_allowed",
"external_cve_lookup_allowed",
"external_license_lookup_allowed",
"sbom_generation_allowed",
"docker_pull_allowed",
"docker_build_allowed",
"docker_push_allowed",
"image_tag_change_allowed",
"image_digest_pin_change_allowed",
"registry_login_allowed",
"workflow_modification_authorized",
"production_deploy_authorized",
"runtime_execution_authorized",
"action_buttons_allowed",
"secret_value_collection_allowed",
]
def fail(message: str) -> None:
raise SystemExit(f"BLOCKED {message}")
def load_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def assert_equal(label: str, actual: Any, expected: Any) -> None:
if actual != expected:
fail(f"{label}: expected {expected!r}, got {actual!r}")
def assert_path_exists(root: Path, relative_path: str) -> None:
if not (root / relative_path).exists():
fail(f"path missing: {relative_path}")
def assert_text_contains(label: str, text: str, expected: str) -> None:
if expected not in text:
fail(f"{label}: missing {expected!r}")
def assert_false_boundaries(label: str, data: dict[str, Any]) -> None:
boundaries = data.get("execution_boundaries", {})
for key in FALSE_BOUNDARY_KEYS:
assert_equal(f"{label}.execution_boundaries.{key}", boundaries.get(key), False)
assert_equal(f"{label}.execution_boundaries.runtime_gate_count", boundaries.get("runtime_gate_count"), 0)
assert_equal(f"{label}.execution_boundaries.not_authorization", boundaries.get("not_authorization"), True)
def validate_baseline(root: Path) -> dict[str, Any]:
baseline_path = root / "docs/security/package-supply-chain-baseline.snapshot.json"
assert_path_exists(root, "docs/security/package-supply-chain-baseline.snapshot.json")
baseline = load_json(baseline_path)
summary = baseline.get("summary", {})
assert_equal("baseline.schema_version", baseline.get("schema_version"), "package_supply_chain_baseline_v1")
assert_equal("baseline.status", baseline.get("status"), "repo_only_inventory_ready_needs_owner_policy")
assert_equal("baseline.mode", baseline.get("mode"), "repo_snapshot_only_no_install_no_network_no_cve_scan")
assert_equal("baseline.summary.package_json_count", summary.get("package_json_count"), 6)
assert_equal("baseline.summary.pyproject_count", summary.get("pyproject_count"), 4)
assert_equal("baseline.summary.requirements_file_count", summary.get("requirements_file_count"), 2)
assert_equal("baseline.summary.requirements_unpinned_entry_count", summary.get("requirements_unpinned_entry_count"), 26)
assert_equal("baseline.summary.python_lockfile_count", summary.get("python_lockfile_count"), 0)
assert_equal("baseline.summary.docker_base_digest_pinned_count", summary.get("docker_base_digest_pinned_count"), 0)
assert_equal(
"baseline.summary.compose_digest_pinned_image_ref_count",
summary.get("compose_digest_pinned_image_ref_count"),
0,
)
assert_equal("baseline.summary.gap_count", summary.get("gap_count"), 5)
assert_equal("baseline.gaps", baseline.get("gaps"), EXPECTED_BASELINE_GAPS)
assert_equal("baseline.owner_response_received_count", summary.get("owner_response_received_count"), 0)
assert_equal("baseline.owner_response_accepted_count", summary.get("owner_response_accepted_count"), 0)
assert_equal("baseline.runtime_gate_count", summary.get("runtime_gate_count"), 0)
assert_equal("baseline.action_button_count", summary.get("action_button_count"), 0)
for relative_path in baseline.get("lockfiles", []):
assert_path_exists(root, relative_path)
for key in ["package_json_manifests", "pyproject_manifests", "requirements_files", "dockerfiles", "compose_files"]:
for item in baseline.get(key, []):
assert_path_exists(root, item["path"])
return baseline
def validate_policy_gate(root: Path, baseline: dict[str, Any]) -> None:
gate_path = root / "docs/security/package-supply-chain-owner-policy-gate.snapshot.json"
doc_path = root / "docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md"
assert_path_exists(root, "docs/security/package-supply-chain-owner-policy-gate.snapshot.json")
assert_path_exists(root, "docs/security/PACKAGE-SUPPLY-CHAIN-OWNER-POLICY-GATE.md")
gate = load_json(gate_path)
summary = gate.get("summary", {})
assert_equal("policy_gate.schema_version", gate.get("schema_version"), "package_supply_chain_owner_policy_gate_v1")
assert_equal("policy_gate.status", gate.get("status"), "draft_waiting_owner_policy_response")
assert_equal("policy_gate.mode", gate.get("mode"), "repo_snapshot_policy_gate_no_install_no_network_no_cve_scan")
assert_equal("policy_gate.baseline_ref", gate.get("baseline_ref"), "docs/security/package-supply-chain-baseline.snapshot.json")
assert_equal("policy_gate.summary.baseline_gap_count", summary.get("baseline_gap_count"), baseline["summary"]["gap_count"])
assert_equal("policy_gate.summary.owner_policy_request_count", summary.get("owner_policy_request_count"), 6)
assert_equal("policy_gate.summary.c0_policy_request_count", summary.get("c0_policy_request_count"), 2)
assert_equal("policy_gate.summary.write_capable_policy_request_count", summary.get("write_capable_policy_request_count"), 6)
assert_equal("policy_gate.summary.required_owner_field_count", summary.get("required_owner_field_count"), 8)
assert_equal("policy_gate.summary.reviewer_check_count", summary.get("reviewer_check_count"), 12)
assert_equal("policy_gate.summary.blocked_action_count", summary.get("blocked_action_count"), 20)
assert_equal("policy_gate.summary.owner_policy_request_sent_count", summary.get("owner_policy_request_sent_count"), 0)
assert_equal("policy_gate.summary.owner_response_received_count", summary.get("owner_response_received_count"), 0)
assert_equal("policy_gate.summary.owner_response_accepted_count", summary.get("owner_response_accepted_count"), 0)
assert_equal("policy_gate.summary.owner_response_rejected_count", summary.get("owner_response_rejected_count"), 0)
assert_equal("policy_gate.summary.runtime_gate_count", summary.get("runtime_gate_count"), 0)
assert_equal("policy_gate.summary.action_button_count", summary.get("action_button_count"), 0)
assert_equal("policy_gate.baseline_gaps", gate.get("baseline_gaps"), EXPECTED_BASELINE_GAPS)
assert_equal("policy_gate.required_owner_fields", gate.get("required_owner_fields"), EXPECTED_OWNER_FIELDS)
assert_equal("policy_gate.owner_policy_request_ids", [item["request_id"] for item in gate["owner_policy_requests"]], EXPECTED_REQUEST_IDS)
for item in gate["owner_policy_requests"]:
assert_equal(f"{item['request_id']}.status", item.get("status"), "draft_waiting_owner_policy_response")
assert_equal(f"{item['request_id']}.required_owner_fields", item.get("required_owner_fields"), EXPECTED_OWNER_FIELDS)
assert_equal(f"{item['request_id']}.request_sent", item.get("request_sent"), False)
assert_equal(f"{item['request_id']}.owner_response_received", item.get("owner_response_received"), False)
assert_equal(f"{item['request_id']}.owner_response_accepted", item.get("owner_response_accepted"), False)
assert_equal(f"{item['request_id']}.runtime_gate_open", item.get("runtime_gate_open"), False)
if not item.get("baseline_gap_refs"):
fail(f"{item['request_id']}.baseline_gap_refs: expected non-empty list")
for ref in item.get("evidence_refs", []):
assert_path_exists(root, ref)
if not item.get("blocked_until"):
fail(f"{item['request_id']}.blocked_until: expected non-empty list")
assert_false_boundaries("policy_gate", gate)
doc = doc_path.read_text(encoding="utf-8")
for text in [
"owner policy gate",
"Python lockfile",
"requirements pinning",
"Docker digest pinning",
"CVE / license / SBOM",
"runtime gate",
"0 / false",
"不 install、不 upgrade、不 pull image、不 build image、不 push image",
]:
assert_text_contains("policy_gate.doc", doc, text)
def validate(root: Path) -> None:
baseline = validate_baseline(root)
validate_policy_gate(root, baseline)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--root",
default=Path(__file__).resolve().parents[2],
type=Path,
help="Repository root. Defaults to the current script's repository.",
)
args = parser.parse_args()
validate(args.root.resolve())
print("PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD_OK")
if __name__ == "__main__":
main()

View File

@@ -79,6 +79,10 @@ def validate(root: Path) -> None:
config_control_guard["validate"](root)
owner_gate_guard = runpy.run_path(str(root / "scripts" / "security" / "iwooos-owner-gate-guard.py"))
owner_gate_guard["validate"](root)
package_policy_guard = runpy.run_path(
str(root / "scripts" / "security" / "package-supply-chain-owner-policy-guard.py")
)
package_policy_guard["validate"](root)
manifest = load_json(security_dir / "security-supply-chain-contract-manifest.snapshot.json")
readiness = load_json(security_dir / "security-mirror-readiness.snapshot.json")
@@ -24992,7 +24996,7 @@ def validate(root: Path) -> None:
assert_equal(
"dry_run.latest_local_validation.result",
local_validation["result"],
"SECURITY_MIRROR_PROGRESS_GUARD_OK; SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK; IWOOOS_CONFIG_CONTROL_GUARD_OK; IWOOOS_OWNER_GATE_GUARD_OK",
"SECURITY_MIRROR_PROGRESS_GUARD_OK; SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK; IWOOOS_CONFIG_CONTROL_GUARD_OK; IWOOOS_OWNER_GATE_GUARD_OK; PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD_OK",
)
assert_contains("dry_run.latest_local_validation.validated_steps", local_validation["validated_steps"], "CHECK_PROGRESS_GUARD")
assert_contains(
@@ -25010,6 +25014,11 @@ def validate(root: Path) -> None:
local_validation["validated_steps"],
"CHECK_CONFIG_CONTROL_GUARD",
)
assert_contains(
"dry_run.latest_local_validation.validated_steps",
local_validation["validated_steps"],
"CHECK_PACKAGE_SUPPLY_CHAIN_OWNER_POLICY_GUARD",
)
assert_false("dry_run.latest_local_validation.runtime_actions_executed", local_validation["runtime_actions_executed"])
assert_false("dry_run.latest_local_validation.payloads_ingested", local_validation["payloads_ingested"])
assert_false("dry_run.latest_local_validation.production_ingestion_enabled", local_validation["production_ingestion_enabled"])