Documentation
¶
Overview ¶
Package eval ports AWSHound's IAM policy evaluator — the correctness-critical core that decides whether an attack edge exists (whether a principal is allowed an action on a resource, and whether a role's trust policy admits a principal). It compiles AWS policy documents into bundled grants and combines a principal's identity policies, permissions boundary, SCPs, and RCPs into queryable effective permissions.
These value types are ported field-for-field from AWSHound's Python implementation: core/evaluation/iam_policy/model.py (Effect, Grant, CompiledPolicy, EffectiveStatement, EffectivePermissions), core/models/types.py (PermissionOutcome, PermissionCheckResult, TrustCheckResult), and core/evaluation/conditions/resolution.py (the Kleene tristate). The IAM policy-document AST and its parser live in policy.go; the action catalog in catalog.go.
Index ¶
- func HasPolicyVariables(pattern string) bool
- func ResolvePolicyVariables(pattern string, ctx *RequestContext, variablesEnabled bool) (string, bool)
- func StatementActionMatches(statement *Statement, action string) bool
- func TrustFederatedMatches(p *Principal, providerArn string) bool
- func TrustPrincipalMatches(p *Principal, principalArn string) bool
- type ActionCatalog
- type ActionSet
- type Bias
- type BiasOutcome
- type CompiledPolicy
- type Condition
- type ContextBuilder
- type ContextFactory
- type Decision
- type DirectConstraintEvaluator
- type Effect
- type EffectivePermissions
- type EffectiveStatement
- type Factor
- type Grant
- type IamPolicyEngine
- type IdentityEvaluator
- type IdentityFunc
- type LambdaAccessCheckResult
- type LambdaEvaluator
- type MatchedStatement
- type PermissionCheckResult
- type PermissionOutcome
- type Policy
- type PolicyEvaluator
- func (pe *PolicyEvaluator) CanPerformAction(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
- func (pe *PolicyEvaluator) CanPerformActionPattern(principalArn, action, resourcePattern string, ctx *RequestContext) *PermissionCheckResult
- func (pe *PolicyEvaluator) CheckDirectGrantConstraints(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
- func (pe *PolicyEvaluator) CheckDirectGrantConstraintsDenyOnly(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
- func (pe *PolicyEvaluator) CheckDirectSameAccountTrustPolicy(role *model.RoleDetail, principalArn string, ctx *RequestContext) *TrustCheckResult
- func (pe *PolicyEvaluator) CheckTrustPolicy(role *model.RoleDetail, principalArn string, ctx *RequestContext) *TrustCheckResult
- func (pe *PolicyEvaluator) IdentityFactor(action, resourceArn string, principals []string) Factor
- func (pe *PolicyEvaluator) PrincipalActionMasksAnyResource(actions []string) map[uint32]uint64
- func (pe *PolicyEvaluator) PrincipalsWithAction(action, resourcePattern string) *roaring.Bitmap
- func (pe *PolicyEvaluator) PrincipalsWithActionAll(action string) *roaring.Bitmap
- func (pe *PolicyEvaluator) PrincipalsWithActionAnyResource(action string) *roaring.Bitmap
- func (pe *PolicyEvaluator) PrincipalsWithAnyActionAnyResource(actions []string) *roaring.Bitmap
- func (pe *PolicyEvaluator) ResourcePolicyContext(principalArn, action, resourceArn string) *RequestContext
- func (pe *PolicyEvaluator) RoleDirectSameAccountTrustBitmap(role *model.RoleDetail, action string) *roaring.Bitmap
- func (pe *PolicyEvaluator) RoleTrustBitmap(role *model.RoleDetail, action string) *roaring.Bitmap
- func (pe *PolicyEvaluator) TrustFactor(role *model.RoleDetail, principals []string) Factor
- type Principal
- type PublicAccessBlock
- type RequestContext
- type Resolution
- type ResourceClass
- type S3ACL
- type S3ACLGrant
- type S3ACLGrantee
- type S3AccessCheckResult
- type S3BucketConfig
- type S3Evaluator
- func (e *S3Evaluator) BucketACLMayAllowAny(bucketArn string, actions []string) bool
- func (e *S3Evaluator) BucketACLMayAllowMask(bucketArn string, actions []string) uint64
- func (e *S3Evaluator) BucketPolicyMayAllowAny(bucketArn, principalArn string, actions []string) bool
- func (e *S3Evaluator) BucketPolicyMayAllowMask(bucketArn, principalArn string, actions []string) uint64
- func (e *S3Evaluator) CrossAccountAccessAlwaysBlocked(bucketArn string) bool
- func (e *S3Evaluator) EvaluateS3Access(principalArn, action, resourceArn string) S3AccessCheckResult
- type Statement
- type StatementResolutionResult
- type TrustCheckResult
- type TrustMatchFn
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HasPolicyVariables ¶
HasPolicyVariables reports whether a pattern contains IAM policy variable syntax. Ported from policy_variables.py has_policy_variables; a None pattern maps to the empty string, which contains no "${".
func ResolvePolicyVariables ¶
func ResolvePolicyVariables(pattern string, ctx *RequestContext, variablesEnabled bool) (string, bool)
ResolvePolicyVariables resolves IAM policy variables in an ARN resource pattern. Ported from policy_variables.py resolve_policy_variables.
AWS only substitutes variables in the ARN resource portion (the 6th colon-separated field). Non-ARN inputs, variables-disabled, and variable-free patterns are returned unchanged with ok=true. The second result is false when a variable is unresolved (the Python None) — the pattern then matches no concrete resource. Callers guard a nil/None pattern before calling (Python's None-input -> None case); an empty pattern here contains no "${" and returns ("", true).
func StatementActionMatches ¶
StatementActionMatches implements the mutually exclusive Action/NotAction selectors used by every resource-policy and trust-policy evaluator.
func TrustFederatedMatches ¶
TrustFederatedMatches reports whether a trust-policy Principal block trusts the federated providerArn. Ported from trust_policy.trust_federated_matches: it inspects only Principal.Federated ("*" trusts everyone; any entry that is "*" or ARN-glob-matches the provider trusts it). Other Principal keys are ignored.
func TrustPrincipalMatches ¶
TrustPrincipalMatches reports whether a trust-policy Principal block trusts the IAM principalArn. Ported from trust_policy.trust_principal_matches; this is the single canonical AWS-principal trust matcher — CheckTrustPolicy (via evaluateTrustDoc) is its one caller, and this logic is what the live suite validates.
It inspects only Principal.AWS ("*" trusts everyone; a bare 12-digit account is normalised to its :root ARN; a :root ARN trusts every principal in that account; anything else is an ARN pattern). Principal.Service and Principal.Federated are ignored here — they have their own checks.
Its :root control flow differs subtly from principalBlockMatches (resource.go): here a bare 12-digit entry is first rewritten to a :root ARN, and a :root entry on a principal with no resolvable account (empty account segment) falls through to an ARN pattern match rather than being skipped. Keep the two matchers separate.
Types ¶
type ActionCatalog ¶
type ActionCatalog struct {
// contains filtered or unexported fields
}
ActionCatalog expands IAM action patterns against the set of known AWS actions. It ports awshound/core/evaluation/iam_policy/catalog.py's ActionCatalog: principal-agnostic data used to expand wildcard actions ("s3:*", "iam:Get*", "*") and the NotAction complement.
The catalog is read-only after construction and safe for concurrent use.
func LoadBundled ¶
func LoadBundled() (*ActionCatalog, error)
LoadBundled loads the catalog from the snapshot bundled with the package. Mirrors ActionCatalog.load_bundled.
func NewActionCatalog ¶
func NewActionCatalog(actionsByService map[string]map[string]string) *ActionCatalog
NewActionCatalog builds a catalog from an in-memory {service: {action: access_level}} mapping. Service prefixes are lowercased; action names keep AWS's canonical casing. Mirrors ActionCatalog.__init__ (no provenance).
func (*ActionCatalog) AllActions ¶
func (c *ActionCatalog) AllActions() []string
AllActions returns every "service:Action" across every service, used to materialize the "*"/NotAction complement. The universe is computed once and cached; callers must treat the result as read-only. Mirrors ActionCatalog.all_actions.
func (*ActionCatalog) Contains ¶
func (c *ActionCatalog) Contains(action string) bool
Contains reports whether action is a known AWS action (case-insensitive). Mirrors ActionCatalog.__contains__.
func (*ActionCatalog) ExpandPattern ¶
func (c *ActionCatalog) ExpandPattern(pattern string) []string
ExpandPattern expands a single policy Action entry to the concrete actions it names. Mirrors ActionCatalog.expand:
- "*" expands to the whole action universe (AllActions). The engine resolves a bare Action:["*"] symbolically via a complement grant (see statementActions), so that hot path never materializes the ~20k-action catalog — but a NotAction:["*"] statement (and any external caller) still reaches this branch, which must return AllActions for parity with catalog.py.
- a service wildcard ("s3:*", "iam:Get*", "s3:Get?bject" — any entry whose action part contains '*' or '?') expands via fnmatch against that service's actions, returning canonical casing with a lowercased service prefix.
- a concrete "service:Action" resolves case-insensitively to canonical casing, or, when unknown to the snapshot (newer than it), passes through as "service:Action" with the service prefix lowercased so a policy referencing it still produces a grant.
The result is deduplicated (by construction) and sorted for determinism. As with catalog.py's frozenset return it must be treated as read-only — the "*" branch shares AllActions' cached slice.
type ActionSet ¶
type ActionSet map[string]struct{}
ActionSet is an unordered set of IAM action strings — the Go equivalent of the frozenset[str] the Python engine bundles into each Grant. Set algebra (union/intersection/difference), used when the engine resolves complement grants and applies ceilings, belongs with the engine port, not here.
func NewActionSet ¶
NewActionSet builds an ActionSet from the given actions.
type Bias ¶
type Bias int
Bias selects attacker- or defender-favorable resolution of conditional bits.
type BiasOutcome ¶
type BiasOutcome int
BiasOutcome is what to do with a statement after its Condition has resolved. Ported from bias.py Outcome (renamed to avoid confusion with PermissionOutcome in types.go).
const ( // BiasDiscard drops the statement (an Allow that resolved FALSE, or any // non-TRUE Deny). BiasDiscard BiasOutcome = iota // BiasRetainClean keeps the statement unconditionally (its condition // resolved TRUE). BiasRetainClean // BiasRetainConditional keeps an Allow whose condition resolved UNKNOWN as a // conditional grant. BiasRetainConditional )
func ApplyConditionBias ¶
func ApplyConditionBias(effect Effect, resolution Resolution) BiasOutcome
ApplyConditionBias decides a statement's fate given its effect and condition resolution. Ported from bias.py apply_condition_bias.
Attack-path bias: UNKNOWN allows are retained as conditional; UNKNOWN denies are discarded (an attacker is assumed to avoid triggering an unknowable deny gate). This is the single place the rule lives, so it cannot drift across evaluators. An effect other than Allow/Deny is a caller bug (a compiled statement always has one) and panics, mirroring the Python ValueError.
type CompiledPolicy ¶
type CompiledPolicy struct {
Grants []Grant
}
CompiledPolicy is a single policy document compiled to bundled grants (catalog-expanded). Ported from iam_policy/model.py CompiledPolicy.
type Condition ¶
Condition is a policy Condition block in AWS's canonical three-level form: operator -> condition key -> values. A single value is normalized to a one-element slice, and a non-string scalar value (a JSON bool or number, both of which AWS accepts) is coerced to its textual form. This mirrors the shape produced by the Python engine's normalize_condition.
func (*Condition) UnmarshalJSON ¶
UnmarshalJSON decodes a Condition block into the canonical three-level form, normalizing single values to one-element slices.
type ContextBuilder ¶
type ContextBuilder func(principalArn, action, resourceArn string) *RequestContext
ContextBuilder builds the RequestContext for a (principal, action, resource) triple — the seam onto store.get_policy_variable_context, which the Python resource evaluators call to resolve policy variables in Resource patterns and condition keys. A nil ContextBuilder (or one that returns nil) yields a blank context in which every key is UNKNOWN, matching the Python None-context path under attacker-favorable bias.
type ContextFactory ¶
type ContextFactory func() *RequestContext
ContextFactory lazily builds the RequestContext for a resolution. Ported from statement_resolution.py's ContextFactory. It is invoked only when at least one matched statement has a Condition, keeping the unconditional hot path free of the (expensive) context build. A nil factory is treated as the Python default (a blank context), which under attacker bias yields allowed=true with the full condition residual retained.
type Decision ¶
Decision is a resolved authorization result: the Allowed set and the subset of it that is conditional.
type DirectConstraintEvaluator ¶
type DirectConstraintEvaluator interface {
CheckDirectGrantConstraints(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
CheckDirectGrantConstraintsDenyOnly(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
}
DirectConstraintEvaluator applies identity-side explicit denies, permission boundaries, and organization guardrails after a resource policy supplied the Allow.
type Effect ¶
type Effect string
Effect is a policy statement's effect. Ported from iam_policy/model.py Effect; the string values match the raw "Effect" field of a policy statement.
type EffectivePermissions ¶
type EffectivePermissions struct {
AllowByAction []*EffectiveStatement
DenyByAction []*EffectiveStatement
IdentityAllowByAction []*EffectiveStatement
BoundaryAllowByAction []*EffectiveStatement
ScpAllowByAction []*EffectiveStatement
RcpAllowByAction []*EffectiveStatement
HasBoundary bool
HasSCP bool
HasRCP bool
ScpAllowPermitsAll bool
RcpAllowPermitsAll bool
// IdentityActionScope is the de-duplicated finite action selectors from the
// identity policy. Complement denies consult it for the original implicit-vs-
// explicit deny classification without retaining every resource grant.
IdentityActionScope []ActionSet
}
EffectivePermissions is a principal's compact effective permission set. The field names identify the represented layer; each holds its logical statements once and compression builds action views lazily.
AllowByAction and DenyByAction are the final, ceiling-baked indexes the query layer resolves. The remaining indexes are kept separately so the deny cause is recoverable at query time without a second evaluation pass: IdentityAllowByAction is what the identity policies alone granted (pre-ceiling), and the guardrail indexes are each guardrail's own permitted set (boundary as-is; SCP/RCP intersected across their attach levels). When a query is denied, comparing these tells a boundary/SCP/RCP clip apart from an implicit deny.
HasBoundary/HasSCP/HasRCP record which guardrails were actually applied, so an absent guardrail (empty permitted set) is not mistaken for one that blocks everything. ScpAllowPermitsAll/RcpAllowPermitsAll flag a guardrail whose own permitted set is every action (e.g. only FullAWSAccess at every level), carried as a flag instead of a ~20k-entry index; deny-cause then treats it as covering any action without consulting the (intentionally empty) index.
func NewEffectivePermissions ¶
func NewEffectivePermissions() *EffectivePermissions
NewEffectivePermissions returns an EffectivePermissions with all six action indexes initialized to empty maps (matching the Python dataclass's default_factory=dict), so the engine can index into them without nil-map panics.
type EffectiveStatement ¶
type EffectiveStatement struct {
Actions ActionSet
Resource *string
NotResource *string
Condition Condition
Source string
// Complement means Actions is the exclusion set: the statement applies to
// every catalog action except those entries. Keeping this symbolic through
// compression avoids expanding Action:"*" into one cached statement per AWS
// action for every principal.
Complement bool
// IdentityScoped limits a complement deny to actions named by at least one
// identity allow. The query layer tests its already-compact identity index,
// avoiding both a materialized union and a retained per-grant scope slice.
IdentityScoped bool
}
EffectiveStatement is a bundled statement in a principal's effective permissions (pattern-based). Ported from iam_policy/model.py EffectiveStatement. Its Actions set stays attached so compressed permissions can build only the action views the graph actually queries.
type Factor ¶
Factor is a tri-state authorization result over one coordinate space (dense int ids — principals or resources), carried as four roaring bit-planes.
func (Factor) Resolve ¶
Resolve collapses the four planes to a Decision under bias b.
attacker: allowed = (allow ∨ condAllow) ANDNOT deny defender: allowed = allow ANDNOT (deny ∨ condDeny)
Bias is applied here so a single set of four raw planes serves both audiences with no recompute (§5.2 of docs/authorization-tensor.md).
type Grant ¶
type Grant struct {
Actions ActionSet
Resource *string
NotResource *string
Condition Condition
Source string
Effect Effect
Complement bool
}
Grant is one bundled (action-set, resource, condition, source) unit from a compiled policy. Ported from iam_policy/model.py Grant.
Complement flips the meaning of Actions to exclusion: the grant then applies to every action EXCEPT those in Actions. It is the symbolic form for Action:"*" (complement of the empty set) and NotAction:[E] (complement of E), so the ~20k-action catalog is never materialized in a compiled grant. The engine keeps complements symbolic through lazy action lookup, so concrete indexes stay small. Complement=false is the ordinary "exactly these actions" grant.
Resource and NotResource are nil when the statement omitted them (the Python None); a non-nil pointer holds the glob pattern (which may itself be "*"). AWS treats Resource and NotResource as mutually exclusive within a statement. Condition is nil when the statement had none; otherwise it is the normalized operator->key->values block, which must hold in addition to the action and resource matching.
type IamPolicyEngine ¶
type IamPolicyEngine struct {
// contains filtered or unexported fields
}
IamPolicyEngine compiles IAM policy documents into bundled grants and combines a principal's identity policies, permissions boundary, SCPs, and RCPs into queryable effective permissions. Ported from awshound/core/evaluation/iam_policy/engine.py IamPolicyEngine.
It is independent of the rest of AWSHound and depends only on the action catalog. The catalog is read-only after construction, so an IamPolicyEngine is safe for concurrent use.
func NewIamPolicyEngine ¶
func NewIamPolicyEngine(catalog *ActionCatalog) *IamPolicyEngine
NewIamPolicyEngine returns an engine over the given action catalog. Mirrors IamPolicyEngine.__init__.
func (*IamPolicyEngine) Combine ¶
func (e *IamPolicyEngine) Combine(identity, boundary *CompiledPolicy, scpLevels, rcpLevels [][]*CompiledPolicy) *EffectivePermissions
Combine combines a principal's compiled policies into effective permissions. Ported from IamPolicyEngine.combine.
identity is the principal's own policies compiled to a single CompiledPolicy (managed + inline + group grants concatenated); its allows and denies are indexed as-is (the query layer resolves allow-vs-deny per resource). boundary is the permissions boundary or nil — a ceiling: an allow survives only where the boundary also allows it, and the boundary's own denies are added to the deny index.
scpLevels and rcpLevels are the ordered SCP/RCP attach levels (root, each OU on the account's path, the account). AWS requires an action to be allowed at *every* level — intersection across levels, union within a level — so each level is applied as a sequential ceiling, which is exactly the across-level intersection. RCPs are composed after SCPs so the strictest applicable guardrail wins. A nil/empty level stack imposes no guardrail.
func (*IamPolicyEngine) Compile ¶
func (e *IamPolicyEngine) Compile(doc *Policy, source string) *CompiledPolicy
Compile compiles one policy document into bundled grants. Ported from IamPolicyEngine.compile.
source is the provenance prefix for each grant's Source (each grant's source becomes "<source>/<Sid-or-index>"). Action:"*" and NotAction:[...] compile to symbolic complement grants (Complement=true, Actions = the excluded set) rather than materializing the ~20k-action catalog. Combine preserves that form through the compact permission cache and lazy per-action lookup. Concrete actions and service wildcards ("s3:*") expand as usual.
A nil document (a principal or resource with no policy) compiles to zero grants.
type IdentityEvaluator ¶
type IdentityEvaluator interface {
CanPerformAction(principalArn, action, resourceArn string) PermissionCheckResult
}
IdentityEvaluator is the identity-policy side the S3 and Lambda resource evaluators compose with. It is the single method the Python evaluators need from their `iam_evaluator: PolicyEvaluator`: whether a principal's identity policies (with permissions boundary, SCP, and RCP ceilings already applied) allow an action on a resource.
The identity kernel's *PolicyEvaluator does NOT satisfy this interface directly: its CanPerformAction carries an extra *RequestContext argument and returns a *PermissionCheckResult. Callers adapt it with IdentityFunc — a closure that supplies the context and dereferences the result — as internal/build/edges does (its iamIdentity helper) when wiring the S3 and Lambda evaluators.
type IdentityFunc ¶
type IdentityFunc func(principalArn, action, resourceArn string) PermissionCheckResult
IdentityFunc adapts a plain function to the IdentityEvaluator interface, so a bound method whose signature does not exactly match (or a test stub) can be supplied without a wrapper type.
func (IdentityFunc) CanPerformAction ¶
func (f IdentityFunc) CanPerformAction(principalArn, action, resourceArn string) PermissionCheckResult
CanPerformAction implements IdentityEvaluator.
type LambdaAccessCheckResult ¶
type LambdaAccessCheckResult struct {
Allowed bool
IAMAllowed bool
ResourcePolicyAllowed bool
Denied bool
DenySource string
AllowSources []string
Conditions []map[string]any
HasConditions bool
}
LambdaAccessCheckResult is the outcome of a Lambda access evaluation across IAM and the function resource policy. Ported field-for-field from lambda_resource_policy_evaluator.LambdaAccessCheckResult.
type LambdaEvaluator ¶
type LambdaEvaluator struct {
// contains filtered or unexported fields
}
LambdaEvaluator evaluates Lambda access across IAM policies and function resource policies. Ported from lambda_resource_policy_evaluator.LambdaResourcePolicyEvaluator.
It is read-only after construction and, provided the IdentityEvaluator and ContextBuilder it holds are safe for concurrent use, is safe for concurrent queries.
func NewLambdaEvaluator ¶
func NewLambdaEvaluator( identity IdentityEvaluator, functionResourcePolicies map[string]*Policy, contextBuilder ContextBuilder, constraints ...DirectConstraintEvaluator, ) *LambdaEvaluator
NewLambdaEvaluator builds a LambdaEvaluator. functionResourcePolicies is keyed by function ARN; each value is the parsed resource policy (parse the raw, NON-url-encoded lambda:GetPolicy JSON via ParseResourcePolicy). contextBuilder is the policy-variable/condition context source (may be nil for a blank context). Mirrors LambdaResourcePolicyEvaluator.__init__.
func (*LambdaEvaluator) EvaluateLambdaResourcePolicy ¶
func (e *LambdaEvaluator) EvaluateLambdaResourcePolicy(principalArn, functionArn string) LambdaAccessCheckResult
EvaluateLambdaResourcePolicy checks whether principalArn can invoke the Lambda function, composing the resource policy with the identity side. Ported from LambdaResourcePolicyEvaluator.can_invoke_function (the action is lambda:InvokeFunction).
A service principal ("….amazonaws.com") is authorized by the resource policy alone. Otherwise: an explicit deny on IAM or the resource policy wins; cross-account requires IAM AND the resource policy; same-account takes their union. When either account cannot be determined the check falls to the same-account rule, exactly as the Python does.
type MatchedStatement ¶
MatchedStatement is a policy statement already matched to the (action, resource) under evaluation, carrying just the condition and audit source the arbiter needs. Ported from statement_resolution.py MatchedStatement.
Condition is nil for an unconditional statement. Source is the audit label (an ARN, or e.g. "trust/0"). The Python fast paths duck-type on Statement instances to avoid a wrapper allocation; the Go engine instead builds a slice of these small value structs, which is a single backing-array allocation.
type PermissionCheckResult ¶
type PermissionCheckResult struct {
Allowed bool
Denied bool
Conditions []map[string]any
AllowSources []string
DenySources []string
Outcome PermissionOutcome
BlockingSource string
}
PermissionCheckResult is the result of checking whether a principal can perform an action on a resource. Ported from core/models/types.py.
Allowed and Denied keep their exact prior meaning; Outcome is additive attack-path enrichment. A zero-value Outcome ("") should be read as OutcomeImplicitDeny, the Python dataclass default. BlockingSource is the ARN of the policy/guardrail responsible for a deny outcome (the explicit-deny source, or the blocking boundary/SCP/RCP), or "" when not applicable (the Python None).
type PermissionOutcome ¶
type PermissionOutcome string
PermissionOutcome explains why a permission check resolved the way it did. Ported from core/models/types.py PermissionOutcome (a StrEnum).
AWS produces semantically distinct "no" outcomes that matter for attack-path analysis: an explicit deny is a durable control (bypass means editing a policy), an implicit deny means nothing grants it (bypass means adding a permission), and a ceiling clip means an identity grant exists but a boundary or SCP/RCP guardrail removes it (bypass means changing the guardrail).
OutcomeDeniedByRCP has no counterpart in the Python enum, which folds RCP denials into DENIED_BY_SCP (its single org-guardrail bucket, see policy_evaluator._classify_deny). The Go port gives Resource Control Policies their own outcome so attack-path enrichment can name the exact blocking guardrail kind.
const ( OutcomeAllowed PermissionOutcome = "ALLOWED" OutcomeExplicitDeny PermissionOutcome = "EXPLICIT_DENY" OutcomeImplicitDeny PermissionOutcome = "IMPLICIT_DENY" OutcomeDeniedByBoundary PermissionOutcome = "DENIED_BY_BOUNDARY" OutcomeDeniedBySCP PermissionOutcome = "DENIED_BY_SCP" OutcomeDeniedByRCP PermissionOutcome = "DENIED_BY_RCP" )
type Policy ¶
Policy is a parsed AWS IAM policy document — an identity policy, a resource policy, or a role trust policy. AWS lets the top-level "Statement" be a single object or an array; both parse into Statements.
func ParsePolicyDocument ¶
ParsePolicyDocument decodes a URL-encoded IAM policy document (as returned by the IAM GetAccountAuthorizationDetails / GetRole APIs and carried on the model InlinePolicy.PolicyDocument, RoleDetail.AssumeRolePolicyDocument, and PolicyVersion.Document fields) and parses it into a Policy.
The document is percent-encoded, so it is decoded with url.PathUnescape, which decodes %XX escapes but leaves "+" untouched — matching Python's urllib.parse.unquote so the decoded JSON is byte-for-byte what the Python evaluator parses. (url.QueryUnescape would additionally rewrite "+" into a space, corrupting any literal "+" inside a decoded string value — e.g. an ARN or condition value — and that divergence would surface as wrong attack paths.) If decoding fails — e.g. the input is already plain JSON — the raw input is parsed as a best-effort fallback.
func ParseResourcePolicy ¶
ParseResourcePolicy parses a resource-based policy document that is *plain JSON* — an S3 bucket policy (s3:GetBucketPolicy) or a Lambda resource policy (lambda:GetPolicy), each carried as a raw JSON string that is NOT percent-encoded. Use this, not ParsePolicyDocument, for those documents: ParsePolicyDocument first url-unescapes its input, which would corrupt a bucket policy whose value happens to contain a literal "%XX" sequence. (IAM identity documents and role trust policies from GetAccountAuthorizationDetails ARE url-encoded and must still go through ParsePolicyDocument.)
This mirrors the Python resource evaluators, which json.loads the string directly. An empty/whitespace input returns (nil, nil): no policy, the Python falsy-document short-circuit.
func (*Policy) UnmarshalJSON ¶
UnmarshalJSON parses a policy document, accepting a single Statement object or an array of statements. Unknown top-level keys (e.g. "Id") are ignored.
type PolicyEvaluator ¶
type PolicyEvaluator struct {
// contains filtered or unexported fields
}
PolicyEvaluator is the query layer over the IAM policy engine: it answers "can principal X perform action Y on resource Z?" and "does role R's trust policy admit principal P?" for the whole store. Ported from AWSHound's core/evaluation/policy_evaluator.py PolicyEvaluator together with the compressed-permissions cache (compressed_permissions.py) and the per-principal warm path of core/models/account_data.py (get_permissions, _compute_principal_key, bundle loading, SCP/RCP level construction).
For a principal it resolves the identity policies (inline + attached-managed + group-inherited for users), the permissions boundary, and the account's ordered SCP/RCP levels from the store's public API, runs the engine's Compile/Combine, then compresses each effective statement to roaring bitmaps of ARN int-ids via the shared ArnIndex (ResolvePattern). The result is cached per principal by a content fingerprint so principals with fingerprint-equal inputs share a single entry (the Python two-level dedup cache).
Deliberate, decision-preserving divergences from the Python ¶
- RCP denials get their own PermissionOutcome (OutcomeDeniedByRCP) rather than folding into DENIED_BY_SCP, matching types.go's documented intent.
- The per-principal fingerprint keys on the raw (source, document) inputs (not Python's normalized content hash). This is stricter — two principals share an entry only when their sources and documents are byte-identical — which yields slightly less sharing but guarantees byte-identical outputs including AllowSources/BlockingSource. Allow/deny decisions are unchanged.
- The request context for a check is built lazily from the store inside CanPerformAction/CheckTrustPolicy (buildPolicyVarContext, the port of store.get_policy_variable_context), so callers pass only Python's per-call request_context overlay (e.g. iam:PassedToService) as ctx, or nil for none.
The cache-build partial resolution (cache_build_resolution.py filter_statements_inplace) IS ported, so account-scoped conditions are pre-resolved identically to the Python cache and the result does not depend on the caller populating account/org keys in ctx.
A PolicyEvaluator is safe for concurrent use once the store is populated (the query phase); its caches are guarded and its scan-once indexes assume the store is stable during evaluation (ingestion has completed).
func NewPolicyEvaluator ¶
func NewPolicyEvaluator(st *store.Store) (*PolicyEvaluator, error)
NewPolicyEvaluator returns an evaluator over the store, loading the bundled IAM action catalog to build the engine. Mirrors PolicyEvaluator.__init__ plus account_data._iam_policy_engine (which loads the catalog on first use).
func NewPolicyEvaluatorWithEngine ¶
func NewPolicyEvaluatorWithEngine(st *store.Store, engine *IamPolicyEngine) *PolicyEvaluator
NewPolicyEvaluatorWithEngine returns an evaluator over the store using a caller-supplied engine (for tests, or a custom action catalog).
func (*PolicyEvaluator) CanPerformAction ¶
func (pe *PolicyEvaluator) CanPerformAction(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
CanPerformAction checks whether a principal can perform an action on a resource. Ported from policy_evaluator.can_perform_action.
It collects the matched allow/deny statements for the action (resource-side matching via bitmaps with a policy-variable / wildcard-target regex fallback), then defers the allow/deny decision to the shared condition-aware resolver so the tri-state attacker-bias rule cannot drift. When no statement matches, the denial is attributed to a cause (implicit deny vs boundary/SCP/RCP clip).
The request context is built internally from the store for this (principal, action, resource) via buildPolicyVarContext (the port of the Python query layer), so query-time keys — aws:PrincipalArn, aws:PrincipalTag/*, aws:PrincipalType, aws:username/userid, aws:ResourceAccount, aws:ResourceTag/*, and the *Known flags — resolve correctly rather than defaulting to UNKNOWN. A non-nil ctx is treated as a per-call OVERLAY (its Values, lower-cased, win over the store-derived ones) — how a PassRole-via-service processor injects iam:PassedToService; nil means no overlay. The base context is built lazily and at most once, only when a policy-variable resource pattern or a matched statement's condition actually needs it.
func (*PolicyEvaluator) CanPerformActionPattern ¶
func (pe *PolicyEvaluator) CanPerformActionPattern(principalArn, action, resourcePattern string, ctx *RequestContext) *PermissionCheckResult
CanPerformActionPattern checks whether an action is authorized on at least one member of a future-resource ARN family.
func (*PolicyEvaluator) CheckDirectGrantConstraints ¶
func (pe *PolicyEvaluator) CheckDirectGrantConstraints(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
CheckDirectGrantConstraints applies the policy layers that still constrain a resource-based grant: all explicit denies, an attached permissions boundary, and each applicable SCP/RCP ceiling. Unlike CanPerformAction it does not require an identity Allow because the caller has already established a direct resource-policy Allow.
func (*PolicyEvaluator) CheckDirectGrantConstraintsDenyOnly ¶
func (pe *PolicyEvaluator) CheckDirectGrantConstraintsDenyOnly(principalArn, action, resourceArn string, ctx *RequestContext) *PermissionCheckResult
CheckDirectGrantConstraintsDenyOnly preserves the direct-user/wildcard/KMS grant rule: a boundary's explicit Deny applies, but its missing Allow does not.
func (*PolicyEvaluator) CheckDirectSameAccountTrustPolicy ¶
func (pe *PolicyEvaluator) CheckDirectSameAccountTrustPolicy(role *model.RoleDetail, principalArn string, ctx *RequestContext) *TrustCheckResult
CheckDirectSameAccountTrustPolicy evaluates only exact same-account IAM principal ARN grants in role's trust policy. Account-root, wildcard and cross-account delegation are intentionally excluded: those require an identity-policy sts:AssumeRole Allow and are handled by CheckTrustPolicy.
func (*PolicyEvaluator) CheckTrustPolicy ¶
func (pe *PolicyEvaluator) CheckTrustPolicy(role *model.RoleDetail, principalArn string, ctx *RequestContext) *TrustCheckResult
CheckTrustPolicy checks whether a role's trust policy admits a principal for sts:AssumeRole. Ported from policy_evaluator.check_trust_policy / _check_trust_policy_impl / trust_evaluator.evaluate_trust_doc.
It is bitmap-accelerated: an interned principal absent from the role's trust bitmap (the set of principals the trust NAMES, ignoring conditions) is rejected immediately; otherwise the trust document is fully evaluated, with conditions resolved through the shared attacker-bias resolver. A non-interned principal (cross-account, not in the store) skips the bitmap and is evaluated directly by string/pattern matching.
func (*PolicyEvaluator) IdentityFactor ¶
func (pe *PolicyEvaluator) IdentityFactor(action, resourceArn string, principals []string) Factor
IdentityFactor inverts the identity side for a fixed (action, resource): over the given candidate principals it produces a Factor whose planes are indexed by interned principal id. This is the §6 "invert identity into the resource's space" primitive — the seam between the per-principal policy arbiter (CanPerformAction) and the bitwise decision algebra (Factor/Decision).
CanPerformAction already applies explicit-deny precedence and the SCP/boundary ceilings per principal, so a principal is classified as a definite allow (Allow plane) or, when the allow rode in on unresolved conditions, a conditional allow (CondAllow plane). Denies surface as absence, so the Deny/CondDeny planes stay empty here; the arbiter has already subtracted them.
func (*PolicyEvaluator) PrincipalActionMasksAnyResource ¶
func (pe *PolicyEvaluator) PrincipalActionMasksAnyResource(actions []string) map[uint32]uint64
PrincipalActionMasksAnyResource returns the per-principal bit mask of effective allow statements for actions (bit i corresponds to actions[i]). It keeps the same evicted-resource safety as PrincipalsWithAnyActionAnyResource while allowing a resource evaluator to skip unrelated action checks.
func (*PolicyEvaluator) PrincipalsWithAction ¶
func (pe *PolicyEvaluator) PrincipalsWithAction(action, resourcePattern string) *roaring.Bitmap
PrincipalsWithAction returns a roaring bitmap of principal ARN int-ids (from the shared ArnIndex) for every user/role whose effective permissions grant an allow for action on some resource matching resourcePattern. Ported from policy_evaluator.principals_with_action / principals_with_action_bm, made resource-aware via the pattern's resolved bitmap so the returned set is the tight edge-candidate set an attack-path edge processor pre-filters with.
The result shares the ArnIndex id-space with RoleTrustBitmap, so a caller can intersect the two bitmaps to find principals that BOTH can-assume a role AND hold an IAM-side allow, in one bitmap AND regardless of which side is more selective.
Soundness note: for concrete (variable-free) statement resources the check is exact bitmap overlap, sound whenever the query targets are interned ARNs (the common case — the pattern resolves to known resources the caller then queries with CanPerformAction). Statements with policy-variable resources, Resource:"*", or a pure NotResource cannot be excluded statically and are always included. Only ALLOW statements are considered — a deny-only principal can never yield an edge, so excluding it drops nothing.
func (*PolicyEvaluator) PrincipalsWithActionAll ¶
func (pe *PolicyEvaluator) PrincipalsWithActionAll(action string) *roaring.Bitmap
PrincipalsWithActionAll returns (and memoises) the principal-id bitmap for PrincipalsWithAction(action, "*") — every principal with an effective identity allow for the action on any resource. This is the candidate pre-filter for edge families where an identity allow is mandatory (IdentityGoverned, MandatoryPolicy): a principal absent from the set has no allow statement that could match an interned target, so it can never yield such an edge and its per-(principal,resource) CanPerformAction calls can be skipped. Mirrors the Python edge processors' principals_with_action pre-filter.
The returned bitmap is shared and MUST be treated as read-only (callers do Contains only); the per-action scan runs at most once per evaluator.
func (*PolicyEvaluator) PrincipalsWithActionAnyResource ¶
func (pe *PolicyEvaluator) PrincipalsWithActionAnyResource(action string) *roaring.Bitmap
PrincipalsWithActionAnyResource returns every user/role that has an effective allow statement for action before binding that statement to an interned target. This is the safe candidate set for resource families such as S3 whose object ARNs are deliberately not interned: a concrete object-only Resource pattern must remain a candidate even though its pre-resolved bitmap is empty.
func (*PolicyEvaluator) PrincipalsWithAnyActionAnyResource ¶
func (pe *PolicyEvaluator) PrincipalsWithAnyActionAnyResource(actions []string) *roaring.Bitmap
PrincipalsWithAnyActionAnyResource is the multi-action form used to build one coarse, safe candidate set for an edge family in a single principal scan.
func (*PolicyEvaluator) ResourcePolicyContext ¶
func (pe *PolicyEvaluator) ResourcePolicyContext(principalArn, action, resourceArn string) *RequestContext
ResourcePolicyContext exposes the same store-backed request context used by CanPerformAction to the S3/Lambda resource-policy evaluators. Resource-policy conditions must not fall back to UNKNOWN merely because those evaluators use the IdentityEvaluator adapter rather than calling CanPerformAction directly.
func (*PolicyEvaluator) RoleDirectSameAccountTrustBitmap ¶
func (pe *PolicyEvaluator) RoleDirectSameAccountTrustBitmap(role *model.RoleDetail, action string) *roaring.Bitmap
RoleDirectSameAccountTrustBitmap returns the known IAM users/roles that are named by exact ARN in an Allow statement in role's trust policy and belong to the role's account. Those direct grants are special in AWS: unlike a trust of the account root, they can authorize sts:AssumeRole without a second identity-policy Allow. Conditions and Deny statements are deliberately ignored in this candidate bitmap and are resolved before an edge is emitted.
func (*PolicyEvaluator) RoleTrustBitmap ¶
func (pe *PolicyEvaluator) RoleTrustBitmap(role *model.RoleDetail, action string) *roaring.Bitmap
RoleTrustBitmap returns a clone of the set of principal ARN int-ids that role's trust policy names for action (default sts:AssumeRole when action is ""). Public companion to PrincipalsWithAction for the bitmap-intersection pre-filter pattern; a clone is returned so the caller may mutate it (roaring And/Or mutate in place) without corrupting the cache. Ported from policy_evaluator.role_trust_bm.
func (*PolicyEvaluator) TrustFactor ¶
func (pe *PolicyEvaluator) TrustFactor(role *model.RoleDetail, principals []string) Factor
TrustFactor is the resource-side factor for the mandatory-policy class (role trust): over the candidate principals it produces the Factor a role's trust policy admits for sts:AssumeRole, keyed by interned principal id. It is the resource-side analogue of IdentityFactor — the two compose under Compose(..., MandatoryPolicy, ...) to yield the assume-role edge set (§5.4/§6).
A trust admittance gated by unresolved conditions lands in the CondAllow plane; CheckTrustPolicy already resolves the trust policy's own deny/allow arbitration, so the Deny/CondDeny planes stay empty.
type Principal ¶
type Principal struct {
// Wildcard is true when the block was the bare JSON string "*"; the keyed
// slices below are then empty.
Wildcard bool
AWS []string
Service []string
Federated []string
CanonicalUser []string
}
Principal is the Principal (or NotPrincipal) block of a resource/trust policy statement. AWS allows a bare "*" (anyone/anonymous) or an object keyed by principal type; each key's value may be a single string or an array.
func (*Principal) UnmarshalJSON ¶
UnmarshalJSON decodes a Principal block: either the bare string "*" or an object keyed by principal type whose values may each be a string or an array.
type PublicAccessBlock ¶
type PublicAccessBlock struct {
BlockPublicAcls bool
IgnorePublicAcls bool
BlockPublicPolicy bool
RestrictPublicBuckets bool
}
PublicAccessBlock is an S3 Block Public Access setting, used both per-bucket and account-wide. Ported from the BPA dict the Python merges; the four flags mirror the AWS response.
type RequestContext ¶
type RequestContext struct {
Values map[string]string
KnownKeys map[string]bool
PrincipalTags map[string]string
ResourceTags map[string]string
PrincipalKnown bool
ResourceKnown bool
OrgDataKnown bool
}
RequestContext is the known request-context data used to resolve condition keys and substitute policy variables. Ported field-for-field from core/evaluation/policy_variables.py PolicyVariableContext.
It answers, for a given (principal, action, resource) under evaluation, "what value(s) does condition key aws:X take?" — but only for the keys AWSHound statically knows. The tri-state mapping of a key to PRESENT/ABSENT/UNKNOWN is produced by resolveKey (conditions.go); this type only holds the raw data and the *Known flags that drive that mapping.
The *Known flags distinguish "we ingested the data and the key is genuinely absent" (Known=true, value missing -> ABSENT -> the atom sees None and the condition typically resolves FALSE) from "we did not ingest the data so we cannot tell" (Known=false -> UNKNOWN, where attacker-favorable bias takes over). The zero value (nil maps, all flags false) is a valid blank context in which every key resolves to UNKNOWN; reads from its nil maps are safe.
Values and KnownKeys keys are lower-cased. KnownKeys records service-specific keys whose presence/absence this request form can determine. Tag names in PrincipalTags/ResourceTags keep their original case (AWS tag keys are case-sensitive). The store/context-builder layer populates these fields.
type Resolution ¶
type Resolution int
Resolution is the tristate result of statically evaluating a Condition (block, key, or value). Ported from conditions/resolution.py.
ResolutionUnknown means the evaluator could not statically determine the outcome — typically because a required context key is unknowable (e.g. aws:SourceIp) or because the relevant data was not ingested. It is the zero value, so an unset Resolution defaults to the conservative "unknown".
const ( ResolutionUnknown Resolution = iota ResolutionTrue ResolutionFalse )
func ResolveCondition ¶
func ResolveCondition(cond Condition, ctx *RequestContext) Resolution
ResolveCondition resolves a Condition block to a tristate Resolution. Ported from condition_evaluator.py resolve_condition.
Composition (Kleene three-valued logic): multiple operators AND together; multiple keys under one operator AND together; multiple policy values for one key OR together (inside the atom); a multi-valued context against a non-quantified operator auto-promotes to ForAnyValue. An absent/empty condition is TRUE. The walker does not embed bias — callers apply ApplyConditionBias to the result to decide statement fate.
The result is independent of Go map iteration order: the operator- and key-level combinators are commutative AND (FALSE dominates, UNKNOWN beats TRUE), so it is deterministic despite the randomized map walk. No memoization: an earlier Python version cached by id(context) and returned stale resolutions after GC reused an address, producing non-deterministic edge counts.
func (Resolution) And ¶
func (a Resolution) And(b Resolution) Resolution
And is three-valued AND. FALSE dominates; UNKNOWN beats TRUE. Ported from conditions/resolution.py kleene_and.
func (Resolution) Not ¶
func (a Resolution) Not() Resolution
Not is three-valued NOT. UNKNOWN is its own negation. Ported from conditions/resolution.py kleene_not.
func (Resolution) Or ¶
func (a Resolution) Or(b Resolution) Resolution
Or is three-valued OR. TRUE dominates; UNKNOWN beats FALSE. Ported from conditions/resolution.py kleene_or.
func (Resolution) String ¶
func (r Resolution) String() string
String returns the lowercase label matching the Python enum's value.
type ResourceClass ¶
type ResourceClass int
ResourceClass selects how a resource's admit-decision composes with the identity decision (§5.4 of docs/authorization-tensor.md).
const ( // IdentityGoverned: no resource policy; access is identity ∧ same-account. IdentityGoverned ResourceClass = iota // MandatoryPolicy: resource policy always required (role trust); access is // identity ∧ resource, even same-account. MandatoryPolicy // OptionalUnion: same-account access is identity ∨ resource (a resource-policy // grant alone suffices), cross-account is identity ∧ resource (S3, KMS). OptionalUnion )
type S3ACL ¶
type S3ACL struct {
Grants []S3ACLGrant
}
S3ACL is a bucket ACL — the meaningful subset (its grants) of a GetBucketAcl response. Ported from the ACL dict the Python evaluator consumes.
type S3ACLGrant ¶
type S3ACLGrant struct {
Grantee S3ACLGrantee
Permission string
}
S3ACLGrant pairs a grantee with the permission it was granted ("READ", "WRITE", "READ_ACP", "WRITE_ACP", "FULL_CONTROL").
type S3ACLGrantee ¶
S3ACLGrantee identifies who an ACL grant is for. Type is the grantee kind ("CanonicalUser", "Group", …); a Group grantee carries a URI (the AllUsers / AuthenticatedUsers group). Ported from the ACL grantee dict the Python reads.
type S3AccessCheckResult ¶
type S3AccessCheckResult struct {
Allowed bool
IAMAllowed bool
BucketPolicyAllowed bool
ACLAllowed bool
Denied bool
DenySource string
AllowSources []string
Conditions []map[string]any
HasConditions bool
RequiresBucketPolicy bool
}
S3AccessCheckResult is the outcome of an S3 access evaluation across every control mechanism. Ported field-for-field from s3_policy_evaluator.S3AccessCheckResult.
type S3BucketConfig ¶
type S3BucketConfig struct {
BucketArn string
BucketPolicy *Policy
ACL *S3ACL
BlockPublicAccess *PublicAccessBlock
OwnershipControls string
OwnerAccountID string
}
S3BucketConfig is the resource-control configuration for one bucket. Ported from s3_policy_evaluator.S3BucketConfig.
BucketPolicy is the parsed bucket policy (parse the raw, NON-url-encoded JSON via ParseResourcePolicy), nil when the bucket has none. ACL and BlockPublicAccess are nil when absent. OwnershipControls is the object-ownership setting ("BucketOwnerEnforced" disables ACLs entirely), "" when unset. OwnerAccountID is the bucket owner's account — the basis for cross-account determination; the caller sets it to the account the bucket was collected from.
type S3Evaluator ¶
type S3Evaluator struct {
// contains filtered or unexported fields
}
S3Evaluator evaluates S3 access across IAM policies, bucket policies, ACLs, and Block Public Access. Ported from s3_policy_evaluator.S3PolicyEvaluator (the bucket_configs constructor path; the deprecated per-parameter path — and with it the object-level ACL override, which that path never populated under the runtime's bucket_configs wiring — is intentionally omitted).
It is read-only after construction and, provided the IdentityEvaluator and ContextBuilder it holds are themselves safe for concurrent use, is safe for concurrent queries.
func NewS3Evaluator ¶
func NewS3Evaluator( identity IdentityEvaluator, bucketConfigs map[string]*S3BucketConfig, accountPublicAccessBlocks map[string]*PublicAccessBlock, contextBuilder ContextBuilder, constraints ...DirectConstraintEvaluator, ) *S3Evaluator
NewS3Evaluator builds an S3Evaluator. bucketConfigs is keyed by bucket ARN (arn:aws:s3:::name); accountPublicAccessBlocks is keyed by account ID. contextBuilder is the policy-variable/condition context source (may be nil for a blank context). Mirrors S3PolicyEvaluator.__init__.
func (*S3Evaluator) BucketACLMayAllowAny ¶
func (e *S3Evaluator) BucketACLMayAllowAny(bucketArn string, actions []string) bool
BucketACLMayAllowAny reports whether one of the public ACL grants that the evaluator recognizes can allow an action of interest. It folds in ownership controls and IgnorePublicAcls, so ordinary owner-only ACL metadata does not force every org principal through every bucket/object check.
func (*S3Evaluator) BucketACLMayAllowMask ¶
func (e *S3Evaluator) BucketACLMayAllowMask(bucketArn string, actions []string) uint64
BucketACLMayAllowMask is BucketACLMayAllowAny with one bit per input action.
func (*S3Evaluator) BucketPolicyMayAllowAny ¶
func (e *S3Evaluator) BucketPolicyMayAllowAny(bucketArn, principalArn string, actions []string) bool
BucketPolicyMayAllowAny reports whether the bucket policy contains an Allow that names principalArn and at least one action of interest. Resource patterns and conditions are deliberately left to EvaluateS3Access: ignoring them here makes this a conservative bucket-level pre-filter while avoiding expansion to hundreds of thousands of object targets when a policy cannot name the principal at all.
func (*S3Evaluator) BucketPolicyMayAllowMask ¶
func (e *S3Evaluator) BucketPolicyMayAllowMask(bucketArn, principalArn string, actions []string) uint64
BucketPolicyMayAllowMask is BucketPolicyMayAllowAny with one bit per input action, allowing the S3 builder to avoid evaluating action families that no bucket-policy statement can possibly grant.
func (*S3Evaluator) CrossAccountAccessAlwaysBlocked ¶
func (e *S3Evaluator) CrossAccountAccessAlwaysBlocked(bucketArn string) bool
CrossAccountAccessAlwaysBlocked reports the principal-independent branch of RestrictPublicBuckets. EvaluateS3Access returns this denial before composing either bucket policy or ACL allows, so a cross-account target can be removed without losing an edge.
func (*S3Evaluator) EvaluateS3Access ¶
func (e *S3Evaluator) EvaluateS3Access(principalArn, action, resourceArn string) S3AccessCheckResult
EvaluateS3Access checks whether principalArn can perform an S3 action on the resource (a bucket or object ARN). Ported from S3PolicyEvaluator.can_perform_s3_action.
The evaluation order: any explicit deny (IAM, then bucket policy, then a Block Public Access clip) wins immediately; otherwise the allow layers are combined — cross-account requires IAM AND bucket policy (or an independent ACL grant), same-account takes the union of IAM, bucket policy, and ACL. When the bucket's owner account cannot be determined the check falls to the same-account (union) rule, exactly as the Python does.
type Statement ¶
type Statement struct {
Sid string
Effect string
Action []string
NotAction []string
Resource []string
NotResource []string
Principal *Principal
NotPrincipal *Principal
Condition Condition
}
Statement is one statement of a policy document. Action/NotAction/Resource/ NotResource are normalized to slices even when the document used a bare string; a nil slice means the key was absent, while a non-nil empty slice means it was present but empty. Principal/NotPrincipal are nil unless present (resource and trust policies only). Condition is nil when absent.
Effect is the raw "Allow"/"Deny" string (the engine maps it to the Effect enum when compiling).
func (*Statement) UnmarshalJSON ¶
UnmarshalJSON decodes a statement, normalizing the string-or-array policy fields (Action/NotAction/Resource/NotResource) to slices.
type StatementResolutionResult ¶
type StatementResolutionResult struct {
Allowed bool
Denied bool
Conditions []map[string]any
AllowSources []string
DenySources []string
}
StatementResolutionResult is the outcome of resolving a set of matched allow/deny statements. Ported from statement_resolution.py StatementResolutionResult. Conditions is the auditable trace of every evaluated gating condition (resolved-true and unknown/defaulted alike), regardless of which branch ultimately grants; AllowSources reflects the deciding branch.
func ResolveMatchedStatements ¶
func ResolveMatchedStatements(matchedAllows, matchedDenies []MatchedStatement, contextFactory ContextFactory) StatementResolutionResult
ResolveMatchedStatements resolves matched allow/deny statements to a final allow/deny decision under attacker-favorable bias. Ported from statement_resolution.py resolve_matched_statements. Every condition-aware evaluator (identity, trust, S3, Lambda, KMS) routes through this so the tri-state bias rule cannot drift.
Algorithm: (1) denies first — only a TRUE deny survives (UNKNOWN/FALSE denies drop); any surviving deny wins. (2) clean-allow short-circuit — any unconditional allow grants immediately. (3) resolve the remaining conditional allows: TRUE -> clean, UNKNOWN -> retained conditional (attacker bias), FALSE -> dropped. (4) a clean allow wins if present, else any conditional allow wins; the conditions trace carries every evaluated gating condition.
type TrustCheckResult ¶
TrustCheckResult is the result of checking whether a role's trust policy allows a principal. Ported from core/models/types.py.
func EvaluateFederatedTrust ¶
func EvaluateFederatedTrust(doc *Policy, providerArn, action string, ctxFactory ContextFactory) TrustCheckResult
EvaluateFederatedTrust checks whether a role's trust policy admits the federated providerArn (a SAML or OIDC provider) for action (e.g. sts:AssumeRoleWithSAML / sts:AssumeRoleWithWebIdentity). It is check_federated_trust_policy specialised to the federated matcher.
doc is the parsed trust policy — parse RoleDetail.AssumeRolePolicyDocument via ParsePolicyDocument (trust documents ARE url-encoded). A nil doc is not trusted. ctxFactory lazily builds the RequestContext for condition resolution (invoked only when a matched statement is conditioned); a nil factory yields a blank context, under which an unresolved condition is retained as a conditional trust (attacker bias).
type TrustMatchFn ¶
TrustMatchFn matches a statement's Principal block against a candidate target ARN. Ported from trust_evaluator.TrustMatchFn. The two implementations are TrustPrincipalMatches (IAM principals, Principal.AWS) and TrustFederatedMatches (federation providers, Principal.Federated).