iam-policy-validator 1.4.0__py3-none-any.whl → 1.5.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 (33) hide show
  1. {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.5.0.dist-info}/METADATA +18 -19
  2. {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.5.0.dist-info}/RECORD +31 -20
  3. iam_validator/__version__.py +1 -1
  4. iam_validator/checks/__init__.py +13 -3
  5. iam_validator/checks/action_condition_enforcement.py +1 -6
  6. iam_validator/checks/condition_key_validation.py +21 -1
  7. iam_validator/checks/full_wildcard.py +67 -0
  8. iam_validator/checks/principal_validation.py +497 -3
  9. iam_validator/checks/sensitive_action.py +178 -0
  10. iam_validator/checks/service_wildcard.py +105 -0
  11. iam_validator/checks/utils/sensitive_action_matcher.py +39 -31
  12. iam_validator/checks/wildcard_action.py +62 -0
  13. iam_validator/checks/wildcard_resource.py +131 -0
  14. iam_validator/commands/download_services.py +3 -8
  15. iam_validator/commands/validate.py +28 -2
  16. iam_validator/core/aws_fetcher.py +25 -12
  17. iam_validator/core/check_registry.py +15 -21
  18. iam_validator/core/config/__init__.py +83 -0
  19. iam_validator/core/config/aws_api.py +35 -0
  20. iam_validator/core/config/condition_requirements.py +535 -0
  21. iam_validator/core/config/defaults.py +390 -0
  22. iam_validator/core/config/principal_requirements.py +421 -0
  23. iam_validator/core/config/sensitive_actions.py +133 -0
  24. iam_validator/core/config/service_principals.py +95 -0
  25. iam_validator/core/config/wildcards.py +124 -0
  26. iam_validator/core/config_loader.py +29 -9
  27. iam_validator/core/formatters/enhanced.py +11 -5
  28. iam_validator/core/formatters/sarif.py +78 -14
  29. iam_validator/checks/security_best_practices.py +0 -536
  30. iam_validator/core/defaults.py +0 -393
  31. {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.5.0.dist-info}/WHEEL +0 -0
  32. {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.5.0.dist-info}/entry_points.txt +0 -0
  33. {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -33,6 +33,7 @@ from typing import Any
33
33
 
34
34
  import httpx
35
35
 
36
+ from iam_validator.core.config import AWS_SERVICE_REFERENCE_BASE_URL
36
37
  from iam_validator.core.models import ServiceDetail, ServiceInfo
37
38
 
38
39
  logger = logging.getLogger(__name__)
@@ -121,7 +122,7 @@ class CompiledPatterns:
121
122
  class AWSServiceFetcher:
122
123
  """Fetches AWS service information from the AWS service reference API with enhanced performance features."""
123
124
 
124
- BASE_URL = "https://servicereference.us-east-1.amazonaws.com/"
125
+ BASE_URL = AWS_SERVICE_REFERENCE_BASE_URL
125
126
 
126
127
  # Common AWS services to pre-fetch
127
128
  # All other services will be fetched on-demand (lazy loading if found in policies)
@@ -797,8 +798,15 @@ class AWSServiceFetcher:
797
798
 
798
799
  async def validate_condition_key(
799
800
  self, action: str, condition_key: str
800
- ) -> tuple[bool, str | None]:
801
- """Validate condition key with optimized caching."""
801
+ ) -> tuple[bool, str | None, str | None]:
802
+ """Validate condition key with optimized caching.
803
+
804
+ Returns:
805
+ Tuple of (is_valid, error_message, warning_message)
806
+ - is_valid: True if key is valid (even with warning)
807
+ - error_message: Error message if invalid (is_valid=False)
808
+ - warning_message: Warning message if valid but not recommended
809
+ """
802
810
  try:
803
811
  from iam_validator.core.aws_global_conditions import get_global_conditions
804
812
 
@@ -814,6 +822,7 @@ class AWSServiceFetcher:
814
822
  return (
815
823
  False,
816
824
  f"Invalid AWS global condition key: '{condition_key}'.",
825
+ None,
817
826
  )
818
827
 
819
828
  # Fetch service detail (cached)
@@ -821,7 +830,7 @@ class AWSServiceFetcher:
821
830
 
822
831
  # Check service-specific condition keys
823
832
  if condition_key in service_detail.condition_keys:
824
- return True, None
833
+ return True, None, None
825
834
 
826
835
  # Check action-specific condition keys
827
836
  if action_name in service_detail.actions:
@@ -830,29 +839,33 @@ class AWSServiceFetcher:
830
839
  action_detail.action_condition_keys
831
840
  and condition_key in action_detail.action_condition_keys
832
841
  ):
833
- return True, None
842
+ return True, None, None
834
843
 
835
844
  # If it's a global key but the action has specific condition keys defined,
836
- # check if the global key is explicitly listed in the action's supported keys
845
+ # AWS allows it but the key may not be available in every request context
837
846
  if is_global_key and action_detail.action_condition_keys is not None:
838
- return (
839
- False,
840
- f"Condition key '{condition_key}' is not supported by action '{action}'. "
841
- f"This action has a specific set of supported condition keys.",
847
+ warning_msg = (
848
+ f"Global condition key '{condition_key}' is used with action '{action}'. "
849
+ f"While global condition keys can be used across all AWS services, "
850
+ f"the key may not be available in every request context. "
851
+ f"Verify that '{condition_key}' is available for this specific action's request context. "
852
+ f"Consider using '*IfExists' operators (e.g., StringEqualsIfExists) if the key might be missing."
842
853
  )
854
+ return True, None, warning_msg
843
855
 
844
856
  # If it's a global key and action doesn't define specific keys, allow it
845
857
  if is_global_key:
846
- return True, None
858
+ return True, None, None
847
859
 
848
860
  return (
849
861
  False,
850
862
  f"Condition key '{condition_key}' is not valid for action '{action}'",
863
+ None,
851
864
  )
852
865
 
853
866
  except Exception as e:
854
867
  logger.error(f"Error validating condition key {condition_key} for {action}: {e}")
855
- return False, f"Failed to validate condition key: {str(e)}"
868
+ return False, f"Failed to validate condition key: {str(e)}", None
856
869
 
857
870
  async def clear_caches(self) -> None:
858
871
  """Clear all caches (memory and disk)."""
@@ -440,27 +440,21 @@ def create_default_registry(
440
440
 
441
441
  if include_builtin_checks:
442
442
  # Import and register built-in checks
443
- from iam_validator.checks import (
444
- ActionConditionEnforcementCheck,
445
- ActionResourceConstraintCheck,
446
- ActionValidationCheck,
447
- ConditionKeyValidationCheck,
448
- PolicySizeCheck,
449
- PrincipalValidationCheck,
450
- ResourceValidationCheck,
451
- SecurityBestPracticesCheck,
452
- SidUniquenessCheck,
453
- )
454
-
455
- registry.register(ActionValidationCheck())
456
- registry.register(ConditionKeyValidationCheck())
457
- registry.register(ResourceValidationCheck())
458
- registry.register(SecurityBestPracticesCheck())
459
- registry.register(ActionConditionEnforcementCheck())
460
- registry.register(ActionResourceConstraintCheck())
461
- registry.register(SidUniquenessCheck())
462
- registry.register(PolicySizeCheck())
463
- registry.register(PrincipalValidationCheck())
443
+ from iam_validator import checks
444
+
445
+ registry.register(checks.ActionValidationCheck())
446
+ registry.register(checks.ConditionKeyValidationCheck())
447
+ registry.register(checks.ResourceValidationCheck())
448
+ registry.register(checks.WildcardActionCheck())
449
+ registry.register(checks.WildcardResourceCheck())
450
+ registry.register(checks.FullWildcardCheck())
451
+ registry.register(checks.ServiceWildcardCheck())
452
+ registry.register(checks.SensitiveActionCheck())
453
+ registry.register(checks.ActionConditionEnforcementCheck())
454
+ registry.register(checks.ActionResourceConstraintCheck())
455
+ registry.register(checks.SidUniquenessCheck())
456
+ registry.register(checks.PolicySizeCheck())
457
+ registry.register(checks.PrincipalValidationCheck())
464
458
 
465
459
  # Note: SID uniqueness check is registered above but its actual execution
466
460
  # happens at the policy level in _validate_policy_with_registry() since it
@@ -0,0 +1,83 @@
1
+ """
2
+ Core configuration modules for IAM Policy Validator.
3
+
4
+ This package contains default configuration data used by validators, organized into
5
+ logical modules for better maintainability and performance.
6
+
7
+ All configuration is defined as Python code (not YAML/JSON) for:
8
+ - Faster loading (no parsing overhead)
9
+ - Better PyPI packaging (no data files to manage)
10
+ - Type hints and IDE support
11
+ - Compiled to .pyc for even faster imports
12
+
13
+ Performance benefits:
14
+ - 5-10x faster than YAML parsing
15
+ - Zero runtime parsing overhead
16
+ - Lazy loading support
17
+ - O(1) frozenset lookups
18
+ """
19
+
20
+ from iam_validator.core.config.aws_api import (
21
+ AWS_SERVICE_REFERENCE_BASE_URL,
22
+ get_service_reference_url,
23
+ )
24
+ from iam_validator.core.config.condition_requirements import (
25
+ ALL_CONDITION_REQUIREMENTS,
26
+ DEFAULT_CONDITION_REQUIREMENTS,
27
+ get_all_requirement_names,
28
+ get_default_requirements,
29
+ get_requirement,
30
+ get_requirements_by_names,
31
+ get_requirements_by_severity,
32
+ )
33
+ from iam_validator.core.config.defaults import DEFAULT_CONFIG
34
+ from iam_validator.core.config.principal_requirements import (
35
+ ALL_PRINCIPAL_REQUIREMENTS,
36
+ DEFAULT_ENABLED_REQUIREMENTS,
37
+ get_all_principal_requirement_names,
38
+ get_default_principal_requirements,
39
+ get_principal_requirement,
40
+ get_principal_requirements_by_names,
41
+ get_principal_requirements_by_severity,
42
+ )
43
+ from iam_validator.core.config.sensitive_actions import (
44
+ DEFAULT_SENSITIVE_ACTIONS,
45
+ get_sensitive_actions,
46
+ )
47
+ from iam_validator.core.config.service_principals import DEFAULT_SERVICE_PRINCIPALS
48
+ from iam_validator.core.config.wildcards import (
49
+ DEFAULT_ALLOWED_WILDCARDS,
50
+ DEFAULT_SERVICE_WILDCARDS,
51
+ )
52
+
53
+ __all__ = [
54
+ # Default configuration
55
+ "DEFAULT_CONFIG",
56
+ # AWS API endpoints
57
+ "AWS_SERVICE_REFERENCE_BASE_URL",
58
+ "get_service_reference_url",
59
+ # Sensitive actions
60
+ "DEFAULT_SENSITIVE_ACTIONS",
61
+ "get_sensitive_actions",
62
+ # Condition requirements (for actions)
63
+ "DEFAULT_CONDITION_REQUIREMENTS",
64
+ "ALL_CONDITION_REQUIREMENTS",
65
+ "get_default_requirements",
66
+ "get_requirement",
67
+ "get_all_requirement_names",
68
+ "get_requirements_by_names",
69
+ "get_requirements_by_severity",
70
+ # Principal requirements (for principals)
71
+ "ALL_PRINCIPAL_REQUIREMENTS",
72
+ "DEFAULT_ENABLED_REQUIREMENTS",
73
+ "get_default_principal_requirements",
74
+ "get_principal_requirement",
75
+ "get_all_principal_requirement_names",
76
+ "get_principal_requirements_by_names",
77
+ "get_principal_requirements_by_severity",
78
+ # Wildcards
79
+ "DEFAULT_ALLOWED_WILDCARDS",
80
+ "DEFAULT_SERVICE_WILDCARDS",
81
+ # Service principals
82
+ "DEFAULT_SERVICE_PRINCIPALS",
83
+ ]
@@ -0,0 +1,35 @@
1
+ """
2
+ AWS API configuration constants.
3
+
4
+ This module centralizes AWS API endpoints and related configuration
5
+ used throughout the IAM Policy Validator.
6
+ """
7
+
8
+ # AWS Service Reference API base URL
9
+ # This is the official AWS service reference that provides action, resource, and condition key metadata
10
+ AWS_SERVICE_REFERENCE_BASE_URL = "https://servicereference.us-east-1.amazonaws.com/"
11
+
12
+ # Alternative endpoints for different regions (currently not used, but available for future expansion)
13
+ AWS_SERVICE_REFERENCE_ENDPOINTS = {
14
+ "us-east-1": "https://servicereference.us-east-1.amazonaws.com/",
15
+ # Add other regional endpoints if they become available
16
+ }
17
+
18
+
19
+ def get_service_reference_url(region: str = "us-east-1") -> str:
20
+ """
21
+ Get the AWS Service Reference API URL for a specific region.
22
+
23
+ Args:
24
+ region: AWS region (default: us-east-1)
25
+
26
+ Returns:
27
+ The service reference base URL for the specified region
28
+
29
+ Example:
30
+ >>> get_service_reference_url()
31
+ 'https://servicereference.us-east-1.amazonaws.com/'
32
+ >>> get_service_reference_url("us-east-1")
33
+ 'https://servicereference.us-east-1.amazonaws.com/'
34
+ """
35
+ return AWS_SERVICE_REFERENCE_ENDPOINTS.get(region, AWS_SERVICE_REFERENCE_BASE_URL)