fivedrisk 0.5.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.
Files changed (52) hide show
  1. fivedrisk/README.md +310 -0
  2. fivedrisk/__init__.py +140 -0
  3. fivedrisk/__main__.py +4 -0
  4. fivedrisk/benchmarks.py +249 -0
  5. fivedrisk/budget_accumulator.py +167 -0
  6. fivedrisk/classifier.py +178 -0
  7. fivedrisk/cli.py +195 -0
  8. fivedrisk/detectors.py +49 -0
  9. fivedrisk/drift.py +209 -0
  10. fivedrisk/events.py +177 -0
  11. fivedrisk/fixtures/attacker/safe_article.txt +2 -0
  12. fivedrisk/fixtures/attacker/webfetch_hidden_override.html +9 -0
  13. fivedrisk/fixtures/attacker/webfetch_prompt_exfil.txt +1 -0
  14. fivedrisk/hooks.py +959 -0
  15. fivedrisk/langgraph_node.py +119 -0
  16. fivedrisk/logger.py +292 -0
  17. fivedrisk/markov.py +368 -0
  18. fivedrisk/policy.py +208 -0
  19. fivedrisk/router.py +236 -0
  20. fivedrisk/schema.py +383 -0
  21. fivedrisk/scorer.py +236 -0
  22. fivedrisk/tests/__init__.py +0 -0
  23. fivedrisk/tests/test_acting_identity.py +107 -0
  24. fivedrisk/tests/test_atlas_coverage.py +201 -0
  25. fivedrisk/tests/test_budget_accumulator.py +121 -0
  26. fivedrisk/tests/test_budget_enforcement_integration.py +148 -0
  27. fivedrisk/tests/test_classifier.py +108 -0
  28. fivedrisk/tests/test_classifier_capability.py +110 -0
  29. fivedrisk/tests/test_classifier_per_dim.py +224 -0
  30. fivedrisk/tests/test_cli_benchmarks.py +149 -0
  31. fivedrisk/tests/test_drift.py +248 -0
  32. fivedrisk/tests/test_drift_capability.py +171 -0
  33. fivedrisk/tests/test_golden_set.py +64 -0
  34. fivedrisk/tests/test_hooks.py +258 -0
  35. fivedrisk/tests/test_logger.py +208 -0
  36. fivedrisk/tests/test_logger_capability.py +170 -0
  37. fivedrisk/tests/test_markov.py +273 -0
  38. fivedrisk/tests/test_ndjson_events.py +146 -0
  39. fivedrisk/tests/test_policy_budget_attributes.py +71 -0
  40. fivedrisk/tests/test_router.py +139 -0
  41. fivedrisk/tests/test_router_capability.py +147 -0
  42. fivedrisk/tests/test_runtime_benchmarks.py +201 -0
  43. fivedrisk/tests/test_runtime_controls.py +168 -0
  44. fivedrisk/tests/test_scorer.py +201 -0
  45. fivedrisk/tests/test_scorer_capability.py +159 -0
  46. fivedrisk/token_costs.py +116 -0
  47. fivedrisk-0.5.0.dist-info/METADATA +342 -0
  48. fivedrisk-0.5.0.dist-info/RECORD +52 -0
  49. fivedrisk-0.5.0.dist-info/WHEEL +5 -0
  50. fivedrisk-0.5.0.dist-info/entry_points.txt +2 -0
  51. fivedrisk-0.5.0.dist-info/licenses/LICENSE +201 -0
  52. fivedrisk-0.5.0.dist-info/top_level.txt +1 -0
fivedrisk/README.md ADDED
@@ -0,0 +1,310 @@
1
+ # fivedrisk — AI Agent Risk Governance Engine
2
+
3
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+ [![Tests](https://github.com/theDoc001/fivedrisk/actions/workflows/tests.yml/badge.svg)](https://github.com/theDoc001/fivedrisk/actions/workflows/tests.yml)
6
+
7
+ **Per-action risk scoring and governance for AI agents.** Drop one decorator on your tool functions, or wire in the Agent SDK hooks — every action is scored, gated, and logged before it runs.
8
+
9
+ ```python
10
+ from fivedrisk.hooks import gate
11
+
12
+ @gate(tool_name="write_to_database", autonomy_context=2)
13
+ async def write_record(table: str, data: dict) -> None:
14
+ ... # only executes if 5D scores GREEN or YELLOW
15
+ # ORANGE → human approval required
16
+ # RED → blocked, never runs
17
+ ```
18
+
19
+ ---
20
+
21
+ ## What it does
22
+
23
+ fivedrisk scores every AI agent action on **5 risk dimensions** (0–4 each):
24
+
25
+ | Dimension | What it measures |
26
+ |---|---|
27
+ | **D** — Data Sensitivity | Public → PII → financial → credentials |
28
+ | **T** — Tool Privilege | Read-only → write → admin → destructive |
29
+ | **R** — Reversibility | Undoable → hard-to-undo → irreversible |
30
+ | **E** — External Impact | Local → internal API → external → untrusted |
31
+ | **A** — Autonomy Context | User-direct → agent-supervised → fully autonomous |
32
+
33
+ 5D stays deterministic. Bands signal what fivedrisk wants your stack to do; your stack chooses the LLM and the workflow.
34
+
35
+ **Default 3-band:**
36
+
37
+ - **GREEN** — execute, normal logging.
38
+ - **ORANGE** — HITL approval required. fivedrisk signals; your stack handles the LLM choice. No auto model promotion.
39
+ - **RED** — blocked.
40
+
41
+ **Opt-in 4-band compliance mode** (`enable_yellow_band: true` in `policy.yaml`): surfaces YELLOW as a stable moderate-risk tier for audit queries and dashboards. Optional model escalation within YELLOW via `yellow_model_escalation: true`.
42
+
43
+ fivedrisk is one layer in a defence-in-depth AI governance stack.
44
+
45
+ ---
46
+
47
+ ## Features
48
+
49
+ - **5D scoring engine** — deterministic, ~40µs per action (p50) on M1, no LLM calls
50
+ - **Markov SafetyDrift** — 16-state Markov chain detects cumulative risk across action sequences; catches compositional attacks that individual scoring misses
51
+ - **Session accumulator** — O(1) counter-based drift tracking for the common case
52
+ - **Injection scanner** — 24+ regex patterns covering GPT-5/Opus-era evasion (Base64, zero-width Unicode, role hijacks, encoded exec calls)
53
+ - **Output leakage scanner** — PII, credentials, crypto keys, injection-echo detection
54
+ - **`@gate` decorator** — wrap any sync or async function with full 5D gating
55
+ - **Agent SDK hooks** — `fivedrisk_pre_tool` / `fivedrisk_post_tool` for Anthropic Agent SDK
56
+ - **LangGraph node** — drop-in integration for LangGraph pipelines
57
+ - **Destination policy** — allowlist/denylist for outbound endpoints
58
+ - **Audit log** — append-only SQLite decision log plus optional NDJSON event stream for SIEM delivery
59
+ - **Policy floor enforcement** — floor rules in `policy.yaml` cannot be overridden at runtime
60
+ - **Defence-in-depth test suite** — pytest markers per OWASP LLM Top 10 category, plus 39-scenario attack-class benchmark (`python -m fivedrisk benchmark`)
61
+ - **424 tests** with 0 failures
62
+
63
+ ---
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ pip install fivedrisk
69
+ ```
70
+
71
+ For LangGraph integration:
72
+ ```bash
73
+ pip install "fivedrisk[langgraph]"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Quick start (30 seconds)
79
+
80
+ ```python
81
+ from fivedrisk import classify_tool_call, score, load_policy, Band
82
+
83
+ policy = load_policy("policy.yaml") # or use defaults
84
+ action = classify_tool_call("Bash", {"command": "rm -rf /tmp/cache"}, policy)
85
+ result = score(action, policy)
86
+
87
+ print(result.band) # Band.ORANGE
88
+ print(result.rationale) # "ORANGE — Bash: Reversibility=3 (≥ ORANGE threshold 3)"
89
+ print(result.routing) # RoutingDecision(model_floor=M3, approval_required=True)
90
+ ```
91
+
92
+ **With the `@gate` decorator:**
93
+ ```python
94
+ from fivedrisk.hooks import gate, configure
95
+ from fivedrisk import load_policy
96
+
97
+ configure(policy=load_policy("policy.yaml"))
98
+
99
+ @gate(tool_name="send_email", autonomy_context=1)
100
+ def send_email(to: str, body: str) -> None:
101
+ # only executes if 5D scores GREEN or YELLOW
102
+ smtp.send(to, body)
103
+ ```
104
+
105
+ **With Anthropic Agent SDK:**
106
+ ```python
107
+ from fivedrisk.hooks import fivedrisk_pre_tool, fivedrisk_post_tool
108
+
109
+ # Register as PreToolUse and PostToolUse hooks in your agent
110
+ ```
111
+
112
+ **With LangGraph:**
113
+ ```python
114
+ from fivedrisk.langgraph_node import fivedrisk_gate_node
115
+ # Add fivedrisk_gate_node to your StateGraph before any tool-executing node
116
+ ```
117
+
118
+ ---
119
+
120
+ ## SafetyDrift — why sequence risk matters
121
+
122
+ A single READ of a config file scores GREEN. But 10 GREENs followed by a write to an external API using credentials extracted two steps earlier is a RED sequence. Most tools miss this.
123
+
124
+ fivedrisk tracks cumulative session state via a **16-state Markov chain** over `(data_exposure_tier × activity_risk_tier)`. When absorption probability into a dangerous state crosses 0.3, the next action is escalated to ORANGE. At 0.7, it's escalated to RED.
125
+
126
+ ```python
127
+ from fivedrisk.markov import MarkovDriftTracker, make_default_transition_matrix
128
+
129
+ tracker = MarkovDriftTracker(make_default_transition_matrix(), session_id="abc")
130
+ bump = tracker.record(scored_action)
131
+ if bump:
132
+ print(f"Drift: {bump.reason}, escalated to {bump.escalated_band}")
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Policy configuration
138
+
139
+ ```yaml
140
+ # policy.yaml
141
+ thresholds:
142
+ red_threshold: 4
143
+ orange_threshold: 3
144
+ orange_score: 1.8
145
+ yellow_score: 1.0
146
+
147
+ [floor]
148
+ # These rules block regardless of per-action score
149
+ - tool_name: "Bash"
150
+ command_contains: "DROP TABLE"
151
+ band: RED
152
+ reason: "floor:no-destructive-sql"
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Benchmark
158
+
159
+ ```bash
160
+ python -m fivedrisk benchmark
161
+ ```
162
+
163
+ Runs 39 offline attack scenarios across: prompt injection (12 categories), output leakage (8 categories), runtime tool misuse (19 scenarios). No external API calls. Deterministic. Safe to run in CI.
164
+
165
+ ---
166
+
167
+ ## Performance
168
+
169
+ Measured on Apple M1, single-thread. Reproducible from `benchmarks/bench_minimal.py`.
170
+
171
+ | Operation | p50 | p99 |
172
+ |---|---|---|
173
+ | **5D core (classify + score)** | **40µs** | **42µs** |
174
+ | Injection scan, 30 char clean | 11µs | 12µs |
175
+ | Injection scan, 3000 char clean | 669µs | 685µs |
176
+ | Leakage scan, 200 char clean | 23µs | 23µs |
177
+ | 5D + injection + leakage scan | 64µs | 65µs |
178
+ | 5D + Markov drift | 43µs | 44µs |
179
+ | 5D + SQLite audit-log write (I/O) | 440µs | 1ms |
180
+ | `@gate` sync overhead (incl. log write) | 439µs | 1.1ms |
181
+ | `@gate` async overhead | 421µs | 660µs |
182
+
183
+ External optional layers (not in fivedrisk):
184
+
185
+ - [PromptArmor](https://promptarmor.com): ~20-30ms typical
186
+ - [LLM Guard](https://llm-guard.com) (token scanners): ~10-15ms typical
187
+ - [LLM Guard](https://llm-guard.com) (ML scanners): ~50-200ms typical
188
+
189
+ Injection scanner is linear in input length; for large RAG contexts, chunk and parallelize. Run the bench script on your target hardware for numbers that match your install.
190
+
191
+ ---
192
+
193
+ ## Audit log
194
+
195
+ fivedrisk produces an append-only decision log entry for every agent action. Each entry records:
196
+ - Risk band and rationale
197
+ - Dimension scores (all 5 axes)
198
+ - Model routing decision and approval history
199
+ - Session drift state
200
+ - Injection and leakage scan results
201
+ - Optional agent identity claim (see below)
202
+
203
+ ### Agent identity passthrough
204
+
205
+ fivedrisk accepts opaque agent identity claims through `Action.metadata["agent_identity"]`. The string flows through unchanged into the audit log for SOC/SIEM correlation. SVID, JWT, and X.509 subject strings are supported as opaque data today.
206
+
207
+ ```python
208
+ action.metadata["agent_identity"] = "spiffe://example.org/agents/triage-bot"
209
+ ```
210
+
211
+ Cryptographic validation, structured parsing, and identity-aware policy hooks are post-OSS scope.
212
+
213
+ ### Reserved metadata keys
214
+
215
+ - `agent_identity` — opaque identity claim string. Do not overwrite with arbitrary values.
216
+
217
+ ---
218
+
219
+ ## Identity capture
220
+
221
+ `Action.acting_identity` is a typed pass-through primitive for the principal an action is being taken on behalf of. Distinct from `agent_identity` (the AI agent's own workload identity); `acting_identity` is who authorized the action.
222
+
223
+ ```python
224
+ from fivedrisk import gate, ActingIdentity, PrincipalType, AttestationSource
225
+
226
+ ai = ActingIdentity(
227
+ principal_id="user-42",
228
+ principal_type=PrincipalType.USER,
229
+ attestation_source=AttestationSource.HTTP_HEADER,
230
+ )
231
+
232
+ # Per-call override
233
+ fn("...", session_id="s1", _fivedrisk_acting_identity=ai)
234
+ ```
235
+
236
+ Declare `identity_required: true` in `policy.yaml` to deny actions where the caller supplied no identity. The deny surfaces as `IdentityRequiredError` and emits an `identity_required_denial` NDJSON event.
237
+
238
+ Identity-aware policy evaluation beyond admission, cryptographic validation, and SPIFFE/SPIRE native binding are post-OSS scope.
239
+
240
+ ---
241
+
242
+ ## Cost management primitives
243
+
244
+ Per-session token budgeting with direct DENY at @gate when a reservation would exceed the session cap.
245
+
246
+ ```yaml
247
+ # policy.yaml
248
+ max_session_budget_tokens: 100000
249
+ max_tool_call_budget_tokens: 4096
250
+ ```
251
+
252
+ ```python
253
+ from fivedrisk import gate, configure
254
+
255
+ configure(event_path="audit.ndjson", default_model_class="claude-sonnet-class")
256
+
257
+ @gate(tool_name="summarize", estimated_input_tokens=2000)
258
+ def summarize(text: str, session_id: str) -> str:
259
+ ...
260
+ ```
261
+
262
+ If the projected token spend exceeds `max_session_budget_tokens`, @gate raises `BudgetExceededError` and emits a `budget_intervention` NDJSON event.
263
+
264
+ Additional Operational FinOps capabilities (Tool Manifest admission layers, useful-progress monitoring, multi-agent budget envelopes, wall-clock / retry / delegation caps) are on the project roadmap.
265
+
266
+ ---
267
+
268
+ ## Planned
269
+
270
+ Future capability surfaces signalled here for search and contributor expectations. No commitment dates.
271
+
272
+ - SPIFFE / MCP reference example (end-to-end workload identity demo)
273
+ - MITRE ATLAS coverage with real tests
274
+ - NIST AI RMF mapping
275
+ - OWASP Agentic Top 10 coverage doc
276
+ - Regulatory crosswalks (AI Act Article 12, NIS2, DORA, ISO 42001)
277
+ - Decision log analysis cookbook (sample SQL for common compliance queries)
278
+
279
+ ---
280
+
281
+ ## Architecture
282
+
283
+ ```
284
+ fivedrisk/
285
+ ├── schema.py # Band, Action, ScoredAction, HITLCard, ModelClass
286
+ ├── scorer.py # score(), model routing (§12-19)
287
+ ├── classifier.py # classify_tool_call() with policy baselines
288
+ ├── hooks.py # @gate, Agent SDK hooks, injection/leakage scanners
289
+ ├── drift.py # SessionAccumulator (O(1) counter-based)
290
+ ├── markov.py # MarkovDriftTracker, Gauss-Jordan, absorption probs
291
+ ├── detectors.py # Versioned detector corpus (2026-04-14.2)
292
+ ├── policy.py # Policy dataclass + YAML loader
293
+ ├── router.py # ModelRouter, EscalationSignal
294
+ ├── logger.py # DecisionLog (SQLite, append-only)
295
+ ├── langgraph_node.py# LangGraph integration
296
+ ├── benchmarks.py # 39-case offline benchmark harness
297
+ └── tests/ # 424 tests
298
+ ```
299
+
300
+ **Coverage**: 14/21 governance spec sections fully implemented.
301
+
302
+ ---
303
+
304
+ ## License
305
+
306
+ Apache 2.0. See [LICENSE](LICENSE).
307
+
308
+ Built by [Loren Angoni](https://langoni.me). Contributions welcome.
309
+
310
+ > "An ambition that doesn't get executed is a hallucination."
fivedrisk/__init__.py ADDED
@@ -0,0 +1,140 @@
1
+ """fivedrisk — 5D Risk Governance Engine.
2
+
3
+ Per-action risk scoring for AI agents. Scores every tool call on
4
+ 5 dimensions (Data Sensitivity, Tool Privilege, Reversibility,
5
+ External Impact, Autonomy Context), assigns a GREEN/YELLOW/ORANGE/RED
6
+ band, routes to the appropriate model, and logs the decision.
7
+
8
+ 4-band system aligned with DotOS Governance Spec v0.3 (§12-19).
9
+
10
+ Quick start:
11
+ from fivedrisk import classify_tool_call, score, load_policy, Band
12
+
13
+ policy = load_policy("policy.yaml")
14
+ action = classify_tool_call("Bash", {"command": "rm -rf /"}, policy)
15
+ result = score(action, policy)
16
+ print(result.band) # Band.RED
17
+ print(result.routing) # RoutingDecision(model_floor=M4, ...)
18
+
19
+ Provenance: 5D Risk Governance Model is DotOS-native.
20
+ Authored by Loren, March 2026. Apache-2.0 license.
21
+ """
22
+
23
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
24
+
25
+ try:
26
+ __version__ = _pkg_version("fivedrisk")
27
+ except PackageNotFoundError: # editable / source checkout without metadata
28
+ __version__ = "0.0.0+unknown"
29
+
30
+ from .budget_accumulator import BudgetAccumulator, ReservationResult
31
+ from .classifier import classify_tool_call
32
+ from .drift import DriftBump, SessionAccumulator
33
+ from .events import (
34
+ NDJSONEventChannel,
35
+ REASON_BUDGET_CAP_EXCEEDED,
36
+ REASON_BUDGET_RESERVATION_BLOCKED,
37
+ REASON_IDENTITY_REQUIRED_NOT_SUPPLIED,
38
+ )
39
+ from .hooks import (
40
+ BudgetExceededError,
41
+ IdentityRequiredError,
42
+ check_destination_policy,
43
+ configure,
44
+ extract_external_destinations,
45
+ fivedrisk_pre_tool,
46
+ fivedrisk_post_tool,
47
+ gate,
48
+ hitl_queue_decrement,
49
+ hitl_queue_increment,
50
+ rate_limit_check,
51
+ scan_input_for_injection,
52
+ scan_output_for_leakage,
53
+ scan_retrieved_content,
54
+ session_id_conventions,
55
+ )
56
+ from .markov import (
57
+ MarkovDriftTracker,
58
+ build_transition_matrix,
59
+ compute_absorption_probabilities,
60
+ index_to_state,
61
+ is_absorbing,
62
+ make_default_transition_matrix,
63
+ matmul,
64
+ matrix_inverse,
65
+ state_to_index,
66
+ )
67
+ from .logger import DecisionLog
68
+ from .policy import AdmissionResult, Policy, load_policy
69
+ from .router import ModelRouter, ModelConfig, EscalationSignal
70
+ from .schema import (
71
+ Action,
72
+ ActingIdentity,
73
+ AttestationSource,
74
+ AutonomySignals,
75
+ Band,
76
+ HITLCard,
77
+ ModelClass,
78
+ PrincipalType,
79
+ RoutingDecision,
80
+ ScoredAction,
81
+ )
82
+ from .scorer import score
83
+ from .token_costs import MODEL_COSTS, ModelCost, get_model_cost
84
+
85
+ __all__ = [
86
+ "Action",
87
+ "ActingIdentity",
88
+ "AdmissionResult",
89
+ "AttestationSource",
90
+ "AutonomySignals",
91
+ "Band",
92
+ "BudgetAccumulator",
93
+ "BudgetExceededError",
94
+ "DecisionLog",
95
+ "DriftBump",
96
+ "EscalationSignal",
97
+ "HITLCard",
98
+ "IdentityRequiredError",
99
+ "MarkovDriftTracker",
100
+ "MODEL_COSTS",
101
+ "ModelClass",
102
+ "ModelConfig",
103
+ "ModelCost",
104
+ "ModelRouter",
105
+ "NDJSONEventChannel",
106
+ "Policy",
107
+ "PrincipalType",
108
+ "REASON_BUDGET_CAP_EXCEEDED",
109
+ "REASON_BUDGET_RESERVATION_BLOCKED",
110
+ "REASON_IDENTITY_REQUIRED_NOT_SUPPLIED",
111
+ "ReservationResult",
112
+ "RoutingDecision",
113
+ "ScoredAction",
114
+ "SessionAccumulator",
115
+ "build_transition_matrix",
116
+ "check_destination_policy",
117
+ "classify_tool_call",
118
+ "compute_absorption_probabilities",
119
+ "configure",
120
+ "extract_external_destinations",
121
+ "fivedrisk_post_tool",
122
+ "fivedrisk_pre_tool",
123
+ "gate",
124
+ "get_model_cost",
125
+ "hitl_queue_decrement",
126
+ "hitl_queue_increment",
127
+ "index_to_state",
128
+ "is_absorbing",
129
+ "load_policy",
130
+ "make_default_transition_matrix",
131
+ "matmul",
132
+ "matrix_inverse",
133
+ "rate_limit_check",
134
+ "scan_input_for_injection",
135
+ "scan_output_for_leakage",
136
+ "scan_retrieved_content",
137
+ "score",
138
+ "session_id_conventions",
139
+ "state_to_index",
140
+ ]
fivedrisk/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ """Allow `python -m fivedrisk` to invoke the CLI."""
2
+ from .cli import main
3
+
4
+ main()