iam-policy-validator 1.10.3__py3-none-any.whl → 1.11.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.
- iam_policy_validator-1.11.0.dist-info/METADATA +782 -0
- {iam_policy_validator-1.10.3.dist-info → iam_policy_validator-1.11.0.dist-info}/RECORD +25 -21
- iam_validator/__version__.py +1 -1
- iam_validator/checks/action_condition_enforcement.py +27 -14
- iam_validator/checks/sensitive_action.py +123 -11
- iam_validator/checks/utils/policy_level_checks.py +47 -10
- iam_validator/commands/__init__.py +6 -0
- iam_validator/commands/completion.py +420 -0
- iam_validator/commands/query.py +485 -0
- iam_validator/commands/validate.py +21 -26
- iam_validator/core/config/category_suggestions.py +77 -0
- iam_validator/core/config/condition_requirements.py +105 -54
- iam_validator/core/config/defaults.py +82 -6
- iam_validator/core/config/wildcards.py +3 -0
- iam_validator/core/diff_parser.py +321 -0
- iam_validator/core/formatters/enhanced.py +34 -27
- iam_validator/core/models.py +2 -0
- iam_validator/core/pr_commenter.py +179 -51
- iam_validator/core/report.py +19 -17
- iam_validator/integrations/github_integration.py +250 -1
- iam_validator/sdk/__init__.py +33 -0
- iam_validator/sdk/query_utils.py +454 -0
- iam_policy_validator-1.10.3.dist-info/METADATA +0 -549
- {iam_policy_validator-1.10.3.dist-info → iam_policy_validator-1.11.0.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.10.3.dist-info → iam_policy_validator-1.11.0.dist-info}/entry_points.txt +0 -0
- {iam_policy_validator-1.10.3.dist-info → iam_policy_validator-1.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
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 download-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
|
+
case "$query_subcmd" in
|
|
219
|
+
action)
|
|
220
|
+
opts="--service --name --access-level --resource-type --condition --output"
|
|
221
|
+
;;
|
|
222
|
+
arn)
|
|
223
|
+
opts="--service --name --list-arn-types --output"
|
|
224
|
+
;;
|
|
225
|
+
condition)
|
|
226
|
+
opts="--service --name --output"
|
|
227
|
+
;;
|
|
228
|
+
esac
|
|
229
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
230
|
+
return 0
|
|
231
|
+
;;
|
|
232
|
+
validate)
|
|
233
|
+
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"
|
|
234
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
235
|
+
return 0
|
|
236
|
+
;;
|
|
237
|
+
analyze)
|
|
238
|
+
opts="--policy --format --output"
|
|
239
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
240
|
+
return 0
|
|
241
|
+
;;
|
|
242
|
+
cache)
|
|
243
|
+
opts="--clear --info"
|
|
244
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
245
|
+
return 0
|
|
246
|
+
;;
|
|
247
|
+
download-services)
|
|
248
|
+
opts="--output-dir --force"
|
|
249
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
250
|
+
return 0
|
|
251
|
+
;;
|
|
252
|
+
esac
|
|
253
|
+
|
|
254
|
+
return 0
|
|
255
|
+
}}
|
|
256
|
+
|
|
257
|
+
complete -F _iam_validator_completion iam-validator
|
|
258
|
+
'''
|
|
259
|
+
|
|
260
|
+
def _generate_zsh_completion(self) -> str:
|
|
261
|
+
"""Generate zsh completion script.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Zsh completion script as string
|
|
265
|
+
"""
|
|
266
|
+
cached_services = self._get_cached_services()
|
|
267
|
+
# For zsh, we need to format as: 'service1' 'service2' ...
|
|
268
|
+
services_list = " ".join(f"'{svc}'" for svc in cached_services) if cached_services else ""
|
|
269
|
+
|
|
270
|
+
return f"""#compdef iam-validator
|
|
271
|
+
# Zsh completion for iam-validator
|
|
272
|
+
# Generated by: iam-validator completion zsh
|
|
273
|
+
|
|
274
|
+
_iam_validator() {{
|
|
275
|
+
local -a commands
|
|
276
|
+
commands=(
|
|
277
|
+
'validate:Validate IAM policies'
|
|
278
|
+
'post-to-pr:Post validation results to GitHub PR'
|
|
279
|
+
'analyze:Analyze IAM policies'
|
|
280
|
+
'cache:Manage cache'
|
|
281
|
+
'download-services:Download AWS service definitions'
|
|
282
|
+
'query:Query AWS service definitions'
|
|
283
|
+
'completion:Generate shell completion scripts'
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
local -a query_subcommands
|
|
287
|
+
query_subcommands=(
|
|
288
|
+
'action:Query IAM actions'
|
|
289
|
+
'arn:Query ARN formats'
|
|
290
|
+
'condition:Query condition keys'
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
local -a access_levels
|
|
294
|
+
access_levels=(
|
|
295
|
+
'read:Read-only actions'
|
|
296
|
+
'write:Write actions'
|
|
297
|
+
'list:List actions'
|
|
298
|
+
'tagging:Tagging actions'
|
|
299
|
+
'permissions-management:Permission management actions'
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
local -a formats
|
|
303
|
+
formats=(
|
|
304
|
+
'json:JSON output'
|
|
305
|
+
'yaml:YAML output'
|
|
306
|
+
'text:Plain text output'
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
local -a shells
|
|
310
|
+
shells=(
|
|
311
|
+
'bash:Bash completion'
|
|
312
|
+
'zsh:Zsh completion'
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Cached AWS services
|
|
316
|
+
local -a aws_services
|
|
317
|
+
aws_services=({services_list})
|
|
318
|
+
|
|
319
|
+
_arguments -C \\
|
|
320
|
+
'1: :->command' \\
|
|
321
|
+
'*:: :->args'
|
|
322
|
+
|
|
323
|
+
case $state in
|
|
324
|
+
command)
|
|
325
|
+
_describe 'command' commands
|
|
326
|
+
;;
|
|
327
|
+
args)
|
|
328
|
+
case $words[1] in
|
|
329
|
+
query)
|
|
330
|
+
case $CURRENT in
|
|
331
|
+
2)
|
|
332
|
+
_describe 'query subcommand' query_subcommands
|
|
333
|
+
;;
|
|
334
|
+
*)
|
|
335
|
+
case $words[2] in
|
|
336
|
+
action)
|
|
337
|
+
_arguments \\
|
|
338
|
+
'--service[AWS service name]:service:($aws_services)' \\
|
|
339
|
+
'--name[Action name]:action name:' \\
|
|
340
|
+
'--access-level[Filter by access level]:access level:($access_levels)' \\
|
|
341
|
+
'--resource-type[Filter by resource type]:resource type:' \\
|
|
342
|
+
'--condition[Filter by condition key]:condition key:' \\
|
|
343
|
+
'--output[Output format]:format:(json yaml text)'
|
|
344
|
+
;;
|
|
345
|
+
arn)
|
|
346
|
+
_arguments \\
|
|
347
|
+
'--service[AWS service name]:service:($aws_services)' \\
|
|
348
|
+
'--name[ARN resource type]:arn type:' \\
|
|
349
|
+
'--list-arn-types[List all ARN types]' \\
|
|
350
|
+
'--output[Output format]:format:(json yaml text)'
|
|
351
|
+
;;
|
|
352
|
+
condition)
|
|
353
|
+
_arguments \\
|
|
354
|
+
'--service[AWS service name]:service:($aws_services)' \\
|
|
355
|
+
'--name[Condition key name]:condition key:' \\
|
|
356
|
+
'--output[Output format]:format:(json yaml text)'
|
|
357
|
+
;;
|
|
358
|
+
esac
|
|
359
|
+
;;
|
|
360
|
+
esac
|
|
361
|
+
;;
|
|
362
|
+
validate)
|
|
363
|
+
_arguments \\
|
|
364
|
+
'(--path -p)'{{--path,-p}}'[Path to policy file or directory]:file:_files' \\
|
|
365
|
+
'--stdin[Read policy from stdin]' \\
|
|
366
|
+
'(--format -f)'{{--format,-f}}'[Output format]:format:(console enhanced json markdown html csv sarif)' \\
|
|
367
|
+
'(--output -o)'{{--output,-o}}'[Output file path]:file:_files' \\
|
|
368
|
+
'--no-recursive[Do not recursively search directories]' \\
|
|
369
|
+
'--fail-on-warnings[Fail validation if warnings are found]' \\
|
|
370
|
+
'(--policy-type -t)'{{--policy-type,-t}}'[Type of IAM policy]:policy type:(IDENTITY_POLICY RESOURCE_POLICY TRUST_POLICY SERVICE_CONTROL_POLICY RESOURCE_CONTROL_POLICY)' \\
|
|
371
|
+
'--github-comment[Post summary comment to PR]' \\
|
|
372
|
+
'--github-review[Create line-specific review comments]' \\
|
|
373
|
+
'--github-summary[Write to GitHub Actions job summary]' \\
|
|
374
|
+
'(--verbose -v)'{{--verbose,-v}}'[Enable verbose logging]' \\
|
|
375
|
+
'(--config -c)'{{--config,-c}}'[Configuration file]:file:_files' \\
|
|
376
|
+
'--custom-checks-dir[Custom checks directory]:directory:_directories' \\
|
|
377
|
+
'--aws-services-dir[AWS service definitions directory]:directory:_directories' \\
|
|
378
|
+
'--stream[Process files one-by-one]' \\
|
|
379
|
+
'--batch-size[Policies per batch]:number:' \\
|
|
380
|
+
'--summary[Show Executive Summary section]' \\
|
|
381
|
+
'--severity-breakdown[Show Issue Severity Breakdown section]'
|
|
382
|
+
;;
|
|
383
|
+
analyze)
|
|
384
|
+
_arguments \\
|
|
385
|
+
'--policy[Policy file]:file:_files' \\
|
|
386
|
+
'--format[Output format]:format:($formats)' \\
|
|
387
|
+
'--output[Output file]:file:_files'
|
|
388
|
+
;;
|
|
389
|
+
cache)
|
|
390
|
+
_arguments \\
|
|
391
|
+
'--clear[Clear cache]' \\
|
|
392
|
+
'--info[Show cache info]'
|
|
393
|
+
;;
|
|
394
|
+
download-services)
|
|
395
|
+
_arguments \\
|
|
396
|
+
'--output-dir[Output directory]:directory:_directories' \\
|
|
397
|
+
'--force[Force re-download]'
|
|
398
|
+
;;
|
|
399
|
+
completion)
|
|
400
|
+
_describe 'shell' shells
|
|
401
|
+
;;
|
|
402
|
+
esac
|
|
403
|
+
;;
|
|
404
|
+
esac
|
|
405
|
+
}}
|
|
406
|
+
|
|
407
|
+
_iam_validator "$@"
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# For testing
|
|
412
|
+
if __name__ == "__main__":
|
|
413
|
+
import asyncio
|
|
414
|
+
import sys
|
|
415
|
+
|
|
416
|
+
cmd = CompletionCommand()
|
|
417
|
+
arg_parser = argparse.ArgumentParser()
|
|
418
|
+
cmd.add_arguments(arg_parser)
|
|
419
|
+
parsed_args = arg_parser.parse_args()
|
|
420
|
+
sys.exit(asyncio.run(cmd.execute(parsed_args)))
|