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,471 @@
1
+ """Generate shell completion scripts for iam-validator.
2
+
3
+ This command generates completion scripts for bash and zsh shells.
4
+ The generated scripts provide intelligent autocompletion for:
5
+ - Command names (validate, query, analyze, etc.)
6
+ - Command options (--service, --access-level, etc.)
7
+ - Cached AWS service names (for --service flag)
8
+
9
+ Usage:
10
+ # Bash completion
11
+ iam-validator completion bash > ~/.bash_completion.d/iam-validator
12
+ source ~/.bash_completion.d/iam-validator
13
+
14
+ # Zsh completion
15
+ iam-validator completion zsh > ~/.zsh/completion/_iam-validator
16
+ # Then add to ~/.zshrc: fpath=(~/.zsh/completion $fpath)
17
+ """
18
+
19
+ import argparse
20
+ import logging
21
+
22
+ from iam_validator.commands.base import Command
23
+ from iam_validator.core.aws_service.storage import ServiceFileStorage
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class CompletionCommand(Command):
29
+ """Generate shell completion scripts."""
30
+
31
+ @property
32
+ def name(self) -> str:
33
+ """Command name."""
34
+ return "completion"
35
+
36
+ @property
37
+ def help(self) -> str:
38
+ """Command help text."""
39
+ return "Generate shell completion scripts (bash or zsh)"
40
+
41
+ @property
42
+ def epilog(self) -> str:
43
+ """Command epilog with examples."""
44
+ return """
45
+ examples:
46
+ # Generate bash completion
47
+ iam-validator completion bash > ~/.bash_completion.d/iam-validator
48
+ source ~/.bash_completion.d/iam-validator
49
+
50
+ # Generate zsh completion
51
+ iam-validator completion zsh > ~/.zsh/completion/_iam-validator
52
+ # Add to ~/.zshrc: fpath=(~/.zsh/completion $fpath)
53
+
54
+ # Direct evaluation (bash)
55
+ eval "$(iam-validator completion bash)"
56
+
57
+ # Direct evaluation (zsh)
58
+ eval "$(iam-validator completion zsh)"
59
+ """
60
+
61
+ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
62
+ """Add completion command arguments."""
63
+ parser.add_argument(
64
+ "shell",
65
+ choices=["bash", "zsh"],
66
+ help="Shell type to generate completion for",
67
+ )
68
+
69
+ async def execute(self, args: argparse.Namespace) -> int:
70
+ """Execute completion command."""
71
+ try:
72
+ if args.shell == "bash":
73
+ script = self._generate_bash_completion()
74
+ else: # zsh
75
+ script = self._generate_zsh_completion()
76
+
77
+ print(script)
78
+ return 0
79
+
80
+ except Exception as e: # pylint: disable=broad-exception-caught
81
+ logger.error(f"Failed to generate completion: {e}", exc_info=True)
82
+ return 1
83
+
84
+ def _get_cached_services(self) -> list[str]:
85
+ """Get list of cached AWS service names.
86
+
87
+ Returns:
88
+ List of service names that are cached locally.
89
+ Returns empty list if cache is not available.
90
+ """
91
+ try:
92
+ storage = ServiceFileStorage()
93
+ cache_dir = storage.cache_directory
94
+
95
+ if not cache_dir.exists():
96
+ return []
97
+
98
+ # Look for cached service files
99
+ # Files are named like: s3_{hash}.json or services_list.json
100
+ services = set()
101
+
102
+ for cache_file in cache_dir.glob("*.json"):
103
+ filename = cache_file.stem
104
+ # Extract service name from cache filename
105
+ # Format: servicename_hash or services_list
106
+ if filename == "services_list":
107
+ continue
108
+ # Extract service name (before underscore)
109
+ if "_" in filename:
110
+ service_name = filename.split("_")[0]
111
+ services.add(service_name)
112
+
113
+ return sorted(list(services))
114
+
115
+ except Exception as e: # pylint: disable=broad-exception-caught
116
+ logger.debug(f"Could not load cached services: {e}")
117
+ return []
118
+
119
+ def _generate_bash_completion(self) -> str:
120
+ """Generate bash completion script.
121
+
122
+ Returns:
123
+ Bash completion script as string
124
+ """
125
+ cached_services = self._get_cached_services()
126
+ services_list = " ".join(cached_services) if cached_services else ""
127
+
128
+ return f'''# Bash completion for iam-validator
129
+ # Generated by: iam-validator completion bash
130
+
131
+ _iam_validator_completion() {{
132
+ local cur prev opts base
133
+ COMPREPLY=()
134
+ cur="${{COMP_WORDS[COMP_CWORD]}}"
135
+ prev="${{COMP_WORDS[COMP_CWORD-1]}}"
136
+
137
+ # Main commands
138
+ local commands="validate post-to-pr analyze cache sync-services query completion"
139
+
140
+ # Get the command (first non-option argument)
141
+ local cmd=""
142
+ for ((i=1; i<COMP_CWORD; i++)); do
143
+ if [[ ${{COMP_WORDS[i]}} != -* ]]; then
144
+ cmd=${{COMP_WORDS[i]}}
145
+ break
146
+ fi
147
+ done
148
+
149
+ # Complete main command if we're at the first argument
150
+ if [[ $COMP_CWORD -eq 1 ]]; then
151
+ COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
152
+ return 0
153
+ fi
154
+
155
+ # Completion based on previous argument
156
+ case "$prev" in
157
+ --service)
158
+ # Provide cached service names
159
+ local services="{services_list}"
160
+ COMPREPLY=( $(compgen -W "$services" -- "$cur") )
161
+ return 0
162
+ ;;
163
+ --access-level)
164
+ COMPREPLY=( $(compgen -W "read write list tagging permissions-management" -- "$cur") )
165
+ return 0
166
+ ;;
167
+ --format|-f)
168
+ COMPREPLY=( $(compgen -W "console enhanced json markdown html csv sarif" -- "$cur") )
169
+ return 0
170
+ ;;
171
+ --policy-type|-t)
172
+ COMPREPLY=( $(compgen -W "IDENTITY_POLICY RESOURCE_POLICY TRUST_POLICY SERVICE_CONTROL_POLICY RESOURCE_CONTROL_POLICY" -- "$cur") )
173
+ return 0
174
+ ;;
175
+ --output|-o)
176
+ # Context-aware: file for validate, format for query
177
+ if [[ "$cmd" == "query" ]]; then
178
+ COMPREPLY=( $(compgen -W "json yaml text" -- "$cur") )
179
+ else
180
+ COMPREPLY=( $(compgen -f -- "$cur") )
181
+ fi
182
+ return 0
183
+ ;;
184
+ --path|-p|--config|-c|--custom-checks-dir|--aws-services-dir)
185
+ # File/directory completion
186
+ COMPREPLY=( $(compgen -f -- "$cur") )
187
+ return 0
188
+ ;;
189
+ --resource-type|--condition|--name|--batch-size)
190
+ # Allow any input
191
+ return 0
192
+ ;;
193
+ completion)
194
+ COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
195
+ return 0
196
+ ;;
197
+ esac
198
+
199
+ # Command-specific completions
200
+ case "$cmd" in
201
+ query)
202
+ # Check if we need to complete the query subcommand
203
+ local query_subcmd=""
204
+ for ((i=2; i<COMP_CWORD; i++)); do
205
+ if [[ ${{COMP_WORDS[i]}} =~ ^(action|arn|condition)$ ]]; then
206
+ query_subcmd=${{COMP_WORDS[i]}}
207
+ break
208
+ fi
209
+ done
210
+
211
+ if [[ -z "$query_subcmd" ]]; then
212
+ # Complete query subcommand
213
+ COMPREPLY=( $(compgen -W "action arn condition" -- "$cur") )
214
+ return 0
215
+ fi
216
+
217
+ # Complete options for query subcommands
218
+ local opts=""
219
+ case "$query_subcmd" in
220
+ action)
221
+ opts="--service --name --access-level --resource-type --condition --output"
222
+ ;;
223
+ arn)
224
+ opts="--service --name --list-arn-types --output"
225
+ ;;
226
+ condition)
227
+ opts="--service --name --output"
228
+ ;;
229
+ esac
230
+
231
+ # Filter out already used options
232
+ local used_opts=""
233
+ for ((i=2; i<COMP_CWORD; i++)); do
234
+ if [[ ${{COMP_WORDS[i]}} == --* ]]; then
235
+ used_opts="$used_opts ${{COMP_WORDS[i]}}"
236
+ fi
237
+ done
238
+
239
+ local available_opts=""
240
+ for opt in $opts; do
241
+ if [[ ! " $used_opts " =~ " $opt " ]]; then
242
+ available_opts="$available_opts $opt"
243
+ fi
244
+ done
245
+
246
+ COMPREPLY=( $(compgen -W "$available_opts" -- "$cur") )
247
+ return 0
248
+ ;;
249
+ validate)
250
+ opts="--path -p --stdin --format -f --output -o --no-recursive --fail-on-warnings --policy-type -t --github-comment --github-review --github-summary --verbose -v --config -c --custom-checks-dir --aws-services-dir --stream --batch-size --summary --severity-breakdown --allow-owner-ignore --no-owner-ignore --ci --ci-output"
251
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
252
+ return 0
253
+ ;;
254
+ post-to-pr)
255
+ opts="--report -r --create-review --no-review --add-summary --no-summary --config -c"
256
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
257
+ return 0
258
+ ;;
259
+ analyze)
260
+ opts="--path -p --policy-type -t --region --profile --format -f --output -o --no-recursive --fail-on-warnings --github-comment --github-review --github-summary --run-all-checks --check-access-not-granted --check-access-resources --check-no-new-access --check-no-public-access --public-access-resource-type --verbose -v"
261
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
262
+ return 0
263
+ ;;
264
+ cache)
265
+ # Check if we need to complete the cache subcommand
266
+ local cache_subcmd=""
267
+ for ((i=2; i<COMP_CWORD; i++)); do
268
+ if [[ ${{COMP_WORDS[i]}} =~ ^(info|list|clear|refresh|prefetch|location)$ ]]; then
269
+ cache_subcmd=${{COMP_WORDS[i]}}
270
+ break
271
+ fi
272
+ done
273
+
274
+ if [[ -z "$cache_subcmd" ]]; then
275
+ # Complete cache subcommand
276
+ COMPREPLY=( $(compgen -W "info list clear refresh prefetch location" -- "$cur") )
277
+ return 0
278
+ fi
279
+ # Cache subcommands have no additional options
280
+ return 0
281
+ ;;
282
+ sync-services)
283
+ opts="--output-dir --max-concurrent"
284
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
285
+ return 0
286
+ ;;
287
+ esac
288
+
289
+ return 0
290
+ }}
291
+
292
+ complete -F _iam_validator_completion iam-validator
293
+ '''
294
+
295
+ def _generate_zsh_completion(self) -> str:
296
+ """Generate zsh completion script.
297
+
298
+ Returns:
299
+ Zsh completion script as string
300
+ """
301
+ cached_services = self._get_cached_services()
302
+ # For zsh, we need to format as: 'service1' 'service2' ...
303
+ services_list = " ".join(f"'{svc}'" for svc in cached_services) if cached_services else ""
304
+
305
+ return f"""#compdef iam-validator
306
+ # Zsh completion for iam-validator
307
+ # Generated by: iam-validator completion zsh
308
+
309
+ _iam_validator() {{
310
+ local curcontext="$curcontext" state line
311
+ typeset -A opt_args
312
+
313
+ # Cached AWS services
314
+ local -a aws_services
315
+ aws_services=({services_list})
316
+
317
+ _arguments -C \\
318
+ '1: :_iam_validator_commands' \\
319
+ '*::arg:->args'
320
+
321
+ case $state in
322
+ args)
323
+ case $words[1] in
324
+ query)
325
+ local query_state
326
+ _arguments -C \\
327
+ '1: :_iam_validator_query_subcommands' \\
328
+ '*::arg:->query_args' && return 0
329
+
330
+ case $state in
331
+ query_args)
332
+ case $words[1] in
333
+ action)
334
+ _arguments \\
335
+ '--service[AWS service name]:service:($aws_services)' \\
336
+ '--name[Action name]:action name:' \\
337
+ '--access-level[Filter by access level]:access level:(read write list tagging permissions-management)' \\
338
+ '--resource-type[Filter by resource type]:resource type:' \\
339
+ '--condition[Filter by condition key]:condition key:' \\
340
+ '--output[Output format]:format:(json yaml text)'
341
+ ;;
342
+ arn)
343
+ _arguments \\
344
+ '--service[AWS service name]:service:($aws_services)' \\
345
+ '--name[ARN resource type]:arn type:' \\
346
+ '--list-arn-types[List all ARN types]' \\
347
+ '--output[Output format]:format:(json yaml text)'
348
+ ;;
349
+ condition)
350
+ _arguments \\
351
+ '--service[AWS service name]:service:($aws_services)' \\
352
+ '--name[Condition key name]:condition key:' \\
353
+ '--output[Output format]:format:(json yaml text)'
354
+ ;;
355
+ esac
356
+ ;;
357
+ esac
358
+ ;;
359
+ validate)
360
+ _arguments \\
361
+ '*--path[Path to policy file or directory]:file:_files' \\
362
+ '*-p[Path to policy file or directory]:file:_files' \\
363
+ '--stdin[Read policy from stdin]' \\
364
+ '(--format -f)'{{--format,-f}}'[Output format]:format:(console enhanced json markdown html csv sarif)' \\
365
+ '(--output -o)'{{--output,-o}}'[Output file path]:file:_files' \\
366
+ '--no-recursive[Do not recursively search directories]' \\
367
+ '--fail-on-warnings[Fail validation if warnings are found]' \\
368
+ '(--policy-type -t)'{{--policy-type,-t}}'[Type of IAM policy]:policy type:(IDENTITY_POLICY RESOURCE_POLICY TRUST_POLICY SERVICE_CONTROL_POLICY RESOURCE_CONTROL_POLICY)' \\
369
+ '--github-comment[Post summary comment to PR]' \\
370
+ '--github-review[Create line-specific review comments]' \\
371
+ '--github-summary[Write to GitHub Actions job summary]' \\
372
+ '(--verbose -v)'{{--verbose,-v}}'[Enable verbose logging]' \\
373
+ '(--config -c)'{{--config,-c}}'[Configuration file]:file:_files' \\
374
+ '--custom-checks-dir[Custom checks directory]:directory:_directories' \\
375
+ '--aws-services-dir[AWS service definitions directory]:directory:_directories' \\
376
+ '--stream[Process files one-by-one]' \\
377
+ '--batch-size[Policies per batch]:number:' \\
378
+ '--summary[Show Executive Summary section]' \\
379
+ '--severity-breakdown[Show Issue Severity Breakdown section]' \\
380
+ '--allow-owner-ignore[Allow CODEOWNERS to ignore findings]' \\
381
+ '--no-owner-ignore[Disable CODEOWNERS ignore feature]' \\
382
+ '--ci[CI mode - print enhanced output, write JSON to file]' \\
383
+ '--ci-output[Output file for JSON report in CI mode]:file:_files'
384
+ ;;
385
+ post-to-pr)
386
+ _arguments \\
387
+ '(--report -r)'{{--report,-r}}'[Path to JSON report file]:file:_files' \\
388
+ '--create-review[Create line-specific review comments]' \\
389
+ '--no-review[Do not create line-specific review comments]' \\
390
+ '--add-summary[Add summary comment]' \\
391
+ '--no-summary[Do not add summary comment]' \\
392
+ '(--config -c)'{{--config,-c}}'[Configuration file]:file:_files'
393
+ ;;
394
+ analyze)
395
+ _arguments \\
396
+ '*--path[Path to policy file or directory]:file:_files' \\
397
+ '*-p[Path to policy file or directory]:file:_files' \\
398
+ '(--policy-type -t)'{{--policy-type,-t}}'[Type of IAM policy]:policy type:(IDENTITY_POLICY RESOURCE_POLICY SERVICE_CONTROL_POLICY)' \\
399
+ '--region[AWS region]:region:' \\
400
+ '--profile[AWS profile]:profile:' \\
401
+ '(--format -f)'{{--format,-f}}'[Output format]:format:(console json markdown)' \\
402
+ '(--output -o)'{{--output,-o}}'[Output file path]:file:_files' \\
403
+ '--no-recursive[Do not recursively search directories]' \\
404
+ '--fail-on-warnings[Fail validation if warnings are found]' \\
405
+ '--github-comment[Post validation results as GitHub PR comment]' \\
406
+ '--github-review[Create line-specific review comments on PR]' \\
407
+ '--github-summary[Write validation summary to GitHub Actions job summary]' \\
408
+ '--run-all-checks[Run full validation checks if Access Analyzer passes]' \\
409
+ '*--check-access-not-granted[Actions to check are NOT granted]:action:' \\
410
+ '*--check-access-resources[Resources to check]:resource:' \\
411
+ '--check-no-new-access[Path to existing policy]:file:_files' \\
412
+ '--check-no-public-access[Check that resource policy does not allow public access]' \\
413
+ '*--public-access-resource-type[Resource type for public access check]:resource type:' \\
414
+ '(--verbose -v)'{{--verbose,-v}}'[Enable verbose logging]'
415
+ ;;
416
+ cache)
417
+ _arguments \\
418
+ '1: :(info list clear refresh prefetch location)'
419
+ ;;
420
+ sync-services)
421
+ _arguments \\
422
+ '--output-dir[Output directory]:directory:_directories' \\
423
+ '--max-concurrent[Maximum concurrent downloads]:number:'
424
+ ;;
425
+ completion)
426
+ _arguments \\
427
+ '1: :(bash zsh)'
428
+ ;;
429
+ esac
430
+ ;;
431
+ esac
432
+ }}
433
+
434
+ _iam_validator_commands() {{
435
+ local -a commands
436
+ commands=(
437
+ 'validate:Validate IAM policies'
438
+ 'post-to-pr:Post validation results to GitHub PR'
439
+ 'analyze:Analyze IAM policies using AWS IAM Access Analyzer'
440
+ 'cache:Manage AWS service definition cache'
441
+ 'sync-services:Sync/download all AWS service definitions for offline use'
442
+ 'query:Query AWS service definitions (actions, ARNs, condition keys)'
443
+ 'completion:Generate shell completion scripts (bash or zsh)'
444
+ )
445
+ _describe 'command' commands
446
+ }}
447
+
448
+ _iam_validator_query_subcommands() {{
449
+ local -a subcommands
450
+ subcommands=(
451
+ 'action:Query IAM actions'
452
+ 'arn:Query ARN formats'
453
+ 'condition:Query condition keys'
454
+ )
455
+ _describe 'query subcommand' subcommands
456
+ }}
457
+
458
+ _iam_validator "$@"
459
+ """
460
+
461
+
462
+ # For testing
463
+ if __name__ == "__main__":
464
+ import asyncio
465
+ import sys
466
+
467
+ cmd = CompletionCommand()
468
+ arg_parser = argparse.ArgumentParser()
469
+ cmd.add_arguments(arg_parser)
470
+ parsed_args = arg_parser.parse_args()
471
+ sys.exit(asyncio.run(cmd.execute(parsed_args)))