Skip to content

fix(github-config): bind a membership's or grant's stored identity to what it declares - #4524

Merged
devantler merged 19 commits into
mainfrom
claude/team-grant-identity-4518
Oct 6, 2026
Merged

devantler merged 19 commits into
mainfrom
claude/team-grant-identity-4518

Conversation

@devantler

@devantler devantler commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

A team membership or repository grant that passed every existing rule could still act on a different team, repository or user than the one it declares, because nothing checked the identity the provider stores for it. For grants that includes changing a permission, not only removing one.

What

Anyone other than the provider may now only leave that stored identity absent, leave it unchanged together with the team and the repository or user, or set it to the approved identity of the declared team and subject (for recovery after a rebuild). Removing it is accepted only while the team and subject stay as they were, so one write cannot clear it and retarget the object. Every current membership and grant on prod already satisfies the rule, so nothing existing is refused.

Fixes #4518

👉 After merge/promotion: confirm the policy is ready, that the GitHub configuration still applies, and that memberships and grants stay in sync.

devantler and others added 6 commits October 5, 2026 10:19
A github-config Team could be created compliant and then re-pointed at a
foreign GitHub team by changing crossplane.io/external-name, because the
provider finds the remote team by that numeric ID and nothing constrained it.

Add an admission policy that accepts the annotation only when it is unset,
unchanged, or the approved identity of that team, on create and update. The
provider's own record of a team it created is exempt, matched by the
revision-suffixed ServiceAccount name it runs as.

Part of #3144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kyverno's Equals and AnyNotIn read * and ? as wildcards on either side, so an
identity of "*" matched the approved one and any later value then counted as
unchanged. Evaluate the three accepted cases in one JMESPath expression, which
compares strings literally, and cover both wildcard routes with fixtures.

Part of #3144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on case

Part of #3144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… directory group

A nested team inherits its parent's repository access on GitHub, outside the
grant rules that cap what the github-config release may hand out, and a
directory link moves membership outside the member allow-list. The Team rules
checked neither. Refuse all four fields the provider exposes, in forProvider
and initProvider, with one literal JMESPath comparison.

Fixes #4511

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e rule itself

Review finding: `x || ''` also reads false, [] and {} as empty, so only the
provider's schema stood between those values and admission. not_null() replaces
a missing value only. Adds fixtures for a boolean, an empty list, a blank
string and a Team with no spec, and words the message to match what is accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… what it declares

A TeamRepository is read, updated and removed by its stored identity alone,
and a TeamMembership is removed by it, so an object that satisfies every rule
in restrict-github-team-management could act on a different team, repository
or user than the one it names (#4518). Established from the released sources
of the provider version prod runs.

Anyone but the provider may now only leave that identity unset, leave it
unchanged together with the team reference and the repository or user, or set
it to the approved identity of the referenced team followed by the declared
repository or user.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Evaluation at 42402808 — trial on a real admission controller: a throwaway single-node cluster with Kyverno v1.19.1 (the version prod runs), the three team CRDs exported read-only from prod, the policy from this branch. Requests were sent as the github-config service account unless the row says "by provider" (a service account named provider-upjet-github-<revision> in crossplane-system). The cluster was removed afterwards.

Request Expected Result
TeamRepository create, no identity admitted admitted
TeamRepository create, approved identity (admins) admitted admitted
TeamRepository create, approved identity (maintainers) admitted admitted
TeamRepository create, refs admins + maintainers identity denied denied
TeamRepository create, refs maintainers + admins identity denied denied
TeamRepository create, identity names another repository denied denied
TeamRepository create, unapproved team denied denied
TeamRepository create, wildcard identity denied denied
TeamRepository create, wildcard repository part denied denied
TeamRepository create by provider, any identity admitted admitted
TeamRepository update, provider-recorded identity re-applied unchanged admitted admitted
TeamRepository update, identity changed to another team denied denied
TeamRepository update, identity kept, team reference moved denied denied
TeamRepository update, identity kept, repository moved denied denied
TeamRepository update, team reference and identity moved together to the approved pair admitted admitted
TeamRepository update, identity removed admitted admitted
TeamRepository create by provider, wildcard identity admitted admitted
TeamRepository update from a wildcard identity to an unapproved team denied denied
TeamRepository update from a wildcard identity to the approved one admitted admitted
TeamMembership create, no identity admitted admitted
TeamMembership create, approved identity (admins) admitted admitted
TeamMembership create, approved identity (maintainers) admitted admitted
TeamMembership create, refs admins + maintainers identity denied denied
TeamMembership create, refs maintainers + admins identity denied denied
TeamMembership create, identity names another username denied denied
TeamMembership create, unapproved team denied denied
TeamMembership create, wildcard identity denied denied
TeamMembership create, wildcard username part denied denied
TeamMembership create by provider, any identity admitted admitted
TeamMembership update, provider-recorded identity re-applied unchanged admitted admitted
TeamMembership update, identity changed to another team denied denied
TeamMembership update, identity kept, team reference moved denied denied
TeamMembership update, identity kept, username moved denied denied
TeamMembership update, team reference and identity moved together to the approved pair admitted admitted
TeamMembership update, identity removed admitted admitted
TeamMembership create by provider, wildcard identity admitted admitted
TeamMembership update from a wildcard identity to an unapproved team denied denied
TeamMembership update from a wildcard identity to the approved one admitted admitted

38 of 38 as expected. What this trial cannot show is the provider itself acting on a mismatched identity; that part rests on the released source, as described in the pull request.

…repository or user

Independent review: the rule read only forProvider, so a repository stated
under initProvider could be moved while the identity counted as unchanged.
No object on prod states one. Also names the way to re-point an object in the
refusal, and covers a lookalike service account and provider updates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review skipped: 105 files exceed the limit of 100.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@devantler devantler left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Generated by the Agentic Engineer

Self-review (fallback — CodeRabbit, Codex and Cursor Bugbot unavailable)

Reviewed commit: c0f4d0236dc9ba3b22f24571cbdf0293a6c8db86

  • CodeRabbit: rate limited — it refused the request ("Review rate limited", 10:20Z) and has reviewed nothing on this pull request.
  • Codex: usage limit since 2026-10-05T08:40Z.
  • Cursor Bugbot: usage limit since 2026-10-05T09:37Z; no review from it on record.

An independent reviewer read the change in two rounds, proving each claim with kyverno 1.19.1 runs on throwaway fixtures and checking the provider claims against its released source.

Round 1, at 42402808: no P0 or P1. One P2: the rule read the repository or user only under forProvider, so one stated only under initProvider could be moved while the identity counted as unchanged. Fixed in cb3c901f: a stored identity may not be combined with a repository or user under initProvider (no object on prod states one). The refusal now also says how to re-point an object, and fixtures cover a lookalike service account and provider updates.

Round 2, at cb3c901f: no P0 or P1, and the round-1 finding is closed. The two rules are identical apart from repository and username, the expression evaluates as written, the routine re-apply passes, and the provider is skipped on create and update. Non-string, empty, templated and whitespace values behave safely.

Round 2 also raised:

  • P2, outside this change: neither policy looks at teamIdRef.namespace. The stored identity here is still the approved pair, so the exposure is in the older reference rules. Filed as #4525, verification first.
  • P3: the team half of "unchanged" relies on the older policy refusing a direct team ID or an initProvider reference. Not reachable today because that policy refuses such an object on create.
  • P3: an object created with only initProvider.repository is admitted and then refused on every re-apply once the provider records its identity. It fails closed, and the message says what to do.
  • P3 fixtures: one initProvider row passes for another reason, the lookalike rows are only caught by the fixture validator, and the provider-update rows do not model an identity change. Not applied; the validator runs in CI and the guard has two other rows that fail without it.
  • P3 comment wording: applied in c0f4d023, which changes two comment lines only.

Not exercised: the provider against GitHub. The trial on a real admission controller is in the evaluation comment and was run at 42402808; the initProvider guard added since is covered by fixtures and mutants only.

Verdict: no P0/P1 findings

devantler and others added 7 commits October 5, 2026 13:09
… rules

Kyverno's list operators read * and ? in either operand as wildcards, so a
value of * matched every entry of an allow-list. Measured on v1.19.1, 25
such values were admitted by restrict-github-team-management: a team display
name, a referenced team, a member login, a role and a repository permission.

Every condition is now one JMESPath boolean compared with `Equals false`, so
each comparison is exact. A membership's or grant's team reference may also
only name the github-config namespace, because the provider resolves the
reference in the namespace it names (#4525).

Fixes #4517
Fixes #4525

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cover an empty one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Integrated the repaired security foundations at ada9540c6234b2a15fbc98f40ec8c660357ff982, without changing the external-identity policy from the previously reviewed head. The literal-comparison and nesting protections are retained alongside the two stored-identity rules; no permission or provider exemption is widened.

All 59 Kyverno fixture files evaluate every declared rule. The 46-rule enforced-action baseline, race-enabled baseline tests, and all ten manifest-layer builds pass locally. Fresh native CI and current-head review are still required. Land #4519, then #4526, then this PR so the overlapping changes have one ordered delivery path.

The earlier admission trial remains evidence only for the earlier tested policy; it is not a new live-provider trial of this merged commit.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b493d1c8-3b1d-4fec-9347-1e8691f6306a
📥 Commits

Reviewing files that changed from the base of the PR and between f28ac8d and 413b8b2.

📒 Files selected for processing (50)
  • k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml
  • tests/policy-failure-actions.json
  • tests/restrict-github-team-external-identity/grants-create/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-create/resources.yaml
  • tests/restrict-github-team-external-identity/grants-create/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-create/values.yaml
  • tests/restrict-github-team-external-identity/grants-lookalike/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-lookalike/resources.yaml
  • tests/restrict-github-team-external-identity/grants-lookalike/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-lookalike/values.yaml
  • tests/restrict-github-team-external-identity/grants-other-account/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-other-account/resources.yaml
  • tests/restrict-github-team-external-identity/grants-other-account/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-other-account/values.yaml
  • tests/restrict-github-team-external-identity/grants-provider-update/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-provider-update/resources.yaml
  • tests/restrict-github-team-external-identity/grants-provider-update/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-provider-update/values.yaml
  • tests/restrict-github-team-external-identity/grants-provider/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-provider/resources.yaml
  • tests/restrict-github-team-external-identity/grants-provider/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-provider/values.yaml
  • tests/restrict-github-team-external-identity/grants-update/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/grants-update/resources.yaml
  • tests/restrict-github-team-external-identity/grants-update/user-info.yaml
  • tests/restrict-github-team-external-identity/grants-update/values.yaml
  • tests/restrict-github-team-external-identity/members-create/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-create/resources.yaml
  • tests/restrict-github-team-external-identity/members-create/user-info.yaml
  • tests/restrict-github-team-external-identity/members-create/values.yaml
  • tests/restrict-github-team-external-identity/members-lookalike/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-lookalike/resources.yaml
  • tests/restrict-github-team-external-identity/members-lookalike/user-info.yaml
  • tests/restrict-github-team-external-identity/members-lookalike/values.yaml
  • tests/restrict-github-team-external-identity/members-other-account/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-other-account/resources.yaml
  • tests/restrict-github-team-external-identity/members-other-account/user-info.yaml
  • tests/restrict-github-team-external-identity/members-other-account/values.yaml
  • tests/restrict-github-team-external-identity/members-provider-update/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-provider-update/resources.yaml
  • tests/restrict-github-team-external-identity/members-provider-update/user-info.yaml
  • tests/restrict-github-team-external-identity/members-provider-update/values.yaml
  • tests/restrict-github-team-external-identity/members-provider/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-provider/resources.yaml
  • tests/restrict-github-team-external-identity/members-provider/user-info.yaml
  • tests/restrict-github-team-external-identity/members-provider/values.yaml
  • tests/restrict-github-team-external-identity/members-update/kyverno-test.yaml
  • tests/restrict-github-team-external-identity/members-update/resources.yaml
  • tests/restrict-github-team-external-identity/members-update/user-info.yaml
  • tests/restrict-github-team-external-identity/members-update/values.yaml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-08-16T03:58:51.588Z
Learnt from: devantler
Repo: devantler-tech/platform PR: 2740
File: k8s/bases/infrastructure/cluster-policies/best-practices/restrict-tenant-route-hostnames.yaml:61-61
Timestamp: 2026-08-16T03:58:51.588Z
Learning: For Kyverno ClusterPolicy manifests under k8s/bases/infrastructure/cluster-policies, do not use the deprecated top-level spec.validationFailureAction field. Configure the equivalent per-rule validate.failureAction instead, preserving each policy's existing Audit or Enforce behavior. Add or run an effective-action validation guard because kyverno test verifies rule results but does not confirm the admission failure action.

Applied to files:

  • k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml
🔇 Additional comments (50)
tests/restrict-github-team-external-identity/grants-update/kyverno-test.yaml (1)

1-33: LGTM!

tests/restrict-github-team-external-identity/grants-update/resources.yaml (1)

1-157: LGTM!

tests/restrict-github-team-external-identity/grants-update/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-update/values.yaml (1)

1-95: LGTM!

tests/restrict-github-team-external-identity/members-update/kyverno-test.yaml (1)

1-33: LGTM!

tests/restrict-github-team-external-identity/members-update/resources.yaml (1)

1-157: LGTM!

tests/restrict-github-team-external-identity/members-update/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-update/values.yaml (1)

1-95: LGTM!

tests/restrict-github-team-external-identity/grants-provider-update/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/grants-provider-update/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/grants-provider-update/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-provider-update/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/members-provider-update/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/members-provider-update/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/members-provider-update/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-provider-update/values.yaml (1)

1-7: LGTM!

tests/policy-failure-actions.json (1)

7-8: LGTM!

k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml (1)

172-175: 🔒 Security & Privacy

The companion policy closes the proposed bypasses. It requires an allow-listed teamIdRef.name, rejects selectors and foreign namespaces, and requires a reference that re-resolves on every reconcile. It also constrains initProvider. The claimed update paths are not admitted.

tests/restrict-github-team-external-identity/grants-create/kyverno-test.yaml (1)

13-37: LGTM!

tests/restrict-github-team-external-identity/grants-create/resources.yaml (1)

1-224: LGTM!

tests/restrict-github-team-external-identity/grants-create/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-create/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/grants-lookalike/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/grants-lookalike/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/grants-lookalike/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-lookalike/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/grants-other-account/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/grants-other-account/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/grants-other-account/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-other-account/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/grants-provider/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/grants-provider/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/grants-provider/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/grants-provider/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/members-create/kyverno-test.yaml (1)

13-37: LGTM!

tests/restrict-github-team-external-identity/members-create/resources.yaml (1)

1-224: LGTM!

tests/restrict-github-team-external-identity/members-create/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-create/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/members-lookalike/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/members-lookalike/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/members-lookalike/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-lookalike/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/members-other-account/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/members-other-account/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/members-other-account/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-other-account/values.yaml (1)

1-7: LGTM!

tests/restrict-github-team-external-identity/members-provider/kyverno-test.yaml (1)

1-19: LGTM!

tests/restrict-github-team-external-identity/members-provider/resources.yaml (1)

1-16: LGTM!

tests/restrict-github-team-external-identity/members-provider/user-info.yaml (1)

1-11: LGTM!

tests/restrict-github-team-external-identity/members-provider/values.yaml (1)

1-7: LGTM!


📝 Walkthrough
📝 Walkthrough

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 413b8

This change adds enforced admission rules that stop a membership or grant's stored identity from pointing at a different team, repository or user than it declares. The supplied evidence shows no concrete merge-blocking defect. Confirm policy readiness and that the GitHub configuration still syncs after promotion, as the PR description says.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed [ #4518 ] The policy records provider behavior for production versions provider-upjet-github v0.20.0 and terraform-provider-github v6.13.0. It states that TeamRepository operations use the stored iden…
Out of Scope Changes check ✅ Passed All reported changes support [ #4518 ]. The policy rules implement the identity constraints. The enforced-action baseline and Kyverno fixtures test those rules. No unrelated changes appear in the supp…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Title check ✅ Passed The title clearly summarizes the main change: binding stored membership and grant identities to their declared values.
Description check ✅ Passed The description explains the risk, the new identity restrictions, and the intended post-merge checks. It is directly related to the changeset.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devantler devantler left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Generated by the Agentic Engineer

Self-review (fallback — CodeRabbit, Codex and Cursor Bugbot unavailable)

Reviewed commit: ada9540c6234b2a15fbc98f40ec8c660357ff982

  • CodeRabbit: this current-head request was refused at 2026-10-05T19:22:33Z; the direct provider response was updated at 19:22:44Z. The head-bound summary at 19:22:41Z reports included reviews unavailable for 57 minutes, until 20:19:41Z. No usage-based billing is authorized.
  • Codex: account code-review quota exhausted in the direct response, event 2026-10-05T16:59:30Z, freshly read this round. Recovery requires restored account capacity; no paid overage is authorized.
  • Cursor Bugbot: user/team usage or spend limit in the direct response, event 2026-10-03T22:35:03Z, freshly read this round. Recovery requires the account administrator; no newer successful servicing evidence was found.

Fresh direct reads covered this PR's conversation, review objects, threads and current-head checks. The older formal review is stale at this head; no current-head substantive external verdict or Bugbot artifact is present. There are no unresolved review threads.

Reviewed the current 48-path change against main 8cb909aeb70f3927a0522f44d4b10ccc2f7a090d, including its integrated nesting and literal-comparison foundations. The external-identity policy itself is byte-for-byte unchanged from c0f4d0236dc9ba3b22f24571cbdf0293a6c8db86. Both stored-identity rules use literal equality, bind the approved team and declared subject, forbid an initProvider subject alongside an identity, and require the previous team and subject as well as the previous identity for the unchanged path. Missing identities remain admissible; invalid existing objects are not grandfathered. The provider exemption remains limited to its revision service accounts in crossplane-system; the lookalike-account case stays denied. The integrated reference rules retain namespace binding, parent/directory restrictions and the question-mark regression. No credential, provider permission or admission exception is expanded.

All 59 fixture files evaluate their named rules; the 46-rule Enforce baseline, race-enabled baseline tests and ten manifest-layer builds pass at this exact commit. Reviewed the create/update, changed identity, changed reference/subject, wildcard, initProvider, provider and lookalike-account fixtures and their oldObject bindings. The previously reported cross-namespace reference issue is handled by the integrated #4526 foundation, not waived.

The earlier 38-request real-admission trial remains evidence only for its earlier tested commit. This review and the fixture results are not a new exact-head admission-controller trial, provider/GitHub exercise or production readback. CI and ordered delivery through #4519, then #4526, remain required; production must subsequently show the policy Ready and the existing resources still synchronized.

Verdict: no P0/P1 findings

@devantler

devantler commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Status note (2026-10-06 10:20Z): the blocker this comment recorded, #4526, merged at 09:57Z. main was merged into this branch at 53ebbbf2ab5f5063afa8339c1319bb9a8d1b2b0b; the two test files this branch carried as an older copy of #4526 now match main, so the diff is this change alone. The 185 policy tests for the two team policies pass at that head. Next: review at the new head, then promotion.

The GitHub Actions incident on 5 October cancelled this head's code
scanning runs, and those runs cannot be retried. No file changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 55 minutes.

…ntity-4518

# Conflicts:
#	tests/restrict-github-team-management/literal-comparison/kyverno-test.yaml
#	tests/restrict-github-team-management/literal-comparison/resources.yaml
@devantler devantler removed the blocked label Oct 6, 2026
…ng the object

Removing the stored identity was accepted unconditionally, so one write
could clear it and point the membership or grant at another team or
subject, after which the provider may record the old identity again on
an object that now declares something else. Removal is now accepted only
while the team reference and subject stay as they were. Adds fixtures
that fail without the rule, and one for another service account in the
provider's namespace so widening the exemption is caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 6 minutes.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Evaluation at 413b8b20

  • Review: CodeRabbit full review, requested at this head at 11:31Z, finished with no actionable comments over the range ending at 413b8b20. No unresolved threads.
  • Checks: all required checks green at this head; no conflict with main.
  • Tried: ran the policy through the Kyverno 1.19.1 engine (the version prod runs) against the request fixtures at this head: 80 of 80 rows as declared, and the fixture-evaluation check confirms every row is really evaluated. The update rows supply a complete previous object, so the rule reads the old and new team, subject and stored identity the same way it does at admission.
  • What changed since the live trial above (at 42402808, 38 of 38): two refusals were added, and both are covered by rows that fail without them — a stored identity next to a repository or user stated under initProvider, and removing the stored identity in the same write that moves the team, repository or user. Removing it while those stay as they were is still admitted, as in the trial.
  • Not repeated: the live admission-controller trial. This host has no container runtime today, so a throwaway cluster could not be started. How the request reaches the rule is unchanged since that trial; only the comparison changed, and that is what the engine run above exercises.

After merge: the infrastructure layer on prod is currently suspended for the runner recovery, so the policy will apply when that resumes. The checks listed in the description are owed then.

@devantler
devantler marked this pull request as ready for review October 6, 2026 11:43
@devantler
devantler added this pull request to the merge queue Oct 6, 2026
@devantler
devantler removed this pull request from the merge queue due to a manual request Oct 6, 2026
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Delivery hold at unchanged head 413b8b2038d87e30587245f1e137306d87bbdc53: this PR was removed from the merge queue at 13:11:50 UTC together with every other entry. It did not fail — no merge-group run was started for it. The restoration of main that followed the failed runner activation does not pass its orphan-resource verification yet, so any production deploy from the queue is expected to be evicted until the recovery in #4565 lands.

The review, checks and evaluation recorded above remain valid for this head. Re-queue once #4565 has merged and a production deploy has completed; if main has moved in a way that touches these files, re-check first. The post-merge production checks in the description are still owed.

@devantler
devantler added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit b61ae0c Oct 6, 2026
33 checks passed
@devantler
devantler deleted the claude/team-grant-identity-4518 branch October 6, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

Establish whether a membership or grant can be re-pointed by its stored identity

1 participant