Skip to content

fix(security): resolve team references on every reconcile - #160

Merged
devantler merged 1 commit into
mainfrom
claude/team-ref-resolve-always-3195
Aug 18, 2026
Merged

fix(security): resolve team references on every reconcile#160
devantler merged 1 commit into
mainfrom
claude/team-ref-resolve-always-3195

Conversation

@devantler

@devantler devantler commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

Our GitHub teams and repository grants are managed declaratively here, and the control that says
which team a grant may target is the team's name. That control does not currently hold:
Crossplane only resolves a name reference when the numeric ID beside it is empty, so a manifest
carrying an allow-listed team name and a directly written team ID would quietly reconcile
against the ID — the name would be decorative. Anything reviewing the name would see an approved
grant while a different team was actually being granted.

This is the change that closes that gap, and it is the prerequisite for the platform-side policy
that enforces the allow-list (platform#3195,
which blocks platform#2718).

What

Tells Crossplane to re-resolve the team reference on every reconcile, on all 39 team memberships and
repository grants, so the referenced team always wins over any ID sitting next to it.

No grant changes. Checked against the live cluster rather than assumed: every deployed resource
already points at exactly the team its name refers to (23 to admins, 16 to maintainers, matching
the manifests one for one). Re-resolving therefore produces the identical result today. The effect
is entirely about what becomes impossible in future.

Merge order

This must land before platform#2718 starts requiring the field, or all 39 resources would be
rejected. That sequencing is why this PR exists separately.

Fixes #3195 is deliberately not used here — that issue lives in devantler-tech/platform and
also covers the platform-side policy change, which is not in this diff. Part of platform#3195.

Crossplane resolves a reference only when the corresponding field is empty
unless told otherwise, so an allow-listed teamIdRef sitting beside a directly
authored teamId is decorative — the numeric value is what reconciles, and a
by-name allow-list cannot constrain it.

Set teamIdRef.policy.resolve to Always on all 39 TeamMembership and
TeamRepository resources so the referenced team always wins.

No manifest here authors a teamId, so nothing is re-pointed by this change; it
closes the bypass rather than altering any current grant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

@devantler I will review the pull request.

✅ Action performed

Review finished.

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.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Readiness evidence — exercised against the live cluster, not reasoned about

1. The field is schema-valid and its semantics are exactly the intent. Read off the live CRD
(teammemberships.team.github.m.upbound.io), which is the authority on reference semantics rather
than the provider docs:

resolve:  enum=[Always, IfNotPresent]
  "Resolve specifies when this reference should be resolved. The default is 'IfNotPresent',
   which will attempt to resolve the reference only when the corresponding field is not present.
   Use 'Always' to resolve the reference on every reconcile."

Note the sibling resolution field (Required|Optional, default Required) is a different
control and is deliberately untouched — confusing the two is the easy mistake here.

2. Every live resource is currently on the default. All 39 TeamMembership +
TeamRepository objects in prod report teamIdRef.policy.resolve UNSET, so this is a real
delta on every one of them, not a no-op restatement.

3. No grant moves — proven by matching IDs, not assumed. This is the claim worth checking, since
resolve: Always overwrites teamId on every reconcile and the live objects do carry one (the
resolver wrote it). If any live ID disagreed with what its reference resolves to, this change would
silently re-point a real permission.

It does not. The two sides reconcile exactly:

Referenced team External ID Manifests referencing it Live objects carrying that ID
admins 18573307 23 23
maintainers 17046606 16 16

Every live teamId is already the value its own reference resolves to, so re-resolving yields the
identical ID. The change is behaviour-preserving today and closes the bypass going forward.

4. What is deliberately not claimed. Acceptance criterion 3 of platform#3195 — that a resource
carrying an allow-listed reference and a foreign ID reconciles to the referenced team — cannot be
demonstrated until this is merged and reconciled, because no such resource exists to observe. That
verification belongs to the post-merge check, and the platform-side policy change stays blocked
until it passes.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review 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: Pro Plus

Run ID: 6212f707-d9ce-4268-9abf-f6f839e3f189

📥 Commits

Reviewing files that changed from the base of the PR and between c129905 and 829a122.

📒 Files selected for processing (39)
  • deploy/team-memberships/add-devantler-to-admins.yaml
  • deploy/team-memberships/add-devantler-to-maintainers.yaml
  • deploy/team-repositories/grant-admins-on-actions.yaml
  • deploy/team-repositories/grant-admins-on-agent-plugins.yaml
  • deploy/team-repositories/grant-admins-on-agent-skills.yaml
  • deploy/team-repositories/grant-admins-on-ascoachingogvaner.yaml
  • deploy/team-repositories/grant-admins-on-aws.yaml
  • deploy/team-repositories/grant-admins-on-doggy-countdown.yaml
  • deploy/team-repositories/grant-admins-on-dot-github.yaml
  • deploy/team-repositories/grant-admins-on-dotnet-template.yaml
  • deploy/team-repositories/grant-admins-on-fleet-gitops.yaml
  • deploy/team-repositories/grant-admins-on-go-template.yaml
  • deploy/team-repositories/grant-admins-on-homebrew-tap.yaml
  • deploy/team-repositories/grant-admins-on-ksail.yaml
  • deploy/team-repositories/grant-admins-on-kyverno-policies.yaml
  • deploy/team-repositories/grant-admins-on-maintenance.yaml
  • deploy/team-repositories/grant-admins-on-monorepo.yaml
  • deploy/team-repositories/grant-admins-on-platform-template.yaml
  • deploy/team-repositories/grant-admins-on-platform-tenant-template.yaml
  • deploy/team-repositories/grant-admins-on-platform.yaml
  • deploy/team-repositories/grant-admins-on-provider-upjet-unifi.yaml
  • deploy/team-repositories/grant-admins-on-unifi.yaml
  • deploy/team-repositories/grant-admins-on-wedding-app.yaml
  • deploy/team-repositories/grant-admins-on-world-at-ruin.yaml
  • deploy/team-repositories/grant-maintainers-on-actions.yaml
  • deploy/team-repositories/grant-maintainers-on-aws.yaml
  • deploy/team-repositories/grant-maintainers-on-doggy-countdown.yaml
  • deploy/team-repositories/grant-maintainers-on-dotnet-template.yaml
  • deploy/team-repositories/grant-maintainers-on-go-template.yaml
  • deploy/team-repositories/grant-maintainers-on-homebrew-tap.yaml
  • deploy/team-repositories/grant-maintainers-on-ksail.yaml
  • deploy/team-repositories/grant-maintainers-on-kyverno-policies.yaml
  • deploy/team-repositories/grant-maintainers-on-monorepo.yaml
  • deploy/team-repositories/grant-maintainers-on-platform-template.yaml
  • deploy/team-repositories/grant-maintainers-on-platform-tenant-template.yaml
  • deploy/team-repositories/grant-maintainers-on-platform.yaml
  • deploy/team-repositories/grant-maintainers-on-provider-upjet-unifi.yaml
  • deploy/team-repositories/grant-maintainers-on-unifi.yaml
  • deploy/team-repositories/grant-maintainers-on-world-at-ruin.yaml

Included review availability: Your plan includes up to 1 review per rolling hour; 0 remain after this review.

📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
deploy/**/*.{yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

deploy/**/*.{yaml,yml}: Ownership goes to a team, not an individual. The canonical owner across the suite is the
maintainers team — model access on Team/TeamRepository, never on individual logins.
Observe-first when adopting an existing resource. A new Repository/IssueLabels/team CR for
an already-live object must adopt it without risk of recreate/delete: set the
crossplane.io/external-name annotation to the live name and use a management policy that
excludes Delete (observe/late-initialize), per platform's docs/github-management.md.
Validate before every PR (the sole required check, CI - Required Checks, gates on this):
A DRIFT line is a real defect in one of those two shapes; fix the resource, never the live repo.

Files:

  • deploy/team-repositories/grant-admins-on-wedding-app.yaml
  • deploy/team-repositories/grant-maintainers-on-unifi.yaml
  • deploy/team-repositories/grant-maintainers-on-world-at-ruin.yaml
  • deploy/team-repositories/grant-admins-on-platform.yaml
  • deploy/team-repositories/grant-admins-on-actions.yaml
  • deploy/team-repositories/grant-admins-on-ksail.yaml
  • deploy/team-repositories/grant-admins-on-dotnet-template.yaml
  • deploy/team-repositories/grant-admins-on-maintenance.yaml
  • deploy/team-repositories/grant-admins-on-platform-template.yaml
  • deploy/team-repositories/grant-admins-on-go-template.yaml
  • deploy/team-repositories/grant-admins-on-agent-plugins.yaml
  • deploy/team-repositories/grant-maintainers-on-monorepo.yaml
  • deploy/team-repositories/grant-maintainers-on-platform-tenant-template.yaml
  • deploy/team-memberships/add-devantler-to-maintainers.yaml
  • deploy/team-repositories/grant-maintainers-on-actions.yaml
  • deploy/team-repositories/grant-admins-on-agent-skills.yaml
  • deploy/team-repositories/grant-admins-on-aws.yaml
  • deploy/team-repositories/grant-admins-on-world-at-ruin.yaml
  • deploy/team-repositories/grant-admins-on-monorepo.yaml
  • deploy/team-repositories/grant-maintainers-on-aws.yaml
  • deploy/team-repositories/grant-admins-on-kyverno-policies.yaml
  • deploy/team-repositories/grant-admins-on-platform-tenant-template.yaml
  • deploy/team-repositories/grant-maintainers-on-go-template.yaml
  • deploy/team-repositories/grant-maintainers-on-kyverno-policies.yaml
  • deploy/team-repositories/grant-admins-on-homebrew-tap.yaml
  • deploy/team-repositories/grant-admins-on-ascoachingogvaner.yaml
  • deploy/team-repositories/grant-admins-on-dot-github.yaml
  • deploy/team-repositories/grant-maintainers-on-provider-upjet-unifi.yaml
  • deploy/team-repositories/grant-admins-on-fleet-gitops.yaml
  • deploy/team-repositories/grant-maintainers-on-platform.yaml
  • deploy/team-repositories/grant-maintainers-on-doggy-countdown.yaml
  • deploy/team-repositories/grant-maintainers-on-homebrew-tap.yaml
  • deploy/team-repositories/grant-maintainers-on-platform-template.yaml
  • deploy/team-repositories/grant-maintainers-on-dotnet-template.yaml
  • deploy/team-memberships/add-devantler-to-admins.yaml
  • deploy/team-repositories/grant-admins-on-provider-upjet-unifi.yaml
  • deploy/team-repositories/grant-admins-on-doggy-countdown.yaml
  • deploy/team-repositories/grant-admins-on-unifi.yaml
  • deploy/team-repositories/grant-maintainers-on-ksail.yaml
🔇 Additional comments (40)
deploy/team-memberships/add-devantler-to-admins.yaml (1)

11-12: LGTM!

deploy/team-memberships/add-devantler-to-maintainers.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-go-template.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-maintainers-on-homebrew-tap.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-ksail.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-maintainers-on-kyverno-policies.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-monorepo.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-maintainers-on-platform-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-platform-tenant-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-platform.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-admins-on-actions.yaml (2)

12-13: LGTM!


12-13: 🗄️ Data Integrity & Integration

Run the required drift validation before merge.

Run CI - Required Checks. If it reports a DRIFT line, fix the manifest instead of changing the live repository.

Source: Coding guidelines

deploy/team-repositories/grant-admins-on-agent-plugins.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-agent-skills.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-ascoachingogvaner.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-aws.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-doggy-countdown.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-dot-github.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-provider-upjet-unifi.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-unifi.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-world-at-ruin.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-dotnet-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-fleet-gitops.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-go-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-homebrew-tap.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-ksail.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-world-at-ruin.yaml (1)

13-14: LGTM!

deploy/team-repositories/grant-maintainers-on-actions.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-maintainers-on-aws.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-doggy-countdown.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-maintainers-on-dotnet-template.yaml (1)

14-15: LGTM!

deploy/team-repositories/grant-admins-on-kyverno-policies.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-maintenance.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-monorepo.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-platform-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-platform-tenant-template.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-platform.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-provider-upjet-unifi.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-unifi.yaml (1)

12-13: LGTM!

deploy/team-repositories/grant-admins-on-wedding-app.yaml (1)

12-13: LGTM!


📝 Walkthrough

Walkthrough

Added policy.resolve: Always to two TeamMembership resources. Added the same resolution policy to admins and maintainers team references in TeamRepository resources across the listed repository grants.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: resolving team references on every reconciliation.
Description check ✅ Passed The description directly explains the security issue, the 39 resource updates, and the intended reconciliation behavior.

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

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Readiness confirmed at 829a1227b625c30c0640cf2b8f6330b3abd8ef67.

  • Programmatically tested — all 39 manifests validated on the semantic path
    (.spec.forProvider.teamIdRef.policy.resolve == "Always", 39 OK / 0 bad, covering 2
    TeamMembership + 37 TeamRepository), each file asserted to gain exactly two lines, and the
    kustomization still renders (100 documents, 39 carrying the field).
  • Reviewed — CodeRabbit green at this head: its summary names the reviewed range ending
    829a1227b625c30c0640cf2b8f6330b3abd8ef67 and reports No actionable comments were generated in
    the recent review
    , with the head's CodeRabbit status reading Review completed.
  • Tried and evaluated as a user — recorded above: CRD read directly for the field's semantics,
    all 39 live objects confirmed on the IfNotPresent default, and the no-grant-moves claim proven by
    matching every live teamId to the team its own reference resolves to (23 admins / 16
    maintainers, both sides agreeing).

Pentad: 0 failing checks, 0 unresolved threads, 0 review findings, CLEAN.

@devantler
devantler marked this pull request as ready for review August 18, 2026 03:38
@devantler
devantler merged commit 5c1d02d into main Aug 18, 2026
16 checks passed
@devantler
devantler deleted the claude/team-ref-resolve-always-3195 branch August 18, 2026 03:43
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