iam-policy-validator 1.0.4__py3-none-any.whl → 1.1.1__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.

Potentially problematic release.


This version of iam-policy-validator might be problematic. Click here for more details.

Files changed (34) hide show
  1. {iam_policy_validator-1.0.4.dist-info → iam_policy_validator-1.1.1.dist-info}/METADATA +88 -10
  2. iam_policy_validator-1.1.1.dist-info/RECORD +53 -0
  3. iam_validator/__version__.py +1 -1
  4. iam_validator/checks/__init__.py +2 -0
  5. iam_validator/checks/action_condition_enforcement.py +112 -28
  6. iam_validator/checks/action_resource_constraint.py +151 -0
  7. iam_validator/checks/action_validation.py +18 -138
  8. iam_validator/checks/security_best_practices.py +241 -400
  9. iam_validator/checks/utils/__init__.py +1 -0
  10. iam_validator/checks/utils/policy_level_checks.py +143 -0
  11. iam_validator/checks/utils/sensitive_action_matcher.py +252 -0
  12. iam_validator/checks/utils/wildcard_expansion.py +89 -0
  13. iam_validator/commands/__init__.py +3 -1
  14. iam_validator/commands/cache.py +402 -0
  15. iam_validator/commands/validate.py +7 -5
  16. iam_validator/core/access_analyzer_report.py +2 -1
  17. iam_validator/core/aws_fetcher.py +79 -19
  18. iam_validator/core/check_registry.py +3 -0
  19. iam_validator/core/cli.py +1 -1
  20. iam_validator/core/config_loader.py +40 -3
  21. iam_validator/core/defaults.py +334 -0
  22. iam_validator/core/formatters/__init__.py +2 -0
  23. iam_validator/core/formatters/console.py +44 -7
  24. iam_validator/core/formatters/csv.py +7 -2
  25. iam_validator/core/formatters/enhanced.py +433 -0
  26. iam_validator/core/formatters/html.py +127 -37
  27. iam_validator/core/formatters/markdown.py +10 -2
  28. iam_validator/core/models.py +30 -6
  29. iam_validator/core/policy_checks.py +21 -2
  30. iam_validator/core/report.py +112 -26
  31. iam_policy_validator-1.0.4.dist-info/RECORD +0 -45
  32. {iam_policy_validator-1.0.4.dist-info → iam_policy_validator-1.1.1.dist-info}/WHEEL +0 -0
  33. {iam_policy_validator-1.0.4.dist-info → iam_policy_validator-1.1.1.dist-info}/entry_points.txt +0 -0
  34. {iam_policy_validator-1.0.4.dist-info → iam_policy_validator-1.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -43,10 +43,18 @@ class MarkdownFormatter(OutputFormatter):
43
43
 
44
44
  output = [
45
45
  "# IAM Policy Validation Report\n",
46
+ "## Summary",
46
47
  f"**Total Policies:** {report.total_policies}",
47
- f"**Valid Policies:** {report.valid_policies}",
48
- f"**Invalid Policies:** {report.invalid_policies}",
48
+ f"**Valid (IAM):** {report.valid_policies}",
49
+ f"**Invalid (IAM):** {report.invalid_policies}",
50
+ f"**Security Findings:** {report.policies_with_security_issues} ⚠️",
51
+ "",
52
+ "## Issue Breakdown",
49
53
  f"**Total Issues:** {report.total_issues}",
54
+ f"**Validity Issues:** {report.validity_issues} (error/warning/info)",
55
+ f"**Security Issues:** {report.security_issues} (critical/high/medium/low)",
56
+ "",
57
+ "## Legacy Severity Counts",
50
58
  f"**Errors:** {errors}",
51
59
  f"**Warnings:** {warnings}",
52
60
  f"**Info:** {infos}\n",
@@ -251,14 +251,38 @@ class ValidationReport(BaseModel):
251
251
 
252
252
  total_policies: int
253
253
  valid_policies: int
254
- invalid_policies: int
254
+ invalid_policies: int # Policies with IAM validity issues (error/warning)
255
+ policies_with_security_issues: int = (
256
+ 0 # Policies with security findings (critical/high/medium/low)
257
+ )
255
258
  total_issues: int
259
+ validity_issues: int = 0 # Count of IAM validity issues (error/warning/info)
260
+ security_issues: int = 0 # Count of security issues (critical/high/medium/low)
256
261
  results: list[PolicyValidationResult] = Field(default_factory=list)
257
262
 
258
263
  def get_summary(self) -> str:
259
264
  """Generate a human-readable summary."""
260
- return (
261
- f"Validated {self.total_policies} policies: "
262
- f"{self.valid_policies} valid, {self.invalid_policies} invalid, "
263
- f"{self.total_issues} total issues found"
264
- )
265
+ parts = []
266
+ parts.append(f"Validated {self.total_policies} policies:")
267
+
268
+ # Always show valid/invalid counts
269
+ parts.append(f"{self.valid_policies} valid")
270
+
271
+ if self.invalid_policies > 0:
272
+ parts.append(f"{self.invalid_policies} invalid (IAM validity)")
273
+
274
+ if self.policies_with_security_issues > 0:
275
+ parts.append(f"{self.policies_with_security_issues} with security findings")
276
+
277
+ parts.append(f"{self.total_issues} total issues")
278
+
279
+ # Show breakdown if there are issues
280
+ if self.total_issues > 0 and (self.validity_issues > 0 or self.security_issues > 0):
281
+ breakdown_parts = []
282
+ if self.validity_issues > 0:
283
+ breakdown_parts.append(f"{self.validity_issues} validity")
284
+ if self.security_issues > 0:
285
+ breakdown_parts.append(f"{self.security_issues} security")
286
+ parts.append(f"({', '.join(breakdown_parts)})")
287
+
288
+ return " ".join(parts)
@@ -474,7 +474,18 @@ async def validate_policies(
474
474
  """
475
475
  if not use_registry:
476
476
  # Legacy path - use old PolicyValidator
477
- async with AWSServiceFetcher() as fetcher:
477
+ # Load config for cache settings even in legacy mode
478
+ from iam_validator.core.config_loader import ConfigLoader
479
+
480
+ config = ConfigLoader.load_config(explicit_path=config_path, allow_missing=True)
481
+ cache_enabled = config.get_setting("cache_enabled", True)
482
+ cache_ttl_hours = config.get_setting("cache_ttl_hours", 168)
483
+ cache_directory = config.get_setting("cache_directory", None)
484
+ cache_ttl_seconds = cache_ttl_hours * 3600
485
+
486
+ async with AWSServiceFetcher(
487
+ enable_cache=cache_enabled, cache_ttl=cache_ttl_seconds, cache_dir=cache_directory
488
+ ) as fetcher:
478
489
  validator = PolicyValidator(fetcher)
479
490
 
480
491
  tasks = [validator.validate_policy(policy, file_path) for file_path, policy in policies]
@@ -530,8 +541,16 @@ async def validate_policies(
530
541
  # Get fail_on_severity setting from config
531
542
  fail_on_severities = config.get_setting("fail_on_severity", ["error"])
532
543
 
544
+ # Get cache settings from config
545
+ cache_enabled = config.get_setting("cache_enabled", True)
546
+ cache_ttl_hours = config.get_setting("cache_ttl_hours", 168) # 7 days default
547
+ cache_directory = config.get_setting("cache_directory", None)
548
+ cache_ttl_seconds = cache_ttl_hours * 3600
549
+
533
550
  # Validate policies using registry
534
- async with AWSServiceFetcher() as fetcher:
551
+ async with AWSServiceFetcher(
552
+ enable_cache=cache_enabled, cache_ttl=cache_ttl_seconds, cache_dir=cache_directory
553
+ ) as fetcher:
535
554
  tasks = [
536
555
  _validate_policy_with_registry(policy, file_path, registry, fetcher, fail_on_severities)
537
556
  for file_path, policy in policies
@@ -11,9 +11,11 @@ from rich.panel import Panel
11
11
  from rich.table import Table
12
12
  from rich.text import Text
13
13
 
14
+ from iam_validator.__version__ import __version__
14
15
  from iam_validator.core.formatters import (
15
16
  ConsoleFormatter,
16
17
  CSVFormatter,
18
+ EnhancedFormatter,
17
19
  HTMLFormatter,
18
20
  JSONFormatter,
19
21
  MarkdownFormatter,
@@ -43,6 +45,8 @@ class ReportGenerator:
43
45
  # Register all built-in formatters
44
46
  if not self.formatter_registry.get_formatter("console"):
45
47
  self.formatter_registry.register(ConsoleFormatter())
48
+ if not self.formatter_registry.get_formatter("enhanced"):
49
+ self.formatter_registry.register(EnhancedFormatter())
46
50
  if not self.formatter_registry.get_formatter("json"):
47
51
  self.formatter_registry.register(JSONFormatter())
48
52
  if not self.formatter_registry.get_formatter("markdown"):
@@ -80,11 +84,27 @@ class ReportGenerator:
80
84
  invalid_count = len(results) - valid_count
81
85
  total_issues = sum(len(r.issues) for r in results)
82
86
 
87
+ # Count policies with security issues (separate from validity issues)
88
+ policies_with_security_issues = sum(
89
+ 1 for r in results if any(issue.is_security_severity() for issue in r.issues)
90
+ )
91
+
92
+ # Count validity vs security issues
93
+ validity_issues = sum(
94
+ sum(1 for issue in r.issues if issue.is_validity_severity()) for r in results
95
+ )
96
+ security_issues = sum(
97
+ sum(1 for issue in r.issues if issue.is_security_severity()) for r in results
98
+ )
99
+
83
100
  return ValidationReport(
84
101
  total_policies=len(results),
85
102
  valid_policies=valid_count,
86
103
  invalid_policies=invalid_count,
104
+ policies_with_security_issues=policies_with_security_issues,
87
105
  total_issues=total_issues,
106
+ validity_issues=validity_issues,
107
+ security_issues=security_issues,
88
108
  results=results,
89
109
  )
90
110
 
@@ -98,10 +118,39 @@ class ReportGenerator:
98
118
  summary_text = Text()
99
119
  summary_text.append(f"Total Policies: {report.total_policies}\n")
100
120
  summary_text.append(f"Valid: {report.valid_policies} ", style="green")
101
- summary_text.append(f"Invalid: {report.invalid_policies}\n", style="red")
102
- summary_text.append(f"Total Issues: {report.total_issues}\n")
103
121
 
104
- self.console.print(Panel(summary_text, title="Validation Summary", border_style="blue"))
122
+ # Show invalid policies (IAM validity issues)
123
+ if report.invalid_policies > 0:
124
+ summary_text.append(f"Invalid: {report.invalid_policies} ", style="red")
125
+
126
+ # Show policies with security findings (separate from validity)
127
+ if report.policies_with_security_issues > 0:
128
+ summary_text.append(
129
+ f"Security Findings: {report.policies_with_security_issues} ", style="yellow"
130
+ )
131
+
132
+ summary_text.append("\n")
133
+
134
+ # Breakdown of issue types
135
+ summary_text.append(f"Total Issues: {report.total_issues}")
136
+ if report.validity_issues > 0 or report.security_issues > 0:
137
+ summary_text.append(" (")
138
+ if report.validity_issues > 0:
139
+ summary_text.append(f"{report.validity_issues} validity", style="red")
140
+ if report.validity_issues > 0 and report.security_issues > 0:
141
+ summary_text.append(", ")
142
+ if report.security_issues > 0:
143
+ summary_text.append(f"{report.security_issues} security", style="yellow")
144
+ summary_text.append(")")
145
+ summary_text.append("\n")
146
+
147
+ self.console.print(
148
+ Panel(
149
+ summary_text,
150
+ title=f"Validation Summary (iam-validator v{__version__})",
151
+ border_style="blue",
152
+ )
153
+ )
105
154
 
106
155
  # Detailed results
107
156
  for result in report.results:
@@ -127,11 +176,11 @@ class ReportGenerator:
127
176
  self.console.print(" [dim]No issues found[/dim]")
128
177
  return
129
178
 
130
- # Create issues table
179
+ # Create issues table with adjusted column widths for better readability
131
180
  table = Table(show_header=True, header_style="bold", box=None, padding=(0, 1))
132
- table.add_column("Severity", style="cyan", width=10)
133
- table.add_column("Type", style="magenta", width=20)
134
- table.add_column("Message", style="white")
181
+ table.add_column("Severity", style="cyan", width=12, no_wrap=False)
182
+ table.add_column("Type", style="magenta", width=25, no_wrap=False)
183
+ table.add_column("Message", style="white", no_wrap=False)
135
184
 
136
185
  for issue in result.issues:
137
186
  severity_style = {
@@ -149,6 +198,8 @@ class ReportGenerator:
149
198
  location = f"Statement {issue.statement_index}"
150
199
  if issue.statement_sid:
151
200
  location += f" ({issue.statement_sid})"
201
+ if issue.line_number is not None:
202
+ location += f" @L{issue.line_number}"
152
203
 
153
204
  message = f"{location}: {issue.message}"
154
205
  if issue.suggestion:
@@ -580,22 +631,35 @@ class ReportGenerator:
580
631
  continue
581
632
 
582
633
  policy_lines = []
583
- policy_lines.append("<details open>")
584
- policy_lines.append(
585
- f"<summary><b>{idx}. <code>{result.policy_file}</code></b> - {len(result.issues)} issue(s) found</summary>"
586
- )
587
- policy_lines.append("")
588
634
 
589
635
  # Group issues by severity - support both IAM validity and security severities
590
636
  errors = [i for i in result.issues if i.severity in ("error", "critical", "high")]
591
637
  warnings = [i for i in result.issues if i.severity in ("warning", "medium")]
592
638
  infos = [i for i in result.issues if i.severity in ("info", "low")]
593
639
 
640
+ # Build severity summary for header
641
+ severity_parts = []
642
+ if errors:
643
+ severity_parts.append(f"🔴 {len(errors)}")
644
+ if warnings:
645
+ severity_parts.append(f"🟡 {len(warnings)}")
646
+ if infos:
647
+ severity_parts.append(f"🔵 {len(infos)}")
648
+ severity_summary = " · ".join(severity_parts)
649
+
650
+ # Only open first 3 policy details by default to avoid wall of text
651
+ is_open = " open" if policies_shown < 3 else ""
652
+ policy_lines.append(f"<details{is_open}>")
653
+ policy_lines.append(
654
+ f"<summary><b>{idx}. <code>{result.policy_file}</code></b> - {severity_summary}</summary>"
655
+ )
656
+ policy_lines.append("")
657
+
594
658
  # Add errors (prioritized)
595
659
  if errors:
596
660
  policy_lines.append("### 🔴 Errors")
597
661
  policy_lines.append("")
598
- for issue in errors:
662
+ for i, issue in enumerate(errors):
599
663
  issue_content = self._format_issue_markdown(issue)
600
664
  test_length = len("\n".join(details_lines + policy_lines)) + len(
601
665
  issue_content
@@ -605,6 +669,10 @@ class ReportGenerator:
605
669
  break
606
670
  policy_lines.append(issue_content)
607
671
  issues_shown += 1
672
+ # Add separator between issues within same severity
673
+ if i < len(errors) - 1:
674
+ policy_lines.append("---")
675
+ policy_lines.append("")
608
676
  policy_lines.append("")
609
677
 
610
678
  if truncated:
@@ -614,7 +682,7 @@ class ReportGenerator:
614
682
  if warnings:
615
683
  policy_lines.append("### 🟡 Warnings")
616
684
  policy_lines.append("")
617
- for issue in warnings:
685
+ for i, issue in enumerate(warnings):
618
686
  issue_content = self._format_issue_markdown(issue)
619
687
  test_length = len("\n".join(details_lines + policy_lines)) + len(
620
688
  issue_content
@@ -624,6 +692,10 @@ class ReportGenerator:
624
692
  break
625
693
  policy_lines.append(issue_content)
626
694
  issues_shown += 1
695
+ # Add separator between issues within same severity
696
+ if i < len(warnings) - 1:
697
+ policy_lines.append("---")
698
+ policy_lines.append("")
627
699
  policy_lines.append("")
628
700
 
629
701
  if truncated:
@@ -633,7 +705,7 @@ class ReportGenerator:
633
705
  if infos:
634
706
  policy_lines.append("### 🔵 Info")
635
707
  policy_lines.append("")
636
- for issue in infos:
708
+ for i, issue in enumerate(infos):
637
709
  issue_content = self._format_issue_markdown(issue)
638
710
  test_length = len("\n".join(details_lines + policy_lines)) + len(
639
711
  issue_content
@@ -643,6 +715,10 @@ class ReportGenerator:
643
715
  break
644
716
  policy_lines.append(issue_content)
645
717
  issues_shown += 1
718
+ # Add separator between issues within same severity
719
+ if i < len(infos) - 1:
720
+ policy_lines.append("---")
721
+ policy_lines.append("")
646
722
  policy_lines.append("")
647
723
 
648
724
  if truncated:
@@ -650,6 +726,8 @@ class ReportGenerator:
650
726
 
651
727
  policy_lines.append("</details>")
652
728
  policy_lines.append("")
729
+ policy_lines.append("---")
730
+ policy_lines.append("")
653
731
 
654
732
  # Check if adding this policy would exceed limit
655
733
  test_length = len("\n".join(details_lines + policy_lines))
@@ -712,7 +790,7 @@ class ReportGenerator:
712
790
  parts.append(f"> {issue.message}")
713
791
  parts.append("")
714
792
 
715
- # Details section
793
+ # Details section - inline format
716
794
  details = []
717
795
  if issue.action:
718
796
  details.append(f"**Action:** `{issue.action}`")
@@ -722,19 +800,27 @@ class ReportGenerator:
722
800
  details.append(f"**Condition Key:** `{issue.condition_key}`")
723
801
 
724
802
  if details:
725
- parts.append("<table>")
726
- parts.append("<tr><td>")
727
- parts.append("")
728
- parts.extend(details)
729
- parts.append("")
730
- parts.append("</td></tr>")
731
- parts.append("</table>")
803
+ parts.append(" · ".join(details))
732
804
  parts.append("")
733
805
 
734
- # Suggestion in highlighted box
806
+ # Suggestion in highlighted box with code examples
735
807
  if issue.suggestion:
736
- parts.append(f"> 💡 **Suggestion:** {issue.suggestion}")
737
- parts.append("")
808
+ # Check if suggestion contains "Example:" section
809
+ if "\nExample:\n" in issue.suggestion:
810
+ text_part, code_part = issue.suggestion.split("\nExample:\n", 1)
811
+ parts.append(f"> 💡 **Suggestion:** {text_part}")
812
+ parts.append("")
813
+ parts.append("<details>")
814
+ parts.append("<summary>📖 View Example</summary>")
815
+ parts.append("")
816
+ parts.append("```json")
817
+ parts.append(code_part)
818
+ parts.append("```")
819
+ parts.append("</details>")
820
+ parts.append("")
821
+ else:
822
+ parts.append(f"> 💡 **Suggestion:** {issue.suggestion}")
823
+ parts.append("")
738
824
 
739
825
  return "\n".join(parts)
740
826
 
@@ -1,45 +0,0 @@
1
- iam_validator/__init__.py,sha256=APnMR3Fu4fHhxfsHBvUM2dJIwazgvLKQbfOsSgFPidg,693
2
- iam_validator/__main__.py,sha256=to_nz3n_IerJpVVZZ6WSFlFR5s_06J0csfPOTfQZG8g,197
3
- iam_validator/__version__.py,sha256=YOIURWR5ocuvaQTQgwFi1XjHm_ifJDzicMOQSJZqmZc,206
4
- iam_validator/checks/__init__.py,sha256=q-_rIYGZJMjsiHK-R_3CbSUCBVGN5e137LPNDnMRZmw,841
5
- iam_validator/checks/action_condition_enforcement.py,sha256=3V8Wnz6BYnataKzuFMx8fHukVjzpIZaVfde9-RqZjPc,25357
6
- iam_validator/checks/action_validation.py,sha256=KbUw1SV-2nN-HtLlj3zrE6sdd0z8iAF0ubqz35Vwb7c,6921
7
- iam_validator/checks/condition_key_validation.py,sha256=bc4LQ8IRKyt0RquaQvQvVjmeJnuOUAFRL8xdduLPa_U,2661
8
- iam_validator/checks/policy_size.py,sha256=4cvZiWRJXGuvYo8PRcdD1Py_ZL8Xw0lOJfXTs6EX-_I,5753
9
- iam_validator/checks/resource_validation.py,sha256=AEIoiR6AKYLuVaA8ne3QE5qy6NCMDe98_2JAiwE9-JU,4261
10
- iam_validator/checks/security_best_practices.py,sha256=Sr3aiLbki8_M3U9qJv7u0fM__GjJRfZzWmJVgJ3ODSw,28185
11
- iam_validator/checks/sid_uniqueness.py,sha256=7S8RtVJgYPTKgr7gSEmxgT0oIGkSoXN6iu0ALHbcSfw,5015
12
- iam_validator/commands/__init__.py,sha256=zuhECuz-1Us5hBNAJtdMae8LaYn1eNeoYPBmQPMwI94,357
13
- iam_validator/commands/analyze.py,sha256=TWlDaZ8gVOdNv6__KQQfzeLVW36qLiL5IzlhGYfvq_g,16501
14
- iam_validator/commands/base.py,sha256=5baCCMwxz7pdQ6XMpWfXFNz7i1l5dB8Qv9dKKR04Gzs,1074
15
- iam_validator/commands/post_to_pr.py,sha256=hl_K-XlELYN-ArjMdgQqysvIE-26yf9XdrMl4ToDwG0,2148
16
- iam_validator/commands/validate.py,sha256=zdfay29HX1v4uz_LfzUUgC4-VUy8TTg5CGfZlAeEWXc,13779
17
- iam_validator/core/__init__.py,sha256=1FvJPMrbzJfS9YbRUJCshJLd5gzWwR9Fd_slS0Aq9c8,416
18
- iam_validator/core/access_analyzer.py,sha256=poeT1i74jXpKr1B3UmvqiTvCTbq82zffWgZHwiFUwoo,24337
19
- iam_validator/core/access_analyzer_report.py,sha256=iTIFKul6zQZd2qBg8V6zaDNPMKF8D_XDSX6pJwXFVYY,24791
20
- iam_validator/core/aws_fetcher.py,sha256=fUCIMItIWmrbgsoVCz_9Oe5k3SjuXBlNBVwQ60IWwns,25492
21
- iam_validator/core/aws_global_conditions.py,sha256=ADVcMEWhgvDZWdBmRUQN3HB7a9OycbTLecXFAy3LPbo,5837
22
- iam_validator/core/check_registry.py,sha256=wXg4Yw5LJ-rAVLiPUIJOtw8Y49Q1PY00Zbu37LzyjHY,15477
23
- iam_validator/core/cli.py,sha256=5UHsHS8o7Fkag4d6MNaaqjCFSGu8evCIbtpa81591lE,3831
24
- iam_validator/core/config_loader.py,sha256=k4D5TT_D6B9N8BbIEg0nE3wUXu0naFLHOVVJsjYZzh4,14880
25
- iam_validator/core/models.py,sha256=SUEbxDUtkX1uvgMy6-LPzomyGu82PTpXdDXZ9RKqfTY,9655
26
- iam_validator/core/policy_checks.py,sha256=xK5CntsEKVDgN27uIdQ92jCL97t7eBqOk0SChWU9cgw,23872
27
- iam_validator/core/policy_loader.py,sha256=TR7SpzlRG3TwH4HBGEFUuhNOmxIR8Cud2SQ-AmHWBpM,14040
28
- iam_validator/core/pr_commenter.py,sha256=TOhVXKTFcRHQ9EVuShXQcKXn9aNjB1mU6FnR2jvltmw,10581
29
- iam_validator/core/report.py,sha256=6k2A82EuhI72y-xCXbxRbykYcBvUP0z977pjUk9w1Cc,28977
30
- iam_validator/core/formatters/__init__.py,sha256=ggIKrI_Uu6tnKBLnUbBaXgaIXeL-JAGwUOrfSql6sow,774
31
- iam_validator/core/formatters/base.py,sha256=SShDeDiy5mYQnS6BpA8xYg91N-KX1EObkOtlrVHqx1Q,4451
32
- iam_validator/core/formatters/console.py,sha256=qkKWcxETRWDr62Zmkz0NXG4oS9hj2LwDX8uH4MoSX0s,750
33
- iam_validator/core/formatters/csv.py,sha256=hyop9gddZc1eOjUvd1YJjxbo8FNlLG7Rvh6UkSCEAhU,5565
34
- iam_validator/core/formatters/html.py,sha256=kW0BVTTX8PbiEbct8mXIyqTiO6jdsGjyK6Y3NNVeRaI,19317
35
- iam_validator/core/formatters/json.py,sha256=A7gZ8P32GEdbDvrSn6v56yQ4fOP_kyMaoFVXG2bgnew,939
36
- iam_validator/core/formatters/markdown.py,sha256=FccevVlD_mC6wtrWsya2Uo-NEphyuWsZyFar9x_ZT2g,1859
37
- iam_validator/core/formatters/sarif.py,sha256=tqp8g7RmUh0HRk-kKDaucx4sa-5I9ikgkSpy1MM8Vi4,7200
38
- iam_validator/integrations/__init__.py,sha256=7Hlor_X9j0NZaEjFuSvoXAAuSKQ-zgY19Rk-Dz3JpKo,616
39
- iam_validator/integrations/github_integration.py,sha256=bKs94vNT4PmcmUPUeuY2WJFhCYpUY2SWiBP1vj-andA,25673
40
- iam_validator/integrations/ms_teams.py,sha256=t2PlWuTDb6GGH-eDU1jnOKd8D1w4FCB68bahGA7MJcE,14475
41
- iam_policy_validator-1.0.4.dist-info/METADATA,sha256=QQAQsAQCDiPen37apynaUzYjOUmiTpn8NYgrM6C7l0E,22070
42
- iam_policy_validator-1.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- iam_policy_validator-1.0.4.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
44
- iam_policy_validator-1.0.4.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
45
- iam_policy_validator-1.0.4.dist-info/RECORD,,