Configuration¶
IAM Policy Validator works with sensible defaults but supports full customization through YAML configuration files.
Quick Start¶
No configuration needed! The validator works out-of-the-box.
To customize, create iam-validator.yaml:
settings:
fail_on_severity: [error, critical, high]
wildcard_action:
severity: critical
Configuration File Discovery¶
The validator automatically searches for configuration in this order:
--configflag (explicit path)- Current directory:
iam-validator.yaml,.iam-validator.yaml - Parent directories (walks up to root)
- Home directory
Settings¶
fail_on_severity¶
Control which severities cause validation failures:
settings:
fail_on_severity: [error, critical, high]
Severity Levels:
| Category | Levels |
|---|---|
| IAM Validity | error, warning, info |
| Security | critical, high, medium, low |
Presets¶
# Strict - fail on everything
fail_on_severity: [error, warning, info, critical, high, medium, low]
# Default - serious issues only
fail_on_severity: [error, critical]
# Relaxed - IAM errors only
fail_on_severity: [error]
hide_severities¶
Hide specific severity levels from all output to reduce noise:
settings:
# Hide low and info severity findings globally
hide_severities: [low, info]
Hidden issues won't appear in:
- Console output
- JSON/SARIF reports
- GitHub PR comments
- Any other output format
Per-check override: You can also set hide_severities on individual checks to override the global setting:
settings:
hide_severities: [info] # Global: hide info
wildcard_resource:
# Override: hide low severity for this check only
# (useful when conditions reduce risk to LOW)
hide_severities: [low]
suppress_superseded_findings¶
When a statement contains Action: "*" and Resource: "*" with no conditions, every
other check produces a redundant finding — the root cause and the fix are always the
same: scope down the wildcard. This setting collapses all of that noise into one
critical finding from full_wildcard.
settings:
suppress_superseded_findings: true # default: true
What gets suppressed
When full_wildcard fires on a statement, all other findings for that statement
only are dropped — both statement-level checks (built-in and custom) and policy-level
check findings that reference that statement index. The suppressed check IDs are listed
inside the full_wildcard finding message so nothing is lost silently:
Statement allows all actions on all resources - CRITICAL SECURITY RISK
**20 checks suppressed** for this statement (abac_enforcement,
action_condition_enforcement, action_resource_matching, …).
Scope the statement and re-run to see remaining findings.
What is never suppressed
| Statement | Suppression |
|---|---|
Deny */* |
No — full_wildcard only fires on Allow |
Allow NotAction: "*" |
No — inverted semantics require full analysis |
| Sibling statements in the same policy | No — only the */* statement is short-circuited |
full_wildcard itself |
Never suppressed |
Conditions do not prevent suppression
Allow */* + Condition: {...} is still treated as a full-wildcard statement.
The condition does not change the root cause or the fix, so suppression still
applies. The full_wildcard finding lists all suppressed checks so nothing is
lost silently.
Custom checks
Custom checks loaded via --custom-checks-dir are suppressed automatically — no
changes to the check code required.
Opt out
settings:
suppress_superseded_findings: false
PR comment fingerprint churn
Switching from false to true (or vice versa) causes a one-time churn of
existing PR comments anchored to the now-suppressed (or now-visible) check IDs.
Re-run the validator once after changing this setting to refresh comment state.
Check Configuration¶
Disable a Check¶
policy_size:
enabled: false
Change Severity¶
wildcard_action:
severity: critical
Custom Messages¶
wildcard_action:
message: "Wildcard actions violate security policy SEC-001"
suggestion: |
Replace with specific actions.
Contact security@company.com for guidance.
Action Condition Enforcement¶
Require specific conditions for sensitive actions:
action_condition_enforcement:
enabled: true
action_condition_requirements:
- actions: ["iam:PassRole"]
required_conditions:
- condition_key: "iam:PassedToService"
description: "Restrict which services can assume the role"
Principal Validation¶
For resource policies and trust policies, validate Principal elements:
principal_validation:
enabled: true
# Block wildcard principal entirely (default: false)
# When false: allows "*" if appropriate conditions are present
# When true: blocks "*" regardless of conditions
block_wildcard_principal: false
# Block {"Service": "*"} patterns (default: true)
# This is a dangerous pattern that allows ANY AWS service
block_service_principal_wildcard: true
# Explicit block list (evaluated after service principal wildcard check)
blocked_principals:
- "arn:aws:iam::*:root"
# Whitelist mode (when set, only these principals are allowed)
allowed_principals:
- "arn:aws:iam::123456789012:*"
# Service principals whitelist (supports glob patterns)
allowed_service_principals:
- "aws:*" # All AWS service principals
Principal Condition Requirements¶
Require specific conditions when certain principals are used:
principal_validation:
principal_condition_requirements:
# Require source verification for wildcard principals
- principals: ["*"]
required_conditions:
any_of: # At least ONE must be present
- condition_key: "aws:SourceArn"
- condition_key: "aws:SourceAccount"
# Require MFA for root account access
- principals: ["arn:aws:iam::*:root"]
required_conditions:
all_of: # ALL must be present
- condition_key: "aws:MultiFactorAuthPresent"
expected_value: true
# Forbid specific conditions
- principals: ["*"]
required_conditions:
none_of: # NONE should be present
- condition_key: "aws:SecureTransport"
expected_value: false
Use Cases¶
Strict mode (block all wildcards):
principal_validation:
block_wildcard_principal: true
block_service_principal_wildcard: true
Permissive mode (allow wildcards with conditions):
principal_validation:
block_wildcard_principal: false
principal_condition_requirements:
- principals: ["*"]
required_conditions:
any_of:
- condition_key: "aws:SourceArn"
- condition_key: "aws:SourceAccount"
- condition_key: "aws:PrincipalOrgID"
Custom Checks¶
Load custom checks from a directory:
settings:
custom_checks_dir: "./my-checks"
checks:
my_custom_check:
enabled: true
severity: high
Environment Variables¶
The validator supports environment variables for configuration:
| Variable | Description | Example |
|---|---|---|
IAM_VALIDATOR_CONFIG |
Path to configuration file | /etc/iam-validator/config.yaml |
IAM_VALIDATOR_MCP_INSTRUCTIONS |
Custom instructions for MCP server | "Require MFA for all actions" |
AWS_REGION |
AWS region for Access Analyzer | us-east-1 |
AWS_PROFILE |
AWS profile for credentials | production |
Configuration Precedence¶
Configuration is applied in this order (later overrides earlier):
- Built-in defaults (from Python modules)
- Configuration file (YAML)
- Environment variables
- CLI arguments (highest priority)
Complete Configuration Reference¶
Global Settings¶
All settings under the settings key:
settings:
# Validation behavior
fail_fast: false # Stop on first error (default: false)
parallel: true # Enable parallel execution (default: true)
max_workers: null # Max concurrent workers (default: auto)
# Failure criteria
fail_on_severity: # Severities that cause exit code 1
- error # IAM validity errors
- critical # Critical security issues
- high # High severity security issues
# - medium # Uncomment to fail on medium
# - warning # Uncomment to fail on warnings
# Output filtering
hide_severities:
null # Hide these severities from output
# Example: [low, info]
# Noise reduction
suppress_superseded_findings: true # Collapse */* noise into one finding (default: true)
# AWS service definitions
aws_services_dir: null # Path to offline service definitions
cache_enabled: true # Cache AWS definitions (default: true)
cache_ttl_hours: 168 # Cache TTL in hours (default: 7 days)
# Template variable support
allow_template_variables: true # Support ${var.name} in ARNs
# GitHub integration
severity_labels: # Map severities to PR labels
error: "iam-validity-error"
critical: "iam-security-critical"
high: "iam-security-high"
# Optional run scope tag (1-32 chars, [A-Za-z0-9._-]) appended to PR
# summary, review, analyzer, and ignored-findings markers. Set this
# when you run the validator multiple times against the same PR (e.g.
# one run per policy type) so each run keeps its own comment thread
# instead of overwriting the others. Unset by default.
comment_tag: null
# Custom checks
custom_checks_dir: null # Auto-discover checks from directory
# Ignore settings
ignore_settings:
enabled: true
allowed_users: [] # Users allowed to add ignore comments
post_denial_feedback: false # Post feedback on denied ignores
# Documentation
documentation:
base_url: null # Custom docs base URL
include_aws_docs: true # Include links to AWS docs
Check Configuration¶
Each check can be configured at the top level using its check_id:
# Common options for all checks
<check_id>:
enabled: true # Enable/disable check (default: true)
severity: medium # Override default severity
description: "Custom desc" # Override description
message: "Custom message" # Override issue message
suggestion: "How to fix" # Override suggestion text
hide_severities: [low] # Per-check severity filtering
# Ignore patterns (available for ALL checks)
ignore_patterns:
# Ignore by file path (regex)
- filepath: "^test/.*"
# Ignore by action (regex)
- action: "^s3:Get.*"
# Ignore by resource ARN (regex)
- resource: "arn:aws:s3:::.*-test-.*"
# Ignore by statement SID
- sid: "AllowReadOnlyAccess"
# Combine conditions (AND logic)
- filepath: "^dev/.*"
action: "^s3:.*"
# Lists within patterns (OR logic)
- filepath:
- "^test/.*"
- "^examples/.*"
Built-in Checks¶
All 22 built-in checks with their default settings:
AWS Validation Checks¶
| Check ID | Default Severity | Description |
|---|---|---|
action_validation |
error | Actions exist in AWS services |
condition_key_validation |
error | Condition keys are valid for actions |
condition_type_mismatch |
error | Operator-value type match + format validation |
resource_validation |
error | ARN format is valid |
principal_validation |
high | Principal format (resource policies) |
policy_structure |
error | Required fields, valid values, version check |
policy_size |
error | Policy size limits (including SCP-specific) |
sid_uniqueness |
error | SIDs are unique across statements |
set_operator_validation |
error | ForAllValues/ForAnyValue used correctly |
mfa_condition_antipattern |
warning | MFA anti-patterns detected |
trust_policy_validation |
high | Trust policy structure + confused deputy |
not_principal_validation |
warning | NotPrincipal usage patterns |
action_resource_matching |
medium | Actions match resource types |
policy_type_validation |
error | Policy matches declared type |
Security Best Practices Checks¶
| Check ID | Default Severity | Description |
|---|---|---|
wildcard_action |
medium | Action: "*" detection |
wildcard_resource |
medium | Resource: "*" detection |
full_wildcard |
critical | Action + Resource: "*" (admin access) |
service_wildcard |
high | s3:* style wildcards |
sensitive_action |
medium | 490+ privilege escalation actions |
action_condition_enforcement |
high | Sensitive actions require conditions |
not_action_not_resource |
high | Dangerous NotAction/NotResource |
Check-Specific Options¶
wildcard_resource¶
wildcard_resource:
enabled: true
severity: medium
# Actions allowed with Resource: "*"
allowed_wildcards:
- "ec2:Describe*"
- "s3:List*"
- "iam:Get*"
- "cloudwatch:Get*"
service_wildcard¶
service_wildcard:
enabled: true
severity: high
# Services allowed to use wildcards
allowed_services:
- "logs"
- "cloudwatch"
- "xray"
sensitive_action¶
sensitive_action:
enabled: true
severity: medium
# Filter by category
categories:
- credential_exposure
- priv_esc
- data_access
- resource_exposure
# Category-specific severities
category_severities:
credential_exposure: high
priv_esc: critical
action_condition_enforcement¶
action_condition_enforcement:
enabled: true
severity: high
# Custom requirements (see full-reference-config.yaml)
requirements:
- actions: ["iam:PassRole"]
required_conditions:
- condition_key: "iam:PassedToService"
description: "Restrict which services can use the role"
principal_validation¶
principal_validation:
enabled: true
severity: high
block_wildcard_principal: false
block_service_principal_wildcard: true
blocked_principals: []
allowed_principals: []
allowed_service_principals:
- "aws:*"
# Condition requirements for principals
principal_condition_requirements:
- principals: ["*"]
required_conditions:
any_of:
- condition_key: "aws:SourceArn"
- condition_key: "aws:SourceAccount"
trust_policy_validation¶
trust_policy_validation:
enabled: true
severity: high
The confused deputy detection is built-in and automatically checks service principals in trust policies for missing aws:SourceArn or aws:SourceAccount conditions. Only compute-bound services (ec2, lambda, edgelambda) are automatically excluded.
Opt-in Check
Trust policy validation is enabled when using --policy-type TRUST_POLICY. The validator auto-detects trust policies and suggests using this flag.
not_principal_validation¶
not_principal_validation:
enabled: true
severity: warning
Detects NotPrincipal usage patterns: NotPrincipal with Effect: Allow is flagged as an error (not supported by AWS), while NotPrincipal with Effect: Deny is flagged as a warning (valid but deprecated).
policy_size¶
policy_size:
enabled: true
severity: error
policy_type: "managed" # managed, inline_user, inline_group, inline_role
# Override default size limits
size_limits:
managed: 6144
inline_user: 2048
inline_group: 5120
inline_role: 10240
SCP Size Validation
When using --policy-type SERVICE_CONTROL_POLICY, the SCP-specific size limit of 5,120 characters is enforced separately, which is stricter than the managed policy limit of 6,144 characters.
Policy Type Resolution¶
When the validator runs, each policy file gets a resolved PolicyType that
drives type-specific checks (for example policy_size limits and the
policy_type_validation rules). There are two mutually exclusive modes:
- If
--policy-typeis supplied on the CLI — that value is applied to every policy in the run, full stop. Auto-detection and thepolicy_types:glob mapping are skipped. Use this when your pipeline only ever validates one type of policy. - If
--policy-typeis omitted — the type is resolved per file in this priority order: policy_types:glob mapping in the config (first match wins).- Content auto-detection: a trust-shaped statement (Principal + exact
sts:AssumeRole*action +Effect: Allow+ no specific resource ARN) resolves toTRUST_POLICY; any other Principal/NotPrincipal resolves toRESOURCE_POLICY; otherwiseIDENTITY_POLICY. - Default fallback:
IDENTITY_POLICY.
SCP and RCP cannot be auto-detected from content alone (they look structurally identical to identity/resource policies). Use the glob mapping or the explicit flag to drive those types.
policy_types: — per-file glob mapping¶
policy_types:
- pattern: "**/scp/*.json"
type: SERVICE_CONTROL_POLICY
- pattern: "**/rcp/*.json"
type: RESOURCE_CONTROL_POLICY
- pattern: "**/trust-policies/*.json"
type: TRUST_POLICY
- Patterns are matched against the POSIX form of each policy file path.
- A leading
**/is stripped automatically so**/scp/*.jsonalso matchesscp/org.jsonat the top of the scan. - First match wins; the list is only consulted when
--policy-typeis not provided on the CLI.
Debugging the resolved type¶
Run with --log-level debug (or --verbose) to see exactly what type each
policy got and why:
policy_type=TRUST_POLICY source=cli-flag file=trust.json
policy_type=SERVICE_CONTROL_POLICY source=config-glob pattern_present=true pattern_len=14 file=org.json
policy_type=RESOURCE_POLICY source=auto-detect file=s3-bucket.json
policy_type=IDENTITY_POLICY source=default file=ro.json
Only the file basename is logged (not the absolute path) and the
config-glob line reports pattern_present=true plus pattern_len=<n>
instead of the raw glob — this keeps the debug output free of
user-controlled content while still letting you grep by source:
iam-validator validate ... --verbose 2>&1 | rg 'source='. The glob
itself is already visible in your own iam-validator.yaml, so there is
no information loss for auditing.
Full Reference¶
See examples/configs/full-reference-config.yaml for all available options with detailed comments.