From c5275d3f8b85f28b7e02956f53204ee48f01b600 Mon Sep 17 00:00:00 2001
From: Rico Huijbers <rix0rrr@gmail.com>
Date: Mon, 27 Sep 2021 14:42:35 +0200
Subject: [PATCH] chore: formalize that `App`s `outdir` property should not be
 used

If this property is passed it has to agree with the CLI's `--output` flag.

If this property is not passed, it will default to the CLI's `--output`
flag.

In either case, it's better to just use `--output` and not pass this
property at all; the property only has value inside tests.

Fixes #3717.
---
 packages/@aws-cdk/core/lib/app.ts             | 11 ++++
 .../lib/api/bootstrap/bootstrap-template.yaml | 56 ++++++++++++++++++-
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts
index 6719995d2b837..68492d005e20b 100644
--- a/packages/@aws-cdk/core/lib/app.ts
+++ b/packages/@aws-cdk/core/lib/app.ts
@@ -1,5 +1,6 @@
 import * as cxapi from '@aws-cdk/cx-api';
 import { Construct } from 'constructs';
+import { Annotations } from '.';
 import { addCustomSynthesis, ICustomSynthesis } from './private/synthesis';
 import { TreeMetadata } from './private/tree-metadata';
 import { Stage } from './stage';
@@ -25,6 +26,12 @@ export interface AppProps {
   /**
    * The output directory into which to emit synthesized artifacts.
    *
+   * You should never need to set this value. By default, the value you pass to
+   * the CLI's `--output` flag will be used, and if you change it to a different
+   * directory the CLI will fail to pick up the generated Cloud Assembly.
+   *
+   * This property is intended for internal and testing use.
+   *
    * @default - If this value is _not_ set, considers the environment variable `CDK_OUTDIR`.
    *            If `CDK_OUTDIR` is not defined, uses a temp directory.
    */
@@ -104,6 +111,10 @@ export class App extends Stage {
       outdir: props.outdir ?? process.env[cxapi.OUTDIR_ENV],
     });
 
+    if (process.env[cxapi.OUTDIR_ENV] && props.outdir) {
+      Annotations.of(this).addWarning('Do not pass the \'outdir\' property when synthesizing via the CDK CLI. Use the CLI\'s \'--output\' flag only.');
+    }
+
     Object.defineProperty(this, APP_SYMBOL, { value: true });
 
     this.loadContext(props.context);
diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml
index 36182c083fa55..c3c4bfaeb974a 100644
--- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml
+++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml
@@ -12,10 +12,15 @@ Parameters:
     Default: ''
     Type: CommaDelimitedList
   CloudFormationExecutionPolicies:
-    Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation
+    Description: List of ManagedPolicy ARN(s) to attach to the CloudFormation
       deployment role
     Default: ''
     Type: CommaDelimitedList
+  PermissionsBoundaryArns:
+    Description: List of ManagedPolicy ARN(s) to require as Permission Boundaries
+      for the CloudFormation execution role
+    Default: ''
+    Type: CommaDelimitedList
   FileAssetsBucketName:
     Description: The name of the S3 bucket used for file assets
     Default: ''
@@ -64,6 +69,13 @@ Conditions:
           - Fn::Join:
               - ''
               - Ref: CloudFormationExecutionPolicies
+  HasPermissionsBoundaryArns:
+    Fn::Not:
+      - Fn::Equals:
+          - ''
+          - Fn::Join:
+              - ''
+              - Ref: PermissionsBoundaryArns
   HasCustomFileAssetsBucketName:
     Fn::Not:
       - Fn::Equals:
@@ -482,6 +494,48 @@ Resources:
             - - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
       RoleName:
         Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}
+
+  # Policy that restricts Execution Role permissions to only be able to create
+  # roles with Permission Boundaries, and will not be able to fiddle with them
+  CloudFormationExecutionRolePermissionBoundaries:
+    Type: AWS::IAM::Policy
+    Condition: HasPermissionsBoundaryArns
+    Properties:
+      PolicyDocument:
+        Statement:
+          - Sid: RequirePermissionsBoundary
+            Effect: Deny
+            Action:
+              - iam:CreateUser
+              - iam:CreateRole
+              - iam:PutUserPermissionsBoundary
+              - iam:PutRolePermissionsBoundary
+            Resource: *
+            Condition:
+              StringNotEquals:
+                "iam:PermissionsBoundary": { Ref: PermissionsBoundaryArns }
+          - Sid: CannotRemovePermissionsBoundary
+            Effect: Deny
+            Action:
+              - iam:DeleteUserPermissionsBoundary
+              - iam:DeleteRolePermissionsBoundary
+            Resource: *
+            Condition:
+              StringEquals:
+                "iam:PermissionsBoundary": { Ref: PermissionsBoundaryArns }
+          - Sid: CannotModifyPermissionsBoundary
+            Effect: Deny
+            Action:
+              - iam:DeletePolicy
+              - iam:DeletePolicyVersion
+              - iam:CreatePolicyVersion
+              - iam:SetDefaultPolicyVersion
+            Resource: { Ref: PermissionsBoundaryArns }
+        Version: '2012-10-17'
+      Roles:
+        - Ref: CloudFormationExecutionRole
+      PolicyName: PermissionBoundaries
+
   # The SSM parameter is used in pipeline-deployed templates to verify the version
   # of the bootstrap resources.
   CdkBootstrapVersion: