Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "4d8b6e2a-9f3c-4a1d-b7e5-8c2f0d6a3b9e",
"queryName": "IAM Role OIDC Trust Missing Sub Condition",
"severity": "HIGH",
"category": "Access Control",
"descriptionText": "An AWS IAM role that trusts an OIDC provider via 'sts:AssumeRoleWithWebIdentity' must include a condition that restricts the 'sub' (subject) claim. Without this condition, any identity that can obtain a token from the OIDC provider (any repository, project, or user) can assume the role.",
"descriptionUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html",
"platform": "Terraform",
"descriptionID": "4d8b6e2a",
"cloudProvider": "aws",
"cwe": "284",
"riskScore": "8.5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package Cx

import data.generic.common as common_lib
import data.generic.terraform as tf_lib

# aws_iam_role with inline JSON assume_role_policy: no sub condition
CxPolicy[result] {
resource := input.document[i].resource.aws_iam_role[name]
policy := common_lib.get_policy(resource.assume_role_policy)
st := common_lib.get_statement(policy)
statement := st[_]

common_lib.is_allow_effect(statement)
statement.Action == "sts:AssumeRoleWithWebIdentity"
statement.Principal.Federated
not has_sub_condition_json(statement)

result := {
"documentId": input.document[i].id,
"resourceType": "aws_iam_role",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("aws_iam_role[%s].assume_role_policy", [name]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("aws_iam_role[%s].assume_role_policy should have a condition restricting the OIDC 'sub' claim", [name]),
"keyActualValue": sprintf("aws_iam_role[%s].assume_role_policy has no 'sub' condition, allowing any identity from the OIDC provider to assume the role", [name]),
"searchLine": common_lib.build_search_line(["resource", "aws_iam_role", name, "assume_role_policy"], []),
}
}

# aws_iam_role, action as array (e.g. "Actions": ["sts:AssumeRoleWithWebIdentity"])
CxPolicy[result] {
resource := input.document[i].resource.aws_iam_role[name]
policy := common_lib.get_policy(resource.assume_role_policy)
st := common_lib.get_statement(policy)
statement := st[_]

common_lib.is_allow_effect(statement)
statement.Action[_] == "sts:AssumeRoleWithWebIdentity"
statement.Principal.Federated
not has_sub_condition_json(statement)

result := {
"documentId": input.document[i].id,
"resourceType": "aws_iam_role",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("aws_iam_role[%s].assume_role_policy", [name]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("aws_iam_role[%s].assume_role_policy should have a condition restricting the OIDC 'sub' claim", [name]),
"keyActualValue": sprintf("aws_iam_role[%s].assume_role_policy has no 'sub' condition, allowing any identity from the OIDC provider to assume the role", [name]),
"searchLine": common_lib.build_search_line(["resource", "aws_iam_role", name, "assume_role_policy"], []),
}
}

# aws_iam_policy_document (single statement): no sub condition
CxPolicy[result] {
resource := input.document[i].data.aws_iam_policy_document[name]
not is_array(resource.statement)
statement := resource.statement

common_lib.is_allow_effect(statement)
statement.actions[_] == "sts:AssumeRoleWithWebIdentity"
lower(statement.principals.type) == "federated"
not has_sub_condition_hcl(statement)

result := {
"documentId": input.document[i].id,
"resourceType": "aws_iam_policy_document",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("aws_iam_policy_document[%s].statement", [name]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("aws_iam_policy_document[%s].statement should have a condition restricting the OIDC 'sub' claim", [name]),
"keyActualValue": sprintf("aws_iam_policy_document[%s].statement has no 'sub' condition, allowing any identity from the OIDC provider to assume the role", [name]),
"searchLine": common_lib.build_search_line(["data", "aws_iam_policy_document", name, "statement"], []),
}
}

# aws_iam_policy_document (array of statements): no sub condition
CxPolicy[result] {
resource := input.document[i].data.aws_iam_policy_document[name]
is_array(resource.statement)
statement := resource.statement[_]

common_lib.is_allow_effect(statement)
statement.actions[_] == "sts:AssumeRoleWithWebIdentity"
lower(statement.principals.type) == "federated"
not has_sub_condition_hcl(statement)

result := {
"documentId": input.document[i].id,
"resourceType": "aws_iam_policy_document",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("aws_iam_policy_document[%s].statement", [name]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("aws_iam_policy_document[%s].statement should have a condition restricting the OIDC 'sub' claim", [name]),
"keyActualValue": sprintf("aws_iam_policy_document[%s].statement has no 'sub' condition, allowing any identity from the OIDC provider to assume the role", [name]),
"searchLine": common_lib.build_search_line(["data", "aws_iam_policy_document", name, "statement"], []),
}
}

# sub condition exists in inline JSON Condition block
has_sub_condition_json(statement) {
statement.Condition[_][key]
endswith(key, ":sub")
}

has_sub_condition_json(statement) {
statement.condition[_][key]
endswith(key, ":sub")
}

# sub condition exists in HCL aws_iam_policy_document condition block
has_sub_condition_hcl(statement) {
not is_array(statement.condition)
endswith(statement.condition.variable, ":sub")
}

has_sub_condition_hcl(statement) {
is_array(statement.condition)
endswith(statement.condition[_].variable, ":sub")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
resource "aws_iam_role" "negative1" {
name = "github-actions-role-safe"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRoleWithWebIdentity",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
}
}
}
]
}
EOF
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
data "aws_iam_policy_document" "negative2" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"

principals {
type = "Federated"
identifiers = ["arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"]
}

condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:myorg/myrepo:ref:refs/heads/main"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "aws_iam_role" "positive1" {
name = "github-actions-role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRoleWithWebIdentity",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
}
}
]
}
EOF
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
resource "aws_iam_role" "positive2" {
name = "gitlab-ci-role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRoleWithWebIdentity",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/gitlab.example.com"
},
"Condition": {
"StringEquals": {
"gitlab.example.com:aud": "https://gitlab.example.com"
}
}
}
]
}
EOF
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
data "aws_iam_policy_document" "positive3" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"

principals {
type = "Federated"
identifiers = ["arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
data "aws_iam_policy_document" "positive4" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"

principals {
type = "Federated"
identifiers = ["arn:aws:iam::123456789012:oidc-provider/gitlab.example.com"]
}

condition {
test = "StringEquals"
variable = "gitlab.example.com:aud"
values = ["https://gitlab.example.com"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"queryName": "IAM Role OIDC Trust Missing Sub Condition",
"severity": "HIGH",
"line": 4,
"fileName": "positive1.tf"
},
{
"queryName": "IAM Role OIDC Trust Missing Sub Condition",
"severity": "HIGH",
"line": 4,
"fileName": "positive2.tf"
},
{
"queryName": "IAM Role OIDC Trust Missing Sub Condition",
"severity": "HIGH",
"line": 2,
"fileName": "positive3.tf"
},
{
"queryName": "IAM Role OIDC Trust Missing Sub Condition",
"severity": "HIGH",
"line": 2,
"fileName": "positive4.tf"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "7c1e5f3a-2b9d-4e6c-a8f0-1d3c7b5e9a2f",
"queryName": "IAM Role OIDC Trust Wildcard Sub Condition",
"severity": "HIGH",
"category": "Access Control",
"descriptionText": "An AWS IAM role that uses 'sts:AssumeRoleWithWebIdentity' should not allow a wildcard in the 'sub' (subject) claim condition. Using StringLike with a value where the repository or project segment is a wildcard (e.g. 'repo:*' or 'project_path:*:...') allows any repository or project on the OIDC provider to assume the role.",
"descriptionUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html",
"platform": "Terraform",
"descriptionID": "7c1e5f3a",
"cloudProvider": "aws",
"cwe": "284",
"riskScore": "8.5"
}
Loading
Loading