iris-security-cli 0.2.14__tar.gz → 0.2.17__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.
Files changed (72) hide show
  1. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/PKG-INFO +19 -6
  2. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/README.md +14 -1
  3. iris_security_cli-0.2.17/iris_cli/compliance_scan_cmd.py +242 -0
  4. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/explain.py +1 -1
  5. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/main.py +41 -7
  6. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/quickstart.py +2 -2
  7. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/scm.py +1 -1
  8. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/PKG-INFO +19 -6
  9. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/SOURCES.txt +2 -0
  10. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/requires.txt +2 -2
  11. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/pyproject.toml +5 -5
  12. iris_security_cli-0.2.17/tests/test_compliance_scan_fail_on.py +56 -0
  13. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/__init__.py +0 -0
  14. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/action_plan.py +0 -0
  15. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/assess.py +0 -0
  16. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/audit_log.py +0 -0
  17. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/cedar_parser.py +0 -0
  18. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/certify.py +0 -0
  19. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/compiler_config.py +0 -0
  20. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/compliance_check_cmd.py +0 -0
  21. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/compliance_fix.py +0 -0
  22. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/cost.py +0 -0
  23. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/declare.py +0 -0
  24. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/delegation.py +0 -0
  25. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/discover.py +0 -0
  26. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/dlp_cmd.py +0 -0
  27. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/drift.py +0 -0
  28. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/enforce.py +0 -0
  29. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/entitlements_cmd.py +0 -0
  30. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/evidence.py +0 -0
  31. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/framework_suggest.py +0 -0
  32. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/framework_test.py +0 -0
  33. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/hitl.py +0 -0
  34. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/license_cmd.py +0 -0
  35. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/list_cmd.py +0 -0
  36. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/mcp_server.py +0 -0
  37. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/models_cmd.py +0 -0
  38. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/onboarding_report.py +0 -0
  39. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/org_policy.py +0 -0
  40. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/policy_cache.py +0 -0
  41. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/policy_commit.py +0 -0
  42. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/policy_diff.py +0 -0
  43. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/policy_status.py +0 -0
  44. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/preview.py +0 -0
  45. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/redteam.py +0 -0
  46. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/regulatory.py +0 -0
  47. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/scan_govern.py +0 -0
  48. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/scan_report.py +0 -0
  49. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/sentinel.py +0 -0
  50. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/status_cmd.py +0 -0
  51. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/users.py +0 -0
  52. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/vault.py +0 -0
  53. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/watch.py +0 -0
  54. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_cli/witness.py +0 -0
  55. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/dependency_links.txt +0 -0
  56. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/entry_points.txt +0 -0
  57. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/iris_security_cli.egg-info/top_level.txt +0 -0
  58. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/setup.cfg +0 -0
  59. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_audit_log.py +0 -0
  60. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_docs_pages_check.py +0 -0
  61. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_entitlements_display.py +0 -0
  62. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_evidence.py +0 -0
  63. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_evidence_export.py +0 -0
  64. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_evidence_query.py +0 -0
  65. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_framework_suggest.py +0 -0
  66. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_framework_test.py +0 -0
  67. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_hitl_cli.py +0 -0
  68. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_list_cmd.py +0 -0
  69. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_mcp_model_governance.py +0 -0
  70. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_policy_diff.py +0 -0
  71. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_quickstart.py +0 -0
  72. {iris_security_cli-0.2.14 → iris_security_cli-0.2.17}/tests/test_vocabulary.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris-security-cli
3
- Version: 0.2.14
3
+ Version: 0.2.17
4
4
  Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
5
5
  License: Apache-2.0
6
- Project-URL: Homepage, https://iris-security.github.io/iris-sdk/
7
- Project-URL: Repository, https://github.com/IRIS-Security/iris-sdk
6
+ Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
7
+ Project-URL: Repository, https://github.com/gimartinb/iris-sdk
8
8
  Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: Topic :: Security
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: iris-security-core>=0.1.16
19
- Requires-Dist: iris-security-sdk>=0.2.14
18
+ Requires-Dist: iris-security-core>=0.1.17
19
+ Requires-Dist: iris-security-sdk>=0.2.17
20
20
  Requires-Dist: click>=8.1
21
21
  Requires-Dist: rich>=13.0
22
22
  Requires-Dist: pyyaml>=6.0
@@ -29,7 +29,7 @@ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
29
29
 
30
30
  Command-line tools for AI agent governance and Colorado AI Act compliance.
31
31
 
32
- Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
32
+ Part of the [IRIS SDK](https://github.com/gimartinb/iris-sdk).
33
33
 
34
34
  ## Install
35
35
 
@@ -45,3 +45,16 @@ iris register --name my-agent --owner you@company.com --team my-team --complianc
45
45
  iris scan
46
46
  iris compliance check --framework colorado-ai-act
47
47
  ```
48
+
49
+ ## GitHub Action
50
+
51
+ Add AI governance to CI with the composite Action in this repo (offline by default):
52
+
53
+ ```yaml
54
+ - uses: IRIS-Security/iris-cli@v1
55
+ with:
56
+ command: compliance scan
57
+ fail-on: blocker
58
+ ```
59
+
60
+ Optional `--push` to IRIS Cloud when `api-key` is set. See [GitHub Action docs](https://iris-security.github.io/iris-sdk/github-action.html).
@@ -4,7 +4,7 @@ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
4
4
 
5
5
  Command-line tools for AI agent governance and Colorado AI Act compliance.
6
6
 
7
- Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
7
+ Part of the [IRIS SDK](https://github.com/gimartinb/iris-sdk).
8
8
 
9
9
  ## Install
10
10
 
@@ -20,3 +20,16 @@ iris register --name my-agent --owner you@company.com --team my-team --complianc
20
20
  iris scan
21
21
  iris compliance check --framework colorado-ai-act
22
22
  ```
23
+
24
+ ## GitHub Action
25
+
26
+ Add AI governance to CI with the composite Action in this repo (offline by default):
27
+
28
+ ```yaml
29
+ - uses: IRIS-Security/iris-cli@v1
30
+ with:
31
+ command: compliance scan
32
+ fail-on: blocker
33
+ ```
34
+
35
+ Optional `--push` to IRIS Cloud when `api-key` is set. See [GitHub Action docs](https://iris-security.github.io/iris-sdk/github-action.html).
@@ -0,0 +1,242 @@
1
+ """iris compliance scan — offline workload detection + local obligation eval."""
2
+
3
+ # Copyright 2024-2025 Gilbert Martin / IRIS Security, Inc.
4
+ # All Rights Reserved.
5
+ # Author: Gilbert Martin <gilbert@iris-security.io>
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ import click
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+ from rich.table import Table
17
+
18
+ from iris.scan import detect_workload
19
+ from iris_core.compliance.workload_eval import (
20
+ applicable_frameworks,
21
+ evaluate_workload_profile,
22
+ top_recommended_actions,
23
+ )
24
+
25
+ console = Console()
26
+
27
+ _SEVERITY_RANK = {"blocker": 0, "required": 1, "recommended": 2}
28
+
29
+
30
+ def _exit_on_fail_threshold(obligations: list[dict], fail_on: str) -> None:
31
+ if fail_on == "none":
32
+ return
33
+ threshold = _SEVERITY_RANK[fail_on]
34
+ blocking = [
35
+ obligation
36
+ for obligation in obligations
37
+ if _SEVERITY_RANK.get(obligation.get("severity", "recommended"), 9) <= threshold
38
+ ]
39
+ if blocking:
40
+ sys.exit(1)
41
+
42
+
43
+ def _resolve_profile(
44
+ *,
45
+ scan_source: str | None,
46
+ path: str,
47
+ litellm_config: str | None,
48
+ litellm_proxy: str | None,
49
+ lookback_days: int,
50
+ ) -> dict:
51
+ if scan_source is None:
52
+ return detect_workload(path)
53
+
54
+ if scan_source == "langfuse":
55
+ try:
56
+ from iris_langfuse import profile_from_langfuse
57
+ except ImportError as exc:
58
+ raise click.ClickException(
59
+ 'iris-langfuse is not installed. Run: pip install "iris-langfuse[live]"'
60
+ ) from exc
61
+ try:
62
+ return profile_from_langfuse(lookback_days=lookback_days)
63
+ except ImportError as exc:
64
+ raise click.ClickException(
65
+ 'Langfuse SDK missing. Run: pip install "iris-langfuse[live]"'
66
+ ) from exc
67
+ except ValueError as exc:
68
+ raise click.ClickException(str(exc)) from exc
69
+
70
+ if scan_source == "litellm":
71
+ try:
72
+ from iris_litellm import profile_from_litellm_config, profile_from_litellm_proxy
73
+ except ImportError as exc:
74
+ raise click.ClickException(
75
+ 'iris-litellm is not installed. Run: pip install "iris-litellm[live]"'
76
+ ) from exc
77
+ if litellm_config:
78
+ return profile_from_litellm_config(litellm_config)
79
+ if litellm_proxy:
80
+ return profile_from_litellm_proxy(litellm_proxy)
81
+ raise click.ClickException(
82
+ "LiteLLM source requires --config <litellm.config.yaml> or --proxy <base_url>"
83
+ )
84
+
85
+ raise click.ClickException(f"Unknown scan source: {scan_source}")
86
+
87
+
88
+ def _scan_subtitle(scan_source: str | None) -> str:
89
+ if scan_source == "langfuse":
90
+ return "Langfuse trace metadata — no prompt content read."
91
+ if scan_source == "litellm":
92
+ return "LiteLLM config/proxy — models and providers inferred."
93
+ return "Offline detection — no network calls required."
94
+
95
+
96
+ def _print_profile(profile: dict) -> None:
97
+ table = Table(title="What you're building", show_header=True, header_style="bold")
98
+ table.add_column("Attribute")
99
+ table.add_column("Detected values")
100
+ for key in (
101
+ "providers",
102
+ "frameworks",
103
+ "models",
104
+ "data_categories",
105
+ "deployment_regions",
106
+ "agent_count",
107
+ "autonomy_level",
108
+ "customer_facing",
109
+ ):
110
+ value = profile.get(key, [])
111
+ if isinstance(value, bool):
112
+ display = "yes" if value else "no"
113
+ elif isinstance(value, list):
114
+ display = ", ".join(value) if value else "—"
115
+ else:
116
+ display = str(value)
117
+ table.add_row(key.replace("_", " ").title(), display)
118
+ console.print(table)
119
+
120
+
121
+ def _print_frameworks(profile: dict) -> None:
122
+ table = Table(title="Applicable frameworks", show_header=True, header_style="bold")
123
+ table.add_column("Framework")
124
+ table.add_column("Triggered by")
125
+ for fw, reasons in applicable_frameworks(profile):
126
+ table.add_row(fw, ", ".join(reasons))
127
+ console.print(table)
128
+
129
+
130
+ def _print_actions(obligations: list[dict]) -> None:
131
+ actions = top_recommended_actions(obligations, limit=5)
132
+ if not actions:
133
+ console.print("[dim]No obligations triggered for detected profile.[/dim]")
134
+ return
135
+ table = Table(title="Top recommended actions", show_header=True, header_style="bold")
136
+ table.add_column("#", style="dim")
137
+ table.add_column("Severity")
138
+ table.add_column("Framework")
139
+ table.add_column("Action")
140
+ for idx, action in enumerate(actions, 1):
141
+ sev = action.get("severity", "recommended")
142
+ color = {"blocker": "red", "required": "yellow"}.get(sev, "cyan")
143
+ table.add_row(
144
+ str(idx),
145
+ f"[{color}]{sev}[/{color}]",
146
+ action.get("framework_key", ""),
147
+ (action.get("recommended_action") or "")[:80],
148
+ )
149
+ console.print(table)
150
+
151
+
152
+ def _push_profile(profile: dict) -> None:
153
+ api_key = os.environ.get("IRIS_API_KEY") or os.environ.get("IRIS_CLOUD_API_KEY")
154
+ base_url = os.environ.get("IRIS_API_URL", "http://localhost:8000")
155
+ if not api_key:
156
+ console.print("[yellow]Set IRIS_API_KEY to push profile to cloud.[/yellow]")
157
+ return
158
+ import httpx
159
+
160
+ response = httpx.post(
161
+ f"{base_url.rstrip('/')}/intelligence/profile/scan",
162
+ headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
163
+ json=profile,
164
+ timeout=30,
165
+ )
166
+ if response.status_code >= 400:
167
+ console.print(f"[red]Push failed ({response.status_code}): {response.text}[/red]")
168
+ return
169
+ console.print("[green]Profile pushed to IRIS Cloud.[/green]")
170
+
171
+
172
+ @click.command("scan")
173
+ @click.option("--path", default=".", help="Project path to scan (default local code scan)")
174
+ @click.option(
175
+ "--from",
176
+ "scan_source",
177
+ type=click.Choice(["langfuse", "litellm"], case_sensitive=False),
178
+ default=None,
179
+ help="Observability source instead of local code scan",
180
+ )
181
+ @click.option("--config", "litellm_config", type=click.Path(exists=True), help="LiteLLM config.yaml")
182
+ @click.option("--proxy", "litellm_proxy", help="LiteLLM proxy base URL")
183
+ @click.option("--lookback-days", default=30, show_default=True, help="Langfuse lookback window")
184
+ @click.option("--push", is_flag=True, help="POST profile to cloud when IRIS_API_KEY is set")
185
+ @click.option(
186
+ "--fail-on",
187
+ default="none",
188
+ type=click.Choice(["blocker", "required", "none"]),
189
+ show_default=True,
190
+ help="Exit 1 when obligations meet or exceed this severity (CI integration)",
191
+ )
192
+ @click.option("--json", "as_json", is_flag=True, help="Output raw JSON")
193
+ def compliance_scan_cmd(
194
+ path: str,
195
+ scan_source: str | None,
196
+ litellm_config: str | None,
197
+ litellm_proxy: str | None,
198
+ lookback_days: int,
199
+ push: bool,
200
+ fail_on: str,
201
+ as_json: bool,
202
+ ) -> None:
203
+ """Detect workload attributes and evaluate local regulatory obligations (offline)."""
204
+ root = Path(path).resolve()
205
+ if scan_source is None and not root.exists():
206
+ raise click.ClickException(f"Path not found: {root}")
207
+
208
+ profile = _resolve_profile(
209
+ scan_source=scan_source,
210
+ path=str(root),
211
+ litellm_config=litellm_config,
212
+ litellm_proxy=litellm_proxy,
213
+ lookback_days=lookback_days,
214
+ )
215
+ obligations = evaluate_workload_profile(profile)
216
+
217
+ if as_json:
218
+ import json
219
+
220
+ click.echo(json.dumps({"profile": profile, "obligations": obligations}, indent=2))
221
+ if push:
222
+ _push_profile(profile)
223
+ _exit_on_fail_threshold(obligations, fail_on)
224
+ return
225
+
226
+ console.print(
227
+ Panel(
228
+ "[bold]IRIS Compliance Intelligence Scan[/bold]\n"
229
+ + _scan_subtitle(scan_source),
230
+ border_style="cyan",
231
+ )
232
+ )
233
+ _print_profile(profile)
234
+ _print_frameworks(profile)
235
+ _print_actions(obligations)
236
+ console.print(
237
+ "\n[dim]Continuous monitoring, evidence mapping, and team workflows: "
238
+ "iris cloud connect -> https://iris-security.io[/dim]"
239
+ )
240
+ if push:
241
+ _push_profile(profile)
242
+ _exit_on_fail_threshold(obligations, fail_on)
@@ -30,7 +30,7 @@ Your existing tests will pass without modification.
30
30
 
31
31
  To verify: run your test suite after adding IRIS.
32
32
  If any test fails, report it as a bug at:
33
- github.com/IRIS-Security/iris-sdk/issues"""
33
+ github.com/gimartinb/iris-sdk/issues"""
34
34
 
35
35
  TECHNICAL_EXPLANATION = """\
36
36
  Proxy pattern (identical attribute forwarding):
@@ -17,6 +17,7 @@ Every iris scan or iris certify is a weekly active developer event.
17
17
  import click
18
18
  import sys
19
19
  from pathlib import Path
20
+ from typing import Any
20
21
  from rich.console import Console
21
22
  from rich.panel import Panel
22
23
 
@@ -25,9 +26,31 @@ from iris_core.cli_timing.instrument import timed_cli_command
25
26
  console = Console()
26
27
 
27
28
 
28
- @click.group()
29
+ class IrisCLI(click.Group):
30
+ """Click group that records daily aggregated CLI usage after each command."""
31
+
32
+ def invoke(self, ctx: click.Context) -> Any:
33
+ result = super().invoke(ctx)
34
+ if not ctx.resilient_parsing:
35
+ command = self._resolved_command(ctx)
36
+ if command:
37
+ from iris._telemetry import maybe_record_cli_usage
38
+
39
+ maybe_record_cli_usage(command)
40
+ return result
41
+
42
+ @staticmethod
43
+ def _resolved_command(ctx: click.Context) -> str | None:
44
+ if ctx.invoked_subcommand is None:
45
+ return None
46
+ if ctx.command_path:
47
+ return " ".join(ctx.command_path)
48
+ return ctx.invoked_subcommand
49
+
50
+
51
+ @click.group(cls=IrisCLI)
29
52
  @click.version_option(
30
- version="0.2.14",
53
+ version="0.2.17",
31
54
  prog_name="iris",
32
55
  message="%(prog)s %(version)s · AARM Core conformant (R1–R6) · "
33
56
  "AIUC-1 Q1 2026 aligned · https://iris-security.io",
@@ -462,12 +485,20 @@ framework.add_command(framework_suggest, name="suggest")
462
485
  from iris_cli.assess import compliance_assess
463
486
  compliance.add_command(compliance_assess)
464
487
 
465
- # Wire in evidence commands
466
- from iris_cli.evidence import evidence
467
- cli.add_command(evidence)
488
+ # Wire in evidence commands (optional — requires iris-security-core>=0.1.17 exporters)
489
+ try:
490
+ from iris_cli.evidence import evidence
468
491
 
469
- from iris_cli.vault import vault
470
- cli.add_command(vault)
492
+ cli.add_command(evidence)
493
+ except ModuleNotFoundError:
494
+ pass
495
+
496
+ try:
497
+ from iris_cli.vault import vault
498
+
499
+ cli.add_command(vault)
500
+ except ModuleNotFoundError:
501
+ pass
471
502
 
472
503
  from iris_cli.framework_test import test as framework_test_cmd_legacy
473
504
  # framework_test kept for internal imports; certify is the primary command
@@ -560,6 +591,9 @@ def ping():
560
591
  from iris_cli.compliance_check_cmd import compliance_check_cmd
561
592
  compliance.add_command(compliance_check_cmd, name="check")
562
593
 
594
+ from iris_cli.compliance_scan_cmd import compliance_scan_cmd
595
+ compliance.add_command(compliance_scan_cmd, name="scan")
596
+
563
597
  cli.add_command(compliance)
564
598
  cli.add_command(framework)
565
599
 
@@ -167,7 +167,7 @@ def _print_welcome() -> None:
167
167
  " · How to register and govern a new agent\n"
168
168
  " · Your compliance score in real time\n\n"
169
169
  "No API key required for most steps.\n"
170
- "github.com/IRIS-Security/iris-sdk",
170
+ "github.com/gimartinb/iris-sdk",
171
171
  title="Welcome to IRIS",
172
172
  style="blue",
173
173
  )
@@ -363,7 +363,7 @@ def _print_summary(findings_count: int) -> None:
363
363
  " [cyan]from iris_anthropic import IrisAnthropic[/cyan]\n"
364
364
  " [cyan]client = IrisAnthropic(passport=passport)[/cyan]\n\n"
365
365
  " 4. Read the full guide:\n"
366
- " github.com/IRIS-Security/iris-sdk/blob/main/QUICKSTART.md\n\n"
366
+ " github.com/gimartinb/iris-sdk/blob/main/QUICKSTART.md\n\n"
367
367
  "Questions? gilbert.martin@gmail.com",
368
368
  title="Quickstart complete",
369
369
  style="green",
@@ -330,7 +330,7 @@ Step 1: Go to github.com/organizations/{org_name}/settings/apps
330
330
  Step 2: Click "New GitHub App"
331
331
  Step 3: Fill in:
332
332
  Name: IRIS Governance
333
- Homepage URL: https://github.com/IRIS-Security/iris-sdk
333
+ Homepage URL: https://github.com/gimartinb/iris-sdk
334
334
  Webhook: leave unchecked for now
335
335
  Step 4: Set permissions (read-only):
336
336
  Repository permissions:
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris-security-cli
3
- Version: 0.2.14
3
+ Version: 0.2.17
4
4
  Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
5
5
  License: Apache-2.0
6
- Project-URL: Homepage, https://iris-security.github.io/iris-sdk/
7
- Project-URL: Repository, https://github.com/IRIS-Security/iris-sdk
6
+ Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
7
+ Project-URL: Repository, https://github.com/gimartinb/iris-sdk
8
8
  Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: Topic :: Security
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: iris-security-core>=0.1.16
19
- Requires-Dist: iris-security-sdk>=0.2.14
18
+ Requires-Dist: iris-security-core>=0.1.17
19
+ Requires-Dist: iris-security-sdk>=0.2.17
20
20
  Requires-Dist: click>=8.1
21
21
  Requires-Dist: rich>=13.0
22
22
  Requires-Dist: pyyaml>=6.0
@@ -29,7 +29,7 @@ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
29
29
 
30
30
  Command-line tools for AI agent governance and Colorado AI Act compliance.
31
31
 
32
- Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
32
+ Part of the [IRIS SDK](https://github.com/gimartinb/iris-sdk).
33
33
 
34
34
  ## Install
35
35
 
@@ -45,3 +45,16 @@ iris register --name my-agent --owner you@company.com --team my-team --complianc
45
45
  iris scan
46
46
  iris compliance check --framework colorado-ai-act
47
47
  ```
48
+
49
+ ## GitHub Action
50
+
51
+ Add AI governance to CI with the composite Action in this repo (offline by default):
52
+
53
+ ```yaml
54
+ - uses: IRIS-Security/iris-cli@v1
55
+ with:
56
+ command: compliance scan
57
+ fail-on: blocker
58
+ ```
59
+
60
+ Optional `--push` to IRIS Cloud when `api-key` is set. See [GitHub Action docs](https://iris-security.github.io/iris-sdk/github-action.html).
@@ -9,6 +9,7 @@ iris_cli/certify.py
9
9
  iris_cli/compiler_config.py
10
10
  iris_cli/compliance_check_cmd.py
11
11
  iris_cli/compliance_fix.py
12
+ iris_cli/compliance_scan_cmd.py
12
13
  iris_cli/cost.py
13
14
  iris_cli/declare.py
14
15
  iris_cli/delegation.py
@@ -53,6 +54,7 @@ iris_security_cli.egg-info/entry_points.txt
53
54
  iris_security_cli.egg-info/requires.txt
54
55
  iris_security_cli.egg-info/top_level.txt
55
56
  tests/test_audit_log.py
57
+ tests/test_compliance_scan_fail_on.py
56
58
  tests/test_docs_pages_check.py
57
59
  tests/test_entitlements_display.py
58
60
  tests/test_evidence.py
@@ -1,5 +1,5 @@
1
- iris-security-core>=0.1.16
2
- iris-security-sdk>=0.2.14
1
+ iris-security-core>=0.1.17
2
+ iris-security-sdk>=0.2.17
3
3
  click>=8.1
4
4
  rich>=13.0
5
5
  pyyaml>=6.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iris-security-cli"
7
- version = "0.2.14"
7
+ version = "0.2.17"
8
8
  description = "IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel"
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -20,8 +20,8 @@ classifiers = [
20
20
  "Programming Language :: Python :: 3.12",
21
21
  ]
22
22
  dependencies = [
23
- "iris-security-core>=0.1.16",
24
- "iris-security-sdk>=0.2.14",
23
+ "iris-security-core>=0.1.17",
24
+ "iris-security-sdk>=0.2.17",
25
25
  "click>=8.1",
26
26
  "rich>=13.0",
27
27
  "pyyaml>=6.0",
@@ -31,8 +31,8 @@ dependencies = [
31
31
  scm = ["iris-security-scm[all]>=0.2.0"]
32
32
 
33
33
  [project.urls]
34
- Homepage = "https://iris-security.github.io/iris-sdk/"
35
- Repository = "https://github.com/IRIS-Security/iris-sdk"
34
+ Homepage = "https://github.com/gimartinb/iris-sdk"
35
+ Repository = "https://github.com/gimartinb/iris-sdk"
36
36
 
37
37
  [project.scripts]
38
38
  iris = "iris_cli.main:cli"
@@ -0,0 +1,56 @@
1
+ """Tests for iris compliance scan --fail-on CI thresholds."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from click.testing import CliRunner
6
+
7
+ from iris_cli.main import cli
8
+
9
+
10
+ def test_compliance_scan_fail_on_blocker_exits_nonzero(monkeypatch):
11
+ runner = CliRunner()
12
+ profile = {
13
+ "providers": ["openai"],
14
+ "frameworks": ["langchain"],
15
+ "models": ["gpt-4"],
16
+ "data_categories": ["pii"],
17
+ "deployment_regions": ["eu"],
18
+ "agent_count": 5,
19
+ "autonomy_level": "autonomous",
20
+ "customer_facing": True,
21
+ }
22
+ obligations = [
23
+ {"severity": "blocker", "framework_key": "eu_ai_act", "recommended_action": "test"},
24
+ ]
25
+
26
+ monkeypatch.setattr(
27
+ "iris_cli.compliance_scan_cmd.detect_workload",
28
+ lambda path: profile,
29
+ )
30
+ monkeypatch.setattr(
31
+ "iris_cli.compliance_scan_cmd.evaluate_workload_profile",
32
+ lambda profile: obligations,
33
+ )
34
+
35
+ result = runner.invoke(cli, ["compliance", "scan", "--json", "--fail-on", "blocker"])
36
+ assert result.exit_code == 1
37
+
38
+
39
+ def test_compliance_scan_fail_on_none_always_zero(monkeypatch):
40
+ runner = CliRunner()
41
+ profile = {"providers": [], "frameworks": [], "models": [], "agent_count": 0}
42
+ obligations = [
43
+ {"severity": "blocker", "framework_key": "eu_ai_act", "recommended_action": "test"},
44
+ ]
45
+
46
+ monkeypatch.setattr(
47
+ "iris_cli.compliance_scan_cmd.detect_workload",
48
+ lambda path: profile,
49
+ )
50
+ monkeypatch.setattr(
51
+ "iris_cli.compliance_scan_cmd.evaluate_workload_profile",
52
+ lambda profile: obligations,
53
+ )
54
+
55
+ result = runner.invoke(cli, ["compliance", "scan", "--json", "--fail-on", "none"])
56
+ assert result.exit_code == 0