iam-policy-validator 1.14.1__py3-none-any.whl → 1.14.3__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.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/METADATA +1 -1
- {iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/RECORD +12 -12
- iam_validator/__version__.py +1 -1
- iam_validator/checks/condition_key_validation.py +1 -1
- iam_validator/core/aws_service/validators.py +99 -8
- iam_validator/core/config/aws_global_conditions.py +8 -4
- iam_validator/core/constants.py +29 -0
- iam_validator/core/pr_commenter.py +6 -3
- iam_validator/integrations/github_integration.py +1 -1
- {iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/entry_points.txt +0 -0
- {iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iam-policy-validator
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.3
|
|
4
4
|
Summary: Validate AWS IAM policies for correctness and security using AWS Service Reference API
|
|
5
5
|
Project-URL: Homepage, https://github.com/boogy/iam-policy-validator
|
|
6
6
|
Project-URL: Documentation, https://github.com/boogy/iam-policy-validator/tree/main/docs
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
iam_validator/__init__.py,sha256=xHdUASOxFHwEXfT_GSr_KrkLlnxZ-pAAr1wW1PwAGko,693
|
|
2
2
|
iam_validator/__main__.py,sha256=to_nz3n_IerJpVVZZ6WSFlFR5s_06J0csfPOTfQZG8g,197
|
|
3
|
-
iam_validator/__version__.py,sha256=
|
|
3
|
+
iam_validator/__version__.py,sha256=Gm8njL8lXkcG-nPd19SPPxnjvafHZUsjluEm-8FCYvo,374
|
|
4
4
|
iam_validator/checks/__init__.py,sha256=OTkPnmlelu4YjMO8krjhu2wXiTV72RzopA5u1SfPQA0,1990
|
|
5
5
|
iam_validator/checks/action_condition_enforcement.py,sha256=2-XUMbof9tQ7SHZNmAHMkR1DgbOIzY2eFWlp9S9dwLk,60625
|
|
6
6
|
iam_validator/checks/action_resource_matching.py,sha256=qND0hfDgNoxFEdLWwrxOPVDfdj3k50nzedT2qF7nK7o,19428
|
|
7
7
|
iam_validator/checks/action_validation.py,sha256=glA2F3iQBU-d8rruiyB9IdzVOESC2Kb-91SnwRCdQF0,2562
|
|
8
|
-
iam_validator/checks/condition_key_validation.py,sha256=
|
|
8
|
+
iam_validator/checks/condition_key_validation.py,sha256=5i8LqqV78SjWK6pLrbttWmeMAD4pDC12_FjTjx5dFSU,4024
|
|
9
9
|
iam_validator/checks/condition_type_mismatch.py,sha256=KJp7zQHDd8VeTcfjcD-ur3S4070cXEDTWkFtxfp7CuE,10652
|
|
10
10
|
iam_validator/checks/full_wildcard.py,sha256=0TkkHtV0MZ6nZtJRtGdn3wwOMM96TRyGO7l7mmdHNUo,2325
|
|
11
11
|
iam_validator/checks/mfa_condition_check.py,sha256=y1LbqcvQ_fL2BPTNaKRQoBYM5hM7JET9cDPUOWKFEVs,4814
|
|
@@ -43,7 +43,7 @@ iam_validator/core/check_registry.py,sha256=oRCdWoCGQ8VZERVYd821u9r5NdKQ9FMC54e6
|
|
|
43
43
|
iam_validator/core/cli.py,sha256=PkXiZjlgrQ21QustBbspefYsdbxst4gxoClyG2_HQR8,3843
|
|
44
44
|
iam_validator/core/codeowners.py,sha256=dfRjYTpcTVmc-h95i4EoPXCXlcblD8yryeJBaTKQfjM,7530
|
|
45
45
|
iam_validator/core/condition_validators.py,sha256=7zBjlcf2xGFKGbcFrXSLvWT5tFhWxoqwzhsJqS2E8uY,21524
|
|
46
|
-
iam_validator/core/constants.py,sha256=
|
|
46
|
+
iam_validator/core/constants.py,sha256=YRT_uOUs1Dnt9J1hE-zG6Ja0numsH4Mf7RNNXNJWZIY,7447
|
|
47
47
|
iam_validator/core/diff_parser.py,sha256=5Jxa6WvQZtG5grblZeUH2OQ2R46tFLK-h8tvkHOSfLk,12110
|
|
48
48
|
iam_validator/core/finding_fingerprint.py,sha256=NJIlu8NhdenWbLS7ww8LyWFasJgpKWN63-DprrNW7Zs,4353
|
|
49
49
|
iam_validator/core/ignore_patterns.py,sha256=pZqDJBtkbck-85QK5eFPM5ZOPEKs3McRh3avqiCT5z0,10398
|
|
@@ -53,7 +53,7 @@ iam_validator/core/label_manager.py,sha256=48CRASWg98wyjfVF_1pUzj6dm9itzmG7SeIWf
|
|
|
53
53
|
iam_validator/core/models.py,sha256=lXUadIsTpp_j0Vt89Ez7aJkTKs2GD2ty3Ukl2NeY9Zo,15680
|
|
54
54
|
iam_validator/core/policy_checks.py,sha256=FNVuS2GTffwCjjrlupVIazC172gSxKYAAT_ObV6Apbo,8803
|
|
55
55
|
iam_validator/core/policy_loader.py,sha256=iid3mGfDzSXASzKDqbLnrqJHBdVQvvebofVqNImsGKM,29201
|
|
56
|
-
iam_validator/core/pr_commenter.py,sha256=
|
|
56
|
+
iam_validator/core/pr_commenter.py,sha256=hDUzn0eQJ3wlNSVbhMCOm2dlOhbS3Pohf8ZdeUYRlCk,32580
|
|
57
57
|
iam_validator/core/report.py,sha256=uMhUYv-8mNoTMZzD0F2buSQTxr4YIRh8UMZjvFq9tmc,37312
|
|
58
58
|
iam_validator/core/aws_service/__init__.py,sha256=UqMh4HUdGlx2QF5OoueJJ2UlCnhX4QW_x3KeE_bxRQc,735
|
|
59
59
|
iam_validator/core/aws_service/cache.py,sha256=DPuOOPPJC867KAYgV1e0RyQs_k3mtefMdYli3jPaN64,3589
|
|
@@ -62,10 +62,10 @@ iam_validator/core/aws_service/fetcher.py,sha256=TD9qQ4tQF4xdEGhVVADGgF8QlXe15R3
|
|
|
62
62
|
iam_validator/core/aws_service/parsers.py,sha256=gJzR7HCD8ItCWCCbguTQIZpPEdj2rdMwC7LPhu7ve14,5174
|
|
63
63
|
iam_validator/core/aws_service/patterns.py,sha256=gGc55Tn-EJ3cmcWtmYAZROUajKYz7DaMchYWGEhHpC0,1726
|
|
64
64
|
iam_validator/core/aws_service/storage.py,sha256=PrfKdvF60IL7E_8xYs_XwFoAJPRcVYw57FVLHCoqwVk,10429
|
|
65
|
-
iam_validator/core/aws_service/validators.py,sha256=
|
|
65
|
+
iam_validator/core/aws_service/validators.py,sha256=7izAvL92TsWpQePU7g9qvegT_F2xz8CqpbrnSgE40Xg,19890
|
|
66
66
|
iam_validator/core/config/__init__.py,sha256=CWSyIA7kEyzrskEenjYbs9Iih10BXRpiY9H2dHg61rU,2671
|
|
67
67
|
iam_validator/core/config/aws_api.py,sha256=HLIzOItQ0A37wxHcgWck6ZFO0wmNY8JNTiWMMK6JKYU,1248
|
|
68
|
-
iam_validator/core/config/aws_global_conditions.py,sha256=
|
|
68
|
+
iam_validator/core/config/aws_global_conditions.py,sha256=PO3zMdzM_QWZduxL3lV2nrmpcMEEwKASCUYHcqX0LBg,7363
|
|
69
69
|
iam_validator/core/config/category_suggestions.py,sha256=fopaZ9kXDrsLgi_r0pERrLwgdPPJl5VIiKvXtQK9tj0,8583
|
|
70
70
|
iam_validator/core/config/check_documentation.py,sha256=Adyt3Q4JSRlVNPWulXVlRIEorxXG_nt0mkPz_ySa8y4,15931
|
|
71
71
|
iam_validator/core/config/condition_requirements.py,sha256=1CeQJfWV-Y2ImW0Mq9YdrgvH-hj9IXe0gVOm3B36Rc8,10655
|
|
@@ -85,7 +85,7 @@ iam_validator/core/formatters/json.py,sha256=A7gZ8P32GEdbDvrSn6v56yQ4fOP_kyMaoFV
|
|
|
85
85
|
iam_validator/core/formatters/markdown.py,sha256=dk4STeY-tOEZsVrlmolIEqZvWYP9JhRtygxxNA49DEE,2293
|
|
86
86
|
iam_validator/core/formatters/sarif.py,sha256=03MHSyuZm9FlzaPeWg7wH-UTzzCDhSy6vMPrFpFNkS8,18884
|
|
87
87
|
iam_validator/integrations/__init__.py,sha256=7Hlor_X9j0NZaEjFuSvoXAAuSKQ-zgY19Rk-Dz3JpKo,616
|
|
88
|
-
iam_validator/integrations/github_integration.py,sha256=
|
|
88
|
+
iam_validator/integrations/github_integration.py,sha256=OZjVFkeEK0PYerqHFOuc0tFtTMmo78JhbqZgFduzq-8,67949
|
|
89
89
|
iam_validator/integrations/ms_teams.py,sha256=t2PlWuTDb6GGH-eDU1jnOKd8D1w4FCB68bahGA7MJcE,14475
|
|
90
90
|
iam_validator/sdk/__init__.py,sha256=AZLnfdn3A9AWb0pMhsbu3GAOAzt6rV7Fi3E3d9_3ZdI,6388
|
|
91
91
|
iam_validator/sdk/arn_matching.py,sha256=HSDpLltOYISq-SoPebAlM89mKOaUaghq_04urchEFDA,12778
|
|
@@ -99,8 +99,8 @@ iam_validator/utils/__init__.py,sha256=NveA2F3G1E6-ANZzFr7J6Q6u5mogvMp862iFokmYu
|
|
|
99
99
|
iam_validator/utils/cache.py,sha256=wOQKOBeoG6QqC5f0oXcHz63Cjtu_-SsSS-0pTSwyAiM,3254
|
|
100
100
|
iam_validator/utils/regex.py,sha256=xHoMECttb7qaMhts-c9b0GIxdhHNZTt-UBr7wNhWfzg,6219
|
|
101
101
|
iam_validator/utils/terminal.py,sha256=FsRaRMH_JAyDgXWBCOgOEhbS89cs17HCmKYoughq5io,724
|
|
102
|
-
iam_policy_validator-1.14.
|
|
103
|
-
iam_policy_validator-1.14.
|
|
104
|
-
iam_policy_validator-1.14.
|
|
105
|
-
iam_policy_validator-1.14.
|
|
106
|
-
iam_policy_validator-1.14.
|
|
102
|
+
iam_policy_validator-1.14.3.dist-info/METADATA,sha256=aH2RAojXqwEay7CKD89eRTVCHphTpvyXON4XUdMcRfg,34456
|
|
103
|
+
iam_policy_validator-1.14.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
104
|
+
iam_policy_validator-1.14.3.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
|
|
105
|
+
iam_policy_validator-1.14.3.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
|
|
106
|
+
iam_policy_validator-1.14.3.dist-info/RECORD,,
|
iam_validator/__version__.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This file is the single source of truth for the package version.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
__version__ = "1.14.
|
|
6
|
+
__version__ = "1.14.3"
|
|
7
7
|
# Parse version, handling pre-release suffixes like -rc, -alpha, -beta
|
|
8
8
|
_version_base = __version__.split("-", maxsplit=1)[0] # Remove pre-release suffix if present
|
|
9
9
|
__version_info__ = tuple(int(part) for part in _version_base.split("."))
|
|
@@ -37,7 +37,7 @@ class ConditionKeyValidationCheck(PolicyCheck):
|
|
|
37
37
|
resources = statement.get_resources()
|
|
38
38
|
|
|
39
39
|
# Extract all condition keys from all condition operators
|
|
40
|
-
for
|
|
40
|
+
for _, conditions in statement.condition.items():
|
|
41
41
|
for condition_key in conditions.keys():
|
|
42
42
|
# Validate this condition key against each action in the statement
|
|
43
43
|
for action in actions:
|
|
@@ -5,14 +5,104 @@ including actions, condition keys, and ARN formats.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
+
import re
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
11
12
|
from iam_validator.core.aws_service.parsers import ServiceParser
|
|
13
|
+
from iam_validator.core.constants import (
|
|
14
|
+
AWS_TAG_KEY_ALLOWED_CHARS,
|
|
15
|
+
AWS_TAG_KEY_MAX_LENGTH,
|
|
16
|
+
AWS_TAG_KEY_PLACEHOLDERS,
|
|
17
|
+
)
|
|
12
18
|
from iam_validator.core.models import ServiceDetail
|
|
13
19
|
|
|
14
20
|
logger = logging.getLogger(__name__)
|
|
15
21
|
|
|
22
|
+
# Pre-compiled regex for AWS tag key validation
|
|
23
|
+
# Uses centralized constants from iam_validator.core.constants
|
|
24
|
+
_TAG_KEY_PATTERN = re.compile(rf"^[{AWS_TAG_KEY_ALLOWED_CHARS}]{{1,{AWS_TAG_KEY_MAX_LENGTH}}}$")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _is_valid_tag_key(tag_key: str) -> bool:
|
|
28
|
+
"""Validate an AWS tag key format.
|
|
29
|
+
|
|
30
|
+
AWS tag keys must:
|
|
31
|
+
- Be 1-128 characters long
|
|
32
|
+
- Contain only: letters, numbers, spaces, and + - = . _ : / @
|
|
33
|
+
- Not be empty
|
|
34
|
+
|
|
35
|
+
Note: The 'aws:' prefix check is not done here as it's for the condition key prefix,
|
|
36
|
+
not the tag key portion (e.g., in 'ssm:resourceTag/owner', 'owner' is the tag key).
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
tag_key: The tag key portion to validate
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
True if valid AWS tag key format
|
|
43
|
+
"""
|
|
44
|
+
if not tag_key or len(tag_key) > AWS_TAG_KEY_MAX_LENGTH:
|
|
45
|
+
return False
|
|
46
|
+
return bool(_TAG_KEY_PATTERN.match(tag_key))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _matches_condition_key_pattern(condition_key: str, pattern: str) -> bool:
|
|
50
|
+
"""Check if a condition key matches a pattern with tag-key placeholders.
|
|
51
|
+
|
|
52
|
+
AWS service definitions use patterns like:
|
|
53
|
+
- `ssm:resourceTag/tag-key` or `ssm:resourceTag/${TagKey}` to match `ssm:resourceTag/owner`
|
|
54
|
+
- `aws:ResourceTag/${TagKey}` to match `aws:ResourceTag/Environment`
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
condition_key: The actual condition key from the policy (e.g., "ssm:resourceTag/owner")
|
|
58
|
+
pattern: The pattern from AWS service definition (e.g., "ssm:resourceTag/tag-key")
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
True if condition_key matches the pattern
|
|
62
|
+
"""
|
|
63
|
+
# Exact match (fast path)
|
|
64
|
+
if condition_key == pattern:
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# Check for tag-key placeholder patterns
|
|
68
|
+
for tag_placeholder in AWS_TAG_KEY_PLACEHOLDERS:
|
|
69
|
+
if tag_placeholder in pattern:
|
|
70
|
+
# Extract the prefix before the placeholder
|
|
71
|
+
prefix = pattern.split(tag_placeholder, 1)[0]
|
|
72
|
+
prefix_with_slash = prefix + "/"
|
|
73
|
+
# Check if condition_key starts with prefix and has a tag key after it
|
|
74
|
+
if condition_key.startswith(prefix_with_slash):
|
|
75
|
+
# Validate tag key format per AWS constraints
|
|
76
|
+
tag_key = condition_key[len(prefix_with_slash) :]
|
|
77
|
+
if _is_valid_tag_key(tag_key):
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _condition_key_in_list(condition_key: str, condition_keys: list[str]) -> bool:
|
|
84
|
+
"""Check if a condition key matches any key in the list, supporting patterns.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
condition_key: The condition key to check
|
|
88
|
+
condition_keys: List of condition keys (may include patterns)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True if condition_key matches any entry in the list
|
|
92
|
+
"""
|
|
93
|
+
# Fast path: check for exact match first (most common case)
|
|
94
|
+
if condition_key in condition_keys:
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
# Slower path: check patterns only if no exact match
|
|
98
|
+
for pattern in condition_keys:
|
|
99
|
+
# Skip exact matches (already checked above)
|
|
100
|
+
if pattern == condition_key:
|
|
101
|
+
continue
|
|
102
|
+
if _matches_condition_key_pattern(condition_key, pattern):
|
|
103
|
+
return True
|
|
104
|
+
return False
|
|
105
|
+
|
|
16
106
|
|
|
17
107
|
@dataclass
|
|
18
108
|
class ConditionKeyValidationResult:
|
|
@@ -134,7 +224,7 @@ class ServiceValidator:
|
|
|
134
224
|
action: str,
|
|
135
225
|
condition_key: str,
|
|
136
226
|
service_detail: ServiceDetail,
|
|
137
|
-
resources: list[str] | None = None,
|
|
227
|
+
resources: list[str] | None = None, # pylint: disable=unused-argument - kept for API compatibility
|
|
138
228
|
) -> ConditionKeyValidationResult:
|
|
139
229
|
"""Validate condition key against action and optionally resource types.
|
|
140
230
|
|
|
@@ -173,22 +263,23 @@ class ServiceValidator:
|
|
|
173
263
|
error_message=f"Invalid AWS global condition key: `{condition_key}`.",
|
|
174
264
|
)
|
|
175
265
|
|
|
176
|
-
# Check service-specific condition keys
|
|
177
|
-
if
|
|
266
|
+
# Check service-specific condition keys (with pattern matching for tag keys)
|
|
267
|
+
if service_detail.condition_keys and _condition_key_in_list(
|
|
268
|
+
condition_key, list(service_detail.condition_keys.keys())
|
|
269
|
+
):
|
|
178
270
|
return ConditionKeyValidationResult(is_valid=True)
|
|
179
271
|
|
|
180
272
|
# Check action-specific condition keys
|
|
181
273
|
if action_name in service_detail.actions:
|
|
182
274
|
action_detail = service_detail.actions[action_name]
|
|
183
|
-
if (
|
|
184
|
-
action_detail.action_condition_keys
|
|
185
|
-
and condition_key in action_detail.action_condition_keys
|
|
275
|
+
if action_detail.action_condition_keys and _condition_key_in_list(
|
|
276
|
+
condition_key, action_detail.action_condition_keys
|
|
186
277
|
):
|
|
187
278
|
return ConditionKeyValidationResult(is_valid=True)
|
|
188
279
|
|
|
189
280
|
# Check resource-specific condition keys
|
|
190
281
|
# Get resource types required by this action
|
|
191
|
-
if
|
|
282
|
+
if action_detail.resources:
|
|
192
283
|
for res_req in action_detail.resources:
|
|
193
284
|
resource_name = res_req.get("Name", "")
|
|
194
285
|
if not resource_name:
|
|
@@ -197,7 +288,7 @@ class ServiceValidator:
|
|
|
197
288
|
# Look up resource type definition
|
|
198
289
|
resource_type = service_detail.resources.get(resource_name)
|
|
199
290
|
if resource_type and resource_type.condition_keys:
|
|
200
|
-
if condition_key
|
|
291
|
+
if _condition_key_in_list(condition_key, resource_type.condition_keys):
|
|
201
292
|
return ConditionKeyValidationResult(is_valid=True)
|
|
202
293
|
|
|
203
294
|
# If it's a global key but the action has specific condition keys defined,
|
|
@@ -11,6 +11,8 @@ Last updated: 2025-01-17
|
|
|
11
11
|
import re
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
|
+
from iam_validator.core.constants import AWS_TAG_KEY_ALLOWED_CHARS
|
|
15
|
+
|
|
14
16
|
# AWS Global Condition Keys with Type Information
|
|
15
17
|
# These condition keys are available for use in IAM policies across all AWS services
|
|
16
18
|
# Format: {key: type} where type is one of: String, ARN, Bool, Date, IPAddress, Numeric
|
|
@@ -71,17 +73,18 @@ AWS_GLOBAL_CONDITION_KEYS = {
|
|
|
71
73
|
|
|
72
74
|
# Patterns that should be recognized (wildcards and tag-based keys)
|
|
73
75
|
# These allow things like aws:RequestTag/Department or aws:PrincipalTag/Environment
|
|
76
|
+
# Uses centralized tag key character class from constants
|
|
74
77
|
AWS_CONDITION_KEY_PATTERNS = [
|
|
75
78
|
{
|
|
76
|
-
"pattern":
|
|
79
|
+
"pattern": rf"^aws:RequestTag/[{AWS_TAG_KEY_ALLOWED_CHARS}]+$",
|
|
77
80
|
"description": "Tag keys in the request (for tag-based access control)",
|
|
78
81
|
},
|
|
79
82
|
{
|
|
80
|
-
"pattern":
|
|
83
|
+
"pattern": rf"^aws:ResourceTag/[{AWS_TAG_KEY_ALLOWED_CHARS}]+$",
|
|
81
84
|
"description": "Tags on the resource being accessed",
|
|
82
85
|
},
|
|
83
86
|
{
|
|
84
|
-
"pattern":
|
|
87
|
+
"pattern": rf"^aws:PrincipalTag/[{AWS_TAG_KEY_ALLOWED_CHARS}]+$",
|
|
85
88
|
"description": "Tags attached to the principal making the request",
|
|
86
89
|
},
|
|
87
90
|
]
|
|
@@ -154,7 +157,8 @@ _global_conditions_instance = None
|
|
|
154
157
|
|
|
155
158
|
def get_global_conditions() -> AWSGlobalConditions:
|
|
156
159
|
"""Get singleton instance of AWSGlobalConditions."""
|
|
157
|
-
global _global_conditions_instance
|
|
160
|
+
global _global_conditions_instance # pylint: disable=global-statement
|
|
161
|
+
|
|
158
162
|
if _global_conditions_instance is None:
|
|
159
163
|
_global_conditions_instance = AWSGlobalConditions()
|
|
160
164
|
return _global_conditions_instance
|
iam_validator/core/constants.py
CHANGED
|
@@ -147,3 +147,32 @@ RCP_SUPPORTED_SERVICES = frozenset(
|
|
|
147
147
|
|
|
148
148
|
# AWS Service Authorization Reference (for finding valid actions, resources, and condition keys)
|
|
149
149
|
AWS_SERVICE_AUTH_REF_URL = "https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html"
|
|
150
|
+
|
|
151
|
+
# ============================================================================
|
|
152
|
+
# AWS Tag Constraints
|
|
153
|
+
# ============================================================================
|
|
154
|
+
# Reference: https://docs.aws.amazon.com/tag-editor/latest/userguide/best-practices-and-strats.html
|
|
155
|
+
|
|
156
|
+
# --- Tag Key Constraints ---
|
|
157
|
+
# Allowed characters in AWS tag keys: letters, numbers, spaces, and + - = . _ : / @
|
|
158
|
+
# This is the character class for use in regex patterns
|
|
159
|
+
AWS_TAG_KEY_ALLOWED_CHARS = r"a-zA-Z0-9 +\-=._:/@"
|
|
160
|
+
|
|
161
|
+
# Maximum length for AWS tag keys (per AWS documentation)
|
|
162
|
+
AWS_TAG_KEY_MAX_LENGTH = 128
|
|
163
|
+
|
|
164
|
+
# Tag-key placeholder patterns used in AWS service definitions
|
|
165
|
+
# These patterns indicate where a tag key should be substituted
|
|
166
|
+
AWS_TAG_KEY_PLACEHOLDERS = ("/tag-key", "/${TagKey}", "/${tag-key}")
|
|
167
|
+
|
|
168
|
+
# --- Tag Value Constraints ---
|
|
169
|
+
# Allowed characters in AWS tag values: letters, numbers, spaces, and + - = . _ : / @
|
|
170
|
+
# Same character set as tag keys
|
|
171
|
+
AWS_TAG_VALUE_ALLOWED_CHARS = r"a-zA-Z0-9 +\-=._:/@"
|
|
172
|
+
|
|
173
|
+
# Maximum length for AWS tag values (per AWS documentation)
|
|
174
|
+
# Note: Tag values can be empty (minimum 0), unlike keys which must have at least 1 char
|
|
175
|
+
AWS_TAG_VALUE_MAX_LENGTH = 256
|
|
176
|
+
|
|
177
|
+
# Minimum length for AWS tag values (can be empty)
|
|
178
|
+
AWS_TAG_VALUE_MIN_LENGTH = 0
|
|
@@ -398,12 +398,15 @@ class PRCommenter:
|
|
|
398
398
|
logger.info("No inline comments to post (after diff filtering)")
|
|
399
399
|
# Still run cleanup to delete any stale comments from resolved findings
|
|
400
400
|
# (unless skip_cleanup is set for streaming mode)
|
|
401
|
+
# Use APPROVE event to dismiss any previous REQUEST_CHANGES review
|
|
401
402
|
if validated_files and self.cleanup_old_comments:
|
|
402
|
-
logger.debug(
|
|
403
|
+
logger.debug(
|
|
404
|
+
"Running cleanup for stale comments and approving PR (no blocking issues)..."
|
|
405
|
+
)
|
|
403
406
|
await self.github.update_or_create_review_comments(
|
|
404
407
|
comments=[],
|
|
405
408
|
body="",
|
|
406
|
-
event=ReviewEvent.
|
|
409
|
+
event=ReviewEvent.APPROVE,
|
|
407
410
|
identifier=self.REVIEW_IDENTIFIER,
|
|
408
411
|
validated_files=validated_files,
|
|
409
412
|
skip_cleanup=False, # Explicitly run cleanup
|
|
@@ -421,7 +424,7 @@ class PRCommenter:
|
|
|
421
424
|
for issue in result.issues
|
|
422
425
|
)
|
|
423
426
|
|
|
424
|
-
event = ReviewEvent.REQUEST_CHANGES if has_blocking_issues else ReviewEvent.
|
|
427
|
+
event = ReviewEvent.REQUEST_CHANGES if has_blocking_issues else ReviewEvent.APPROVE
|
|
425
428
|
logger.info(
|
|
426
429
|
f"Creating PR review with {len(inline_comments)} comments, event: {event.value}"
|
|
427
430
|
)
|
|
File without changes
|
{iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{iam_policy_validator-1.14.1.dist-info → iam_policy_validator-1.14.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|