iris-security-cli 0.2.17__tar.gz → 0.2.18__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.
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/PKG-INFO +8 -6
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/README.md +1 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/audit_log.py +10 -5
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/certify.py +4 -2
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/compliance_scan_cmd.py +25 -5
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/cost.py +2 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/drift.py +1 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/evidence.py +6 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/explain.py +1 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/hitl.py +68 -3
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/main.py +4 -3
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/mcp_server.py +86 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/quickstart.py +2 -2
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/scm.py +6 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/users.py +8 -2
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/PKG-INFO +8 -6
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/SOURCES.txt +3 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/requires.txt +2 -2
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/pyproject.toml +7 -5
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_audit_log.py +8 -4
- iris_security_cli-0.2.18/tests/test_cli_free_tier_boundary.py +256 -0
- iris_security_cli-0.2.18/tests/test_compliance_scan_fail_on.py +113 -0
- iris_security_cli-0.2.18/tests/test_docs_pages_check.py +92 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_entitlements_display.py +4 -4
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_evidence.py +44 -45
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_evidence_query.py +5 -3
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_framework_test.py +3 -1
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_hitl_cli.py +54 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_mcp_model_governance.py +3 -2
- iris_security_cli-0.2.18/tests/vault_fixtures.py +14 -0
- iris_security_cli-0.2.17/tests/test_compliance_scan_fail_on.py +0 -56
- iris_security_cli-0.2.17/tests/test_docs_pages_check.py +0 -45
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/__init__.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/action_plan.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/assess.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/cedar_parser.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/compiler_config.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/compliance_check_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/compliance_fix.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/declare.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/delegation.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/discover.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/dlp_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/enforce.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/entitlements_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/framework_suggest.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/framework_test.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/license_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/list_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/models_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/onboarding_report.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/org_policy.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/policy_cache.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/policy_commit.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/policy_diff.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/policy_status.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/preview.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/redteam.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/regulatory.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/scan_govern.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/scan_report.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/sentinel.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/status_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/vault.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/watch.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_cli/witness.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/dependency_links.txt +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/entry_points.txt +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/top_level.txt +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/setup.cfg +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_evidence_export.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_framework_suggest.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_list_cmd.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_policy_diff.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_quickstart.py +0 -0
- {iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/tests/test_vocabulary.py +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-security-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.18
|
|
4
4
|
Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
|
|
5
5
|
License: Apache-2.0
|
|
6
|
-
Project-URL: Homepage, https://github.
|
|
7
|
-
Project-URL: Repository, https://github.com/
|
|
6
|
+
Project-URL: Homepage, https://iris-security.github.io/iris-sdk/
|
|
7
|
+
Project-URL: Repository, https://github.com/IRIS-Security/iris-sdk
|
|
8
|
+
Project-URL: Documentation, https://iris-security.github.io/iris-sdk/cli-reference.html
|
|
9
|
+
Project-URL: Issues, https://github.com/IRIS-Security/iris-sdk/issues
|
|
8
10
|
Classifier: Development Status :: 3 - Alpha
|
|
9
11
|
Classifier: Intended Audience :: Developers
|
|
10
12
|
Classifier: Topic :: Security
|
|
@@ -15,8 +17,8 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
18
|
Requires-Python: >=3.10
|
|
17
19
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: iris-security-core>=0.1.
|
|
19
|
-
Requires-Dist: iris-security-sdk>=0.2.
|
|
20
|
+
Requires-Dist: iris-security-core>=0.1.18
|
|
21
|
+
Requires-Dist: iris-security-sdk>=0.2.18
|
|
20
22
|
Requires-Dist: click>=8.1
|
|
21
23
|
Requires-Dist: rich>=13.0
|
|
22
24
|
Requires-Dist: pyyaml>=6.0
|
|
@@ -29,7 +31,7 @@ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
|
|
|
29
31
|
|
|
30
32
|
Command-line tools for AI agent governance and Colorado AI Act compliance.
|
|
31
33
|
|
|
32
|
-
Part of the [IRIS SDK](https://github.com/
|
|
34
|
+
Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
|
|
33
35
|
|
|
34
36
|
## Install
|
|
35
37
|
|
|
@@ -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/
|
|
7
|
+
Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -55,6 +55,11 @@ def _event_policy(event: dict) -> str:
|
|
|
55
55
|
return "—"
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def _event_r5_satisfied(event: dict) -> bool:
|
|
59
|
+
"""R5 tamper-evidence applies only to HMAC-signed vault events (Evidence Vault v2)."""
|
|
60
|
+
return bool(event.get("signature"))
|
|
61
|
+
|
|
62
|
+
|
|
58
63
|
def _event_hash(event: dict) -> str:
|
|
59
64
|
if event.get("hash"):
|
|
60
65
|
return str(event["hash"])
|
|
@@ -150,7 +155,7 @@ def _format_splunk(event: dict) -> dict:
|
|
|
150
155
|
"iris_regulation": _event_regulation(event),
|
|
151
156
|
"iris_event_id": event.get("event_id", ""),
|
|
152
157
|
"iris_hash": _event_hash(event),
|
|
153
|
-
"aarm_r5":
|
|
158
|
+
"aarm_r5": _event_r5_satisfied(event),
|
|
154
159
|
"aiuc1_control": _aiuc1_control(event),
|
|
155
160
|
},
|
|
156
161
|
}
|
|
@@ -195,7 +200,7 @@ def _format_elastic(event: dict) -> dict:
|
|
|
195
200
|
"risk.score": _event_risk_score(event),
|
|
196
201
|
"labels": {
|
|
197
202
|
"iris_regulation": _event_regulation(event),
|
|
198
|
-
"aarm_r5": "true",
|
|
203
|
+
"aarm_r5": "true" if _event_r5_satisfied(event) else "false",
|
|
199
204
|
},
|
|
200
205
|
}
|
|
201
206
|
|
|
@@ -247,7 +252,7 @@ def _format_otel_envelope(events: List[dict]) -> dict:
|
|
|
247
252
|
_otel_attribute("service.name", "iris-cli"),
|
|
248
253
|
_otel_attribute("service.version", IRIS_VERSION),
|
|
249
254
|
_otel_attribute("iris.vendor", "IRIS Security, Inc."),
|
|
250
|
-
_otel_attribute("aarm.
|
|
255
|
+
_otel_attribute("aarm.alignment", "designed-toward"),
|
|
251
256
|
],
|
|
252
257
|
},
|
|
253
258
|
"scopeSpans": [
|
|
@@ -409,7 +414,7 @@ def _print_export_summary(
|
|
|
409
414
|
"Formats: Splunk HEC · Datadog · Elastic ECS · OTel OTLP"
|
|
410
415
|
)
|
|
411
416
|
err_console.print(
|
|
412
|
-
"
|
|
417
|
+
"OTel export · https://iris-security.io"
|
|
413
418
|
)
|
|
414
419
|
|
|
415
420
|
|
|
@@ -524,7 +529,7 @@ def audit_log_export(
|
|
|
524
529
|
Plug IRIS evidence directly into your existing security stack.
|
|
525
530
|
No IRIS account required — reads from local Evidence Vault.
|
|
526
531
|
|
|
527
|
-
|
|
532
|
+
Exports governance telemetry in standard SIEM formats (including OTel OTLP).
|
|
528
533
|
|
|
529
534
|
Examples:
|
|
530
535
|
iris audit-log export --format splunk --output events.json
|
|
@@ -404,11 +404,13 @@ def certify_cmd(
|
|
|
404
404
|
|
|
405
405
|
loader = DynamicBundleLoader(pinned_version=registry_version)
|
|
406
406
|
|
|
407
|
+
machine_output = output_format in ("json", "markdown", "aiuc1-export")
|
|
407
408
|
if not offline and not registry_version:
|
|
408
409
|
if not loader.is_cache_fresh():
|
|
409
|
-
|
|
410
|
+
if not machine_output:
|
|
411
|
+
console.print("[dim]Checking for regulatory updates...[/dim]")
|
|
410
412
|
updated = loader.refresh_registry()
|
|
411
|
-
if updated:
|
|
413
|
+
if updated and not machine_output:
|
|
412
414
|
console.print("[green]✓ Regulatory intelligence updated[/green]")
|
|
413
415
|
console.print("[dim] Run: iris regulatory check for change summary[/dim]")
|
|
414
416
|
|
|
@@ -17,8 +17,8 @@ from rich.table import Table
|
|
|
17
17
|
|
|
18
18
|
from iris.scan import detect_workload
|
|
19
19
|
from iris_core.compliance.workload_eval import (
|
|
20
|
-
applicable_frameworks,
|
|
21
20
|
evaluate_workload_profile,
|
|
21
|
+
framework_coverage,
|
|
22
22
|
top_recommended_actions,
|
|
23
23
|
)
|
|
24
24
|
|
|
@@ -34,7 +34,8 @@ def _exit_on_fail_threshold(obligations: list[dict], fail_on: str) -> None:
|
|
|
34
34
|
blocking = [
|
|
35
35
|
obligation
|
|
36
36
|
for obligation in obligations
|
|
37
|
-
if
|
|
37
|
+
if not obligation.get("informational", False)
|
|
38
|
+
and _SEVERITY_RANK.get(obligation.get("severity", "recommended"), 9) <= threshold
|
|
38
39
|
]
|
|
39
40
|
if blocking:
|
|
40
41
|
sys.exit(1)
|
|
@@ -119,13 +120,32 @@ def _print_profile(profile: dict) -> None:
|
|
|
119
120
|
|
|
120
121
|
|
|
121
122
|
def _print_frameworks(profile: dict) -> None:
|
|
123
|
+
coverage = framework_coverage(profile)
|
|
122
124
|
table = Table(title="Applicable frameworks", show_header=True, header_style="bold")
|
|
123
125
|
table.add_column("Framework")
|
|
124
|
-
table.add_column("
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
table.add_column("Why this applies to you")
|
|
127
|
+
table.add_column("Depth")
|
|
128
|
+
for entry in coverage:
|
|
129
|
+
depth = (
|
|
130
|
+
f"[yellow]mapped — thin[/yellow]"
|
|
131
|
+
if entry["thin"]
|
|
132
|
+
else f"{entry['rule_count']} rules"
|
|
133
|
+
)
|
|
134
|
+
table.add_row(entry["bundle_id"], entry["why"], depth)
|
|
127
135
|
console.print(table)
|
|
128
136
|
|
|
137
|
+
thin = [entry for entry in coverage if entry["thin"]]
|
|
138
|
+
if thin:
|
|
139
|
+
console.print(
|
|
140
|
+
"[dim]Thin coverage means the registry has this framework mapped but "
|
|
141
|
+
"not yet deeply evaluated here — run one of the following for a real "
|
|
142
|
+
"control-by-control check:[/dim]"
|
|
143
|
+
)
|
|
144
|
+
for entry in thin:
|
|
145
|
+
console.print(
|
|
146
|
+
f" [dim]•[/dim] iris compliance check --framework {entry['bundle_id']}"
|
|
147
|
+
)
|
|
148
|
+
|
|
129
149
|
|
|
130
150
|
def _print_actions(obligations: list[dict]) -> None:
|
|
131
151
|
actions = top_recommended_actions(obligations, limit=5)
|
|
@@ -18,6 +18,7 @@ from rich.table import Table
|
|
|
18
18
|
|
|
19
19
|
from iris_core.cost.pricing import DEFAULT_PRICING, overrides_path, PricingRegistry
|
|
20
20
|
from iris_core.cost.tracker import CostSummary, CostTracker, discover_agent_trackers
|
|
21
|
+
from iris_core.entitlements import Entitlements, Feature
|
|
21
22
|
|
|
22
23
|
console = Console()
|
|
23
24
|
|
|
@@ -298,6 +299,7 @@ def cost_report(agent: Optional[str], days: int, output_format: str, since: Opti
|
|
|
298
299
|
@click.option("--since", default=None, help="ISO date string for period start")
|
|
299
300
|
def cost_summary(days: int, since: Optional[str]) -> None:
|
|
300
301
|
"""CFO report — cost across all agents sorted by spend."""
|
|
302
|
+
Entitlements().require(Feature.COST_ORG_SUMMARY, context="org-wide cost summary")
|
|
301
303
|
since_iso = _since_from_date(since, days)
|
|
302
304
|
trackers = discover_agent_trackers()
|
|
303
305
|
if not trackers:
|
|
@@ -254,6 +254,7 @@ def drift_alert_config(slack_webhook: Optional[str], email: Optional[str], webho
|
|
|
254
254
|
_require_pro_license("Email drift alerts", Feature.DRIFT_EMAIL_ALERT)
|
|
255
255
|
config["email"] = email
|
|
256
256
|
if webhook:
|
|
257
|
+
_require_pro_license("Webhook drift alerts", Feature.DRIFT_WEBHOOK_ALERT)
|
|
257
258
|
config["webhook"] = webhook
|
|
258
259
|
|
|
259
260
|
if not any([slack_webhook, email, webhook]):
|
|
@@ -981,7 +981,8 @@ def evidence_query(
|
|
|
981
981
|
to exactly the events that need attention.
|
|
982
982
|
|
|
983
983
|
This is the CLI equivalent of Evidence Vault Signal mode.
|
|
984
|
-
|
|
984
|
+
Local vault events are unsigned JSONL by default; Evidence Vault v2 adds
|
|
985
|
+
HMAC signing and hash chaining for tamper-evident receipts.
|
|
985
986
|
|
|
986
987
|
Examples:
|
|
987
988
|
iris evidence query --decision deny
|
|
@@ -1150,6 +1151,10 @@ def evidence_export(agent, output_path, output_format, governance_dir, vault_dir
|
|
|
1150
1151
|
return
|
|
1151
1152
|
|
|
1152
1153
|
if output_format == "oscal":
|
|
1154
|
+
Entitlements().require(
|
|
1155
|
+
Feature.EVIDENCE_EXPORT_AUDITOR,
|
|
1156
|
+
context="evidence export --format oscal (auditor deliverable)",
|
|
1157
|
+
)
|
|
1153
1158
|
vault = _open_vault(agent, vault_dir)
|
|
1154
1159
|
vault_data = vault.export_vault()
|
|
1155
1160
|
oscal_payload = {
|
|
@@ -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/
|
|
33
|
+
github.com/IRIS-Security/iris-sdk/issues"""
|
|
34
34
|
|
|
35
35
|
TECHNICAL_EXPLANATION = """\
|
|
36
36
|
Proxy pattern (identical attribute forwarding):
|
|
@@ -214,6 +214,35 @@ def hitl_setup(agent: str):
|
|
|
214
214
|
rules.append(HITLConditionRule(condition=condition, reason=reason, notify_channel=channel))
|
|
215
215
|
config.condition_rules = rules
|
|
216
216
|
|
|
217
|
+
console.print(
|
|
218
|
+
"\nRisk tiers — gate the clearly-irreversible, auto-allow the routine.\n"
|
|
219
|
+
"Leave any of these blank to skip; an empty list never triggers HITL.\n"
|
|
220
|
+
)
|
|
221
|
+
if click.confirm("Require HITL for specific violation severities?", default=False):
|
|
222
|
+
levels = click.prompt(
|
|
223
|
+
"Severities (comma-separated, e.g. CRITICAL,HIGH)", default="CRITICAL"
|
|
224
|
+
)
|
|
225
|
+
config.required_for_risk_levels = [
|
|
226
|
+
lvl.strip().upper() for lvl in levels.split(",") if lvl.strip()
|
|
227
|
+
]
|
|
228
|
+
if click.confirm(
|
|
229
|
+
"Require HITL for specific actions in staging/production (e.g. write, delete)?",
|
|
230
|
+
default=False,
|
|
231
|
+
):
|
|
232
|
+
actions = click.prompt("Actions (comma-separated)", default="write,delete")
|
|
233
|
+
config.step_up_actions = [a.strip() for a in actions.split(",") if a.strip()]
|
|
234
|
+
if click.confirm(
|
|
235
|
+
"Require HITL for specific data classifications (e.g. phi, pii)?", default=False
|
|
236
|
+
):
|
|
237
|
+
classes = click.prompt("Data classifications (comma-separated)", default="phi,pii")
|
|
238
|
+
config.sensitive_data_classifications = [
|
|
239
|
+
c.strip() for c in classes.split(",") if c.strip()
|
|
240
|
+
]
|
|
241
|
+
config.step_up_on_intent_drift = click.confirm(
|
|
242
|
+
"Require HITL when an action drifts outside the agent's declared intent?",
|
|
243
|
+
default=False,
|
|
244
|
+
)
|
|
245
|
+
|
|
217
246
|
if click.confirm("Configure Slack webhook URL (Pro)?", default=False):
|
|
218
247
|
config.slack_webhook_url = click.prompt("Slack webhook URL", default="")
|
|
219
248
|
if click.confirm("Configure email recipients (Pro)?", default=False):
|
|
@@ -223,7 +252,15 @@ def hitl_setup(agent: str):
|
|
|
223
252
|
path = _save_hitl_config(agent, config)
|
|
224
253
|
console.print(f"[green]✓[/green] HITL configured for {agent}")
|
|
225
254
|
console.print(f"[green]✓[/green] {len(rules)} condition rule(s) declared")
|
|
226
|
-
|
|
255
|
+
if config.required_for_risk_levels:
|
|
256
|
+
console.print(f"[green]✓[/green] Step-up on severities: {config.required_for_risk_levels}")
|
|
257
|
+
if config.step_up_actions:
|
|
258
|
+
console.print(f"[green]✓[/green] Step-up on actions: {config.step_up_actions}")
|
|
259
|
+
if config.sensitive_data_classifications:
|
|
260
|
+
console.print(f"[green]✓[/green] Step-up on data classifications: {config.sensitive_data_classifications}")
|
|
261
|
+
if config.step_up_on_intent_drift:
|
|
262
|
+
console.print("[green]✓[/green] Step-up on intent drift: enabled")
|
|
263
|
+
console.print("[green]✓[/green] HITL fires ONLY for what you declared above — everything else auto-allows")
|
|
227
264
|
console.print(f"[green]✓[/green] Saved to {path}")
|
|
228
265
|
|
|
229
266
|
|
|
@@ -255,10 +292,38 @@ def hitl_rules(agent: str):
|
|
|
255
292
|
console.print(f" Reason: {entry['reason']}")
|
|
256
293
|
console.print(" Channel: All channels")
|
|
257
294
|
|
|
295
|
+
if config.required_for_risk_levels:
|
|
296
|
+
console.print(
|
|
297
|
+
f"\nWILL trigger HITL — violation severity in "
|
|
298
|
+
f"{config.required_for_risk_levels}:"
|
|
299
|
+
)
|
|
300
|
+
console.print(
|
|
301
|
+
"● Any policy violation at these severities steps up instead of "
|
|
302
|
+
"logging silently or auto-denying"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if config.step_up_actions:
|
|
306
|
+
console.print(f"\nWILL trigger HITL — declared step-up actions in staging/production:")
|
|
307
|
+
console.print(f"● {config.step_up_actions}")
|
|
308
|
+
|
|
309
|
+
if config.sensitive_data_classifications:
|
|
310
|
+
console.print(
|
|
311
|
+
f"\nWILL trigger HITL — sensitive data classifications:"
|
|
312
|
+
)
|
|
313
|
+
console.print(f"● {config.sensitive_data_classifications}")
|
|
314
|
+
|
|
315
|
+
if config.step_up_on_intent_drift:
|
|
316
|
+
console.print(
|
|
317
|
+
"\nWILL trigger HITL — action drifts outside the agent's declared "
|
|
318
|
+
"intent (semantic distance > 0.7)"
|
|
319
|
+
)
|
|
320
|
+
|
|
258
321
|
console.print("\nWILL NOT trigger HITL — everything else:")
|
|
259
322
|
console.print("✗ Calls with no matching condition rule → automatic")
|
|
260
|
-
|
|
261
|
-
|
|
323
|
+
if not config.required_for_risk_levels:
|
|
324
|
+
console.print("✗ Risk level alone → never triggers HITL (declare required_for_risk_levels to change this)")
|
|
325
|
+
if not config.sensitive_data_classifications:
|
|
326
|
+
console.print("✗ Data classification alone → never triggers HITL (declare sensitive_data_classifications to change this)")
|
|
262
327
|
console.print("\nRun: iris hitl test --agent {agent} to verify.")
|
|
263
328
|
|
|
264
329
|
|
|
@@ -50,10 +50,11 @@ class IrisCLI(click.Group):
|
|
|
50
50
|
|
|
51
51
|
@click.group(cls=IrisCLI)
|
|
52
52
|
@click.version_option(
|
|
53
|
-
version="0.2.
|
|
53
|
+
version="0.2.18",
|
|
54
54
|
prog_name="iris",
|
|
55
|
-
message="%(prog)s %(version)s ·
|
|
56
|
-
"
|
|
55
|
+
message="%(prog)s %(version)s · Evaluates agent actions through an in-process "
|
|
56
|
+
"Cedar engine before adapter-wrapped calls proceed; records decisions "
|
|
57
|
+
"to a local Evidence Vault.",
|
|
57
58
|
)
|
|
58
59
|
def cli():
|
|
59
60
|
"""
|
|
@@ -19,6 +19,7 @@ Setup:
|
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
21
|
import asyncio
|
|
22
|
+
import re
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
from typing import Any
|
|
24
25
|
from datetime import datetime
|
|
@@ -142,6 +143,75 @@ MCP_TOOLS = [
|
|
|
142
143
|
|
|
143
144
|
# ── Tool handlers ─────────────────────────────────────────────────────────────
|
|
144
145
|
|
|
146
|
+
_MODEL_ID_PATTERN = re.compile(r"""model\s*=\s*["']([^"']+)["']""")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _governance_root(workspace: Path) -> Path | None:
|
|
150
|
+
candidate = workspace / "governance"
|
|
151
|
+
return candidate if candidate.exists() else None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _model_governance_violations(code: str, workspace: Path) -> list[dict]:
|
|
155
|
+
from iris_core.engine.model_governance import check_model_governance
|
|
156
|
+
from iris_core.models.context import EvaluationContext
|
|
157
|
+
from iris_core.models.directives import DirectiveRegistry
|
|
158
|
+
from iris_core.models.model_registry import ModelRegistry
|
|
159
|
+
from iris_core.models.passport import AgentPassport, Environment
|
|
160
|
+
|
|
161
|
+
model_ids = _MODEL_ID_PATTERN.findall(code)
|
|
162
|
+
if not model_ids:
|
|
163
|
+
return []
|
|
164
|
+
|
|
165
|
+
gov_root = _governance_root(workspace)
|
|
166
|
+
registry = ModelRegistry.load(gov_root)
|
|
167
|
+
directives = DirectiveRegistry.load(gov_root)
|
|
168
|
+
passport = AgentPassport(
|
|
169
|
+
name="mcp-scan",
|
|
170
|
+
owner="unknown@local",
|
|
171
|
+
allowed_model_tiers=["standard", "frontier", "frontier-restricted"],
|
|
172
|
+
)
|
|
173
|
+
violations: list[dict] = []
|
|
174
|
+
for model_id in model_ids:
|
|
175
|
+
ctx = EvaluationContext(
|
|
176
|
+
agent_id="mcp-scan",
|
|
177
|
+
action="call",
|
|
178
|
+
resource="llm-api",
|
|
179
|
+
resource_type="model",
|
|
180
|
+
environment=Environment.DEV,
|
|
181
|
+
model_id=model_id,
|
|
182
|
+
)
|
|
183
|
+
for violation in check_model_governance(passport, ctx, registry, directives):
|
|
184
|
+
violations.append(
|
|
185
|
+
{
|
|
186
|
+
"rule_id": violation.rule_id,
|
|
187
|
+
"severity": violation.severity.value,
|
|
188
|
+
"message": violation.message,
|
|
189
|
+
"compliance_refs": violation.compliance_refs,
|
|
190
|
+
"fix": violation.remediation,
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
return violations
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def handle_models_status(params: dict) -> dict:
|
|
197
|
+
"""Return bundled/user model registry and active directives."""
|
|
198
|
+
from iris_core.models.directives import DirectiveRegistry
|
|
199
|
+
from iris_core.models.model_registry import ModelRegistry
|
|
200
|
+
|
|
201
|
+
workspace = Path(params.get("workspace_path", "."))
|
|
202
|
+
gov_root = _governance_root(workspace)
|
|
203
|
+
registry = ModelRegistry.load(gov_root)
|
|
204
|
+
directives = DirectiveRegistry.load(gov_root)
|
|
205
|
+
return {
|
|
206
|
+
"models": {
|
|
207
|
+
model_id: capability.to_dict()
|
|
208
|
+
for model_id, capability in registry.models.items()
|
|
209
|
+
},
|
|
210
|
+
"active_directives": [d.to_dict() for d in directives.active_directives()],
|
|
211
|
+
"docs": "docs/MODEL_GOVERNANCE.md",
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
145
215
|
def handle_check_agent_code(params: dict) -> dict:
|
|
146
216
|
"""
|
|
147
217
|
Analyze agent code for compliance violations.
|
|
@@ -150,6 +220,7 @@ def handle_check_agent_code(params: dict) -> dict:
|
|
|
150
220
|
code = params.get("code", "")
|
|
151
221
|
file_path = params.get("file_path", "unknown")
|
|
152
222
|
framework = params.get("framework", "colorado-ai-act")
|
|
223
|
+
workspace = Path(params.get("workspace_path", "."))
|
|
153
224
|
|
|
154
225
|
violations = []
|
|
155
226
|
suggestions = []
|
|
@@ -242,6 +313,8 @@ def handle_check_agent_code(params: dict) -> dict:
|
|
|
242
313
|
"command": f"iris register --name my-agent --owner you@company.com --compliance colorado-ai-act",
|
|
243
314
|
})
|
|
244
315
|
|
|
316
|
+
violations.extend(_model_governance_violations(code, workspace))
|
|
317
|
+
|
|
245
318
|
return {
|
|
246
319
|
"file": file_path,
|
|
247
320
|
"framework": framework,
|
|
@@ -386,6 +459,18 @@ agent = IrisAgent(
|
|
|
386
459
|
"explanation": "Declare the destination region so IRIS can enforce cross-region policy",
|
|
387
460
|
"code_snippet": "@agent.guard(tool=\"storage\", action=\"write\", data_region=\"us-east-1\", destination_region=\"us-east-1\")",
|
|
388
461
|
},
|
|
462
|
+
"IRIS-MODEL-001": {
|
|
463
|
+
"explanation": "Model is suspended by an active directive — switch to the approved fallback",
|
|
464
|
+
"command": "Review governance/directives/active.yaml and use the fallback_model listed there",
|
|
465
|
+
},
|
|
466
|
+
"IRIS-MODEL-004": {
|
|
467
|
+
"explanation": "Export-control restricted model requires valid user work authorization",
|
|
468
|
+
"code_snippet": "Set user_work_authorization (e.g. us-citizen) in EvaluationContext or use an unrestricted model",
|
|
469
|
+
},
|
|
470
|
+
"IRIS-MODEL-005": {
|
|
471
|
+
"explanation": "Frontier model requires HITL approval in staging/production",
|
|
472
|
+
"code_snippet": "Set hitl_approved=True after security review, or use iris HITL workflow",
|
|
473
|
+
},
|
|
389
474
|
}
|
|
390
475
|
|
|
391
476
|
fix = fixes.get(rule_id, {
|
|
@@ -469,6 +554,7 @@ HANDLERS = {
|
|
|
469
554
|
"iris_suggest_policy": handle_suggest_policy,
|
|
470
555
|
"iris_fix_violation": handle_fix_violation,
|
|
471
556
|
"iris_scan_workspace": handle_scan_workspace,
|
|
557
|
+
"iris_models_status": handle_models_status,
|
|
472
558
|
}
|
|
473
559
|
|
|
474
560
|
|
|
@@ -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/
|
|
170
|
+
"github.com/IRIS-Security/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/
|
|
366
|
+
" github.com/IRIS-Security/iris-sdk/blob/main/QUICKSTART.md\n\n"
|
|
367
367
|
"Questions? gilbert.martin@gmail.com",
|
|
368
368
|
title="Quickstart complete",
|
|
369
369
|
style="green",
|
|
@@ -9,6 +9,8 @@ from pathlib import Path
|
|
|
9
9
|
import click
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
|
|
12
|
+
from iris_core.entitlements import Entitlements, Feature
|
|
13
|
+
|
|
12
14
|
console = Console()
|
|
13
15
|
|
|
14
16
|
|
|
@@ -94,6 +96,9 @@ def scan_org(
|
|
|
94
96
|
export GITHUB_TOKEN=ghp_... && iris scm scan-org --platform github --org my-org
|
|
95
97
|
iris scm scan-org --platform gitlab --group my-group --token $GITLAB_TOKEN
|
|
96
98
|
"""
|
|
99
|
+
Entitlements().require(Feature.SCM_ORG_SCANNER, context="org-wide SCM scan")
|
|
100
|
+
if comment:
|
|
101
|
+
Entitlements().require(Feature.SCM_PR_COMMENTS, context="SCM PR comments")
|
|
97
102
|
if platform == "github":
|
|
98
103
|
if not org:
|
|
99
104
|
console.print("[red]--org is required for GitHub scans[/red]")
|
|
@@ -330,7 +335,7 @@ Step 1: Go to github.com/organizations/{org_name}/settings/apps
|
|
|
330
335
|
Step 2: Click "New GitHub App"
|
|
331
336
|
Step 3: Fill in:
|
|
332
337
|
Name: IRIS Governance
|
|
333
|
-
Homepage URL: https://github.com/
|
|
338
|
+
Homepage URL: https://github.com/IRIS-Security/iris-sdk
|
|
334
339
|
Webhook: leave unchecked for now
|
|
335
340
|
Step 4: Set permissions (read-only):
|
|
336
341
|
Repository permissions:
|
|
@@ -8,12 +8,14 @@ import click
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
+
from iris_core.entitlements import Entitlements, Feature
|
|
12
|
+
|
|
11
13
|
console = Console()
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
@click.group()
|
|
15
17
|
def users():
|
|
16
|
-
"""Manage which users may invoke governed agents (
|
|
18
|
+
"""Manage which users may invoke governed agents (Business tier RBAC beyond solo owner)."""
|
|
17
19
|
pass
|
|
18
20
|
|
|
19
21
|
|
|
@@ -31,7 +33,11 @@ def users_add(email: str, role: str, agent: str, governance_dir: Path):
|
|
|
31
33
|
Example:
|
|
32
34
|
iris users add --email alice@co.com --role developer --agent my-agent
|
|
33
35
|
"""
|
|
34
|
-
from iris_core.rbac.registry import add_user
|
|
36
|
+
from iris_core.rbac.registry import add_user, load_users
|
|
37
|
+
|
|
38
|
+
existing = load_users(agent, governance_dir)
|
|
39
|
+
if existing:
|
|
40
|
+
Entitlements().require(Feature.TEAM_RBAC, context="add second+ user to agent")
|
|
35
41
|
|
|
36
42
|
try:
|
|
37
43
|
entry = add_user(agent, email, role, governance_dir)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-security-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.18
|
|
4
4
|
Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
|
|
5
5
|
License: Apache-2.0
|
|
6
|
-
Project-URL: Homepage, https://github.
|
|
7
|
-
Project-URL: Repository, https://github.com/
|
|
6
|
+
Project-URL: Homepage, https://iris-security.github.io/iris-sdk/
|
|
7
|
+
Project-URL: Repository, https://github.com/IRIS-Security/iris-sdk
|
|
8
|
+
Project-URL: Documentation, https://iris-security.github.io/iris-sdk/cli-reference.html
|
|
9
|
+
Project-URL: Issues, https://github.com/IRIS-Security/iris-sdk/issues
|
|
8
10
|
Classifier: Development Status :: 3 - Alpha
|
|
9
11
|
Classifier: Intended Audience :: Developers
|
|
10
12
|
Classifier: Topic :: Security
|
|
@@ -15,8 +17,8 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
18
|
Requires-Python: >=3.10
|
|
17
19
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: iris-security-core>=0.1.
|
|
19
|
-
Requires-Dist: iris-security-sdk>=0.2.
|
|
20
|
+
Requires-Dist: iris-security-core>=0.1.18
|
|
21
|
+
Requires-Dist: iris-security-sdk>=0.2.18
|
|
20
22
|
Requires-Dist: click>=8.1
|
|
21
23
|
Requires-Dist: rich>=13.0
|
|
22
24
|
Requires-Dist: pyyaml>=6.0
|
|
@@ -29,7 +31,7 @@ IRIS CLI — `iris scan`, `iris register`, `iris policy`, `iris compliance`.
|
|
|
29
31
|
|
|
30
32
|
Command-line tools for AI agent governance and Colorado AI Act compliance.
|
|
31
33
|
|
|
32
|
-
Part of the [IRIS SDK](https://github.com/
|
|
34
|
+
Part of the [IRIS SDK](https://github.com/IRIS-Security/iris-sdk).
|
|
33
35
|
|
|
34
36
|
## Install
|
|
35
37
|
|
{iris_security_cli-0.2.17 → iris_security_cli-0.2.18}/iris_security_cli.egg-info/SOURCES.txt
RENAMED
|
@@ -54,6 +54,7 @@ iris_security_cli.egg-info/entry_points.txt
|
|
|
54
54
|
iris_security_cli.egg-info/requires.txt
|
|
55
55
|
iris_security_cli.egg-info/top_level.txt
|
|
56
56
|
tests/test_audit_log.py
|
|
57
|
+
tests/test_cli_free_tier_boundary.py
|
|
57
58
|
tests/test_compliance_scan_fail_on.py
|
|
58
59
|
tests/test_docs_pages_check.py
|
|
59
60
|
tests/test_entitlements_display.py
|
|
@@ -67,4 +68,5 @@ tests/test_list_cmd.py
|
|
|
67
68
|
tests/test_mcp_model_governance.py
|
|
68
69
|
tests/test_policy_diff.py
|
|
69
70
|
tests/test_quickstart.py
|
|
70
|
-
tests/test_vocabulary.py
|
|
71
|
+
tests/test_vocabulary.py
|
|
72
|
+
tests/vault_fixtures.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iris-security-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.18"
|
|
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.
|
|
24
|
-
"iris-security-sdk>=0.2.
|
|
23
|
+
"iris-security-core>=0.1.18",
|
|
24
|
+
"iris-security-sdk>=0.2.18",
|
|
25
25
|
"click>=8.1",
|
|
26
26
|
"rich>=13.0",
|
|
27
27
|
"pyyaml>=6.0",
|
|
@@ -31,8 +31,10 @@ dependencies = [
|
|
|
31
31
|
scm = ["iris-security-scm[all]>=0.2.0"]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
34
|
-
Homepage = "https://github.
|
|
35
|
-
Repository = "https://github.com/
|
|
34
|
+
Homepage = "https://iris-security.github.io/iris-sdk/"
|
|
35
|
+
Repository = "https://github.com/IRIS-Security/iris-sdk"
|
|
36
|
+
Documentation = "https://iris-security.github.io/iris-sdk/cli-reference.html"
|
|
37
|
+
Issues = "https://github.com/IRIS-Security/iris-sdk/issues"
|
|
36
38
|
|
|
37
39
|
[project.scripts]
|
|
38
40
|
iris = "iris_cli.main:cli"
|