Skip to content

feat(proof): least-privilege proof that runs as orgseed-ci through the real chain - #19

Merged
DustyStudy merged 1 commit into
mainfrom
feat/prove-least-privilege
Sep 21, 2026
Merged

DustyStudy merged 1 commit into
mainfrom
feat/prove-least-privilege

Conversation

@DustyStudy

Copy link
Copy Markdown
Owner

What

The least-privilege half of "prove orgseed works", with real calls made as the orgseed-ci role through the real OIDC -> hub -> orgseed-ci chain. So far that claim was supported by the IAM policy simulator only.

  • cli/prove_ci_negatives.py - 17 probes in three groups, each of which must get the right kind of answer:
    • self_escalation (7): edit the CI role's own permissions, rewrite OrgSeedAdmin's trust, touch the bootstrap stacks. Must hit an explicit deny in an identity-based policy - the belt-and-braces statement that keeps these closed even if the allow statements are ever broadened. A merely implicit denial fails: it would mean that guard is missing or unused.
    • outside_scope (6): things never granted (read EC2, list IAM users, other buckets, move accounts). Must fail because no policy allows them.
    • control (4): what the role is for (read the org, its SCPs, its state bucket, whoami). Must work, so a dead session can't masquerade as "everything denied". Expired-token/throttling errors are classified as errors, never as verdicts.
  • prove.yml - runs it in the orgseed Environment via the shared session action, so it runs as orgseed-ci, after a human approval. No || true; the summary is written even on failure; the evidence artifact (verdicts only) lives 7 days.

Safety (this runs against a live management account)

Every call expected to be denied is harmless if wrongly allowed: nonexistent policy ARNs, deliberately malformed documents ({} - IAM rejects them before applying), nonexistent stack names, fake OU/account IDs. A broken deny therefore fails a check and does no damage. Tests enforce it: no probe may call any of a list of destructive operations, and every mutating probe is checked to be inert.

Verification

  • 36 new tests (written first; they failed before the code): classification (explicit vs implicit vs SCP vs credential errors), evaluation, the safety properties above, no identifiers in output, and the workflow's controls. 149 total; ruff, bandit, actionlint, checkov, gitleaks clean.
  • Predicted against real IAM before spending an approval: I asked the IAM policy simulator what the deployed orgseed-ci role answers for each probe's exact action and resource. All 16 simulatable probes matched the script's expectation (explicitDeny / implicitDeny / allowed).

Not verified yet

The workflow has not run. Simulator prediction is not the same as the live API's answer (resource ARN forms differ per API); the first run is the real test, and if a probe disagrees that's a finding about either the role or the probe.

…e real chain

prove_ci_negatives.py makes real calls as the CI role and checks each got the RIGHT kind of denial: the self-escalation calls (rewrite its own or the admin role's permissions, touch the bootstrap stacks) must hit an explicit identity-policy deny, so they stay closed even if the allow statements are ever broadened; calls outside its remit must fail because nothing allows them; the calls it exists for must work. Only the right kind of denial counts for each group.

Every call expected to be denied is harmless if it is wrongly allowed - a nonexistent policy ARN, a deliberately malformed document, a nonexistent stack - so a broken deny fails a check and does no damage. A test enforces that no probe makes a destructive call and that the mutating ones are inert. The design was checked against the IAM policy simulator for the deployed role: all 16 probes predicted as expected.

prove.yml runs it through the shared session action in the orgseed environment, so it runs as orgseed-ci after a human approval; it has no || true and writes its summary even on failure.
@DustyStudy
DustyStudy merged commit fab5fda into main Sep 21, 2026
5 checks passed
@DustyStudy
DustyStudy deleted the feat/prove-least-privilege branch September 21, 2026 23:09
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.

1 participant