packages/core/src/awsService/accessanalyzer/vue/iamPolicyChecks.vue (506 lines of code) (raw):

/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ <template> <div id="app"> <div> <h1>IAM Policy Checks</h1> <div v-if="!initialData.pythonToolsInstalled"> <h3>Getting Started</h3> <p> Policy Checks requires Python 3.6+ and the respective Python CLI tools installed, based on the document type: </p> <ol> <li> <p>Install Python 3.6+</p> </li> <li> <code> pip install cfn-policy-validator==0.0.34 </code> </li> <li> <code> pip install tf-policy-validator==0.0.8 </code> </li> <li> <p>Provide IAM Roles Credentials</p> </li> </ol> </div> <div style="justify-content: space-between"> <div style="display: flex"> <div style="display: block; margin-right: 25px"> <label for="select-document-type" style="display: block; margin-top: 5px; margin-bottom: 3px" >Select a Document Type</label > <select id="select-document-type" v-on:change="setDocumentType" v-model="documentType"> <option value="CloudFormation">CloudFormation Template</option> <option value="Terraform Plan">Terraform Plan</option> <option value="JSON Policy Language">JSON Policy Language</option> </select> </div> <div style="display: block" v-if="documentType == 'JSON Policy Language'"> <label for="select-policy-type" style="display: block; margin-top: 5px; margin-bottom: 3px" >Policy Type</label > <select id="select-policy-type" v-on:change="setValidatePolicyType" v-model="validatePolicyType" > <option value="Identity">Identity</option> <option value="Resource">Resource</option> </select> </div> </div> <label for="input-path" style="display: block; cursor: not-allowed; margin-top: 15px; opacity: 0.4"> Open a file with the selected Document Type in the VS Code text editor to begin </label> <input type="text" style=" display: flex; cursor: not-allowed; box-sizing: border-box; position: relative; opacity: 0.4; width: 70%; " id="input-path" placeholder="Input policy file path" readOnly disabled v-model="inputPath" /> </div> <div v-if="documentType == 'CloudFormation'"> <label for="input-path" style="display: block; margin-top: 15px; margin-bottom: 3px" >CloudFormation Parameter File (Optional)</label > <input type="text" style="display: flex; box-sizing: border-box; position: relative; margin-bottom: 10px; width: 70%" id="input-path" placeholder="CloudFormation Parameter File Path" v-on:change="setCfnParameterFilePath" v-model="initialData.cfnParameterPath" /> </div> <div v-if="documentType == 'Terraform Plan'" style="margin-top: 15px"> <p> For Terraform Plans, generate terraform plan file and convert the plan files to machine-readable JSON files before running policy checks. </p> <ol> <li><code>$terraform init</code></li> <li><code>$terraform plan -out tf.plan</code></li> <li><code>$terraform show -json -no-color tf.plan > tf.json</code></li> - For TF 0.12 and prior, use command <code>$terraform show tf.plan > tf.out</code> <li>View the converted JSON file in VS Code and run the desired policy check</li> </ol> </div> </div> <hr style="margin-top: 25px" /> <div class="validate-container"> <h2 style="border-bottom-style: none">Validate Policies</h2> <div style="display: grid"> <p> Validate your policy against IAM policy grammar and AWS best practices. You can view policy validation check findings that include security warnings, errors, general warnings, and suggestions for your policy. These findings provide actionable recommendations that help you author policies that are functional and conform to security best practices. </p> <div style="display: grid"> <div> <button class="button-theme-primary" v-on:click="runValidator" :disabled="validateButtonDisabled" > Run Policy Validation </button> <div style="margin-top: 5px"> <p :style="{ color: validatePolicyResponseColor }"> {{ validatePolicyResponse }} </p> </div> </div> </div> </div> </div> <hr style="margin-top: 25px" /> <div class="custom-checks-container" v-if="documentType != 'JSON Policy Language'"> <h2 style="border-bottom-style: none">Custom Policy Checks</h2> <div style="display: block"> <p> Validate your policy against your specified security standards using IAM Access Analyzer custom policy checks. You can check for new access against a reference policy, whether access is granted to a list of IAM actions and/or resource ARNs, or public access to supported resource types. </p> <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-custom-policy-checks.html" >More about Custom Policy Checks</a > <div style="justify-content: space-between"> <div style="display: flex"> <div style="display: block; margin-right: 25px"> <label for="select-check-type" style="display: block; margin-top: 15px; margin-bottom: 3px" >Select a Check Type</label > <select id="select-check-type" style="margin-bottom: 5px" v-on:change="setCheckType"> <option value="CheckAccessNotGranted">CheckAccessNotGranted</option> <option value="CheckNoNewAccess">CheckNoNewAccess</option> <option value="CheckNoPublicAccess">CheckNoPublicAccess</option> </select> </div> <div style="display: block" v-if=" (documentType == 'CloudFormation' || documentType == 'Terraform Plan') && checkType == 'CheckNoNewAccess' " > <label for="select-reference-type" style="display: block; margin-top: 15px; margin-bottom: 3px" >Select a Reference Policy Type</label > <select id="select-reference-type" v-on:change="setCheckNoNewAccessPolicyType" v-model="checkNoNewAccessPolicyType" > <option value="Identity">Identity</option> <option value="Resource">Resource</option> </select> </div> </div> </div> <div v-if="checkType == 'CheckNoNewAccess'"> <div> <label for="input-path" style="display: block; margin-bottom: 3px" >Provide a reference file containing a JSON Policy Document</label > <input type="text" style=" display: flex; box-sizing: border-box; position: relative; margin-bottom: 10px; width: 70%; " id="input-path" placeholder="Enter reference policy document" v-on:change="setCheckNoNewAccessFilePath" v-model="initialData.checkNoNewAccessFilePath" /> </div> <div style="margin-top: 5px" v-if="initialData.customChecksFileErrorMessage"> <p style="color: var(--vscode-errorForeground)"> {{ initialData.customChecksFileErrorMessage }} </p> </div> <div> <label style="margin-bottom: 3px">Enter a JSON Policy Document</label> <textarea style=" width: 100%; margin-bottom: 10px; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; " rows="30" v-model="initialData.checkNoNewAccessTextArea" v-on:change="setCheckNoNewAccessTextArea" placeholder="Reference policy document" ></textarea> </div> </div> <div v-if="checkType == 'CheckAccessNotGranted'"> <div> <label for="input-path" style="display: block; margin-bottom: 3px"> Provide a reference JSON file containing actions and/or resources. The file should be structured as follows: <code >{"actions": ["action1", "action2", "action3"], "resources": ["resource1", "resource2", "resource3"]}</code > </label> <input type="text" style=" display: flex; box-sizing: border-box; position: relative; margin-bottom: 10px; width: 70%; " id="input-path" placeholder="File path to JSON file" v-on:change="setCheckAccessNotGrantedFilePath" v-model="initialData.checkAccessNotGrantedFilePath" /> </div> <div style="margin-top: 5px" v-if="initialData.customChecksFileErrorMessage"> <p style="color: var(--vscode-errorForeground)"> {{ initialData.customChecksFileErrorMessage }} </p> </div> <div> <label style="margin-bottom: 3px">Enter a comma-separated list of actions</label> <textarea style=" width: 100%; margin-bottom: 10px; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; " rows="15" v-on:change="setcheckAccessNotGrantedActionsTextArea" v-model="initialData.checkAccessNotGrantedActionsTextArea" placeholder="List of actions" ></textarea> </div> <div> <label style="margin-bottom: 3px">Enter a comma-separated list of resource ARNs</label> <textarea style=" width: 100%; margin-bottom: 10px; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; " rows="15" v-on:change="setcheckAccessNotGrantedResourcesTextArea" v-model="initialData.checkAccessNotGrantedResourcesTextArea" placeholder="List of resource ARNs" ></textarea> </div> </div> <div style="display: grid"> <b style="margin-bottom: 5px" >A charge is associated with each custom policy check. For more details about pricing, see <a href="https://aws.amazon.com/iam/access-analyzer/pricing/"> IAM Access Analyzer pricing </a>. </b> <div> <button class="button-theme-primary" style="margin-bottom: 5px" v-on:click="runCustomPolicyCheck" :disabled="customCheckButtonDisabled" > Run Custom Policy Check </button> <div style="margin-top: 5px"> <p :style="{ color: customPolicyCheckResponseColor }"> {{ customPolicyCheckResponse }} </p> </div> </div> </div> </div> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue' import { WebviewClientFactory } from '../../../webviews/client' import saveData from '../../../webviews/mixins/saveData' import { IamPolicyChecksWebview } from './iamPolicyChecks' import { PolicyChecksDocumentType, PolicyChecksPolicyType } from './constants' import '@../../../resources/css/base.css' import '@../../../resources/css/securityIssue.css' const client = WebviewClientFactory.create<IamPolicyChecksWebview>() export default defineComponent({ mixins: [saveData], data: () => ({ documentType: 'CloudFormation', validatePolicyType: 'Identity', checkNoNewAccessPolicyType: 'Identity', checkType: 'CheckAccessNotGranted', initialData: { checkNoNewAccessFilePath: '', checkNoNewAccessTextArea: '', checkAccessNotGrantedFilePath: '', checkAccessNotGrantedActionsTextArea: '', checkAccessNotGrantedResourcesTextArea: '', customChecksFileErrorMessage: '', cfnParameterPath: '', pythonToolsInstalled: false, }, inputPath: '', checkAccessNotGrantedPathPlaceholder: 'List of actions file path', checkAccessNotGrantedActionsTextAreaPlaceholder: 'Enter a list of actions', checkAccessNotGrantedResourcesTextAreaPlaceholder: 'Enter a list of resource ARNs', validatePolicyResponse: '', validatePolicyResponseColor: 'var(--vscode-errorForeground)', customPolicyCheckResponse: '', customPolicyCheckResponseColor: 'var(--vscode-errorForeground)', validateButtonDisabled: false, customCheckButtonDisabled: false, }), async created() { this.initialData = (await client.init()) ?? this.initialData client.onChangeInputPath((data: string) => { this.inputPath = data }) client.onChangeCheckNoNewAccessFilePath((data: string) => { this.initialData.checkNoNewAccessFilePath = data client .readCustomChecksFile(this.initialData.checkNoNewAccessFilePath) .then(response => { this.initialData.checkNoNewAccessTextArea = response }) .catch(err => console.log(err)) }) client.onChangeCheckAccessNotGrantedFilePath((data: string) => { this.initialData.checkAccessNotGrantedFilePath = data client .readCustomChecksJsonFile(this.initialData.checkAccessNotGrantedFilePath) .then(response => { this.initialData.checkAccessNotGrantedActionsTextArea = response.actions ? response.actions.toString() : '' this.initialData.checkAccessNotGrantedResourcesTextArea = response.resources ? response.resources.toString() : '' }) .catch(err => console.log(err)) }) client.onChangeCloudformationParameterFilePath((data: string) => { this.initialData.cfnParameterPath = data }) client.onValidatePolicyResponse((data: [string, string]) => { this.validatePolicyResponse = data[0] this.validatePolicyResponseColor = data[1] }) client.onCustomPolicyCheckResponse((data: [string, string]) => { this.customPolicyCheckResponse = data[0] this.customPolicyCheckResponseColor = data[1] }) client.onFileReadError((data: string) => { this.initialData.customChecksFileErrorMessage = data }) }, methods: { setDocumentType: function (event: any) { client.emitUiClick('accessanalyzer_selectDocumentType') this.documentType = event.target.value }, setValidatePolicyType: function (event: any) { client.emitUiClick('accessanalyzer_selectInputPolicyType') this.validatePolicyType = event.target.value }, setCheckNoNewAccessPolicyType: function (event: any) { client.emitUiClick('accessanalyzer_selectReferencePolicyType') this.checkNoNewAccessPolicyType = event.target.value }, setCheckType: function (event: any) { client.emitUiClick('accessanalyzer_selectCustomCheckType') this.checkType = event.target.value }, setCheckNoNewAccessFilePath: function (event: any) { client.emitUiClick('accessanalyzer_selectCheckNoNewAccessFilePath') this.initialData.checkNoNewAccessFilePath = event.target.value client .readCustomChecksFile(this.initialData.checkNoNewAccessFilePath) .then(response => { this.initialData.checkNoNewAccessTextArea = response }) .catch(err => console.log(err)) }, setCheckNoNewAccessTextArea: function (event: any) { this.initialData.checkNoNewAccessTextArea = event.target.value this.initialData.checkNoNewAccessFilePath = '' }, setCheckAccessNotGrantedFilePath: function (event: any) { client.emitUiClick('accessanalyzer_selectCheckAccessNotGrantedFilePath') this.initialData.checkAccessNotGrantedFilePath = event.target.value client .readCustomChecksJsonFile(this.initialData.checkAccessNotGrantedFilePath) .then(response => { this.initialData.checkAccessNotGrantedActionsTextArea = response.actions ? response.actions.toString() : '' this.initialData.checkAccessNotGrantedResourcesTextArea = response.resources ? response.resources.toString() : '' }) .catch(err => console.log(err)) }, setcheckAccessNotGrantedActionsTextArea: function (event: any) { this.initialData.checkAccessNotGrantedActionsTextArea = event.target.value this.initialData.checkAccessNotGrantedFilePath = '' }, setcheckAccessNotGrantedResourcesTextArea: function (event: any) { this.initialData.checkAccessNotGrantedResourcesTextArea = event.target.value this.initialData.checkAccessNotGrantedFilePath = '' }, setCfnParameterFilePath: function (event: any) { client.emitUiClick('accessanalyzer_selectCfnParameterFilePath') this.initialData.cfnParameterPath = event.target.value }, runValidator: async function () { this.validateButtonDisabled = true client.emitUiClick('accessanalyzer_runValidatePolicy') await client.validatePolicy( this.documentType as PolicyChecksDocumentType, this.validatePolicyType as PolicyChecksPolicyType, this.initialData.cfnParameterPath ) this.validateButtonDisabled = false }, runCustomPolicyCheck: async function () { this.customCheckButtonDisabled = true client.emitUiClick('accessanalyzer_runCustomPolicyCheck') if (this.checkType == 'CheckNoNewAccess') { await client.checkNoNewAccess( this.documentType as PolicyChecksDocumentType, this.checkNoNewAccessPolicyType as PolicyChecksPolicyType, this.initialData.checkNoNewAccessTextArea, this.initialData.cfnParameterPath ) } else if (this.checkType == 'CheckAccessNotGranted') { await client.checkAccessNotGranted( this.documentType as PolicyChecksDocumentType, this.initialData.checkAccessNotGrantedActionsTextArea, this.initialData.checkAccessNotGrantedResourcesTextArea, this.initialData.cfnParameterPath ) } else if (this.checkType == 'CheckNoPublicAccess') { await client.checkNoPublicAccess( this.documentType as PolicyChecksDocumentType, this.initialData.cfnParameterPath ) } this.customCheckButtonDisabled = false }, }, computed: {}, }) </script>