iris-security-cli 0.1.0__tar.gz

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.
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: iris-security-cli
3
+ Version: 0.1.0
4
+ Summary: IRIS CLI — iris scan, iris register, iris policy, iris compliance
5
+ License: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
7
+ Project-URL: Repository, https://github.com/gimartinb/iris-sdk
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Security
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: iris-security-core>=0.1.0
19
+ Requires-Dist: iris-security-sdk>=0.1.0
20
+ Requires-Dist: click>=8.1
21
+ Requires-Dist: rich>=13.0
22
+ Requires-Dist: pyyaml>=6.0
23
+
24
+ # iris-security-cli
25
+
26
+ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
27
+
28
+ Command-line tools for AI agent governance and Colorado AI Act compliance.
29
+
30
+ Part of the [IRIS SDK](https://github.com/gimartinb/iris-sdk).
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install iris-security-cli
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ iris --version
42
+ iris register --name my-agent --owner you@company.com --team my-team --compliance colorado-ai-act
43
+ iris scan
44
+ iris compliance check --framework colorado-ai-act
45
+ ```
@@ -0,0 +1,22 @@
1
+ # iris-security-cli
2
+
3
+ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
4
+
5
+ Command-line tools for AI agent governance and Colorado AI Act compliance.
6
+
7
+ Part of the [IRIS SDK](https://github.com/gimartinb/iris-sdk).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install iris-security-cli
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```bash
18
+ iris --version
19
+ iris register --name my-agent --owner you@company.com --team my-team --compliance colorado-ai-act
20
+ iris scan
21
+ iris compliance check --framework colorado-ai-act
22
+ ```
File without changes
@@ -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}")