iam-policy-validator 1.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. iam_policy_validator-1.14.0.dist-info/METADATA +782 -0
  2. iam_policy_validator-1.14.0.dist-info/RECORD +106 -0
  3. iam_policy_validator-1.14.0.dist-info/WHEEL +4 -0
  4. iam_policy_validator-1.14.0.dist-info/entry_points.txt +2 -0
  5. iam_policy_validator-1.14.0.dist-info/licenses/LICENSE +21 -0
  6. iam_validator/__init__.py +27 -0
  7. iam_validator/__main__.py +11 -0
  8. iam_validator/__version__.py +9 -0
  9. iam_validator/checks/__init__.py +45 -0
  10. iam_validator/checks/action_condition_enforcement.py +1442 -0
  11. iam_validator/checks/action_resource_matching.py +472 -0
  12. iam_validator/checks/action_validation.py +67 -0
  13. iam_validator/checks/condition_key_validation.py +88 -0
  14. iam_validator/checks/condition_type_mismatch.py +257 -0
  15. iam_validator/checks/full_wildcard.py +62 -0
  16. iam_validator/checks/mfa_condition_check.py +105 -0
  17. iam_validator/checks/policy_size.py +114 -0
  18. iam_validator/checks/policy_structure.py +556 -0
  19. iam_validator/checks/policy_type_validation.py +331 -0
  20. iam_validator/checks/principal_validation.py +708 -0
  21. iam_validator/checks/resource_validation.py +135 -0
  22. iam_validator/checks/sensitive_action.py +438 -0
  23. iam_validator/checks/service_wildcard.py +98 -0
  24. iam_validator/checks/set_operator_validation.py +153 -0
  25. iam_validator/checks/sid_uniqueness.py +146 -0
  26. iam_validator/checks/trust_policy_validation.py +509 -0
  27. iam_validator/checks/utils/__init__.py +17 -0
  28. iam_validator/checks/utils/action_parser.py +149 -0
  29. iam_validator/checks/utils/policy_level_checks.py +190 -0
  30. iam_validator/checks/utils/sensitive_action_matcher.py +293 -0
  31. iam_validator/checks/utils/wildcard_expansion.py +86 -0
  32. iam_validator/checks/wildcard_action.py +58 -0
  33. iam_validator/checks/wildcard_resource.py +374 -0
  34. iam_validator/commands/__init__.py +31 -0
  35. iam_validator/commands/analyze.py +549 -0
  36. iam_validator/commands/base.py +48 -0
  37. iam_validator/commands/cache.py +393 -0
  38. iam_validator/commands/completion.py +471 -0
  39. iam_validator/commands/download_services.py +255 -0
  40. iam_validator/commands/post_to_pr.py +86 -0
  41. iam_validator/commands/query.py +485 -0
  42. iam_validator/commands/validate.py +830 -0
  43. iam_validator/core/__init__.py +13 -0
  44. iam_validator/core/access_analyzer.py +671 -0
  45. iam_validator/core/access_analyzer_report.py +640 -0
  46. iam_validator/core/aws_fetcher.py +29 -0
  47. iam_validator/core/aws_service/__init__.py +21 -0
  48. iam_validator/core/aws_service/cache.py +108 -0
  49. iam_validator/core/aws_service/client.py +205 -0
  50. iam_validator/core/aws_service/fetcher.py +641 -0
  51. iam_validator/core/aws_service/parsers.py +149 -0
  52. iam_validator/core/aws_service/patterns.py +51 -0
  53. iam_validator/core/aws_service/storage.py +291 -0
  54. iam_validator/core/aws_service/validators.py +380 -0
  55. iam_validator/core/check_registry.py +679 -0
  56. iam_validator/core/cli.py +134 -0
  57. iam_validator/core/codeowners.py +245 -0
  58. iam_validator/core/condition_validators.py +626 -0
  59. iam_validator/core/config/__init__.py +81 -0
  60. iam_validator/core/config/aws_api.py +35 -0
  61. iam_validator/core/config/aws_global_conditions.py +160 -0
  62. iam_validator/core/config/category_suggestions.py +181 -0
  63. iam_validator/core/config/check_documentation.py +390 -0
  64. iam_validator/core/config/condition_requirements.py +258 -0
  65. iam_validator/core/config/config_loader.py +670 -0
  66. iam_validator/core/config/defaults.py +739 -0
  67. iam_validator/core/config/principal_requirements.py +421 -0
  68. iam_validator/core/config/sensitive_actions.py +672 -0
  69. iam_validator/core/config/service_principals.py +132 -0
  70. iam_validator/core/config/wildcards.py +127 -0
  71. iam_validator/core/constants.py +149 -0
  72. iam_validator/core/diff_parser.py +325 -0
  73. iam_validator/core/finding_fingerprint.py +131 -0
  74. iam_validator/core/formatters/__init__.py +27 -0
  75. iam_validator/core/formatters/base.py +147 -0
  76. iam_validator/core/formatters/console.py +68 -0
  77. iam_validator/core/formatters/csv.py +171 -0
  78. iam_validator/core/formatters/enhanced.py +481 -0
  79. iam_validator/core/formatters/html.py +672 -0
  80. iam_validator/core/formatters/json.py +33 -0
  81. iam_validator/core/formatters/markdown.py +64 -0
  82. iam_validator/core/formatters/sarif.py +251 -0
  83. iam_validator/core/ignore_patterns.py +297 -0
  84. iam_validator/core/ignore_processor.py +309 -0
  85. iam_validator/core/ignored_findings.py +400 -0
  86. iam_validator/core/label_manager.py +197 -0
  87. iam_validator/core/models.py +404 -0
  88. iam_validator/core/policy_checks.py +220 -0
  89. iam_validator/core/policy_loader.py +785 -0
  90. iam_validator/core/pr_commenter.py +780 -0
  91. iam_validator/core/report.py +942 -0
  92. iam_validator/integrations/__init__.py +28 -0
  93. iam_validator/integrations/github_integration.py +1821 -0
  94. iam_validator/integrations/ms_teams.py +442 -0
  95. iam_validator/sdk/__init__.py +220 -0
  96. iam_validator/sdk/arn_matching.py +382 -0
  97. iam_validator/sdk/context.py +222 -0
  98. iam_validator/sdk/exceptions.py +48 -0
  99. iam_validator/sdk/helpers.py +177 -0
  100. iam_validator/sdk/policy_utils.py +451 -0
  101. iam_validator/sdk/query_utils.py +454 -0
  102. iam_validator/sdk/shortcuts.py +283 -0
  103. iam_validator/utils/__init__.py +35 -0
  104. iam_validator/utils/cache.py +105 -0
  105. iam_validator/utils/regex.py +205 -0
  106. iam_validator/utils/terminal.py +22 -0
@@ -0,0 +1,442 @@
1
+ """Microsoft Teams Integration for IAM Policy Validator.
2
+
3
+ This module provides functionality to send notifications to MS Teams channels
4
+ via incoming webhooks, including validation reports and alerts.
5
+ """
6
+
7
+ import logging
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from typing import Any
11
+
12
+ import httpx
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class MessageType(str, Enum):
18
+ """MS Teams message types."""
19
+
20
+ INFO = "info"
21
+ SUCCESS = "success"
22
+ WARNING = "warning"
23
+ ERROR = "error"
24
+
25
+
26
+ class CardTheme(str, Enum):
27
+ """MS Teams adaptive card accent colors."""
28
+
29
+ DEFAULT = "default"
30
+ DARK = "dark"
31
+ LIGHT = "light"
32
+ ACCENT = "accent"
33
+ ATTENTION = "attention" # Red for errors
34
+ GOOD = "good" # Green for success
35
+ WARNING = "warning" # Yellow for warnings
36
+
37
+
38
+ @dataclass
39
+ class TeamsMessage:
40
+ """Represents a message to send to MS Teams."""
41
+
42
+ title: str
43
+ message: str
44
+ message_type: MessageType = MessageType.INFO
45
+ facts: list[dict[str, str]] | None = None
46
+ actions: list[dict[str, Any]] | None = None
47
+ sections: list[dict[str, Any]] | None = None
48
+
49
+
50
+ class MSTeamsIntegration:
51
+ """Handles Microsoft Teams notifications via webhooks.
52
+
53
+ This class provides methods to:
54
+ - Send simple text notifications
55
+ - Send adaptive cards with rich formatting
56
+ - Send validation reports as formatted messages
57
+ - Send alerts for critical findings
58
+ """
59
+
60
+ def __init__(self, webhook_url: str | None = None):
61
+ """Initialize MS Teams integration.
62
+
63
+ Args:
64
+ webhook_url: MS Teams incoming webhook URL
65
+ """
66
+ self.webhook_url = self._validate_webhook_url(webhook_url)
67
+ self._client: httpx.AsyncClient | None = None
68
+
69
+ def _validate_webhook_url(self, webhook_url: str | None) -> str | None:
70
+ """Validate and sanitize webhook URL.
71
+
72
+ Args:
73
+ webhook_url: Webhook URL to validate
74
+
75
+ Returns:
76
+ Validated URL or None
77
+ """
78
+ if webhook_url is None:
79
+ return None
80
+
81
+ if not isinstance(webhook_url, str) or not webhook_url.strip():
82
+ logger.warning("Invalid webhook URL provided (empty or non-string)")
83
+ return None
84
+
85
+ webhook_url = webhook_url.strip()
86
+
87
+ # Must be HTTPS for security
88
+ if not webhook_url.startswith("https://"):
89
+ logger.warning(
90
+ f"Webhook URL must use HTTPS: {webhook_url[:50]}... "
91
+ "(MS Teams webhooks require HTTPS)"
92
+ )
93
+ return None
94
+
95
+ # Basic URL validation - should contain office.com or webhook.office365.com
96
+ if "webhook.office" not in webhook_url.lower():
97
+ logger.warning(
98
+ f"Webhook URL doesn't appear to be a valid MS Teams webhook: {webhook_url[:50]}..."
99
+ )
100
+ # Still allow it, but warn
101
+
102
+ # Length check to prevent extremely long URLs
103
+ if len(webhook_url) > 2048:
104
+ logger.warning(
105
+ f"Webhook URL is unusually long ({len(webhook_url)} chars), may be invalid"
106
+ )
107
+ return None
108
+
109
+ # Ensure only ASCII characters
110
+ if not webhook_url.isascii():
111
+ logger.warning("Webhook URL contains non-ASCII characters")
112
+ return None
113
+
114
+ return webhook_url
115
+
116
+ async def __aenter__(self) -> "MSTeamsIntegration":
117
+ """Async context manager entry."""
118
+ self._client = httpx.AsyncClient(timeout=httpx.Timeout(30.0))
119
+ return self
120
+
121
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
122
+ """Async context manager exit."""
123
+ del exc_type, exc_val, exc_tb # Unused
124
+ if self._client:
125
+ await self._client.aclose()
126
+ self._client = None
127
+
128
+ def is_configured(self) -> bool:
129
+ """Check if MS Teams integration is configured.
130
+
131
+ Returns:
132
+ True if webhook URL is set
133
+ """
134
+ return bool(self.webhook_url)
135
+
136
+ def _get_card_color(self, message_type: MessageType) -> str:
137
+ """Get the accent color for the card based on message type."""
138
+ color_mapping = {
139
+ MessageType.INFO: "0078D4", # Blue
140
+ MessageType.SUCCESS: "107C10", # Green
141
+ MessageType.WARNING: "FFB900", # Yellow
142
+ MessageType.ERROR: "D83B01", # Red
143
+ }
144
+ return color_mapping.get(message_type, "0078D4")
145
+
146
+ def _create_adaptive_card(self, message: TeamsMessage) -> dict[str, Any]:
147
+ """Create an adaptive card for MS Teams.
148
+
149
+ Args:
150
+ message: TeamsMessage object with content
151
+
152
+ Returns:
153
+ Adaptive card payload
154
+ """
155
+ card = {
156
+ "type": "message",
157
+ "attachments": [
158
+ {
159
+ "contentType": "application/vnd.microsoft.card.adaptive",
160
+ "contentUrl": None,
161
+ "content": {
162
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
163
+ "type": "AdaptiveCard",
164
+ "version": "1.4",
165
+ "body": [
166
+ {
167
+ "type": "Container",
168
+ "style": "emphasis",
169
+ "items": [
170
+ {
171
+ "type": "TextBlock",
172
+ "text": message.title,
173
+ "weight": "bolder",
174
+ "size": "large",
175
+ "wrap": True,
176
+ }
177
+ ],
178
+ },
179
+ {
180
+ "type": "Container",
181
+ "items": [
182
+ {
183
+ "type": "TextBlock",
184
+ "text": message.message,
185
+ "wrap": True,
186
+ }
187
+ ],
188
+ },
189
+ ],
190
+ },
191
+ }
192
+ ],
193
+ }
194
+
195
+ # Add facts if provided
196
+ if message.facts:
197
+ fact_set = {
198
+ "type": "FactSet",
199
+ "facts": [{"title": f["title"], "value": f["value"]} for f in message.facts],
200
+ }
201
+ card["attachments"][0]["content"]["body"].append(fact_set)
202
+
203
+ # Add custom sections if provided
204
+ if message.sections:
205
+ for section in message.sections:
206
+ card["attachments"][0]["content"]["body"].append(section)
207
+
208
+ # Add actions if provided
209
+ if message.actions:
210
+ card["attachments"][0]["content"]["actions"] = message.actions
211
+
212
+ return card
213
+
214
+ def _create_simple_card(self, title: str, text: str, theme_color: str) -> dict[str, Any]:
215
+ """Create a simple message card (legacy format).
216
+
217
+ Args:
218
+ title: Card title
219
+ text: Card text (markdown supported)
220
+ theme_color: Hex color for the card accent
221
+
222
+ Returns:
223
+ Message card payload
224
+ """
225
+ return {
226
+ "@type": "MessageCard",
227
+ "@context": "https://schema.org/extensions",
228
+ "themeColor": theme_color,
229
+ "title": title,
230
+ "text": text,
231
+ }
232
+
233
+ async def send_message(self, message: TeamsMessage, use_adaptive_card: bool = True) -> bool:
234
+ """Send a message to MS Teams.
235
+
236
+ Args:
237
+ message: TeamsMessage object to send
238
+ use_adaptive_card: If True, use adaptive card format; else use legacy format
239
+
240
+ Returns:
241
+ True if successful, False otherwise
242
+ """
243
+ if not self.is_configured():
244
+ logger.warning("MS Teams integration not configured (no webhook URL)")
245
+ return False
246
+
247
+ # Type safety: webhook_url is guaranteed to be str here due to is_configured() check
248
+ if self.webhook_url is None:
249
+ logger.error("Webhook URL is None despite configuration check")
250
+ return False
251
+
252
+ try:
253
+ if use_adaptive_card:
254
+ payload = self._create_adaptive_card(message)
255
+ else:
256
+ color = self._get_card_color(message.message_type)
257
+ payload = self._create_simple_card(message.title, message.message, color)
258
+
259
+ if self._client:
260
+ response = await self._client.post(self.webhook_url, json=payload, timeout=30.0)
261
+ else:
262
+ async with httpx.AsyncClient(timeout=httpx.Timeout(30.0)) as client:
263
+ response = await client.post(self.webhook_url, json=payload)
264
+
265
+ response.raise_for_status()
266
+ logger.info(f"Successfully sent message to MS Teams: {message.title}")
267
+ return True
268
+
269
+ except httpx.HTTPStatusError as e:
270
+ logger.error(f"HTTP error sending to MS Teams: {e.response.status_code}")
271
+ return False
272
+ except Exception as e:
273
+ logger.error(f"Failed to send message to MS Teams: {e}")
274
+ return False
275
+
276
+ async def send_validation_report(
277
+ self,
278
+ report_title: str,
279
+ total_issues: int,
280
+ errors: int,
281
+ warnings: int,
282
+ suggestions: int,
283
+ policy_files: list[str],
284
+ report_url: str | None = None,
285
+ ) -> bool:
286
+ """Send a validation report to MS Teams.
287
+
288
+ Args:
289
+ report_title: Title of the report
290
+ total_issues: Total number of issues found
291
+ errors: Number of errors
292
+ warnings: Number of warnings
293
+ suggestions: Number of suggestions
294
+ policy_files: List of policy files validated
295
+ report_url: Optional URL to full report
296
+
297
+ Returns:
298
+ True if successful, False otherwise
299
+ """
300
+ # Determine message type based on findings
301
+ if errors > 0:
302
+ message_type = MessageType.ERROR
303
+ status = "❌ Validation Failed"
304
+ elif warnings > 0:
305
+ message_type = MessageType.WARNING
306
+ status = "⚠️ Validation Passed with Warnings"
307
+ else:
308
+ message_type = MessageType.SUCCESS
309
+ status = "✅ Validation Passed"
310
+
311
+ facts = [
312
+ {"title": "Status", "value": status},
313
+ {"title": "Total Issues", "value": str(total_issues)},
314
+ {"title": "Errors", "value": str(errors)},
315
+ {"title": "Warnings", "value": str(warnings)},
316
+ {"title": "Suggestions", "value": str(suggestions)},
317
+ {"title": "Files Validated", "value": str(len(policy_files))},
318
+ ]
319
+
320
+ actions = []
321
+ if report_url:
322
+ actions.append(
323
+ {
324
+ "type": "Action.OpenUrl",
325
+ "title": "View Full Report",
326
+ "url": report_url,
327
+ }
328
+ )
329
+
330
+ message = TeamsMessage(
331
+ title=report_title,
332
+ message=f"IAM Policy validation completed for {len(policy_files)} file(s).",
333
+ message_type=message_type,
334
+ facts=facts,
335
+ actions=actions,
336
+ )
337
+
338
+ return await self.send_message(message)
339
+
340
+ async def send_pr_notification(
341
+ self,
342
+ pr_number: int,
343
+ pr_title: str,
344
+ pr_url: str,
345
+ validation_passed: bool,
346
+ issue_summary: dict[str, int],
347
+ ) -> bool:
348
+ """Send a PR validation notification to MS Teams.
349
+
350
+ Args:
351
+ pr_number: Pull request number
352
+ pr_title: Pull request title
353
+ pr_url: URL to the PR
354
+ validation_passed: Whether validation passed
355
+ issue_summary: Dictionary with issue counts by severity
356
+
357
+ Returns:
358
+ True if successful, False otherwise
359
+ """
360
+ if validation_passed:
361
+ message_type = MessageType.SUCCESS
362
+ status = "✅ PR validation passed"
363
+ else:
364
+ message_type = MessageType.ERROR
365
+ status = "❌ PR validation failed"
366
+
367
+ facts = [
368
+ {"title": "PR Number", "value": f"#{pr_number}"},
369
+ {"title": "Status", "value": status},
370
+ ]
371
+
372
+ # Add issue counts
373
+ for severity, count in issue_summary.items():
374
+ if count > 0:
375
+ facts.append({"title": severity.capitalize(), "value": str(count)})
376
+
377
+ actions = [
378
+ {
379
+ "type": "Action.OpenUrl",
380
+ "title": "View Pull Request",
381
+ "url": pr_url,
382
+ }
383
+ ]
384
+
385
+ message = TeamsMessage(
386
+ title=f"PR #{pr_number}: {pr_title}",
387
+ message="IAM Policy validation completed for pull request.",
388
+ message_type=message_type,
389
+ facts=facts,
390
+ actions=actions,
391
+ )
392
+
393
+ return await self.send_message(message)
394
+
395
+ async def send_alert(
396
+ self,
397
+ title: str,
398
+ message: str,
399
+ severity: MessageType = MessageType.WARNING,
400
+ details: list[str] | None = None,
401
+ ) -> bool:
402
+ """Send an alert notification to MS Teams.
403
+
404
+ Args:
405
+ title: Alert title
406
+ message: Alert message
407
+ severity: Alert severity level
408
+ details: Optional list of detail items
409
+
410
+ Returns:
411
+ True if successful, False otherwise
412
+ """
413
+ sections = []
414
+ if details:
415
+ detail_text = "\n".join([f"• {detail}" for detail in details])
416
+ sections.append(
417
+ {
418
+ "type": "Container",
419
+ "items": [
420
+ {
421
+ "type": "TextBlock",
422
+ "text": "Details:",
423
+ "weight": "bolder",
424
+ },
425
+ {
426
+ "type": "TextBlock",
427
+ "text": detail_text,
428
+ "wrap": True,
429
+ "spacing": "small",
430
+ },
431
+ ],
432
+ }
433
+ )
434
+
435
+ teams_message = TeamsMessage(
436
+ title=title,
437
+ message=message,
438
+ message_type=severity,
439
+ sections=sections,
440
+ )
441
+
442
+ return await self.send_message(teams_message)
@@ -0,0 +1,220 @@
1
+ """
2
+ IAM Policy Validator SDK - Public API for library usage.
3
+
4
+ This module provides the complete public API for using IAM Policy Validator
5
+ as a Python library. It exposes both high-level convenience functions and
6
+ low-level components for custom integrations.
7
+
8
+ Quick Start:
9
+ Basic validation:
10
+ >>> from iam_validator.sdk import validate_file
11
+ >>> result = await validate_file("policy.json")
12
+ >>> print(f"Valid: {result.is_valid}")
13
+
14
+ With context manager:
15
+ >>> from iam_validator.sdk import validator
16
+ >>> async with validator() as v:
17
+ ... result = await v.validate_file("policy.json")
18
+ ... v.generate_report([result])
19
+
20
+ Policy manipulation:
21
+ >>> from iam_validator.sdk import parse_policy, get_policy_summary
22
+ >>> policy = parse_policy(policy_json)
23
+ >>> summary = get_policy_summary(policy)
24
+ >>> print(f"Actions: {summary['action_count']}")
25
+
26
+ Query AWS service definitions:
27
+ >>> from iam_validator.sdk import AWSServiceFetcher, query_actions, query_arn_formats
28
+ >>> async with AWSServiceFetcher() as fetcher:
29
+ ... # Query all S3 write actions
30
+ ... write_actions = await query_actions(fetcher, "s3", access_level="write")
31
+ ... # Get ARN formats for S3
32
+ ... arns = await query_arn_formats(fetcher, "s3")
33
+
34
+ Custom check development:
35
+ >>> from iam_validator.sdk import PolicyCheck, CheckHelper
36
+ >>> class MyCheck(PolicyCheck):
37
+ ... @property
38
+ ... def check_id(self) -> str:
39
+ ... return "my_check"
40
+ ... async def execute(self, statement, idx, fetcher, config):
41
+ ... helper = CheckHelper(fetcher)
42
+ ... # Use helper.arn_matches(), helper.create_issue(), etc.
43
+ ... return []
44
+ """
45
+
46
+ # === High-level validation functions (shortcuts) ===
47
+ # === AWS utilities ===
48
+ from iam_validator.core.aws_service import AWSServiceFetcher
49
+
50
+ # === Core validation components (for advanced usage) ===
51
+ from iam_validator.core.check_registry import CheckRegistry, PolicyCheck
52
+
53
+ # === ValidatorConfiguration ===
54
+ from iam_validator.core.config.config_loader import ValidatorConfig, load_validator_config
55
+
56
+ # === Reporting ===
57
+ from iam_validator.core.formatters.csv import CSVFormatter
58
+ from iam_validator.core.formatters.html import HTMLFormatter
59
+ from iam_validator.core.formatters.json import JSONFormatter
60
+ from iam_validator.core.formatters.markdown import MarkdownFormatter
61
+ from iam_validator.core.formatters.sarif import SARIFFormatter
62
+
63
+ # === Models (for type hints and inspection) ===
64
+ from iam_validator.core.models import (
65
+ IAMPolicy,
66
+ PolicyValidationResult,
67
+ Statement,
68
+ ValidationIssue,
69
+ )
70
+ from iam_validator.core.policy_checks import validate_policies
71
+ from iam_validator.core.policy_loader import PolicyLoader
72
+ from iam_validator.core.report import ReportGenerator
73
+
74
+ # === ARN matching utilities ===
75
+ from iam_validator.sdk.arn_matching import (
76
+ arn_matches,
77
+ arn_strictly_valid,
78
+ convert_aws_pattern_to_wildcard,
79
+ is_glob_match,
80
+ )
81
+
82
+ # === Context managers ===
83
+ from iam_validator.sdk.context import (
84
+ ValidationContext,
85
+ validator,
86
+ validator_from_config,
87
+ )
88
+
89
+ # === Public exceptions ===
90
+ from iam_validator.sdk.exceptions import (
91
+ AWSServiceError,
92
+ ConfigurationError,
93
+ IAMValidatorError,
94
+ InvalidPolicyFormatError,
95
+ PolicyLoadError,
96
+ PolicyValidationError,
97
+ UnsupportedPolicyTypeError,
98
+ )
99
+
100
+ # === Custom check development ===
101
+ from iam_validator.sdk.helpers import CheckHelper, expand_actions
102
+
103
+ # === Policy manipulation utilities ===
104
+ from iam_validator.sdk.policy_utils import (
105
+ extract_actions,
106
+ extract_condition_keys,
107
+ extract_resources,
108
+ find_statements_with_action,
109
+ find_statements_with_resource,
110
+ get_policy_summary,
111
+ has_public_access,
112
+ is_resource_policy,
113
+ merge_policies,
114
+ normalize_policy,
115
+ parse_policy,
116
+ policy_to_dict,
117
+ policy_to_json,
118
+ )
119
+
120
+ # === Query utilities (AWS service definition queries) ===
121
+ from iam_validator.sdk.query_utils import (
122
+ get_actions_by_access_level,
123
+ get_actions_supporting_condition,
124
+ get_wildcard_only_actions,
125
+ query_action_details,
126
+ query_actions,
127
+ query_arn_format,
128
+ query_arn_formats,
129
+ query_arn_types,
130
+ query_condition_key,
131
+ query_condition_keys,
132
+ )
133
+ from iam_validator.sdk.shortcuts import (
134
+ count_issues_by_severity,
135
+ get_issues,
136
+ quick_validate,
137
+ validate_directory,
138
+ validate_file,
139
+ validate_json,
140
+ )
141
+
142
+ __all__ = [
143
+ # === High-level shortcuts ===
144
+ "validate_file",
145
+ "validate_directory",
146
+ "validate_json",
147
+ "quick_validate",
148
+ "get_issues",
149
+ "count_issues_by_severity",
150
+ # === Context managers ===
151
+ "validator",
152
+ "validator_from_config",
153
+ "ValidationContext",
154
+ # === Policy utilities ===
155
+ "parse_policy",
156
+ "normalize_policy",
157
+ "extract_actions",
158
+ "extract_resources",
159
+ "extract_condition_keys",
160
+ "find_statements_with_action",
161
+ "find_statements_with_resource",
162
+ "merge_policies",
163
+ "get_policy_summary",
164
+ "policy_to_json",
165
+ "policy_to_dict",
166
+ "is_resource_policy",
167
+ "has_public_access",
168
+ # === Query utilities ===
169
+ "query_actions",
170
+ "query_action_details",
171
+ "query_arn_formats",
172
+ "query_arn_types",
173
+ "query_arn_format",
174
+ "query_condition_keys",
175
+ "query_condition_key",
176
+ "get_actions_by_access_level",
177
+ "get_wildcard_only_actions",
178
+ "get_actions_supporting_condition",
179
+ # === ARN utilities ===
180
+ "arn_matches",
181
+ "arn_strictly_valid",
182
+ "is_glob_match",
183
+ "convert_aws_pattern_to_wildcard",
184
+ # === Custom check development ===
185
+ "PolicyCheck",
186
+ "CheckRegistry",
187
+ "CheckHelper",
188
+ "expand_actions",
189
+ # === Core validation (advanced) ===
190
+ "validate_policies",
191
+ "PolicyLoader",
192
+ # === Reporting ===
193
+ "ReportGenerator",
194
+ "JSONFormatter",
195
+ "HTMLFormatter",
196
+ "CSVFormatter",
197
+ "MarkdownFormatter",
198
+ "SARIFFormatter",
199
+ # === Models ===
200
+ "ValidationIssue",
201
+ "PolicyValidationResult",
202
+ "IAMPolicy",
203
+ "Statement",
204
+ # === ValidatorConfiguration ===
205
+ "ValidatorConfig",
206
+ "load_validator_config",
207
+ # === AWS utilities ===
208
+ "AWSServiceFetcher",
209
+ # === Exceptions ===
210
+ "IAMValidatorError",
211
+ "PolicyLoadError",
212
+ "PolicyValidationError",
213
+ "ConfigurationError",
214
+ "AWSServiceError",
215
+ "InvalidPolicyFormatError",
216
+ "UnsupportedPolicyTypeError",
217
+ ]
218
+
219
+ # SDK version
220
+ __version__ = "0.1.0"