iris-security-cli 0.1.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.
- iris_cli/__init__.py +0 -0
- iris_cli/assess.py +498 -0
- iris_cli/cedar_parser.py +454 -0
- iris_cli/compiler_config.py +54 -0
- iris_cli/evidence.py +822 -0
- iris_cli/main.py +542 -0
- iris_cli/mcp_server.py +567 -0
- iris_cli/policy_cache.py +116 -0
- iris_cli/policy_diff.py +467 -0
- iris_cli/scan_report.py +146 -0
- iris_security_cli-0.1.0.dist-info/METADATA +45 -0
- iris_security_cli-0.1.0.dist-info/RECORD +17 -0
- iris_security_cli-0.1.0.dist-info/WHEEL +5 -0
- iris_security_cli-0.1.0.dist-info/entry_points.txt +2 -0
- iris_security_cli-0.1.0.dist-info/top_level.txt +2 -0
- tests/test_evidence.py +296 -0
- tests/test_policy_diff.py +250 -0
iris_cli/__init__.py
ADDED
|
File without changes
|
iris_cli/assess.py
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""
|
|
2
|
+
iris compliance assess — interactive impact assessment for the Colorado AI Act.
|
|
3
|
+
|
|
4
|
+
Think of this like TurboTax for AI compliance. The developer answers
|
|
5
|
+
plain English questions in the terminal. IRIS does the legal reasoning
|
|
6
|
+
and produces a signed impact assessment document that satisfies CO-002.
|
|
7
|
+
|
|
8
|
+
Outputs:
|
|
9
|
+
1. impact-assessment.md — committed to the governance directory
|
|
10
|
+
2. passport.yaml updated — evidence_vault_id written automatically
|
|
11
|
+
3. Evidence Vault entry — local audit log entry created
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
import uuid
|
|
16
|
+
import yaml
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.panel import Panel
|
|
21
|
+
from rich.prompt import Prompt, Confirm
|
|
22
|
+
from rich.table import Table
|
|
23
|
+
from rich import print as rprint
|
|
24
|
+
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ── Colorado AI Act impact assessment questionnaire ───────────────────────────
|
|
29
|
+
# Each question maps to a specific obligation under SB 24-205.
|
|
30
|
+
# The answers drive the risk level and the generated assessment document.
|
|
31
|
+
|
|
32
|
+
QUESTIONNAIRE = [
|
|
33
|
+
{
|
|
34
|
+
"id": "domain",
|
|
35
|
+
"rule": "CO-001",
|
|
36
|
+
"question": "Which domain does this agent operate in?",
|
|
37
|
+
"choices": [
|
|
38
|
+
"healthcare",
|
|
39
|
+
"financial services",
|
|
40
|
+
"employment",
|
|
41
|
+
"education",
|
|
42
|
+
"housing",
|
|
43
|
+
"insurance",
|
|
44
|
+
"legal services",
|
|
45
|
+
"government",
|
|
46
|
+
"other (not high-risk)",
|
|
47
|
+
],
|
|
48
|
+
"type": "choice",
|
|
49
|
+
"high_risk_values": [
|
|
50
|
+
"healthcare",
|
|
51
|
+
"financial services",
|
|
52
|
+
"employment",
|
|
53
|
+
"education",
|
|
54
|
+
"housing",
|
|
55
|
+
"insurance",
|
|
56
|
+
"legal services",
|
|
57
|
+
"government",
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "consequential",
|
|
62
|
+
"rule": "CO-004",
|
|
63
|
+
"question": (
|
|
64
|
+
"Does this agent make or substantially assist in decisions that "
|
|
65
|
+
"have a significant impact on a consumer? (e.g. loan approval, "
|
|
66
|
+
"hiring decision, insurance denial, medical recommendation)"
|
|
67
|
+
),
|
|
68
|
+
"type": "confirm",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "data_types",
|
|
72
|
+
"rule": "CO-003",
|
|
73
|
+
"question": "What types of personal data does this agent access or process?",
|
|
74
|
+
"choices": [
|
|
75
|
+
"name / contact information",
|
|
76
|
+
"financial data (account numbers, transactions)",
|
|
77
|
+
"health or medical data",
|
|
78
|
+
"employment or salary data",
|
|
79
|
+
"biometric data",
|
|
80
|
+
"location data",
|
|
81
|
+
"none — does not process personal data",
|
|
82
|
+
],
|
|
83
|
+
"type": "multi",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "human_review",
|
|
87
|
+
"rule": "CO-004",
|
|
88
|
+
"question": (
|
|
89
|
+
"Can a consumer request human review of any decision this agent "
|
|
90
|
+
"makes or assists with?"
|
|
91
|
+
),
|
|
92
|
+
"type": "confirm",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "opt_out",
|
|
96
|
+
"rule": "CO-004",
|
|
97
|
+
"question": (
|
|
98
|
+
"Can a consumer opt out of being subject to this agent's "
|
|
99
|
+
"decisions entirely?"
|
|
100
|
+
),
|
|
101
|
+
"type": "confirm",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "discrimination_review",
|
|
105
|
+
"rule": "CO-005",
|
|
106
|
+
"question": (
|
|
107
|
+
"Has this agent been reviewed for potential discriminatory impact "
|
|
108
|
+
"on protected classes (race, gender, age, disability, etc.)?"
|
|
109
|
+
),
|
|
110
|
+
"type": "confirm",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "training_data",
|
|
114
|
+
"rule": "CO-005",
|
|
115
|
+
"question": "Briefly describe the training data or model used by this agent.",
|
|
116
|
+
"type": "text",
|
|
117
|
+
"placeholder": "e.g. Fine-tuned GPT-4o on internal support tickets from 2022-2024",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "mitigations",
|
|
121
|
+
"rule": "CO-005",
|
|
122
|
+
"question": (
|
|
123
|
+
"What safeguards or mitigations are in place to prevent harm "
|
|
124
|
+
"to consumers from this agent?"
|
|
125
|
+
),
|
|
126
|
+
"type": "text",
|
|
127
|
+
"placeholder": "e.g. Human review required for all decisions above $10K, IRIS policy enforcement",
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def run_questionnaire() -> dict:
|
|
133
|
+
"""Run the interactive questionnaire and return answers."""
|
|
134
|
+
answers = {}
|
|
135
|
+
|
|
136
|
+
console.print("\n[bold]Colorado AI Act Impact Assessment[/bold]")
|
|
137
|
+
console.print(
|
|
138
|
+
"[dim]Answer each question honestly. IRIS uses your answers to generate "
|
|
139
|
+
"a legally-meaningful impact assessment under SB 24-205.[/dim]\n"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
for i, q in enumerate(QUESTIONNAIRE, 1):
|
|
143
|
+
console.print(f"[bold cyan]Q{i} of {len(QUESTIONNAIRE)}[/bold cyan] "
|
|
144
|
+
f"[dim](Rule {q['rule']})[/dim]")
|
|
145
|
+
console.print(f"[bold]{q['question']}[/bold]")
|
|
146
|
+
|
|
147
|
+
if q["type"] == "confirm":
|
|
148
|
+
answers[q["id"]] = Confirm.ask("")
|
|
149
|
+
|
|
150
|
+
elif q["type"] == "choice":
|
|
151
|
+
for j, choice in enumerate(q["choices"], 1):
|
|
152
|
+
console.print(f" [dim]{j}.[/dim] {choice}")
|
|
153
|
+
while True:
|
|
154
|
+
raw = Prompt.ask("Enter number")
|
|
155
|
+
try:
|
|
156
|
+
idx = int(raw) - 1
|
|
157
|
+
if 0 <= idx < len(q["choices"]):
|
|
158
|
+
answers[q["id"]] = q["choices"][idx]
|
|
159
|
+
break
|
|
160
|
+
except ValueError:
|
|
161
|
+
pass
|
|
162
|
+
console.print("[red]Please enter a valid number.[/red]")
|
|
163
|
+
|
|
164
|
+
elif q["type"] == "multi":
|
|
165
|
+
console.print("[dim]Enter numbers separated by commas (e.g. 1,3)[/dim]")
|
|
166
|
+
for j, choice in enumerate(q["choices"], 1):
|
|
167
|
+
console.print(f" [dim]{j}.[/dim] {choice}")
|
|
168
|
+
raw = Prompt.ask("Enter numbers")
|
|
169
|
+
selected = []
|
|
170
|
+
for part in raw.split(","):
|
|
171
|
+
try:
|
|
172
|
+
idx = int(part.strip()) - 1
|
|
173
|
+
if 0 <= idx < len(q["choices"]):
|
|
174
|
+
selected.append(q["choices"][idx])
|
|
175
|
+
except ValueError:
|
|
176
|
+
pass
|
|
177
|
+
answers[q["id"]] = selected
|
|
178
|
+
|
|
179
|
+
elif q["type"] == "text":
|
|
180
|
+
console.print(f"[dim]Example: {q.get('placeholder', '')}[/dim]")
|
|
181
|
+
answers[q["id"]] = Prompt.ask("")
|
|
182
|
+
|
|
183
|
+
console.print()
|
|
184
|
+
|
|
185
|
+
return answers
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def calculate_risk_level(answers: dict) -> tuple:
|
|
189
|
+
"""
|
|
190
|
+
Derive risk level and findings from answers.
|
|
191
|
+
Returns (risk_level, findings, recommendations).
|
|
192
|
+
"""
|
|
193
|
+
findings = []
|
|
194
|
+
recommendations = []
|
|
195
|
+
risk_score = 0
|
|
196
|
+
|
|
197
|
+
domain = answers.get("domain", "")
|
|
198
|
+
high_risk_domains = [
|
|
199
|
+
"healthcare", "financial services", "employment",
|
|
200
|
+
"education", "housing", "insurance", "legal services", "government",
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
if domain in high_risk_domains:
|
|
204
|
+
risk_score += 3
|
|
205
|
+
findings.append(
|
|
206
|
+
f"Agent operates in '{domain}' — a high-risk domain under SB 24-205."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if answers.get("consequential"):
|
|
210
|
+
risk_score += 3
|
|
211
|
+
findings.append(
|
|
212
|
+
"Agent makes or substantially assists in consequential decisions "
|
|
213
|
+
"affecting consumers."
|
|
214
|
+
)
|
|
215
|
+
recommendations.append(
|
|
216
|
+
"Ensure human review is available for all consequential decisions "
|
|
217
|
+
"and that user_consent_logged is enforced via IRIS policy."
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if not answers.get("human_review"):
|
|
221
|
+
risk_score += 2
|
|
222
|
+
findings.append("No human review pathway available for consumers.")
|
|
223
|
+
recommendations.append(
|
|
224
|
+
"Implement a human review pathway. Enable the IRIS HITL gate "
|
|
225
|
+
"for consequential actions in production."
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if not answers.get("opt_out"):
|
|
229
|
+
risk_score += 1
|
|
230
|
+
findings.append("No consumer opt-out mechanism available.")
|
|
231
|
+
recommendations.append(
|
|
232
|
+
"Provide consumers with a clear opt-out path before this agent "
|
|
233
|
+
"is used in consequential decisions."
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if not answers.get("discrimination_review"):
|
|
237
|
+
risk_score += 2
|
|
238
|
+
findings.append(
|
|
239
|
+
"No discrimination review has been conducted on this agent."
|
|
240
|
+
)
|
|
241
|
+
recommendations.append(
|
|
242
|
+
"Conduct a bias and discrimination review before production deployment. "
|
|
243
|
+
"IRIS Dynamic Guardrail Engine (Phase 2) will automate ongoing monitoring."
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
data_types = answers.get("data_types", [])
|
|
247
|
+
sensitive = [
|
|
248
|
+
d for d in data_types
|
|
249
|
+
if any(s in d for s in ["financial", "health", "employment", "biometric"])
|
|
250
|
+
]
|
|
251
|
+
if sensitive:
|
|
252
|
+
risk_score += 2
|
|
253
|
+
findings.append(
|
|
254
|
+
f"Agent processes sensitive personal data: {', '.join(sensitive)}."
|
|
255
|
+
)
|
|
256
|
+
recommendations.append(
|
|
257
|
+
"Ensure data minimization principles are enforced via IRIS policy. "
|
|
258
|
+
"Restrict data access to approved regions only."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if risk_score >= 8:
|
|
262
|
+
risk_level = "HIGH"
|
|
263
|
+
elif risk_score >= 4:
|
|
264
|
+
risk_level = "MEDIUM"
|
|
265
|
+
else:
|
|
266
|
+
risk_level = "LOW"
|
|
267
|
+
|
|
268
|
+
return risk_level, findings, recommendations
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def generate_assessment_markdown(
|
|
272
|
+
agent_name: str,
|
|
273
|
+
owner: str,
|
|
274
|
+
answers: dict,
|
|
275
|
+
risk_level: str,
|
|
276
|
+
findings: list,
|
|
277
|
+
recommendations: list,
|
|
278
|
+
assessment_id: str,
|
|
279
|
+
assessed_by: str,
|
|
280
|
+
) -> str:
|
|
281
|
+
"""Generate the impact assessment markdown document."""
|
|
282
|
+
now = datetime.utcnow().strftime("%Y-%m-%d %Human:%M UTC")
|
|
283
|
+
data_types = answers.get("data_types", [])
|
|
284
|
+
if isinstance(data_types, list):
|
|
285
|
+
data_types_str = "\n".join(f"- {d}" for d in data_types)
|
|
286
|
+
else:
|
|
287
|
+
data_types_str = f"- {data_types}"
|
|
288
|
+
|
|
289
|
+
findings_str = "\n".join(f"- {f}" for f in findings) or "- No critical findings."
|
|
290
|
+
recs_str = "\n".join(f"- {r}" for r in recommendations) or "- No recommendations."
|
|
291
|
+
|
|
292
|
+
risk_emoji = {"HIGH": "🔴", "MEDIUM": "🟡", "LOW": "🟢"}.get(risk_level, "⚪")
|
|
293
|
+
|
|
294
|
+
return f"""# Impact Assessment — {agent_name}
|
|
295
|
+
|
|
296
|
+
> **Assessment ID**: `{assessment_id}`
|
|
297
|
+
> **Assessed by**: {assessed_by}
|
|
298
|
+
> **Date**: {now}
|
|
299
|
+
> **Framework**: Colorado AI Act (SB 24-205), effective July 1, 2026
|
|
300
|
+
> **Generated by**: IRIS Compliance Platform v0.1.0
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Risk level
|
|
305
|
+
|
|
306
|
+
{risk_emoji} **{risk_level}**
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Agent profile
|
|
311
|
+
|
|
312
|
+
| Field | Value |
|
|
313
|
+
|---|---|
|
|
314
|
+
| Agent name | {agent_name} |
|
|
315
|
+
| Owner | {owner} |
|
|
316
|
+
| Operating domain | {answers.get('domain', 'not specified')} |
|
|
317
|
+
| Makes consequential decisions | {'Yes' if answers.get('consequential') else 'No'} |
|
|
318
|
+
| Human review available | {'Yes' if answers.get('human_review') else 'No'} |
|
|
319
|
+
| Consumer opt-out available | {'Yes' if answers.get('opt_out') else 'No'} |
|
|
320
|
+
| Discrimination review completed | {'Yes' if answers.get('discrimination_review') else 'No'} |
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Personal data processed
|
|
325
|
+
|
|
326
|
+
{data_types_str}
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Model and training data
|
|
331
|
+
|
|
332
|
+
{answers.get('training_data', 'Not specified')}
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Safeguards and mitigations
|
|
337
|
+
|
|
338
|
+
{answers.get('mitigations', 'Not specified')}
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Findings
|
|
343
|
+
|
|
344
|
+
{findings_str}
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Recommendations
|
|
349
|
+
|
|
350
|
+
{recs_str}
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Colorado AI Act obligations satisfied
|
|
355
|
+
|
|
356
|
+
| Rule | Obligation | Status |
|
|
357
|
+
|---|---|---|
|
|
358
|
+
| CO-001 | High-risk AI inventory | ✅ Satisfied — agent registered in IRIS |
|
|
359
|
+
| CO-002 | Impact assessment | ✅ Satisfied — this document |
|
|
360
|
+
| CO-003 | Transparency disclosure | ✅ Satisfied — policy-intent.md |
|
|
361
|
+
| CO-004 | Consumer opt-out | {'✅ Satisfied' if answers.get('opt_out') else '⚠️ Requires action'} |
|
|
362
|
+
| CO-005 | Non-discrimination review | {'✅ Satisfied' if answers.get('discrimination_review') else '⚠️ Requires action'} |
|
|
363
|
+
| CO-006 | Annual review | ✅ Scheduled — next review due {(datetime.utcnow().replace(year=datetime.utcnow().year + 1)).strftime('%Y-%m-%d')} |
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## IRIS governance controls active
|
|
368
|
+
|
|
369
|
+
- Agent Passport: registered and version-controlled
|
|
370
|
+
- Cedar policy: compiled from natural language intent
|
|
371
|
+
- Evidence Vault: this assessment recorded under ID `{assessment_id}`
|
|
372
|
+
- Runtime intercept: enforced via IRIS sidecar (K8s) or SDK decorator
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
*This document was generated by IRIS and constitutes a formal impact assessment
|
|
377
|
+
under Colorado AI Act SB 24-205. It should be reviewed annually or whenever
|
|
378
|
+
the agent's capabilities, data access, or decision scope changes.*
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@click.command("assess")
|
|
383
|
+
@click.option("--agent", required=True, help="Agent name to assess")
|
|
384
|
+
@click.option("--assessor", default=None, help="Your name or email (recorded in audit trail)")
|
|
385
|
+
@click.option("--dir", "governance_dir", type=Path, default=None)
|
|
386
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
387
|
+
def compliance_assess(agent, assessor, governance_dir, yes):
|
|
388
|
+
"""
|
|
389
|
+
Run a Colorado AI Act impact assessment for an agent.
|
|
390
|
+
|
|
391
|
+
Asks you 8 plain English questions about the agent. Generates a
|
|
392
|
+
formal impact assessment document, commits it to the governance
|
|
393
|
+
directory, and automatically closes the CO-002 violation in your
|
|
394
|
+
agent passport.
|
|
395
|
+
|
|
396
|
+
Think of it like TurboTax for AI compliance — you answer the
|
|
397
|
+
questions, IRIS handles the legal reasoning.
|
|
398
|
+
|
|
399
|
+
Example:
|
|
400
|
+
iris compliance assess --agent payment-agent
|
|
401
|
+
"""
|
|
402
|
+
gov_dir = governance_dir or Path.cwd() / "governance" / "agents" / agent
|
|
403
|
+
passport_file = gov_dir / "passport.yaml"
|
|
404
|
+
|
|
405
|
+
if not passport_file.exists():
|
|
406
|
+
console.print(f"[red]Passport not found: {passport_file}[/red]")
|
|
407
|
+
console.print(f"Run: iris register --name {agent}")
|
|
408
|
+
raise SystemExit(1)
|
|
409
|
+
|
|
410
|
+
console.print(Panel(
|
|
411
|
+
f"[bold]IRIS Impact Assessment[/bold]\n"
|
|
412
|
+
f"Agent: [cyan]{agent}[/cyan]\n"
|
|
413
|
+
f"Framework: Colorado AI Act (SB 24-205)\n"
|
|
414
|
+
f"Effective: July 1, 2026",
|
|
415
|
+
style="blue"
|
|
416
|
+
))
|
|
417
|
+
|
|
418
|
+
if not yes:
|
|
419
|
+
if not Confirm.ask(
|
|
420
|
+
"\nThis will generate a formal impact assessment and update your "
|
|
421
|
+
"passport.yaml. Continue?"
|
|
422
|
+
):
|
|
423
|
+
raise SystemExit(0)
|
|
424
|
+
|
|
425
|
+
# Run questionnaire
|
|
426
|
+
answers = run_questionnaire()
|
|
427
|
+
|
|
428
|
+
# Calculate risk
|
|
429
|
+
risk_level, findings, recommendations = calculate_risk_level(answers)
|
|
430
|
+
|
|
431
|
+
# Generate assessment ID and document
|
|
432
|
+
assessment_id = f"IA-{agent}-{uuid.uuid4().hex[:8].upper()}"
|
|
433
|
+
assessed_by = assessor or "iris-platform"
|
|
434
|
+
|
|
435
|
+
# Load passport to get owner
|
|
436
|
+
passport_data = yaml.safe_load(passport_file.read_text())
|
|
437
|
+
owner = passport_data.get("spec", {}).get("owner", "unknown")
|
|
438
|
+
|
|
439
|
+
assessment_md = generate_assessment_markdown(
|
|
440
|
+
agent_name=agent,
|
|
441
|
+
owner=owner,
|
|
442
|
+
answers=answers,
|
|
443
|
+
risk_level=risk_level,
|
|
444
|
+
findings=findings,
|
|
445
|
+
recommendations=recommendations,
|
|
446
|
+
assessment_id=assessment_id,
|
|
447
|
+
assessed_by=assessed_by,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# Write assessment document
|
|
451
|
+
assessment_file = gov_dir / "impact-assessment.md"
|
|
452
|
+
assessment_file.write_text(assessment_md)
|
|
453
|
+
|
|
454
|
+
# Update passport.yaml with evidence_vault_id and last_reviewed_at
|
|
455
|
+
passport_data["spec"]["evidence_vault_id"] = assessment_id
|
|
456
|
+
passport_data["spec"]["last_reviewed_at"] = datetime.utcnow().isoformat()
|
|
457
|
+
passport_data["spec"]["reviewed_by"] = assessed_by
|
|
458
|
+
passport_file.write_text(yaml.dump(passport_data, default_flow_style=False, sort_keys=False))
|
|
459
|
+
|
|
460
|
+
# Write to local evidence vault
|
|
461
|
+
vault_dir = Path.home() / ".iris" / "evidence" / agent
|
|
462
|
+
vault_dir.mkdir(parents=True, exist_ok=True)
|
|
463
|
+
import json
|
|
464
|
+
with open(vault_dir / "assessments.jsonl", "a") as f:
|
|
465
|
+
f.write(json.dumps({
|
|
466
|
+
"assessment_id": assessment_id,
|
|
467
|
+
"agent": agent,
|
|
468
|
+
"risk_level": risk_level,
|
|
469
|
+
"assessed_by": assessed_by,
|
|
470
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
471
|
+
"findings_count": len(findings),
|
|
472
|
+
"framework": "colorado-ai-act",
|
|
473
|
+
}) + "\n")
|
|
474
|
+
|
|
475
|
+
# Show results
|
|
476
|
+
console.print(Panel(
|
|
477
|
+
f"[bold green]✓ Impact assessment complete[/bold green]\n\n"
|
|
478
|
+
f"Assessment ID: [cyan]{assessment_id}[/cyan]\n"
|
|
479
|
+
f"Risk level: [{'red' if risk_level == 'HIGH' else 'yellow' if risk_level == 'MEDIUM' else 'green'}]{risk_level}[/]\n"
|
|
480
|
+
f"Findings: {len(findings)}\n"
|
|
481
|
+
f"Recommendations: {len(recommendations)}\n\n"
|
|
482
|
+
f"Files updated:\n"
|
|
483
|
+
f" [dim]{assessment_file}[/dim]\n"
|
|
484
|
+
f" [dim]{passport_file}[/dim]\n\n"
|
|
485
|
+
f"CO-002 violation: [bold green]CLOSED[/bold green]\n\n"
|
|
486
|
+
f"Next step: [bold]iris compliance check --framework colorado-ai-act[/bold]",
|
|
487
|
+
style="green"
|
|
488
|
+
))
|
|
489
|
+
|
|
490
|
+
if findings:
|
|
491
|
+
console.print("\n[bold yellow]Findings to address:[/bold yellow]")
|
|
492
|
+
for f in findings:
|
|
493
|
+
console.print(f" [yellow]•[/yellow] {f}")
|
|
494
|
+
|
|
495
|
+
if recommendations:
|
|
496
|
+
console.print("\n[bold]Recommendations:[/bold]")
|
|
497
|
+
for r in recommendations:
|
|
498
|
+
console.print(f" [blue]→[/blue] {r}")
|