mira
Version:
NearForm Accelerator for Cloud Native Serverless AWS
115 lines (107 loc) • 4.27 kB
text/typescript
import * as cdk from '@aws-cdk/core'
import { PolicyStatement, CfnManagedPolicy, CfnPolicy, ManagedPolicyProps } from '@aws-cdk/aws-iam'
import { CfnQueuePolicy } from '@aws-cdk/aws-sqs'
import { CfnBucketPolicy } from '@aws-cdk/aws-s3'
import { CfnVPCEndpoint } from '@aws-cdk/aws-ec2'
import { CfnTopicPolicy } from '@aws-cdk/aws-sns'
interface HasPolicyDocument extends cdk.IConstruct {
policyDocument: ManagedPolicyProps
cfnResourceType: string
};
/**
* The Policy class is used by Mira to validate policy aspects of various cloud services.
*/
export class Policies implements cdk.IAspect {
constructor (customList?: string[]) {
if (customList) {
this.allowedServices = customList
}
}
/**
* reasons why to exclude some actions from validation:
*
* https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazonmobileanalytics.html
* https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts
*
* @ignore - Excluded from documentation generation.
*/
private allowedServices: string[] = [
'mobileanalytics:PutEvents',
'cognito-idp:CreateUserPool',
'secretsmanager:GetRandomPassword',
'ec2:AllocateAddress',
'ec2:AssociateRouteTable',
'ec2:AttachInternetGateway',
'ec2:CreateInternetGateway',
'ec2:CreateNatGateway',
'ec2:CreateRoute',
'ec2:CreateRouteTable',
'ec2:CreateSecurityGroup',
'ec2:CreateSubnet',
'ec2:CreateVpc',
'ec2:DeleteNatGateway',
'ec2:DeleteSubnet',
'ec2:DeleteVpc',
'ec2:Describe*',
'ec2:DetachInternetGateway',
'ec2:DisassociateAddress',
'ec2:DisassociateRouteTable',
'ec2:ModifySubnetAttribute',
'ec2:ModifyVpcAttribute',
'ec2:ReleaseAddress',
'ecr:GetAuthorizationToken',
'acm:RequestCertificate',
'acm:DescribeCertificate',
'acm:DeleteCertificate',
'acm:ListCertificates',
'route53:GetChange',
'route53:ListResourceRecordSets',
'cloudfront:GetInvalidation',
'cloudfront:CreateInvalidation',
'personalize:CreateSchema',
'personalize:DeleteSchema',
'personalize:CreateDatasetGroup',
'personalize:DeleteDatasetGroup',
'personalize:CreateDataset',
'personalize:DeleteDataset',
'personalize:CreateDatasetImportJob'
]
/**
* The list of services that supports policyDocument
*
* @ignore - Excluded from documentation generation.
*/
private readonly policiesResourceType: string[] = [
CfnQueuePolicy.CFN_RESOURCE_TYPE_NAME,
CfnBucketPolicy.CFN_RESOURCE_TYPE_NAME,
CfnManagedPolicy.CFN_RESOURCE_TYPE_NAME,
CfnPolicy.CFN_RESOURCE_TYPE_NAME,
CfnVPCEndpoint.CFN_RESOURCE_TYPE_NAME,
CfnTopicPolicy.CFN_RESOURCE_TYPE_NAME
]
/**
*
* @ignore - Excluded from documentation generation.
*/
private actionsAllowed (actions: string[]): boolean {
return actions.filter((action: string) => !this.allowedServices.includes(action)).length === 0
}
public visit (node: cdk.IConstruct | HasPolicyDocument): void {
// The check is done using the `cfnResourceType`.
// NOTE: The original check was done with a `instanceof CfnPolicy`. That check always returned false,
// probably caused by a conflict with the import of the file from 2 different `node_module` places
if ('cfnResourceType' in node && this.policiesResourceType.includes(node.cfnResourceType) && node.policyDocument?.statements) {
const statements: PolicyStatement[] = node.policyDocument.statements
statements.forEach(statement => {
const statementJson = statement.toJSON()
const resource = Array.isArray(statementJson.Resource) ? statementJson.Resource : [statementJson.Resource]
const action = Array.isArray(statementJson.Action) ? statementJson.Action : [statementJson.Action]
resource.forEach((resource: string) => {
if (resource === '*' && !this.actionsAllowed(action)) {
node.node.addError('Unqualified asterisks are not allowed in resource specification for policies')
}
})
})
}
}
}