Skip to content

feat(common): add is_iam_policy_principal_scoped_by_condition helper#208

Merged
NathanGallet-dd merged 2 commits into
mainfrom
dd/sns-condition-scoping-helper
Jun 25, 2026
Merged

feat(common): add is_iam_policy_principal_scoped_by_condition helper#208
NathanGallet-dd merged 2 commits into
mainfrom
dd/sns-condition-scoping-helper

Conversation

@NathanGallet-dd

Copy link
Copy Markdown
Contributor

Adds is_iam_policy_principal_scoped_by_condition and the supporting condition_keys_scoping_principal set to the shared library.

The helper returns true when an IAM policy statement's Condition block genuinely restricts the caller — covering source-resource (aws:SourceArn), account/org, VPC, network, and principal-identity keys. Three checks prevent false negatives:

  • Negating operators (containing "not") are rejected, so StringNotEquals:SourceAccount("X") still reads as "public to everyone except X".
  • ...IfExists operators are rejected; they pass silently when the key is absent, providing no guaranteed restriction.
  • A wildcard value ("") is rejected; aws:SourceArn:"" restricts nothing.

This is a superset of is_access_limited_to_an_account_id (account-id scoping only, presence-based) and is intended for rules that need to avoid false positives on legitimate AWS service-integration patterns such as S3/CloudTrail -> SNS event notifications.

Resources checked in the condition:

Family Keys What they restrict
Source resource aws:SourceArn, aws:SourceOwner Only a specific AWS resource (like one S3 bucket) can trigger the action
Source account/org aws:SourceAccount, aws:SourceOrgID, aws:SourceOrgPaths,aws:ResourceAccount,aws:VpceAccount Only a specific AWS account or org can call
Network aws:SourceVpc, aws:SourceVpce, aws:SourceIp, aws:VpcSourceIp Only calls from a specific VPC or IP range
Principal identity aws:PrincipalArn, aws:PrincipalAccount, aws:PrincipalOrgID,aws:PrincipalOrgPaths Only calls from a specific IAM principal or org

Adds is_iam_policy_principal_scoped_by_condition and the supporting
condition_keys_scoping_principal set to the shared library.

The helper returns true when an IAM policy statement's Condition block
genuinely restricts the caller — covering source-resource (aws:SourceArn),
account/org, VPC, network, and principal-identity keys. Three checks prevent
false negatives:

  - Negating operators (containing "not") are rejected, so
    StringNotEquals:SourceAccount("X") still reads as "public to everyone
    except X".
  - ...IfExists operators are rejected; they pass silently when the key is
    absent, providing no guaranteed restriction.
  - A wildcard value ("*") is rejected; aws:SourceArn:"*" restricts nothing.

This is a superset of is_access_limited_to_an_account_id (account-id scoping
only, presence-based) and is intended for rules that need to avoid false
positives on legitimate AWS service-integration patterns such as
S3/CloudTrail -> SNS event notifications.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@NathanGallet-dd
NathanGallet-dd requested a review from a team as a code owner June 23, 2026 09:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d66b4166c3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread assets/libraries/common.rego Outdated
not contains(lower(op), "not")
not endswith(lower(op), "ifexists")
lower(key) == lower(condition_keys_scoping_principal[_])
value != "*"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject wildcard-equivalent condition values

Condition values are not always scalar: Terraform aws_iam_policy_document conversion stores them as []string, and IAM JSON allows arrays. With this comparison, an array such as ["*"] or a network value such as "0.0.0.0/0" for aws:SourceIp is not equal to "*" and therefore makes is_iam_policy_principal_scoped_by_condition return true, even though a wildcard principal remains unrestricted. Rules that use this helper to suppress public-policy findings will miss those policies; normalize arrays and reject wildcard/unrestricted CIDR values before returning scoped.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting: could you give it a thought?

@NathanGallet-dd NathanGallet-dd Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the array case — replaced value != "*" with values := as_array(value); not "*" in values, using the existing as_array helper. This catches both the scalar "*" and the array ["*"] form that Terraform's aws_iam_policy_document emits. Added a test for the array wildcard case.

I also handled the wildcard-CIDR suggestion (0.0.0.0/0 for aws:SourceIp)

@ChouraquiBen ChouraquiBen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment!

Comment thread assets/libraries/common.rego Outdated
not contains(lower(op), "not")
not endswith(lower(op), "ifexists")
lower(key) == lower(condition_keys_scoping_principal[_])
value != "*"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting: could you give it a thought?

@datadog-datadog-prod-us1

This comment has been minimized.

…policy_principal_scoped_by_condition

Terraform's aws_iam_policy_document emits condition values as arrays,
so ["*"] would pass the previous scalar != "*" check. Normalize with
as_array() and also reject 0.0.0.0/0 / ::/0 via is_unrestricted.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@NathanGallet-dd
NathanGallet-dd force-pushed the dd/sns-condition-scoping-helper branch from 7c6bfde to ced5fb3 Compare June 24, 2026 15:45

@ChouraquiBen ChouraquiBen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for going through my comment!

not endswith(lower(op), "ifexists")
lower(key) == lower(condition_keys_scoping_principal[_])
value != "*"
values := as_array(value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: ow, nice! That means that you don't have to make two cases depending on whether it is an array or not! 🙏

@NathanGallet-dd
NathanGallet-dd merged commit b05c2e7 into main Jun 25, 2026
21 checks passed
@NathanGallet-dd
NathanGallet-dd deleted the dd/sns-condition-scoping-helper branch June 25, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants