swiftapi-python 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
swiftapi/enforcement.py CHANGED
@@ -1,280 +1,281 @@
1
- """
2
- SwiftAPI SDK - Enforcement Point (The Golden Loop)
3
-
4
- This module provides the high-level enforcement mechanism that ensures
5
- no action executes without proper verification and attestation.
6
-
7
- The Golden Loop:
8
- 1. API Call: client.verify() -> Get Attestation
9
- 2. Crypto Check: verifier.verify_signature() -> OFFLINE TRUTH
10
- 3. Online Check: client.check_revocation() -> ONLINE TRUTH (optional)
11
- 4. Execute: Run the protected function
12
-
13
- If any step fails, the action is BLOCKED.
14
- """
15
-
16
- from typing import Callable, Any, Dict, Optional
17
- from functools import wraps
18
-
19
- from .client import SwiftAPI
20
- from .verifier import verify_signature, is_valid
21
- from .exceptions import (
22
- PolicyViolation,
23
- SignatureVerificationError,
24
- AttestationRevokedError,
25
- AttestationExpiredError,
26
- SwiftAPIError,
27
- )
28
- from .utils import print_approved, print_denied, print_verified, print_error
29
-
30
-
31
- class Enforcement:
32
- """
33
- SwiftAPI Enforcement Point.
34
-
35
- This is the "ignition key" - no action executes without verification.
36
-
37
- Usage:
38
- api = SwiftAPI(key="swiftapi_live_...")
39
- guard = Enforcement(api)
40
-
41
- # Option 1: Run with enforcement
42
- guard.run(
43
- lambda: dangerous_operation(),
44
- action="file_delete",
45
- intent="Remove temp files"
46
- )
47
-
48
- # Option 2: Decorator
49
- @guard.protect(action="api_call", intent="Send notification")
50
- def send_notification(user_id, message):
51
- ...
52
- """
53
-
54
- def __init__(
55
- self,
56
- client: SwiftAPI,
57
- paranoid: bool = False,
58
- verbose: bool = True,
59
- ):
60
- """
61
- Initialize enforcement point.
62
-
63
- Args:
64
- client: SwiftAPI client instance
65
- paranoid: If True, always check revocation online (slower but safer)
66
- verbose: If True, print status messages
67
- """
68
- self.client = client
69
- self.paranoid = paranoid
70
- self.verbose = verbose
71
-
72
- def run(
73
- self,
74
- func: Callable[[], Any],
75
- action: str,
76
- intent: str,
77
- params: Optional[Dict[str, Any]] = None,
78
- actor: str = "sdk",
79
- ) -> Any:
80
- """
81
- Execute a function with SwiftAPI enforcement.
82
-
83
- The function will ONLY execute if:
84
- 1. SwiftAPI approves the action
85
- 2. The attestation signature is valid (cryptographic proof)
86
- 3. The attestation is not revoked (if paranoid mode)
87
-
88
- Args:
89
- func: The function to execute (takes no arguments)
90
- action: Action type for verification
91
- intent: Human-readable intent description
92
- params: Optional action parameters
93
- actor: Actor identifier
94
-
95
- Returns:
96
- The return value of func()
97
-
98
- Raises:
99
- PolicyViolation: If action is denied by policy
100
- SignatureVerificationError: If attestation signature is invalid
101
- AttestationRevokedError: If attestation was revoked
102
- SwiftAPIError: For other API errors
103
- """
104
- # Step 1: API Call - Get attestation
105
- try:
106
- result = self.client.verify(
107
- action_type=action,
108
- intent=intent,
109
- params=params,
110
- actor=actor,
111
- )
112
- except PolicyViolation as e:
113
- if self.verbose:
114
- print_denied(action, intent, e.denial_reason)
115
- raise
116
-
117
- # Check if approved
118
- if not result.get("approved"):
119
- reason = result.get("reason", "Unknown denial reason")
120
- if self.verbose:
121
- print_denied(action, intent, reason)
122
- raise PolicyViolation(
123
- message=f"Action denied: {reason}",
124
- action_type=action,
125
- denial_reason=reason,
126
- )
127
-
128
- attestation = result.get("execution_attestation")
129
- if not attestation:
130
- raise SwiftAPIError("No attestation in response")
131
-
132
- jti = attestation.get("jti")
133
-
134
- # Step 2: Crypto Check - OFFLINE TRUTH
135
- try:
136
- verify_signature(attestation)
137
- if self.verbose:
138
- print_verified(jti)
139
- except (SignatureVerificationError, AttestationExpiredError) as e:
140
- if self.verbose:
141
- print_error(str(e))
142
- raise
143
-
144
- # Step 3: Online Check - ONLINE TRUTH (optional but recommended)
145
- if self.paranoid:
146
- if self.client.check_revocation(jti):
147
- if self.verbose:
148
- print_error(f"Attestation {jti} has been revoked")
149
- raise AttestationRevokedError(jti)
150
-
151
- # Step 4: Execute - THE ACTION RUNS
152
- if self.verbose:
153
- print_approved(action, intent)
154
-
155
- return func()
156
-
157
- def protect(
158
- self,
159
- action: str,
160
- intent: str,
161
- params: Optional[Dict[str, Any]] = None,
162
- ):
163
- """
164
- Decorator to protect a function with SwiftAPI enforcement.
165
-
166
- Usage:
167
- @guard.protect(action="file_write", intent="Save config")
168
- def save_config(data):
169
- with open("/etc/config", "w") as f:
170
- f.write(data)
171
-
172
- Args:
173
- action: Action type for verification
174
- intent: Human-readable intent description
175
- params: Optional action parameters
176
- """
177
- def decorator(func: Callable) -> Callable:
178
- @wraps(func)
179
- def wrapper(*args, **kwargs):
180
- return self.run(
181
- func=lambda: func(*args, **kwargs),
182
- action=action,
183
- intent=intent,
184
- params=params,
185
- actor=func.__name__,
186
- )
187
- return wrapper
188
- return decorator
189
-
190
- def guard(
191
- self,
192
- action: str,
193
- intent: str,
194
- params: Optional[Dict[str, Any]] = None,
195
- ) -> "AttestationGuard":
196
- """
197
- Context manager for guarded execution blocks.
198
-
199
- Usage:
200
- with guard.guard(action="file_delete", intent="Cleanup temp"):
201
- os.remove("/tmp/file.txt")
202
-
203
- Args:
204
- action: Action type for verification
205
- intent: Human-readable intent description
206
- params: Optional action parameters
207
- """
208
- return AttestationGuard(self, action, intent, params)
209
-
210
-
211
- class AttestationGuard:
212
- """Context manager for guarded execution."""
213
-
214
- def __init__(
215
- self,
216
- enforcement: Enforcement,
217
- action: str,
218
- intent: str,
219
- params: Optional[Dict[str, Any]] = None,
220
- ):
221
- self.enforcement = enforcement
222
- self.action = action
223
- self.intent = intent
224
- self.params = params
225
- self.attestation = None
226
-
227
- def __enter__(self):
228
- # Get and verify attestation before entering the block
229
- result = self.enforcement.client.verify(
230
- action_type=self.action,
231
- intent=self.intent,
232
- params=self.params,
233
- )
234
-
235
- if not result.get("approved"):
236
- reason = result.get("reason", "Unknown")
237
- if self.enforcement.verbose:
238
- print_denied(self.action, self.intent, reason)
239
- raise PolicyViolation(
240
- message=f"Action denied: {reason}",
241
- action_type=self.action,
242
- denial_reason=reason,
243
- )
244
-
245
- self.attestation = result.get("execution_attestation")
246
- verify_signature(self.attestation)
247
-
248
- if self.enforcement.paranoid:
249
- jti = self.attestation.get("jti")
250
- if self.enforcement.client.check_revocation(jti):
251
- raise AttestationRevokedError(jti)
252
-
253
- if self.enforcement.verbose:
254
- print_approved(self.action, self.intent)
255
-
256
- return self.attestation
257
-
258
- def __exit__(self, exc_type, exc_val, exc_tb):
259
- return False # Don't suppress exceptions
260
-
261
-
262
- # Convenience function for one-off protected execution
263
- def enforce(
264
- client: SwiftAPI,
265
- func: Callable[[], Any],
266
- action: str,
267
- intent: str,
268
- params: Optional[Dict[str, Any]] = None,
269
- ) -> Any:
270
- """
271
- One-off enforcement without creating an Enforcement instance.
272
-
273
- Usage:
274
- from swiftapi import SwiftAPI, enforce
275
-
276
- api = SwiftAPI(key="...")
277
- enforce(api, lambda: rm_rf("/"), action="file_delete", intent="Nuke it")
278
- """
279
- guard = Enforcement(client)
280
- return guard.run(func, action, intent, params)
1
+ """
2
+ SwiftAPI SDK - Enforcement Point (The Golden Loop)
3
+
4
+ This module provides the high-level enforcement mechanism that ensures
5
+ callbacks mediated by this class require verification and attestation.
6
+
7
+ The Golden Loop:
8
+ 1. API Call: client.verify() -> Get Attestation
9
+ 2. Crypto Check: verifier.verify_signature() -> OFFLINE TRUTH
10
+ 3. Online Check: client.check_revocation() -> ONLINE TRUTH (optional)
11
+ 4. Execute: Run the protected function
12
+
13
+ If any step fails, the action is BLOCKED.
14
+ """
15
+
16
+ from typing import Callable, Any, Dict, Optional
17
+ from functools import wraps
18
+
19
+ from .client import SwiftAPI
20
+ from .verifier import (
21
+ verify_signature,
22
+ claim_attestation,
23
+ is_valid as is_valid,
24
+ _action_fingerprint,
25
+ _snapshot_action_data,
26
+ _validate_action_binding,
27
+ )
28
+ from .exceptions import (
29
+ PolicyViolation,
30
+ SignatureVerificationError,
31
+ AttestationRevokedError,
32
+ AttestationExpiredError,
33
+ SwiftAPIError,
34
+ )
35
+ from .utils import print_approved, print_denied, print_verified, print_error
36
+
37
+
38
+ class Enforcement:
39
+ """
40
+ SwiftAPI Enforcement Point.
41
+
42
+ This is the "ignition key" - no action executes without verification.
43
+
44
+ Usage:
45
+ api = SwiftAPI(key="swiftapi_live_...")
46
+ guard = Enforcement(api)
47
+
48
+ # Option 1: Run with enforcement
49
+ guard.run(
50
+ lambda: dangerous_operation(),
51
+ action="file_delete",
52
+ intent="Remove temp files"
53
+ )
54
+
55
+ # Option 2: Decorator
56
+ @guard.protect(action="api_call", intent="Send notification")
57
+ def send_notification(user_id, message):
58
+ ...
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ client: SwiftAPI,
64
+ paranoid: bool = False,
65
+ verbose: bool = True,
66
+ ):
67
+ """
68
+ Initialize enforcement point.
69
+
70
+ Args:
71
+ client: SwiftAPI client instance
72
+ paranoid: If True, always check revocation online (slower but safer)
73
+ verbose: If True, print status messages
74
+ """
75
+ self.client = client
76
+ self.paranoid = paranoid
77
+ self.verbose = verbose
78
+
79
+ def run(
80
+ self,
81
+ func: Callable[[], Any],
82
+ action: str,
83
+ intent: str,
84
+ params: Optional[Dict[str, Any]] = None,
85
+ actor: str = "sdk",
86
+ ) -> Any:
87
+ """
88
+ Execute a function with SwiftAPI enforcement.
89
+
90
+ The function will ONLY execute if:
91
+ 1. SwiftAPI approves the action
92
+ 2. The attestation signature is valid (cryptographic proof)
93
+ 3. The attestation is not revoked (if paranoid mode)
94
+
95
+ Args:
96
+ func: The function to execute (takes no arguments)
97
+ action: Action type for verification
98
+ intent: Human-readable intent description
99
+ params: Optional action parameters
100
+ actor: Actor identifier
101
+
102
+ Returns:
103
+ The return value of func()
104
+
105
+ Raises:
106
+ PolicyViolation: If action is denied by policy
107
+ SignatureVerificationError: If attestation signature is invalid
108
+ AttestationRevokedError: If attestation was revoked
109
+ SwiftAPIError: For other API errors
110
+ """
111
+ self._authorize(action, intent, params, actor)
112
+ return func()
113
+
114
+ def _authorize(self, action, intent, params=None, actor="sdk"):
115
+ """Verify the authority's signed decision for this exact action."""
116
+ action_data = _snapshot_action_data({
117
+ "action_type": action,
118
+ "intent": intent,
119
+ "params": params or {},
120
+ })
121
+ expected_fingerprint = _action_fingerprint(action_data)
122
+
123
+ # Step 1: API Call - Get attestation
124
+ try:
125
+ result = self.client.verify(
126
+ action_type=action,
127
+ intent=intent,
128
+ params=action_data["params"],
129
+ actor=actor,
130
+ )
131
+ except PolicyViolation as e:
132
+ if self.verbose:
133
+ print_denied(action, intent, e.denial_reason)
134
+ raise
135
+
136
+ # Check if approved
137
+ if not isinstance(result, dict):
138
+ raise SwiftAPIError("Invalid verification response")
139
+ if result.get("approved") is not True:
140
+ reason = result.get("reason", "Unknown denial reason")
141
+ if self.verbose:
142
+ print_denied(action, intent, reason)
143
+ raise PolicyViolation(
144
+ message=f"Action denied: {reason}",
145
+ action_type=action,
146
+ denial_reason=reason,
147
+ )
148
+
149
+ attestation = result.get("execution_attestation")
150
+ if not attestation:
151
+ raise SwiftAPIError("No attestation in response")
152
+
153
+ # Step 2: Crypto Check - OFFLINE TRUTH
154
+ try:
155
+ verify_signature(attestation)
156
+ if not _validate_action_binding(attestation, action, expected_fingerprint):
157
+ raise PolicyViolation(
158
+ "Signed attestation denies the action", action_type=action,
159
+ denial_reason="Signed attestation denies the action",
160
+ )
161
+ jti = attestation["jti"]
162
+ if self.verbose:
163
+ print_verified(jti)
164
+ except (SignatureVerificationError, AttestationExpiredError) as e:
165
+ if self.verbose:
166
+ print_error(str(e))
167
+ raise
168
+
169
+ # Step 3: Online Check - ONLINE TRUTH (optional but recommended)
170
+ if self.paranoid:
171
+ if self.client.check_revocation(jti):
172
+ if self.verbose:
173
+ print_error(f"Attestation {jti} has been revoked")
174
+ raise AttestationRevokedError(jti)
175
+
176
+ claim_attestation(attestation)
177
+
178
+ # Step 4: Execute - THE ACTION RUNS
179
+ if self.verbose:
180
+ print_approved(action, intent)
181
+
182
+ return attestation
183
+
184
+ def protect(
185
+ self,
186
+ action: str,
187
+ intent: str,
188
+ params: Optional[Dict[str, Any]] = None,
189
+ ):
190
+ """
191
+ Decorator to protect a function with SwiftAPI enforcement.
192
+
193
+ Usage:
194
+ @guard.protect(action="file_write", intent="Save config")
195
+ def save_config(data):
196
+ with open("/etc/config", "w") as f:
197
+ f.write(data)
198
+
199
+ Args:
200
+ action: Action type for verification
201
+ intent: Human-readable intent description
202
+ params: Optional action parameters
203
+ """
204
+ def decorator(func: Callable) -> Callable:
205
+ @wraps(func)
206
+ def wrapper(*args, **kwargs):
207
+ return self.run(
208
+ func=lambda: func(*args, **kwargs),
209
+ action=action,
210
+ intent=intent,
211
+ params=params,
212
+ actor=func.__name__,
213
+ )
214
+ return wrapper
215
+ return decorator
216
+
217
+ def guard(
218
+ self,
219
+ action: str,
220
+ intent: str,
221
+ params: Optional[Dict[str, Any]] = None,
222
+ ) -> "AttestationGuard":
223
+ """
224
+ Context manager for guarded execution blocks.
225
+
226
+ Usage:
227
+ with guard.guard(action="file_delete", intent="Cleanup temp"):
228
+ os.remove("/tmp/file.txt")
229
+
230
+ Args:
231
+ action: Action type for verification
232
+ intent: Human-readable intent description
233
+ params: Optional action parameters
234
+ """
235
+ return AttestationGuard(self, action, intent, params)
236
+
237
+
238
+ class AttestationGuard:
239
+ """Context manager for guarded execution."""
240
+
241
+ def __init__(
242
+ self,
243
+ enforcement: Enforcement,
244
+ action: str,
245
+ intent: str,
246
+ params: Optional[Dict[str, Any]] = None,
247
+ ):
248
+ self.enforcement = enforcement
249
+ self.action = action
250
+ self.intent = intent
251
+ self.params = params
252
+ self.attestation = None
253
+
254
+ def __enter__(self):
255
+ # Get and verify attestation before entering the block
256
+ self.attestation = self.enforcement._authorize(self.action, self.intent, self.params)
257
+ return self.attestation
258
+
259
+ def __exit__(self, exc_type, exc_val, exc_tb):
260
+ return False # Don't suppress exceptions
261
+
262
+
263
+ # Convenience function for one-off protected execution
264
+ def enforce(
265
+ client: SwiftAPI,
266
+ func: Callable[[], Any],
267
+ action: str,
268
+ intent: str,
269
+ params: Optional[Dict[str, Any]] = None,
270
+ ) -> Any:
271
+ """
272
+ One-off enforcement without creating an Enforcement instance.
273
+
274
+ Usage:
275
+ from swiftapi import SwiftAPI, enforce
276
+
277
+ api = SwiftAPI(key="...")
278
+ enforce(api, lambda: rm_rf("/"), action="file_delete", intent="Nuke it")
279
+ """
280
+ guard = Enforcement(client)
281
+ return guard.run(func, action, intent, params)