UNPKG

@cloud-copilot/iam-lens

Version:

Visibility in IAM in and across AWS accounts

280 lines 11.2 kB
import { type TopLevelConfig } from '@cloud-copilot/iam-collect'; import { IamCollectClient } from '../collect/client.js'; import { type ClientFactoryPlugin } from '../collect/collect.js'; import { type ResourceType } from '@cloud-copilot/iam-data'; import { type RequestDenial, type RequestGrant } from '@cloud-copilot/iam-simulate'; import { type S3AbacOverride } from '../utils/s3Abac.js'; import { type LightRequestAnalysis } from './requestAnalysis.js'; import { type WorkerBootstrapPlugin } from './WhoCanProcessor.js'; /** * Limits the set of principals that `whoCan` tests. The scope is a union of * principal ARNs, account IDs, and OU paths. It is **intersected** with the * resource-policy-derived scope so that principals outside the resource * policy's reach are still excluded. */ export interface WhoCanPrincipalScope { /** Exact principal ARNs to test individually (does NOT expand to whole-account search). */ principals?: string[]; /** Account IDs — test all principals in these accounts. */ accounts?: string[]; /** OU paths — test all principals in accounts under these OUs. Each string is a slash-separated path like `o-aaa/r-bbb/ou-ccc`, matching the format used by `aws:PrincipalOrgPaths` and `specificOrganizationalUnits`. */ ous?: string[]; } export interface ResourceAccessRequest { /** * The ARN of the resource to check access for. If not provided, actions must be specified. */ resource?: string; /** * The account ID the resource belongs to. * By default this will be looked up based on the resource ARN, but that may * not be possible for all actions, such as wildcard actions like `s3:ListAllMyBuckets`. */ resourceAccount?: string; /** * The actions to check access for. If not provided, actions will be looked up based on the resource ARN. */ actions: string[]; /** * Whether to sort the results for consistent output. */ sort?: boolean; /** * An override for S3 ABAC being enabled when checking access to S3 Bucket resources. */ s3AbacOverride?: S3AbacOverride; /** * The number of worker threads to use for simulations beyond the main thread. * If not provided, defaults to number of CPUs - 1. */ workerThreads?: number; /** * Deny details callback for simulations. If the callback returns true, deny details will be included for that simulation. */ denyDetailsCallback?: (details: LightRequestAnalysis) => boolean; /** * If true, grant details will be collected for allowed simulations. */ collectGrantDetails?: boolean; /** * Optional context keys to consider strict when running simulations for this whoCan request. * These will be added to the simulation strict context keys used by default. */ strictContextKeys?: string[]; /** * Optional plugin to wrap the collect client with a custom implementation. * Used for scenario testing where a layered client needs to be used in worker threads. */ clientFactoryPlugin?: ClientFactoryPlugin; /** * Optional plugin that runs once per worker thread at startup before any work * is processed. Use this for loading instrumentation, initializing logging * context, or other worker-lifetime setup. */ workerBootstrapPlugin?: WorkerBootstrapPlugin; /** * Optional scope to limit the set of principals tested. When provided, the * scope is intersected with the resource-policy-derived scope to narrow the * search space. */ principalScope?: WhoCanPrincipalScope; /** * Whether to ignore an existing principal index. This is for testing purposes. */ ignorePrincipalIndex?: boolean; } /** * Represents a resource pattern that is allowed for a principal, used when wildcards * are in the simulation request. */ export interface WhoCanAllowedResourcePattern { /** * The resource pattern that allows access. */ pattern: string; /** * The resource type for the pattern. */ resourceType: string; /** * The conditions under which access is allowed for this pattern, if any. */ conditions?: any; /** * If true, access is only allowed when the session has a specific session name. */ dependsOnSessionName?: boolean; /** * The policy statements that granted access for this resource pattern. */ details?: RequestGrant[]; } export interface WhoCanAllowed { principal: string; service: string; action: string; level: string; /** * The conditions under which access is allowed, if any. * This will be undefined if access is allowed unconditionally or * if `allowedPatterns` are provided. */ conditions?: any; /** * The resource type for the allowed action. This will be undefined if `allowedPatterns` are provided, * since those patterns specify the resource type directly. */ resourceType?: string; /** * If true, indicates that access is only allowed when the session has a specific session name. * This will be false or undefined if `allowedPatterns` are provided, since those patterns would specify the session name condition directly. */ dependsOnSessionName?: boolean; /** * If there are multiple "allowed" patterns for a single principal because of wildcards * in the simulation request, this array will contain the different resource patterns that allow access. */ allowedPatterns?: WhoCanAllowedResourcePattern[]; /** * The policy statements that granted access for this result. * Only populated for single resource simulations. For wildcard * simulations, see `details` on each entry in `allowedPatterns`. */ details?: RequestGrant[]; } /** * Base type for WhoCanDenyDetails */ interface BaseWhoCanDenyDetail { /** * The principal that was denied */ principal: string; /** * The service the denied action belongs to */ service: string; /** * The action that was denied, without the service prefix (e.g. "GetObject" instead of "s3:GetObject") */ action: string; } /** * Denial details for a single resource request. */ export interface SingleWhoCanDenyDetail extends BaseWhoCanDenyDetail { type: 'single'; /** * The specific details of why the request was denied */ details: RequestDenial[]; } /** * Denial details for a wildcard resource request that may have matched multiple patterns. */ export interface WildcardWhoCanDenyDetail extends BaseWhoCanDenyDetail { type: 'wildcard'; /** * The resource patterns that were denied. Could be empty if there * were no patterns found that matched the resource for the principal and action. * * The same pattern can be returned multiple times if there are multiple resource * types for that pattern/action combination. */ deniedResources: { /** * The pattern tested in the simulation that resulted in a denial. */ pattern: string; /** * The resource type the pattern was tested against. */ resourceType: string; /** * The specific details of why the request was denied for this pattern. */ details: RequestDenial[]; }[]; } /** * Details on why a principal was denied access to a resource for a specific action, including the specific patterns that were tested and resulted in denials. */ export type WhoCanDenyDetail = SingleWhoCanDenyDetail | WildcardWhoCanDenyDetail; export interface WhoCanResponse { simulationCount: number; allowed: WhoCanAllowed[]; allAccountsChecked: boolean; accountsNotFound: string[]; organizationsNotFound: string[]; organizationalUnitsNotFound: string[]; principalsNotFound: string[]; denyDetails?: WhoCanDenyDetail[] | undefined; } /** * Processes a single whoCan request by creating a temporary WhoCanProcessor, * enqueuing the request, waiting for it to settle, and shutting down. This * preserves the original one-shot behavior where workers and cache are created * and destroyed per call. * * For better performance when running multiple requests, use WhoCanProcessor * directly to keep workers and cache alive across calls. * * @param collectConfigs the collect configurations for loading IAM data * @param partition the AWS partition (e.g. 'aws', 'aws-cn') * @param request the whoCan request parameters * @returns the whoCan response with allowed principals and optional deny details */ export declare function whoCan(collectConfigs: TopLevelConfig[], partition: string, request: ResourceAccessRequest): Promise<WhoCanResponse>; export declare function uniqueAccountsToCheck(collectClient: IamCollectClient, accountsToCheck: AccountsToCheck): Promise<{ accountsNotFound: string[]; organizationsNotFound: string[]; organizationalUnitsNotFound: string[]; accounts: string[]; }>; export interface AccountsToCheck { allAccounts: boolean; specificAccounts: string[]; specificPrincipals: string[]; specificOrganizations: string[]; specificOrganizationalUnits: string[]; checkAnonymous: boolean; /** * Whether every principal from the resource account should be checked. */ checkAllFromResourceAccount: boolean; /** * Whether the resource policy explicitly grants access to principals in the * resource account. This is true when: * - The policy has no narrowing conditions (open wildcard or NotPrincipal), or * - The policy conditions explicitly reference the resource account, or * - The policy narrows to orgs/OUs (which may include the resource account). */ resourceAccountTrustedByPolicy: boolean; } export declare function accountsToCheckBasedOnResourcePolicy(resourcePolicy: any, resourceAccount: string | undefined): Promise<AccountsToCheck>; export declare function actionsForWhoCan(request: Pick<ResourceAccessRequest, 'actions' | 'resource'>): Promise<string[]>; /** * Get the the possible resource types for an action and resource * * @param service the service the action belongs to * @param action the action to get the resource type for * @param resourceArn the resource type matching the action, if any * @throws an error if the service or action does not exist, or if the action is a wildcard only action */ export declare function lookupActionsForResourceArn(resourceArn: string): Promise<string[]>; export declare function findResourceTypeForArn(resourceArn: string): Promise<[string, ResourceType]>; /** * Convert a resource pattern from iam-data to a regex pattern * * @param pattern the pattern to convert to a regex * @returns the regex pattern */ export declare function convertResourcePatternToRegex(pattern: string): string; /** * Sort the results in a WhoCanResponse in place for consistent output * * @param whoCanResponse the WhoCanResponse to sort */ export declare function sortWhoCanResults(whoCanResponse: WhoCanResponse): void; export {}; //# sourceMappingURL=whoCan.d.ts.map