# IAM Role With Wildcard Principal

> An IAM role whose trust policy contains a "Principal": "*" statement lets any AWS principal assume it, so ZopNight raises critical finding RC-080 the moment the discoverer stamps has_wildcard_principal as true. With no verdict recorded the rule abstains, and the fix is scoping the principal to exact accounts or services.

Source: https://zop.dev/integrations/aws/recommendations/iam-role-with-wildcard-principal
Updated: 2026-08-19

---

## What a wildcard principal hands out

A role's trust policy names who may assume it. Writing `"Principal": "*"` into a statement
names everyone: any AWS account, any user, and (depending on the actions involved)
unauthenticated requests. Whatever permissions the role carries are now effectively public,
which is why this is one of the few findings ZopNight files at critical severity. A wildcard
that slipped in during debugging ("just make it work, we'll tighten it later") looks exactly
like one an attacker would plant.

## From trust-policy scan to finding

The discoverer parses each role's trust policy during inventory and records whether any
statement carries the wildcard, writing `has_wildcard_principal` onto the role as a string
verdict. RC-080 fires only when that verdict is present and reads true.

When no verdict was recorded, the rule stays quiet. That is deliberate fail-closed
behaviour, and it has history: an earlier revision read the flag from a tag field no
producer ever populated, so the rule could never fire at all. Reading the field the
discoverer actually writes is what brought it to life.

## Hunting wildcard trust policies yourself

```bash
aws iam list-roles --output json | jq -r '.Roles[]
  | select(.AssumeRolePolicyDocument.Statement[]?
      | (.Principal == "*") or (.Principal.AWS? == "*"))
  | .RoleName'
```

Both spellings matter. A bare `"Principal": "*"` and the nested `"AWS": "*"` form grant
the same thing, and audits that grep for only one of them miss half the exposure.

## Narrowing the principal down

Remediation is replacement, not deletion. Substitute the wildcard with the exact account
IDs, role ARNs or service principals that genuinely need to assume the role, then add
condition keys as a second seatbelt: `aws:PrincipalOrgID` to fence access inside your
organization, or `aws:SourceAccount` where a service acts on your behalf.

Before saving, check CloudTrail for `AssumeRole` events against the role. If unfamiliar
principals have been assuming it, the task is no longer hygiene; treat it as a potential
compromise, rotate whatever the role could reach, and then close the trust policy.
