Skip to content

fix(github-config): refuse a managed team that names a parent team or directory group - #4519

Merged
devantler merged 3 commits into
mainfrom
claude/team-nesting-guard-4511
Oct 6, 2026
Merged

devantler merged 3 commits into
mainfrom
claude/team-nesting-guard-4511

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

The rules that limit what our GitHub configuration release may do check a team's name, members and repository access, but not whether the team is placed under another team or tied to an outside directory group. On GitHub a team placed under another inherits that team's repository access, so a faulty or compromised release could widen access without any of the existing limits noticing.

What

A managed team can no longer be placed under another team or linked to a directory group unless the platform's rules are changed in a reviewed pull request. Our two existing teams use neither and are unaffected.

Fixes #4511

… 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>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Evaluation at 29976126292f2cbc5ae89cafa1dce5924c0d7399 (no cluster was run; read-only checks against prod plus offline evaluation).

Check Result
Live teams (read-only kubectl, both Team objects in github-config) Neither sets a parent or directory field in forProvider or initProvider; the provider reports all four empty for both.
Installed CRD Exposes parentTeamId, parentTeamReadId, parentTeamReadSlug, ldapDn in both blocks, all string. The rule covers all eight.
Policy applied offline to the exported live teams (kyverno apply) 6 pass, 0 fail — both teams are admitted unchanged by all three Team rules.
Same live admins object with a parent added Refused by the new rule.
Rendered output The rule is present in both provider layers (hetzner, docker) with allowExistingViolations: false.
Fixtures 16 rows for the rule: each field alone in each block, * and ?, an approved forProvider fronting an initProvider parent, false, [], a blank string — all refused; empty strings, no spec, and the three existing teams — admitted. The fixture gate confirms every row evaluates.
Mutations Neutralising any one of the eight comparisons, flipping the compared value, or reverting to the || default form each turns the suite red (10 of 10).
Independent review pass No blocking finding. Taken: the first version read false, [] and {} as empty and relied on the provider's schema to reject them; the rule now refuses them itself.

One behaviour to know about: the provider is not exempt. If a team were nested on GitHub by hand, the provider's attempt to record that on the object would be refused and the object would show as out of sync until the nesting is undone or the rule is changed in review.

After deploy I will confirm the policy is Ready and both teams still reconcile, and record that on #4511.

@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 rate limited.

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.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 29 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ddb9a610-3973-4977-afd4-ec6481779068
📥 Commits

Reviewing files that changed from the base of the PR and between d927e68 and 2997612.

📒 Files selected for processing (5)
  • k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-management.yaml
  • tests/policy-failure-actions.json
  • tests/restrict-github-team-management/kyverno-test.yaml
  • tests/restrict-github-team-management/nested-or-directory-linked/kyverno-test.yaml
  • tests/restrict-github-team-management/nested-or-directory-linked/resources.yaml
  • 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: 29976126292f2cbc5ae89cafa1dce5924c0d7399

  • CodeRabbit: rate limited — it refused the request on this head ("Review rate limited", 10:08Z) 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 at this head, against the released provider source (terraform-provider-github v6.13.0, provider-upjet-github v0.20.0) and with kyverno 1.19.1.

No P0 or P1. It found no admitted Team that ends up nested or directory-linked, no refusal of admins or maintainers, and no way the rule blocks the provider's own writes: the provider omits empty values when it fills in defaults, so the empty parent fields never reach the spec.

What it checked: the Terraform schema has exactly the four arguments the rule covers and the CRD generates no reference or selector variants for Team; replacing each of the eight clauses with true in turn made the suite fail every time; numbers, booleans, empty objects, variable syntax and a quote-injection string are all refused; an explicit null is admitted and is the same as unset. The update path and background scans were reasoned from the rule, not run.

P3 notes, not applied on this head because none changes what is admitted or refused:

  • The Team fixtures use v1beta1, which the provider does not serve (it serves v1alpha1). This predates the change and the rule matches every version.
  • The comment says an empty object is refused, and no fixture row pins that or the typed values. The reviewer's probes confirm they are refused.
  • "The provider reports all four empty" is exact for the three parent fields; ldapDn is absent rather than empty.
  • If someone nests a team by hand on GitHub, the provider's attempt to record it is refused, so the nesting stays and other drift on that Team stops being corrected until someone intervenes. The comment could say so more plainly.

Outside this change: other kinds in the same API group could link a team to a directory group, but they are neither activated nor granted to the tenant.

Verdict: no P0/P1 findings

@devantler
devantler marked this pull request as ready for review October 5, 2026 11:31
@devantler
devantler added this pull request to the merge queue Oct 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 5, 2026
@devantler
devantler marked this pull request as draft October 5, 2026 18:54
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Resolved the merge conflict at 665c78fdd85566b3ee898035986d89285bfdb552. Both protections are retained: managed teams cannot acquire inherited or directory-controlled access, and the approved external-identity guard already on main remains unchanged.

The full 48-file Kyverno fixture evaluation, the 44-rule enforced-action baseline, race-tested baseline validator, and all ten manifest-layer builds pass locally. The conflict reproduced a YAML parse failure before the resolution. Fresh native CI and current-head review are still required; the PR is back in draft until they pass.

@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: 665c78fdd85566b3ee898035986d89285bfdb552

  • CodeRabbit: included public-repository review quota refused the request on this repository at 2026-10-05T18:32:29Z; the edited provider summary at 18:32:38Z says the next included review is in 39 minutes (19:11:38Z). Verified again this round; the old 10:08 refusal on this PR is not used as an unexpired timer. No current-head substantive artifact is present.
  • Codex: account code-review usage limit in the direct provider response at 2026-10-05T16:59:30Z, freshly read this round. Recovery requires account credits/limit restoration; no paid overage is authorized.
  • Cursor Bugbot: user/team usage or spend limit in the direct provider response at 2026-10-03T22:35:03Z, freshly read this round. Recovery requires the account administrator; no successful newer servicing evidence was found.

Fresh direct reads covered this PR's reviews, conversation, threads and current-head check runs, including the absence of a Bugbot review. There are no current-head reviewer findings or unresolved review threads.

Reviewed all five changed paths against current main 8cb909aeb70f3927a0522f44d4b10ccc2f7a090d: the merge preserves main's external-identity policy and all existing Enforce actions. The new rule checks each of four parent/directory fields in both provider blocks using literal JMESPath equality; missing/null/empty strings remain admitted, while wildcard text, whitespace and non-text values are denied. It does not exempt the provider, weaken the existing team/member/grant rules, expand a credential reader, or change rollout configuration. Existing-violation updates remain denied until the offending fields are cleared.

The conflict was reproduced as a YAML parse failure before resolution. After resolution, all 48 Kyverno fixture files actually evaluate their named rules; the 44-rule action baseline passes, its Go tests pass under the race detector three times, and all ten manifest layers build. The Team and external-identity suites both pass together. The previously noted Team-fixture API-version mismatch remains a test-fidelity limitation: these CLI assertions evaluate the all-version policy match and expressions, not API-server admission or production deployment. It is not attributed to #4518/#4524, which address external grant identity. Live eligibility and deployment readback remain separate from these offline results.

Verdict: no P0/P1 findings

@devantler
devantler marked this pull request as ready for review October 5, 2026 22:03
@devantler
devantler added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit 856dd86 Oct 6, 2026
50 of 51 checks passed
@devantler
devantler deleted the claude/team-nesting-guard-4511 branch October 6, 2026 03:22
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.

Stop a managed GitHub team being nested under another team

1 participant