"""NeoLogic / 外部資料に従った送信要求を、通常コードで制限する固定教材。
実行: python3 action-approval-example-20261026.py
Python 3標準機能のみ。AI・メール送信・ネットワーク通信は使いません。
依頼の候補を文字列として用意し、実行可否だけを判定します。攻撃防止率の測定ではありません。
"""
import hashlib
import json


def digest(action):
    return hashlib.sha256(json.dumps(action, sort_keys=True).encode()).hexdigest()


def decide(action, approved, stopped=False):
    if stopped: return "停止中"
    if action.get("tool") != "send": return "許可機能ではない"
    if action.get("destination") != "BOX-A": return "許可宛先ではない"
    if action.get("documents") != ["S"]: return "許可データではない"
    if approved is None: return "利用者の承認がない"
    if digest(action) != approved: return "承認した内容と違う"
    return "実行候補として受理"


def run():
    allowed = {"tool": "send", "destination": "BOX-A", "documents": ["S"], "body": "資料Sの要約"}
    approval = digest(allowed)
    cases = [
        ("資料中の別宛先へ変更", {**allowed, "destination": "BOX-B"}, approval, False, "許可宛先ではない"),
        ("外部資料に承認済みと書いてあるだけ", allowed, None, False, "利用者の承認がない"),
        ("別資料Tを追加", {**allowed, "documents": ["S", "T"]}, approval, False, "許可データではない"),
        ("承認後に本文を変更", {**allowed, "body": "別の本文"}, approval, False, "承認した内容と違う"),
        ("削除機能へ変更", {**allowed, "tool": "delete"}, approval, False, "許可機能ではない"),
        ("停止後の同じ候補", allowed, approval, True, "停止中"),
        ("許可条件と承認が一致", allowed, approval, False, "実行候補として受理"),
    ]
    result = []
    for name, action, approved, stopped, expected in cases:
        actual = decide(action, approved, stopped)
        assert actual == expected
        result.append({"case": name, "decision": actual})
    return {"scope": "fixed local authorization model; no AI or send", "cases": result}


if __name__ == "__main__":
    print(json.dumps(run(), ensure_ascii=False, indent=2))
