@cloud-copilot/iam-lens
Version:
Visibility in IAM in and across AWS accounts
280 lines • 11.2 kB
TypeScript
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