From 3478c9d539cdf23244bac4eb605f177dafead3f9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 15:29:05 +0000 Subject: [PATCH] fix(scp): deny platform-path changes in bootstrap simulate (PLAT-233) Add a simulate case for role/platform/hcptf-example and fail when CreateRole, PutRolePolicy, or DeleteRole is allowed. The unpathed hcptf-* import check stays. Co-authored-by: Adam Moussa --- scripts/create-hcptf-bootstrap-roles.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/create-hcptf-bootstrap-roles.sh b/scripts/create-hcptf-bootstrap-roles.sh index 75e143c..207980a 100755 --- a/scripts/create-hcptf-bootstrap-roles.sh +++ b/scripts/create-hcptf-bootstrap-roles.sh @@ -18,6 +18,9 @@ # --simulate runs iam:SimulatePrincipalPolicy against the apply role. Requires # the role to already exist. The policy source is the live Role.Arn from # iam:GetRole, so an existing unpathed role and a /platform/ create both match. +# The platform-path case must come back denied. role/hcptf-* does not cover +# role/platform/*, and ProtectPlatformPath denies that resource for every +# principal except OrganizationAccountAccessRole and the Platform SSO role. # # Default trust is exact StringEquals for workspace iam-bootstrap- only. # HCP workspace names are org-unique, so prod and dev cannot both be @@ -45,7 +48,7 @@ while [[ $# -gt 0 ]]; do --dry-run) DRY_RUN=1; shift ;; --simulate) SIMULATE=1; shift ;; --allow-workspace) ALLOW_WORKSPACE="$2"; shift 2 ;; - -h|--help) sed -n '2,36p' "$0"; exit 0 ;; + -h|--help) sed -n '2,35p' "$0"; exit 0 ;; -*) echo "unknown flag: $1" >&2; exit 2 ;; *) echo "unexpected argument: $1" >&2; exit 2 ;; esac @@ -205,7 +208,7 @@ if [[ "$SIMULATE" -eq 1 ]]; then --resource-arns "arn:aws:iam::${ACCOUNT_ID}:policy/tf-managed/example" \ --query 'EvaluationResults[].{Action:EvalActionName,Decision:EvalDecision}' \ --output table - echo "-- PutRolePolicy on hcptf-* (expect allowed; import/first-apply path) --" + echo "-- PutRolePolicy on unpathed role/hcptf-example (expect allowed; import/first-apply path) --" aws iam simulate-principal-policy \ --policy-source-arn "$APPLY_ARN" \ --action-names iam:PutRolePolicy iam:DetachRolePolicy \ @@ -221,6 +224,24 @@ if [[ "$SIMULATE" -eq 1 ]]; then "arn:aws:iam::${ACCOUNT_ID}:role/OrganizationAccountAccessRole" \ --query 'EvaluationResults[].{Action:EvalActionName,Resource:EvalResourceName,Decision:EvalDecision}' \ --output table + echo "-- IAM changes on role/platform/hcptf-example (expect denied) --" + platform_sim="$(aws iam simulate-principal-policy \ + --policy-source-arn "$APPLY_ARN" \ + --action-names iam:CreateRole iam:PutRolePolicy iam:DeleteRole \ + --resource-arns "arn:aws:iam::${ACCOUNT_ID}:role/platform/hcptf-example" \ + --query 'EvaluationResults[].{Action:EvalActionName,Decision:EvalDecision}' \ + --output table)" + printf '%s\n' "$platform_sim" + if grep -q 'allowed' <<<"$platform_sim"; then + echo "role/platform/* simulation allowed an IAM change" >&2 + exit 1 + fi + for action in iam:CreateRole iam:PutRolePolicy iam:DeleteRole; do + grep -q "$action" <<<"$platform_sim" || { + echo "role/platform/* simulation missing ${action}" >&2 + exit 1 + } + done exit 0 fi