Skip to content

CIEM-718: skip the never-read denied_permissions report section (opt-in) - #40

Merged
yanas-orca merged 1 commit into
mainfrom
CIEM-718/ape-skip-denied-report
Aug 10, 2026
Merged

CIEM-718: skip the never-read denied_permissions report section (opt-in)#40
yanas-orca merged 1 commit into
mainfrom
CIEM-718/ape-skip-denied-report

Conversation

@yanas-orca

Copy link
Copy Markdown
Collaborator

Summary

create_json_report gains include_denied_permissions: bool = True. The default is unchanged, so the CLI and the golden test keep current behavior; only callers that don't read the section opt out.

This is the low-risk half of #39, carved out so it can land on its own. It touches 16 lines, does not go near evaluate(), and adds no caches, no shared state and no object aliasing.

Why this section is safe to skip

evaluate() unions the SCP's expanded Deny statements into denied_permissions and then never reads them back — the only consumer is this report section:

final_permissions, ineffective = apply_permission_boundary(final_permissions, self.scp_policy)  # SCP applied here
ineffective_permissions.update(more_ineffective)                                                # dropped actions land here
denied_permissions = deep_update(denied_permissions, boundary.denied_permissions)               # write-only echo

An SCP's actual effect lands in the two sections we keep:

  • unconditional deny → action dropped from allowed_permissions, recorded in ineffective_permissions with denied_by = the SCP ARN
  • conditional deny → action stays in allowed_permissions with the negated SCP condition merged into its Condition

Measured on a conditional-SCP account, comparing the same principal with and without SCPs:

allowed entries only WITH scps    : 0
allowed entries only WITHOUT scps : 0
entries in BOTH with a DIFFERENT Condition : 479     <- the SCP's entire footprint

denied_permissions: 91 chars -> 271,119 chars        <- account-constant echo, per principal

Because self.scp_policy is built once in __init__, that blob is identical for every principal. It is also the section that scales with the SCP set, which is why enabling the SCP-hierarchy flag made report-building dominate the handler.

Incidentally, the accumulation is subtly wrong and has been for as long as it has existed: deep_update only merges dict and list values, but denied_permissions is Dict[str, Set[Action]], so when an action key appears in both the principal's own denies and the SCP's, the SCP's set is silently dropped. Nobody has noticed, because nobody reads it.

No consumer reads it

  • graph parser — move_to_graph_utils/orca/effective_permissions/effective_permissions_parser.py:119,125 reads only allowed_permissions + ineffective_permissions
  • UI / GraphQL — base_api/gql/gql_query.py:1249,1256 likewise
  • swept orca and ui.v2: every other denied_permissions occurrence is a test fixture or an Azure-path comment

Measured — real accounts, full (uncapped), one shared evaluator

account principals main this PR (default) this PR + skip
tempus 2,774 72.2 s / 253 MB 72.2 s / 256 MB 30.6 s / 204 MB (−58%)
a2 179 4.5 s 4.5 s 4.6 s
a3 297 2.9 s 2.9 s 2.8 s

Report phase on tempus: 43.5 s → 1.8 s. a2/a3 have no SCPs, so the denied section is small and there is nothing to win — as expected.

Correctness

allowed_permissions, ineffective_permissions and the shrunk policy are hash-identical to main across a2, a3 and tempus — both with the flag defaulted and with the section skipped. Default-path timings are identical to main, i.e. no overhead for callers that don't opt in.

New test test_include_denied_permissions_preserves_read_sections locks the invariant. 39 unit tests pass.

Gate on the canonicalized permission set, not the serialized artifact: main is order-nondeterministic on SCP-bearing accounts (denied_by attribution and statement ordering both vary run to run), so byte-comparing the JSON produces false alarms.

Rollout

Safe by default — the denied section is still built unless a caller opts out. The matching orca PR flips the AWS handler to include_denied_permissions=False and bumps the pin; it is blocked on publishing 1.1.9.

#39 remains open with the caches, hash memo and _finalize rewrite; it will need a rebase and a version bump past 1.1.9.

create_json_report gains include_denied_permissions: bool = True. Default is
unchanged, so every existing caller (CLI, golden test) keeps current behavior;
callers that don't read the section opt out.

Why the section is skippable: evaluate() unions the SCP's expanded Deny
statements into denied_permissions and then never reads them back. An SCP's
actual effect lands elsewhere - dropped actions go to ineffective_permissions
with denied_by set to the SCP ARN, and conditionally-denied actions stay in
allowed_permissions with the negated deny condition merged in. Measured on a
conditional-SCP account, adding SCPs changed 479 allowed_permissions conditions
and added/removed 0 entries, while denied_permissions grew 91 -> 271,119 chars
of account-constant echo repeated for every principal.

No consumer reads it: the graph parser
(move_to_graph_utils/orca/effective_permissions/effective_permissions_parser.py)
and the UI/GraphQL path (base_api/gql/gql_query.py) both read only
allowed_permissions + ineffective_permissions.

Measured on real accounts, full (uncapped), one shared evaluator:
  tempus (2,774 principals)  report 43.5s -> 1.8s, total 72.2s -> 30.6s (-58%),
                             peak RSS 253MB -> 204MB
  a2 (179) / a3 (297)        unchanged (no SCPs, report is not the bottleneck)

Output-neutral: allowed_permissions, ineffective_permissions and the shrunk
policy are hash-identical to main across a2, a3 and tempus, both with the flag
defaulted and with the section skipped.

@orca-security-eu orca-security-eu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

@yanas-orca
yanas-orca merged commit 4106cf0 into main Aug 10, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants