aws-amplify-attach-ssr-compute-role

UpdateBranch + iam:PassRole can replace an Amplify Branch's computeRoleArn, changing the SSR request-serving identity.

derived aws emits CanAttachIdentity

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanPassIdentity {'role': None}

where

?principal has effective amplify:UpdateBranch on the target branch ARN ?role trust policy allows amplify-hosting-compute.amazonaws.com

emit

source typeIdentity
target typeBuildWorker
source<principal>
target<AWS::Amplify::Branch>
permissionsamplify:UpdateBranch iam:PassRole
conditionsiam_permission role_compatibility
state logicACTIVE if both amplify:UpdateBranch on the branch ARN and iam:PassRole for a role trusting amplify-hosting-compute.amazonaws.com are satisfied. CONDITIONAL(role_compatibility) if the role trust is unresolved. After UpdateBranch the app's NEXT SSR request will execute code running as the newly-attached compute role. This is a distinct path from the build-time service role (iamServiceRoleArn); the SSR Compute role is used by the serving plane at request time. An attacker who swaps to a privileged SSR role then triggers or waits for an SSR request (server-side rendering traffic) runs code as the new role.

Narrative

{principal.name} can replace {branch.name}'s SSR Compute role with {role.name} (amplify:UpdateBranch + iam:PassRole), changing the AWS identity that SSR requests execute as when served by that branch.

Raw rule rules/derived/aws/amplify.yaml

id: aws-amplify-attach-ssr-compute-role
emits: CanAttachIdentity
description: UpdateBranch + iam:PassRole can replace an Amplify Branch's computeRoleArn, changing the
  SSR request-serving identity.
match:
- - principal: null
  - CanPassIdentity
  - role: null
where:
- ?principal has effective amplify:UpdateBranch on the target branch ARN
- ?role trust policy allows amplify-hosting-compute.amazonaws.com
emit:
  source_type: Identity
  target_type: BuildWorker
  source: <principal>
  target: <AWS::Amplify::Branch>
  permissions:
  - amplify:UpdateBranch
  - iam:PassRole
  conditions:
  - iam_permission
  - role_compatibility
  state_logic: ACTIVE if both amplify:UpdateBranch on the branch ARN and iam:PassRole for a role trusting
    amplify-hosting-compute.amazonaws.com are satisfied. CONDITIONAL(role_compatibility) if the role trust
    is unresolved. After UpdateBranch the app's NEXT SSR request will execute code running as the newly-attached
    compute role. This is a distinct path from the build-time service role (iamServiceRoleArn); the SSR
    Compute role is used by the serving plane at request time. An attacker who swaps to a privileged SSR
    role then triggers or waits for an SSR request (server-side rendering traffic) runs code as the new
    role.
  derived_from:
  - <CanPassIdentity edge_id>
  confidence: 0.9
  false_positive_note: "UpdateBranch computeRoleArn is the SSR Compute role, not the build service role.\
    \ It requires iam:PassRole for a role trusting amplify-hosting-compute. amazonaws.com (the SSR compute\
    \ principal), not amplify.amazonaws.com. After the role swap, the next SSR request runs as the new\
    \ role. If the branch does not serve SSR content (no Next.js SSR, no dynamic server-side rendering),\
    \ this edge is CONDITIONAL(ssr_enabled). CanAttachIdentity by itself does not grant execution \u2014\
    \ it becomes an attack step when combined with SSR traffic (a request or the attacker can trigger\
    \ via a public URL)."
  narrative: '{principal.name} can replace {branch.name}''s SSR Compute role with {role.name} (amplify:UpdateBranch
    + iam:PassRole), changing the AWS identity that SSR requests execute as when served by that branch.'
move · open · esc close