File size: 3,281 Bytes
6abc8c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""Policy-oracle matrix for deterministic authorization verification."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

try:
    from ..models import CyberSecurityOWASPState
    from ..validators import simulate_request
except ImportError:  # pragma: no cover
    from models import CyberSecurityOWASPState
    from validators import simulate_request


@dataclass(frozen=True)
class AuthzOracle:
    """Builds hidden allowed/denied tuples from policy facts."""

    def matrix(self, state: CyberSecurityOWASPState) -> list[dict[str, Any]]:
        hidden = state.hidden_facts
        return [
            {
                "name": "owner_can_read_owned_invoice",
                "method": "GET",
                "path": f"/invoices/{hidden['owner_invoice_id']}",
                "user_id": hidden["owner_user_id"],
                "expected_status": 200,
                "allowed": True,
            },
            {
                "name": "resource_owner_can_read_own_invoice",
                "method": "GET",
                "path": f"/invoices/{hidden['other_invoice_id']}",
                "user_id": hidden["intruder_user_id"],
                "expected_status": 200,
                "allowed": True,
            },
            {
                "name": "billing_admin_can_read_same_tenant_invoice",
                "method": "GET",
                "path": f"/invoices/{hidden['other_invoice_id']}",
                "user_id": hidden["admin_user_id"],
                "expected_status": 200,
                "allowed": True,
            },
            {
                "name": "same_tenant_non_owner_denied",
                "method": "GET",
                "path": f"/invoices/{hidden['other_invoice_id']}",
                "user_id": hidden["owner_user_id"],
                "expected_status": 403,
                "allowed": False,
            },
            {
                "name": "cross_tenant_admin_denied",
                "method": "GET",
                "path": f"/invoices/{hidden['foreign_invoice_id']}",
                "user_id": hidden["admin_user_id"],
                "expected_status": 403,
                "allowed": False,
            },
            {
                "name": "health_remains_public",
                "method": "GET",
                "path": "/health",
                "user_id": None,
                "expected_status": 200,
                "allowed": True,
            },
        ]

    def evaluate(self, state: CyberSecurityOWASPState) -> dict[str, Any]:
        cases = []
        for case in self.matrix(state):
            response = simulate_request(
                state,
                str(case["method"]),
                str(case["path"]),
                case.get("user_id"),
            )
            actual = int(response["status"])
            cases.append(
                {
                    "name": case["name"],
                    "allowed": bool(case["allowed"]),
                    "expected_status": int(case["expected_status"]),
                    "actual_status": actual,
                    "passed": actual == int(case["expected_status"]),
                }
            )
        return {"passed": all(case["passed"] for case in cases), "cases": cases}