aws-amplify-attach-ssr-compute-role
UpdateBranch + iam:PassRole can replace an Amplify Branch's computeRoleArn, changing the SSR request-serving identity.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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. |
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.'