From 219fc3835cd1089697061323c23bda9e2bb79489 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Jun 2026 22:38:28 +0800 Subject: [PATCH] test(awooop): guard controlled automation copy --- ...awooop_controlled_automation_copy_guard.py | 15 +++ ...awooop-controlled-automation-copy-guard.py | 102 ++++++++++++++++++ .../security-mirror-progress-guard.py | 4 + 3 files changed, 121 insertions(+) create mode 100644 apps/api/tests/test_awooop_controlled_automation_copy_guard.py create mode 100755 scripts/security/awooop-controlled-automation-copy-guard.py diff --git a/apps/api/tests/test_awooop_controlled_automation_copy_guard.py b/apps/api/tests/test_awooop_controlled_automation_copy_guard.py new file mode 100644 index 00000000..ab90c981 --- /dev/null +++ b/apps/api/tests/test_awooop_controlled_automation_copy_guard.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +import runpy +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[3] + + +def test_awooop_controlled_automation_copy_guard_blocks_legacy_manual_gate_text() -> None: + guard = runpy.run_path( + str(ROOT / "scripts" / "security" / "awooop-controlled-automation-copy-guard.py") + ) + + guard["validate"](ROOT) diff --git a/scripts/security/awooop-controlled-automation-copy-guard.py b/scripts/security/awooop-controlled-automation-copy-guard.py new file mode 100755 index 00000000..c24d5c23 --- /dev/null +++ b/scripts/security/awooop-controlled-automation-copy-guard.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Guard AwoooP public copy against legacy manual-gate defaults. + +This guard is static and read-only. It prevents AwoooP pages and serialized +message payloads from reintroducing old manual-gate wording as the default +state for low / medium / high controlled automation. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +TEXT_FILES = [ + Path("apps/web/messages/zh-TW.json"), + Path("apps/web/messages/en.json"), +] + +AWOOOP_SOURCE_ROOT = Path("apps/web/src/app/[locale]/awooop") +ALERTS_ROUTE = AWOOOP_SOURCE_ROOT / "alerts" / "page.tsx" + +FORBIDDEN_FRAGMENTS = [ + "待人工決策", + "等待人工決策", + "阻塞與人工閘門", + "人工接手", + "人工決策佇列", + "人工關卡", + "人工 gate", + "人工閘門", + "人工升級", + "owner review", + "owner packet", + "manual gate", + "manual handoff", +] + +REQUIRED_FRAGMENTS = [ + "待 AI 受控決策", + "阻塞與 AI 受控隊列", + "AI 處置包與工作項", + "受控執行邊界", + "受控授權閘門", + "controlled gate", + "controlled review", +] + + +def _iter_guarded_files(root: Path) -> list[Path]: + files = [root / path for path in TEXT_FILES] + source_root = root / AWOOOP_SOURCE_ROOT + files.extend(sorted(source_root.rglob("*.tsx"))) + return files + + +def validate(root: Path) -> None: + root = root.resolve() + violations: list[str] = [] + guarded_text = [] + + for path in _iter_guarded_files(root): + if not path.exists(): + violations.append(f"{path.relative_to(root)}: missing guarded file") + continue + text = path.read_text(encoding="utf-8") + guarded_text.append(text) + for line_number, line in enumerate(text.splitlines(), start=1): + for fragment in FORBIDDEN_FRAGMENTS: + if fragment in line: + relative = path.relative_to(root) + violations.append(f"{relative}:{line_number}: forbidden {fragment!r}") + + alerts_route = root / ALERTS_ROUTE + if not alerts_route.exists(): + violations.append(f"{ALERTS_ROUTE}: missing AwoooP Alerts route") + else: + route_text = alerts_route.read_text(encoding="utf-8") + if "#ai-alert-card-delivery-readback" not in route_text: + violations.append(f"{ALERTS_ROUTE}: missing ai-alert-card-delivery-readback redirect target") + + combined_text = "\n".join(guarded_text) + for fragment in REQUIRED_FRAGMENTS: + if fragment not in combined_text: + violations.append(f"messages/source: missing required controlled automation text {fragment!r}") + + if violations: + formatted = "\n".join(violations[:40]) + raise SystemExit(f"BLOCKED awooop_controlled_automation_copy_guard:\n{formatted}") + + +def main() -> int: + parser = argparse.ArgumentParser(description="Validate AwoooP controlled automation public copy.") + parser.add_argument("--root", default=".") + args = parser.parse_args() + validate(Path(args.root)) + print("AWOOOP_CONTROLLED_AUTOMATION_COPY_GUARD_OK") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index e57a57e5..22513b1c 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -91,6 +91,10 @@ def validate(root: Path) -> None: str(root / "scripts" / "security" / "iwooos-frontend-display-redaction-guard.py") ) iwooos_frontend_display_redaction_guard["validate"](root) + awooop_controlled_automation_copy_guard = runpy.run_path( + str(root / "scripts" / "security" / "awooop-controlled-automation-copy-guard.py") + ) + awooop_controlled_automation_copy_guard["validate"](root) wazuh_readonly_route_boundary_guard = runpy.run_path( str(root / "scripts" / "security" / "wazuh-readonly-route-boundary-guard.py") )