Skip to content

fix(pulumi-guard): align the OIDC contract with the retired personal repository - #96

Merged
kyle-sexton merged 2 commits into
mainfrom
fix/pulumi-guard-contract-v3
Jul 14, 2026
Merged

fix(pulumi-guard): align the OIDC contract with the retired personal repository#96
kyle-sexton merged 2 commits into
mainfrom
fix/pulumi-guard-contract-v3

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Why

Production deploys on melodic-software/github-iac fail closed at the deploy guard: Pulumi personal-token allow policies do not exactly match the reviewed contract (first failure: run 29372145481). Root cause: Epic #78 C4 deleted kyle-sexton/github-iac and reduced live Pulumi OIDC trust to exactly the one organization rule, while the bundled contract still required the deleted repo's policy. Exact-match guard = correct fail-closed behavior; the reviewed contract is what must move.

What

  • contracts/kyle-sexton-github-iac.json: the single surviving melodic-software/github-iac policy (byte-identical rules to the prior org entry); replaces kyle-sexton-github-iac-v2.json — version markers dropped from the contract name, since the contract is a living exact-match document whose identity is the stack it guards
  • guard.test.sh + pulumi-deploy-guard.test.cjs: repointed to the unversioned contract; content test asserts the single-policy shape

Verification

  • guard.test.sh: 22/22 PASS
  • node --test pulumi-deploy-guard.test.cjs: 5 pass / 0 fail

Follow-up

melodic-software/github-iac PR bumps its pinned guard SHA + policy-contract: kyle-sexton-github-iac, unblocking the dotfiles-enrollment deploy (#109 merged but unapplied).

Closes #85

🤖 Generated with Claude Code

https://claude.ai/code/session_01JLfjHY8PY6ozhgZPiuHKik

…ract v3)

Epic #78 C4 deleted kyle-sexton/github-iac and reduced live Pulumi OIDC
trust to the single organization rule, so the exact-match v2 contract now
fails every production deploy before mutation. Contract v3 carries only
the surviving melodic-software/github-iac policy; v2 is removed with the
repository it described.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLfjHY8PY6ozhgZPiuHKik
@claude

claude Bot commented Jul 14, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 33s —— View job


PR Review: fix(pulumi-guard): retire the deleted personal-repo OIDC policy (contract v3)

Tasks

  • Gather context and understand the request
  • Read the changed files
  • Review contract JSON, test scripts, and shell test
  • Post findings

Summary

This is a clean, minimal fix. The exact-match guard is working as intended — it correctly failed because the live Pulumi OIDC trust no longer matches the v2 contract. The right response is to advance the reviewed contract (v3), not relax the guard, and that's exactly what this does.

OIDC trust surface: 2 allow policies → 1 allow policy. This is the correct direction.


Contract JSON (kyle-sexton-github-iac-v3.json)

The surviving policy is byte-identical to v2's melodic-software/github-iac entry.

Key checks:

  • sub field is consistent with guard.sh's derivation formula (repo:${owner}@${ownerId}/${repo}@${repoId}:environment:${env}): repo:melodic-software@58273638/github-iac@1277417810:environment:github-iac-production
  • No wildcards in any rule value — guard.sh's test("[*?.]") | not check still passes ✓
  • schemaVersion: 2 refers to the contract file schema (not the v3 revision number); consistent with v2.json which also carried schemaVersion: 2
  • actor_id: "153232337" pins the personal actor, ensuring only kyle-sexton's identity can mint the Pulumi token even from the org repo ✓
  • run_attempt: "1" is a pre-existing constraint (present in v2 as well) — re-run of a failed deploy job would fail the OIDC grant, which is intentional behavior for this guard design

Test Coverage

guard.test.sh — all three references updated (contract path at line 5, POLICY_CONTRACT env var at line 74, temp-dir invalid contract path at line 140). The tampered-contract rejection tests at lines 137–145 still work correctly — they write a mutant v3 file and verify the guard rejects it. 22/22 pass reported ✓

pulumi-deploy-guard.test.cjs — contract path, test name, length assertion (2→1), identity map, and final deepEqual all correctly reduced to the single surviving policy. The actor_id pin is still asserted (line 116). The no-wildcards loop (lines 124–126) still covers all rule values ✓

No orphaned v2 references remain in the codebase.


Security

  • Trust surface shrinks — this PR cannot escalate privileges
  • Guard's fail-closed exact-match behavior is preserved; only the reviewed contract document moves
  • kyle-sexton/github-iac being removed from the OIDC allow set is appropriate since the repo no longer exists
  • CLAUDE.md security rules are not implicated (no workflow files modified, no trigger changes)

One Minor Note (non-blocking)

The downstream consumer (melodic-software/github-iac) must bump its policy-contract input from kyle-sexton-github-iac-v2 to kyle-sexton-github-iac-v3 and pin the new guard SHA before production deploys resume. The PR body confirms this work is tracked (#109). Nothing to change here — just confirming the follow-up is the right next step.


Verdict: Approve. The change is correct, minimal, and security-appropriate. All test assertions are consistent with the reduced single-policy contract.

The contract is a living exact-match document; its identity is the stack
it guards, not a revision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLfjHY8PY6ozhgZPiuHKik
@kyle-sexton kyle-sexton changed the title fix(pulumi-guard): retire the deleted personal-repo OIDC policy (contract v3) fix(pulumi-guard): align the OIDC contract with the retired personal repository Jul 14, 2026
@claude

claude Bot commented Jul 14, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 29s —— View job


Tasks

  • Gather context and understand the request
  • Read changed files and PR diff
  • Review contract JSON, test scripts, and naming refactor
  • Post findings

PR Review: fix(pulumi-guard): align the OIDC contract with the retired personal repository

This review covers both commits: 283787a (retire deleted repo policy) and 88eaf4c (drop version marker from contract name).


Contract JSON (kyle-sexton-github-iac.json)

OIDC trust surface: 2 allow policies → 1 allow policy. Correct direction. The surviving entry is byte-identical to v2's melodic-software/github-iac entry.

Verified against the old kyle-sexton-github-iac-v2.json (recovered from git history):

Check Result
sub field matches guard.sh derivation formula (repo:${owner}@${ownerId}/${repo}@${repoId}:environment:${env}) repo:melodic-software@58273638/github-iac@1277417810:environment:github-iac-production
No wildcards in any rule value — test("[*?.]") | not still passes
schemaVersion: 2 (contract file schema, not a revision counter)
actor_id: "153232337" pin preserved — only kyle-sexton's identity can mint the token
run_attempt: "1" constraint preserved (intentional; re-runs fail the OIDC grant)
kyle-sexton/github-iac (deleted repo) entry fully removed

Unversioned naming (the 88eaf4c commit): Renaming from kyle-sexton-github-iac-v3.jsonkyle-sexton-github-iac.json is the right call. The file is a living exact-match document — its identity is the stack it guards, not which revision it is. Future OIDC trust changes update this file in-place rather than creating v4, v5, etc. Consumers identify by the unversioned name, so future contract updates won't require caller changes in melodic-software/github-iac.

No orphaned version references remain — confirmed by grepping the full codebase for kyle-sexton-github-iac.


Test Coverage

guard.test.sh — All three name references updated consistently:

  • line 5: contract path variable
  • line 74: POLICY_CONTRACT env var
  • line 140: temp-dir mutant contract path for tampered-contract rejection tests

The tampered-contract tests (lines 137–145) still write a mutant file and verify the guard rejects it. 22/22 pass reported ✓

pulumi-deploy-guard.test.cjs — All reductions are correct:

  • line 25: contract path updated to unversioned name ✓
  • line 89: personalAllowPolicies.length === 1
  • lines 96–101: identity map reduced to single melodic-software/github-iac entry ✓
  • line 116: actor_id pin still asserted ✓
  • lines 124–126: wildcard-free loop still covers all rule values ✓
  • lines 128–138: final deepEqual correctly reduced to single repo:id pair ✓

5/5 pass, 0 fail reported ✓


Security

  • Trust surface shrinks — this PR cannot escalate privileges
  • Fail-closed exact-match guard behavior is fully preserved; only the reviewed contract document moves
  • Removing kyle-sexton/github-iac from the OIDC allow set is correct (repo no longer exists)
  • No workflow files modified — no trigger changes, CLAUDE.md security rules not implicated

Follow-up Confirmation

The downstream consumer (melodic-software/github-iac) must set policy-contract: kyle-sexton-github-iac (the new unversioned name, not kyle-sexton-github-iac-v3 as the earlier review noted) and pin the new guard SHA. PR body confirms this is tracked.


Verdict: Approve. Both commits are correct, minimal, and security-appropriate. The naming refactor is a sound design decision. All test assertions are consistent with the reduced single-policy contract.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review


P2 Badge Keep the replacement contract name versioned

This adds the changed single-policy contract under the unversioned stem kyle-sexton-github-iac, while the action contract I checked (action.yml and guard.sh) still describes policy-contract as a versioned contract name and resolves it directly to contracts/$POLICY_CONTRACT.json. For callers following that documented interface, the old kyle-sexton-github-iac-v2 name was removed and there is no new versioned name such as ...-v3 to pass, so upgrading to this action SHA with the documented naming scheme fails as unknown policy contract (or forces callers onto an undocumented mutable name). Please publish this semantic contract change under a new versioned stem.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kyle-sexton
kyle-sexton merged commit 15aefd8 into main Jul 14, 2026
30 of 31 checks passed
@kyle-sexton
kyle-sexton deleted the fix/pulumi-guard-contract-v3 branch July 14, 2026 22:54
kyle-sexton added a commit to melodic-software/standards that referenced this pull request Jul 15, 2026
…ct (#111)

## Why

melodic-software/github-iac#112 bumps its
`pulumi-version-drift-check.yml` pin to ci-workflows `15aefd8` (the
commit whose deploy-guard contract matches post-retirement Pulumi OIDC
trust — melodic-software/ci-workflows#96). The runner-policy engine
allowlists reusable-workflow contracts by exact `path@SHA`, so the
consumer lane rejects the new pin until it is reviewed here (Codex
confirmed this on github-iac#112; the Runner policy check fails there
today).

## What

- `policy.json`: add the `pulumi-version-drift-check.yml@15aefd8`
contract (identical shape: hosted-only, `fixedRunsOn: ubuntu-24.04`)
- `runner-policy.test.mjs`: assert the new reviewed reference alongside
the existing one (same staged-set pattern the selector allowlist already
uses)

## Cleanup contract

The `@99ac2f8` drift-check entry stays only until github-iac#112 merges;
a follow-up removes it the same day — no lingering legacy references.

## Verification

- `node --test components/runner-policy/runner-policy.test.mjs`: 101
pass / 0 fail

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JLfjHY8PY6ozhgZPiuHKik

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

Rename OIDC policy contract off the versioned name (kyle-sexton-github-iac-v2 → kyle-sexton-github-iac)

1 participant