iris-security-cli 0.2.1__tar.gz → 0.2.4__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.1 → iris_security_cli-0.2.4}/PKG-INFO +4 -4
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/certify.py +114 -13
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/framework_suggest.py +76 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/main.py +1 -1
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/regulatory.py +36 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/PKG-INFO +4 -4
- iris_security_cli-0.2.4/iris_security_cli.egg-info/requires.txt +8 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/pyproject.toml +4 -4
- iris_security_cli-0.2.1/iris_security_cli.egg-info/requires.txt +0 -8
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/README.md +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/__init__.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/action_plan.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/assess.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/cedar_parser.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/compiler_config.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/compliance_check_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/compliance_fix.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/cost.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/declare.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/delegation.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/dlp_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/drift.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/enforce.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/entitlements_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/evidence.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/explain.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/framework_test.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/hitl.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/license_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/mcp_server.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/models_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/policy_cache.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/policy_diff.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/preview.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/quickstart.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/redteam.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/scan_govern.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/scan_report.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/scm.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/sentinel.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/status_cmd.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/users.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/vault.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/watch.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_cli/witness.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/SOURCES.txt +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/dependency_links.txt +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/entry_points.txt +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/top_level.txt +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/setup.cfg +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_entitlements_display.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_evidence.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_framework_suggest.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_framework_test.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_hitl_cli.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_mcp_model_governance.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_policy_diff.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_quickstart.py +0 -0
- {iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/tests/test_vocabulary.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-security-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
|
|
@@ -15,13 +15,13 @@ 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.
|
|
19
|
-
Requires-Dist: iris-security-sdk>=0.2.
|
|
18
|
+
Requires-Dist: iris-security-core>=0.1.11
|
|
19
|
+
Requires-Dist: iris-security-sdk>=0.2.4
|
|
20
20
|
Requires-Dist: click>=8.1
|
|
21
21
|
Requires-Dist: rich>=13.0
|
|
22
22
|
Requires-Dist: pyyaml>=6.0
|
|
23
23
|
Provides-Extra: scm
|
|
24
|
-
Requires-Dist: iris-security-scm[all]>=0.
|
|
24
|
+
Requires-Dist: iris-security-scm[all]>=0.2.0; extra == "scm"
|
|
25
25
|
|
|
26
26
|
# iris-security-cli
|
|
27
27
|
|
|
@@ -10,6 +10,7 @@ import click
|
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
|
|
13
|
+
from iris_core.compliance.dynamic_loader import ComplianceRule, DynamicBundleLoader
|
|
13
14
|
from iris_core.entitlements import Entitlements, Feature
|
|
14
15
|
from iris_core.models.passport import AgentPassport
|
|
15
16
|
|
|
@@ -26,20 +27,60 @@ from iris_cli.framework_test import (
|
|
|
26
27
|
console = Console()
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def _evaluate_custom_rule(rule: ComplianceRule, passport: AgentPassport) -> str:
|
|
31
|
+
safe_globals = {"__builtins__": {}}
|
|
32
|
+
safe_locals = {
|
|
33
|
+
"passport": passport,
|
|
34
|
+
"hitl_config": getattr(passport, "hitl_config", None),
|
|
35
|
+
"cost_config": getattr(passport, "cost_config", None),
|
|
36
|
+
"bool": bool,
|
|
37
|
+
"len": len,
|
|
38
|
+
}
|
|
39
|
+
try:
|
|
40
|
+
value = eval(rule.check_expression, safe_globals, safe_locals)
|
|
41
|
+
except Exception:
|
|
42
|
+
return "FAIL"
|
|
43
|
+
return "PASS" if value else "FAIL"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _rule_source_label(loader: DynamicBundleLoader) -> str:
|
|
47
|
+
info = loader.registry_info()
|
|
48
|
+
if info.source == "live":
|
|
49
|
+
return "Live registry"
|
|
50
|
+
if info.source == "cache":
|
|
51
|
+
hours = info.cache_age_hours
|
|
52
|
+
if hours is not None and hours < 1:
|
|
53
|
+
return "Live registry (updated recently)"
|
|
54
|
+
if hours is not None:
|
|
55
|
+
return f"Live registry (updated {int(hours)} hours ago)"
|
|
56
|
+
return "Cached registry"
|
|
57
|
+
return "Bundled fallback"
|
|
58
|
+
|
|
59
|
+
|
|
29
60
|
def _render_certification_header(
|
|
30
61
|
framework: str,
|
|
31
62
|
agent_name: str,
|
|
32
63
|
result,
|
|
64
|
+
*,
|
|
65
|
+
loader: DynamicBundleLoader,
|
|
66
|
+
custom_rule_count: int,
|
|
33
67
|
) -> None:
|
|
68
|
+
bundle = loader.load_bundle(framework)
|
|
69
|
+
info = loader.registry_info()
|
|
34
70
|
framework_label = _framework_name(framework)
|
|
35
71
|
if framework == "colorado-ai-act":
|
|
36
72
|
framework_label = "Colorado AI Act (SB 26-189)"
|
|
37
73
|
|
|
74
|
+
version = bundle.metadata.get("current_version", info.registry_version)
|
|
75
|
+
law_name = bundle.metadata.get("law_name", framework_label)
|
|
76
|
+
|
|
38
77
|
console.print(Panel(
|
|
39
|
-
f"Agent:
|
|
40
|
-
f"Framework:
|
|
41
|
-
f"
|
|
42
|
-
f"
|
|
78
|
+
f"Agent: {agent_name}\n"
|
|
79
|
+
f"Framework: {law_name} v{version}\n"
|
|
80
|
+
f"Rule source: {_rule_source_label(loader)}\n"
|
|
81
|
+
f"Custom rules: {custom_rule_count} organization rule{'s' if custom_rule_count != 1 else ''} evaluated\n"
|
|
82
|
+
f"Generated: {datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')}\n"
|
|
83
|
+
f"Attested: IRIS · Commit: local",
|
|
43
84
|
title=f"IRIS Certification — {framework}",
|
|
44
85
|
style="blue",
|
|
45
86
|
))
|
|
@@ -63,25 +104,72 @@ def _render_certification_header(
|
|
|
63
104
|
)
|
|
64
105
|
|
|
65
106
|
|
|
107
|
+
def _render_custom_rules_section(
|
|
108
|
+
custom_rules: list[ComplianceRule],
|
|
109
|
+
passport: AgentPassport,
|
|
110
|
+
) -> None:
|
|
111
|
+
if not custom_rules:
|
|
112
|
+
return
|
|
113
|
+
console.print("\n[bold]ORGANIZATION POLICY[/bold]")
|
|
114
|
+
console.print("─" * 65)
|
|
115
|
+
for rule in custom_rules:
|
|
116
|
+
status = _evaluate_custom_rule(rule, passport)
|
|
117
|
+
icon = "[green]✓[/green]" if status == "PASS" else "[red]✗[/red]"
|
|
118
|
+
console.print(f"{icon} {rule.rule_id:<14} {rule.name}")
|
|
119
|
+
if status != "PASS" and rule.remediation_command:
|
|
120
|
+
console.print(f" Fix: {rule.remediation_command}")
|
|
121
|
+
|
|
122
|
+
|
|
66
123
|
@click.command("certify")
|
|
67
124
|
@click.option(
|
|
68
125
|
"--framework",
|
|
69
126
|
required=True,
|
|
70
|
-
type=click.Choice(sorted([
|
|
127
|
+
type=click.Choice(sorted([
|
|
128
|
+
"colorado-ai-act", "nist-ai-rmf", "fedramp-moderate", "hipaa", "soc2", "gdpr",
|
|
129
|
+
"nyc-ll144", "illinois-ai-video", "eu-ai-act", "ccpa-admt", "china-pipl",
|
|
130
|
+
])),
|
|
71
131
|
)
|
|
72
132
|
@click.option("--agent", "agent_name", required=True, help="Agent name under governance/agents")
|
|
73
133
|
@click.option("--format", "output_format", default="table", type=click.Choice(["table", "json", "markdown"]))
|
|
74
|
-
|
|
134
|
+
@click.option(
|
|
135
|
+
"--registry-version",
|
|
136
|
+
default=None,
|
|
137
|
+
help="Pin to a specific registry version for reproducible certifications",
|
|
138
|
+
)
|
|
139
|
+
@click.option("--dir", "governance_dir", type=click.Path(path_type=Path), default=None)
|
|
140
|
+
@click.option("--offline", is_flag=True, help="Use bundled registry only (no network refresh)")
|
|
141
|
+
def certify_cmd(
|
|
142
|
+
framework: str,
|
|
143
|
+
agent_name: str,
|
|
144
|
+
output_format: str,
|
|
145
|
+
registry_version: Optional[str],
|
|
146
|
+
governance_dir: Optional[Path],
|
|
147
|
+
offline: bool,
|
|
148
|
+
) -> None:
|
|
75
149
|
"""
|
|
76
150
|
Certify compliance readiness against a regulatory framework.
|
|
77
151
|
|
|
78
152
|
Alias: iris test
|
|
79
153
|
"""
|
|
80
|
-
|
|
154
|
+
gov_dir = governance_dir or Path.cwd() / "governance"
|
|
155
|
+
passport_path = gov_dir / "agents" / agent_name / "passport.yaml"
|
|
81
156
|
if not passport_path.exists():
|
|
82
157
|
raise click.ClickException(f"Passport not found: {passport_path}")
|
|
83
158
|
passport = AgentPassport.from_yaml(passport_path.read_text())
|
|
84
159
|
|
|
160
|
+
loader = DynamicBundleLoader(pinned_version=registry_version)
|
|
161
|
+
|
|
162
|
+
if not offline and not registry_version:
|
|
163
|
+
if not loader.is_cache_fresh():
|
|
164
|
+
console.print("[dim]Checking for regulatory updates...[/dim]")
|
|
165
|
+
updated = loader.refresh_registry()
|
|
166
|
+
if updated:
|
|
167
|
+
console.print("[green]✓ Regulatory intelligence updated[/green]")
|
|
168
|
+
console.print("[dim] Run: iris regulatory check for change summary[/dim]")
|
|
169
|
+
|
|
170
|
+
custom_rules = loader.load_custom_rules(gov_dir)
|
|
171
|
+
loader.get_effective_rules(framework, gov_dir)
|
|
172
|
+
|
|
85
173
|
ents = Entitlements()
|
|
86
174
|
has_pro = ents.has(Feature.CLI_TEST_FULL_REPORT)
|
|
87
175
|
|
|
@@ -97,13 +185,22 @@ def certify_cmd(framework: str, agent_name: str, output_format: str) -> None:
|
|
|
97
185
|
elif output_format == "markdown":
|
|
98
186
|
click.echo(_render_markdown(result, has_pro))
|
|
99
187
|
else:
|
|
100
|
-
_render_certification_header(
|
|
188
|
+
_render_certification_header(
|
|
189
|
+
framework, agent_name, result,
|
|
190
|
+
loader=loader,
|
|
191
|
+
custom_rule_count=len(custom_rules),
|
|
192
|
+
)
|
|
101
193
|
_render_table(result, has_pro)
|
|
102
|
-
|
|
194
|
+
_render_custom_rules_section(custom_rules, passport)
|
|
195
|
+
_render_response_configuration(framework, passport, loader=loader)
|
|
103
196
|
|
|
104
197
|
|
|
105
|
-
def _render_response_configuration(
|
|
106
|
-
|
|
198
|
+
def _render_response_configuration(
|
|
199
|
+
framework: str,
|
|
200
|
+
passport: AgentPassport,
|
|
201
|
+
*,
|
|
202
|
+
loader: Optional[DynamicBundleLoader] = None,
|
|
203
|
+
) -> None:
|
|
107
204
|
from iris_core.compliance.violation_response import (
|
|
108
205
|
get_effective_response,
|
|
109
206
|
rule_response_label,
|
|
@@ -111,7 +208,11 @@ def _render_response_configuration(framework: str, passport: AgentPassport) -> N
|
|
|
111
208
|
)
|
|
112
209
|
|
|
113
210
|
try:
|
|
114
|
-
|
|
211
|
+
if loader:
|
|
212
|
+
bundle = loader.load_bundle(framework).to_dict()
|
|
213
|
+
else:
|
|
214
|
+
from iris_core.compliance.framework_check import load_bundle_data
|
|
215
|
+
bundle = load_bundle_data(framework)
|
|
115
216
|
except ValueError:
|
|
116
217
|
return
|
|
117
218
|
rules = bundle.get("rules", [])
|
|
@@ -131,7 +232,7 @@ def _render_response_configuration(framework: str, passport: AgentPassport) -> N
|
|
|
131
232
|
if base == ViolationResponse.BLOCK:
|
|
132
233
|
line += " (unconditional — cannot be overridden)"
|
|
133
234
|
elif effective != default:
|
|
134
|
-
line +=
|
|
235
|
+
line += " (override active)"
|
|
135
236
|
else:
|
|
136
237
|
line += " (default)"
|
|
137
238
|
console.print(line)
|
|
@@ -44,6 +44,25 @@ CONVERSATIONAL_KEYWORDS = (
|
|
|
44
44
|
"chat bot",
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
+
EMPLOYMENT_KEYWORDS = (
|
|
48
|
+
"hiring",
|
|
49
|
+
"recruiting",
|
|
50
|
+
"recruitment",
|
|
51
|
+
"hr",
|
|
52
|
+
"ats",
|
|
53
|
+
"screening",
|
|
54
|
+
"employment",
|
|
55
|
+
"interview",
|
|
56
|
+
"resume",
|
|
57
|
+
"candidate",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
VIDEO_KEYWORDS = (
|
|
61
|
+
"video",
|
|
62
|
+
"interview",
|
|
63
|
+
"biometric",
|
|
64
|
+
)
|
|
65
|
+
|
|
47
66
|
Q1_CHOICES = [
|
|
48
67
|
"Makes decisions that affect individual people (hiring, loans, medical, etc.)",
|
|
49
68
|
"Automates business processes (summarization, classification, routing)",
|
|
@@ -282,6 +301,33 @@ def build_recommendations(answers: dict[str, Any]) -> list[Recommendation]:
|
|
|
282
301
|
or (q2 == Q2_CHOICES[4] and "Outside the US" in q8)
|
|
283
302
|
)
|
|
284
303
|
|
|
304
|
+
employment_domain = (
|
|
305
|
+
q1 == Q1_CHOICES[0]
|
|
306
|
+
or any(kw in q1.lower() for kw in EMPLOYMENT_KEYWORDS)
|
|
307
|
+
or any(kw in agent_description for kw in EMPLOYMENT_KEYWORDS)
|
|
308
|
+
or q2 in {Q2_CHOICES[4], Q2_CHOICES[6]}
|
|
309
|
+
)
|
|
310
|
+
illinois_video = (
|
|
311
|
+
employment_domain
|
|
312
|
+
and (
|
|
313
|
+
"Illinois" in q8
|
|
314
|
+
or "Other / multiple states" in q8
|
|
315
|
+
)
|
|
316
|
+
and (
|
|
317
|
+
any(kw in agent_description for kw in VIDEO_KEYWORDS)
|
|
318
|
+
or q4 == Q4_CHOICES[3]
|
|
319
|
+
or "video" in q1.lower()
|
|
320
|
+
or "interview" in q1.lower()
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
nyc_ll144 = (
|
|
324
|
+
employment_domain
|
|
325
|
+
and (
|
|
326
|
+
"New York" in q8
|
|
327
|
+
or "Other / multiple states" in q8
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
|
|
285
331
|
colorado_reason = (
|
|
286
332
|
"Your agent makes consequential decisions for Colorado users. "
|
|
287
333
|
"SB 26-189 applies (replaces SB 24-205, effective Jan. 1, 2027). "
|
|
@@ -419,6 +465,36 @@ def build_recommendations(answers: dict[str, Any]) -> list[Recommendation]:
|
|
|
419
465
|
),
|
|
420
466
|
command="iris compliance check --framework china-pipl" if china_pipl else None,
|
|
421
467
|
),
|
|
468
|
+
Recommendation(
|
|
469
|
+
framework="illinois-ai-video",
|
|
470
|
+
tier="PRO",
|
|
471
|
+
status="REQUIRED" if illinois_video else "NOT APPLICABLE",
|
|
472
|
+
reason=(
|
|
473
|
+
"Illinois AI Video Interview Act applies to AI analysis of video "
|
|
474
|
+
"interviews for Illinois-based positions. Consent must be logged "
|
|
475
|
+
"before any video analysis (820 ILCS 42)."
|
|
476
|
+
if illinois_video
|
|
477
|
+
else "No Illinois video interview AI scope detected."
|
|
478
|
+
),
|
|
479
|
+
command=(
|
|
480
|
+
"iris compliance check --framework illinois-ai-video"
|
|
481
|
+
if illinois_video
|
|
482
|
+
else None
|
|
483
|
+
),
|
|
484
|
+
),
|
|
485
|
+
Recommendation(
|
|
486
|
+
framework="nyc-ll144",
|
|
487
|
+
tier="PRO",
|
|
488
|
+
status="REQUIRED" if nyc_ll144 else "NOT APPLICABLE",
|
|
489
|
+
reason=(
|
|
490
|
+
"NYC Local Law 144 requires annual independent bias audits, public "
|
|
491
|
+
"disclosure, and candidate notice before using Automated Employment "
|
|
492
|
+
"Decision Tools (AEDTs) for NYC hiring."
|
|
493
|
+
if nyc_ll144
|
|
494
|
+
else "No NYC AEDT hiring scope detected."
|
|
495
|
+
),
|
|
496
|
+
command="iris compliance check --framework nyc-ll144" if nyc_ll144 else None,
|
|
497
|
+
),
|
|
422
498
|
]
|
|
423
499
|
return recommendations
|
|
424
500
|
|
|
@@ -50,6 +50,41 @@ def _format_effective_date(iso_date: str) -> str:
|
|
|
50
50
|
return iso_date
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def _render_rule_changes(bundle_id: str, old_version: str, new_version: str) -> None:
|
|
54
|
+
"""Show rule-level changes when registry versions differ."""
|
|
55
|
+
import json
|
|
56
|
+
|
|
57
|
+
from iris_core.compliance.dynamic_loader import DynamicBundleLoader, _bundled_registry_path
|
|
58
|
+
|
|
59
|
+
loader = DynamicBundleLoader()
|
|
60
|
+
cache_path = Path.home() / ".iris" / "registry-cache.json"
|
|
61
|
+
bundled = _bundled_registry_path()
|
|
62
|
+
|
|
63
|
+
old_registry: dict = {}
|
|
64
|
+
if cache_path.exists():
|
|
65
|
+
try:
|
|
66
|
+
old_registry = json.loads(cache_path.read_text())
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
elif bundled.exists():
|
|
70
|
+
old_registry = json.loads(bundled.read_text())
|
|
71
|
+
|
|
72
|
+
if not bundled.exists():
|
|
73
|
+
return
|
|
74
|
+
new_registry = json.loads(bundled.read_text())
|
|
75
|
+
changes = loader.diff_rules(bundle_id, old_registry, new_registry)
|
|
76
|
+
if not changes:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
console.print(f"\n [bold]REGULATORY UPDATE — {bundle_id} v{old_version} → v{new_version}[/bold]")
|
|
80
|
+
console.print(" [bold]RULE CHANGES:[/bold]")
|
|
81
|
+
for change in changes[:10]:
|
|
82
|
+
console.print(f" ● {change}")
|
|
83
|
+
console.print(
|
|
84
|
+
"\n Run: [bold]iris certify --agent <name>[/bold] to see updated status"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
53
88
|
def _render_check_output(
|
|
54
89
|
tracker: RegulatoryTracker,
|
|
55
90
|
updates: list,
|
|
@@ -101,6 +136,7 @@ def _render_check_output(
|
|
|
101
136
|
console.print(
|
|
102
137
|
f"Effective: {_format_effective_date(update.effective_date)}"
|
|
103
138
|
)
|
|
139
|
+
_render_rule_changes(update.bundle_id, update.current_installed_version, update.available_version)
|
|
104
140
|
console.print(" Action required:")
|
|
105
141
|
console.print(
|
|
106
142
|
" 1. Run: [bold]pip install --upgrade iris-security-sdk[/bold]"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-security-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: IRIS CLI — declare, compile, preview, enforce, witness, certify, sentinel
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
|
|
@@ -15,13 +15,13 @@ 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.
|
|
19
|
-
Requires-Dist: iris-security-sdk>=0.2.
|
|
18
|
+
Requires-Dist: iris-security-core>=0.1.11
|
|
19
|
+
Requires-Dist: iris-security-sdk>=0.2.4
|
|
20
20
|
Requires-Dist: click>=8.1
|
|
21
21
|
Requires-Dist: rich>=13.0
|
|
22
22
|
Requires-Dist: pyyaml>=6.0
|
|
23
23
|
Provides-Extra: scm
|
|
24
|
-
Requires-Dist: iris-security-scm[all]>=0.
|
|
24
|
+
Requires-Dist: iris-security-scm[all]>=0.2.0; extra == "scm"
|
|
25
25
|
|
|
26
26
|
# iris-security-cli
|
|
27
27
|
|
|
@@ -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.4"
|
|
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,15 +20,15 @@ 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.11",
|
|
24
|
+
"iris-security-sdk>=0.2.4",
|
|
25
25
|
"click>=8.1",
|
|
26
26
|
"rich>=13.0",
|
|
27
27
|
"pyyaml>=6.0",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[project.optional-dependencies]
|
|
31
|
-
scm = ["iris-security-scm[all]>=0.
|
|
31
|
+
scm = ["iris-security-scm[all]>=0.2.0"]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
34
34
|
Homepage = "https://github.com/gimartinb/iris-sdk"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{iris_security_cli-0.2.1 → iris_security_cli-0.2.4}/iris_security_cli.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|