UNPKG

@cloud-copilot/iam-lens

Version:

Visibility in IAM in and across AWS accounts

206 lines 9.66 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cli_1 = require("@cloud-copilot/cli"); const client_js_1 = require("./collect/client.js"); const collect_js_1 = require("./collect/collect.js"); const principalCan_js_1 = require("./principalCan/principalCan.js"); const makePrincipalIndex_js_1 = require("./principalIndex/makePrincipalIndex.js"); const simulate_js_1 = require("./simulate/simulate.js"); const packageVersion_js_1 = require("./utils/packageVersion.js"); const stringOrFileArgument_js_1 = require("./utils/stringOrFileArgument.js"); const whoCan_js_1 = require("./whoCan/whoCan.js"); const main = async () => { const cli = await (0, cli_1.parseCliArguments)('iam-lens', { simulate: { description: 'Simulate an IAM request', arguments: { principal: (0, cli_1.stringArgument)({ description: 'The principal to simulate. Can be a user, role, session, or AWS service' }), resource: (0, cli_1.stringArgument)({ description: 'The ARN of the resource to simulate access to. Ignore for wildcard actions' }), resourceAccount: (0, cli_1.stringArgument)({ description: 'The account ID of the resource, only required if it cannot be determined from the resource ARN.' }), action: (0, cli_1.stringArgument)({ description: 'The action to simulate; must be a valid IAM service and action such as `s3:ListBucket`' }), context: (0, cli_1.mapArgument)({ description: 'The context keys to use for the simulation. The first value is the key and the rest are the values. Specify multiple keys by using --context multiple times', defaultValue: {} }), verbose: (0, cli_1.booleanArgument)({ description: 'Enable verbose output for the simulation', character: 'v' }), expect: (0, cli_1.enumArgument)({ description: 'The expected result of the simulation, if the result does not match the expected response a non-zero exit code will be returned', validValues: ['Allowed', 'ImplicitlyDenied', 'ExplicitlyDenied', 'AnyDeny'] }), ignoreMissingPrincipal: (0, cli_1.booleanArgument)({ description: 'Ignore if the principal does not exist. Useful for simulating actions from principals that may not exist or are outside your data set', character: 'i' }), s3AbacOverride: (0, cli_1.enumArgument)({ description: 'Override the S3 ABAC setting for S3 buckets. Defaults to the bucket setting stored in your iam-collect data', validValues: ['enabled', 'disabled'], defaultValue: undefined }), sessionPolicy: (0, stringOrFileArgument_js_1.stringOrFileArgument)({ description: 'The session policy to use for the simulation, if the principal type supports it' }) } }, 'who-can': { description: 'Find who can perform an action on a resource', arguments: { resource: (0, cli_1.stringArgument)({ description: 'The ARN of the resource to check permissions for. Ignore for wildcard actions' }), resourceAccount: (0, cli_1.stringArgument)({ description: 'The account ID of the resource, only required if it cannot be determined from the resource ARN. Required for wildcard actions' }), actions: (0, cli_1.stringArrayArgument)({ description: 'The actions to check permissions for; must be a valid IAM service and action such as `s3:GetObject`', defaultValue: [] }), s3AbacOverride: (0, cli_1.enumArgument)({ description: 'Override the S3 ABAC setting for S3 buckets. Defaults to the bucket setting stored in your iam-collect data', validValues: ['enabled', 'disabled'], defaultValue: undefined }), sort: (0, cli_1.booleanArgument)({ description: 'Sort the results before outputting', character: 's' }) } }, 'principal-can': { description: 'Create a consolidated view of all permissions for a principal, see readme for limitations', arguments: { principal: (0, cli_1.stringArgument)({ description: 'The principal to check permissions for. Can be a user or role' }), shrinkActionLists: (0, cli_1.booleanArgument)({ description: 'Shrink action lists to reduce policy size', character: 's' }) } }, 'index-principals': { description: 'Index all principals', arguments: {} } }, { collectConfigs: (0, cli_1.stringArrayArgument)({ description: 'The iam-collect configuration files to use', defaultValue: [] }), partition: (0, cli_1.stringArgument)({ description: 'The AWS partition to use (aws, aws-cn, aws-us-gov). Defaults to aws', defaultValue: 'aws' }) }, { envPrefix: 'IAM_LENS', showHelpIfNoArgs: true, requireSubcommand: true, expectOperands: false, version: { currentVersion: packageVersion_js_1.iamLensVersion, checkForUpdates: '@cloud-copilot/iam-lens' } }); if (cli.args.collectConfigs.length === 0) { cli.args.collectConfigs.push('./iam-collect.jsonc'); } const collectConfigs = await (0, collect_js_1.loadCollectConfigs)(cli.args.collectConfigs); const collectClient = await (0, collect_js_1.getCollectClient)(collectConfigs, cli.args.partition); if (cli.subcommand === 'simulate') { const { principal, resource, resourceAccount, action, context, ignoreMissingPrincipal, sessionPolicy } = cli.args; const { request, result } = await (0, simulate_js_1.simulateRequest)({ sessionPolicy, principal: principal, resourceArn: resource, resourceAccount: resourceAccount, action: action, customContextKeys: singularizeOneEntryArrays(context), simulationMode: 'Strict', ignoreMissingPrincipal, s3AbacOverride: cli.args.s3AbacOverride }, collectClient); if (result.resultType === 'error') { console.error('Simulation Errors:'); console.log(JSON.stringify(result.errors, null, 2)); process.exit(1); } console.log(`Simulation Result: ${result.overallResult}`); if (cli.args.verbose) { console.log(JSON.stringify({ request, result }, null, 2)); } if (!(0, simulate_js_1.resultMatchesExpectation)(cli.args.expect, result.overallResult)) { process.exit(1); } } else if (cli.subcommand === 'who-can') { const { resource, resourceAccount, actions } = cli.args; if (!resourceAccount && !resource && actions.length === 0) { console.error('Error: At least 1) resource or 2) resource-account and actions must be provided for who-can command'); process.exit(1); } const results = await (0, whoCan_js_1.whoCan)(collectConfigs, cli.args.partition, { resource: cli.args.resource, actions: cli.args.actions, resourceAccount: cli.args.resourceAccount, s3AbacOverride: cli.args.s3AbacOverride, sort: cli.args.sort }); console.log(JSON.stringify(results, null, 2)); } else if (cli.subcommand === 'principal-can') { const { principal, shrinkActionLists } = cli.args; if (!principal) { console.error('Error: Principal must be provided for principal-can command'); process.exit(1); } const results = await (0, principalCan_js_1.principalCan)(collectClient, { principal: principal, shrinkActionLists }); console.log(JSON.stringify(results, null, 2)); } else if (cli.subcommand === 'index-principals') { const indexClient = await (0, collect_js_1.getCollectClient)(collectConfigs, cli.args.partition, { cacheProvider: new client_js_1.NoCacheProvider() }); await (0, makePrincipalIndex_js_1.makePrincipalIndex)(indexClient); } }; /** * Take a record of string arrays and convert it to a record of strings or string arrays, * where any array with a single element is converted to a string. * * @param input - The input record of string arrays. * @returns A new record with singularized values. */ function singularizeOneEntryArrays(input) { const output = {}; for (const [key, value] of Object.entries(input)) { if (value.length === 1) { output[key] = value[0]; } else { output[key] = value; } } return output; } main() .catch((e) => { console.error(e); process.exit(1); }) .then(() => { }) .finally(() => { }); //# sourceMappingURL=cli.js.map