Skip to content

[WIP] Fix check_permissions to handle missing inherited_role field - #50183

Merged
pelikhan merged 9 commits into
mainfrom
copilot/fix-check-permissions-inherited-role
Aug 4, 2026
Merged

[WIP] Fix check_permissions to handle missing inherited_role field#50183
pelikhan merged 9 commits into
mainfrom
copilot/fix-check-permissions-inherited-role

Conversation

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
  • Remove non-existent inherited_role field usage from checkRepositoryPermission
  • Add resolveCustomRoleBaseRole(org, roleName) using github.rest.orgs.listCustomRepoRoles, case-insensitive name match, fail closed on miss/error
  • Update debug logging to report resolved base_role
  • Rewrite custom-role tests against realistic API fixtures (incl. real-world payload, maintain base role, admin denial, case-insensitivity, fail-closed)
  • Verify no remaining inherited_role/inheritedRole references
  • Run make agent-report-progress

Caution

agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.

Details

Potential security threats were detected in the agent output.

Review the workflow run logs for details.

Run: https://github.com/github/gh-aw/actions/runs/30909334614

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 11.8 AIC · ⌖ 11.3 AIC · ⊞ 8.3K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30912825871

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 18.4 AIC · ⌖ 7.69 AIC · ⊞ 5.9K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.3 AIC · ⌖ 9.44 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Thanks for tackling the inherited_role bug in check_permissions_utils.cjs! 🎉 This looks like exactly the kind of focused, well-scoped fix the project wants — it removes the non-existent field usage, adds a case-insensitive resolveCustomRoleBaseRole lookup with fail-closed behavior, and rewrites the custom-role tests against realistic API fixtures. The PR description clearly documents what was done and links back to the originating issue (#50068), and test coverage was updated alongside the code change.

One note: this PR is still marked [WIP] and is a draft — before it's ready for review, please confirm the checklist items are fully complete (e.g., make agent-report-progress output, and a final check that no inherited_role/inheritedRole references remain) and then mark it ready for review.

Once that's done, this should be in good shape for a maintainer to review.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • patchdiff.githubusercontent.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "patchdiff.githubusercontent.com"

See Network Configuration for more information.

Generated by ✅ Contribution Check · auto · 72.9 AIC · ⌖ 3.94 AIC · ⊞ 8.8K ·

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Triage: bug (security) / medium risk

  • Category: bug
  • Risk: medium
  • Priority score: 78/100 (impact 35, urgency 25, quality 18)
  • Recommended action: fast_track — fixes a fail-open permission-check bug (missing inherited_role field) with fail-closed remediation and expanded test coverage (3 files, 154 additions/35 deletions)
  • Notes: Security-relevant permissions fix. CI unstable — verify before merge. Checklist fully checked by author, tests rewritten against realistic fixtures.

Generated by 🔧 PR Triage Agent · auto · 28.5 AIC · ⌖ 2.46 AIC · ⊞ 8K ·

@pelikhan
pelikhan marked this pull request as ready for review August 4, 2026 11:56
Copilot AI balanced review requested due to automatic review settings August 4, 2026 11:56
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #50183 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes custom repository role resolution by replacing the nonexistent inherited_role field with organization role metadata.

Changes:

  • Resolves custom roles through listCustomRepoRoles.
  • Adds realistic success and fail-closed tests.
  • Refreshes a generated workflow label.
Show a summary per file
File Description
actions/setup/js/check_permissions_utils.cjs Adds custom role base-role resolution.
actions/setup/js/check_permissions_utils.test.cjs Expands custom-role test coverage.
.github/workflows/smoke-goose.lock.yml Updates generated step capitalization.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

*/
async function resolveCustomRoleBaseRole(org, roleName) {
try {
const response = await github.rest.orgs.listCustomRepoRoles({ org });

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: Fix check_permissions to handle missing inherited_role field

The approach is sound — removing the unreliable inherited_role field and instead calling GET /orgs/{org}/custom-repository-roles to resolve base_role is the correct design. The fail-closed behaviour, case-insensitive matching, and comprehensive test coverage are all good.

One blocking security issue found:

resolveCustomRoleBaseRole validates the resolved base_role against STANDARD_ROLES, which includes "admin". The GitHub API contract says custom role base_role can only be read | triage | write | maintain, never admin — but the code does not enforce this. If the API unexpectedly returns base_role: "admin", the function would return "admin" and a custom-role user could bypass an admin permission requirement. A narrower guard set (excluding admin) is needed, plus a test for this case.

See inline comment for the specific fix.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 38.9 AIC · ⌖ 12.3 AIC · ⊞ 5.4K

Comments that could not be inline-anchored

actions/setup/js/check_permissions_utils.cjs:45

Security: STANDARD_ROLES includes &quot;admin&quot; but base_role for custom roles should never be admin.

The GitHub API spec for GET /orgs/{org}/custom-repository-roles only allows base_role values of read, triage, write, or maintain. However, if an unexpected base_role: &quot;admin&quot; were returned (API change, malicious mock, or unexpected response), STANDARD_ROLES.has(baseRole) would pass and resolveCustomRoleBaseRole would return &quot;admin&quot;. This would allow a custom-role user…

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 78/100 — Acceptable

Analyzed 6 new tests: 5 design, 1 implementation, 0 violations.

📊 Metrics (6 tests)
Metric Value
Analyzed 6 new tests (JS: 6, Go: 0)
✅ Design 5 (83%)
⚠️ Implementation 1 (17%)
Edge/error coverage 5 (83%)
Duplicate clusters 0
Inflation ratio 2.4:1 ⚠️ (exceeds 2:1 threshold)
🚨 Violations 0
Test Classification Coverage Notes
should authorize custom org role via base_role when base_role matches design_test ✅ Complete Core bug fix; happy path + mock verification + debug assertions
should authorize the real-world Project Lead payload with base_role write design_test ✅ Complete Real-world issue scenario; regression prevention
should never authorize a custom org role for admin design_test ✅ Complete Security-critical: admin grant must fail
should match custom role names case-insensitively implementation_test ✅ Complete Case-handling detail; acceptable for bugfix scope
should fail closed for custom org role when the role is absent from the org role list design_test ✅ Complete Fail-safe invariant; missing role → unauthorized
should fail closed for custom org role when the custom roles request fails design_test ✅ Complete Error path; API failure → unauthorized
📈 Test Inflation Analysis
  • Production code: +63/-35 = +45 net lines
  • Test code: +124/-16 = +108 net lines
  • Inflation ratio: 2.4:1 (threshold: 2.0:1)

Justified by:

  • Complex mocking setup (mockGithub.rest.orgs.listCustomRepoRoles required in each test)
  • Multiple edge cases with separate coverage (missing role, API failure, case-insensitivity, admin grant)
  • Debug message assertions (3–4 per test) for supportability
  • Real-world scenario testing

Assessment: ⚠️ Slight inflation is acceptable given the quality and necessary complexity.

Verdict

Passed. 17% implementation tests (threshold: 30%).

Quality Assessment: Excellent behavioral coverage. All 6 new tests enforce design invariants (83%). Comprehensive error-path testing (5/6 tests cover error conditions). No mock violations. No missing build tags.

⚠️ Caution: Test inflation slightly exceeds 2:1 threshold (2.4:1), but the excess is justified by necessary mocking complexity and thorough edge-case coverage. High-quality tests offset the volume concern.

Recommendation: Approve. Tests demonstrate strong design discipline and comprehensive behavioral coverage despite slight inflation.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 25.4 AIC · ⌖ 7 AIC · ⊞ 8.5K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 78/100. 17% implementation tests (threshold: 30%). Excellent behavioral coverage with comprehensive error-path testing. Slight inflation (2.4:1) is justified by necessary mocking complexity.

@github-actions github-actions Bot mentioned this pull request Aug 4, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on two correctness issues and two test-coverage gaps.

📋 Key Themes & Highlights

Key Themes

  • Case mismatch risk (correctness): resolvedBaseRole is always lowercased, but normalizedRequired is not. Mixed-case required permissions from env vars will fail to match.
  • No org-roles cache: listCustomRepoRoles is called on every custom-role check per run; a simple module-level Map would prevent redundant API calls and rate-limit exposure.
  • Test spec gaps: The new real-world "Project Lead" test lacks key assertions; there is no test for a missing/null custom_roles field in the API response.

Positive Highlights

  • ✅ Excellent root-cause fix: removing the non-existent inherited_role field and replacing it with a proper API call is the right approach.
  • ✅ Fail-closed behaviour is preserved and well-tested across multiple error scenarios.
  • ✅ Case-insensitive role name matching is correctly implemented and tested.
  • ✅ JSDoc types added to the new function for better tooling support.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 58.8 AIC · ⌖ 8.37 AIC · ⊞ 7.1K
Comment /matt to run again

Comments that could not be inline-anchored

actions/setup/js/check_permissions_utils.cjs:319

[/diagnosing-bugs] normalizeRoleName does not lowercase its input, but resolvedBaseRole is always stored lowercased (via match.base_role.toLowerCase()). If a caller passes a mixed-case required permission such as &quot;Write&quot; from an unprocessed env var, normalizedRequired === resolvedBaseRole silently mismatches and the user is denied.

<details>
<summary>💡 Suggested fix</summary>

Lowercase before normalising:

const normalizedRequired = normalizeRoleName(requiredPerm.toLowerCa…

</details>

<details><summary>actions/setup/js/check_permissions_utils.cjs:303</summary>

**[/diagnosing-bugs]** `resolveCustomRoleBaseRole` is called unconditionally for every custom-role user  even when the same org/role combination was already resolved earlier in a workflow run. The `GET /orgs/{org}/custom-repository-roles` response is stable within a single run; caching it (even in a module-level `Map`) would eliminate redundant API calls and protect against rate-limit failures on large permission checks.

&lt;details&gt;
&lt;summary&gt;💡 Suggested approach&lt;/summary&gt;

```js
const _customR…

</details>

<details><summary>actions/setup/js/check_permissions_utils.cjs:259</summary>

**[/diagnosing-bugs]** `normalizeRoleName(match.base_role.toLowerCase())` double-applies case transformation: `base_role` is already lowercased before `normalizeRoleName` is called (which maps `&quot;maintainer&quot;` → `&quot;maintain&quot;`), so the normalization works. However, the intent isn&#39;t obvious. Consider extracting the lowercasing inside `normalizeRoleName` directly so callers don&#39;t need to pre-lowercase.

This is low-severity today, but it establishes an inconsistent calling convention that could hide …

</details>

<details><summary>actions/setup/js/check_permissions_utils.test.cjs:371</summary>

**[/tdd]** The real-world `&quot;Project Lead&quot;` test (line ~355) is missing assertions on the debug log output and the `listCustomRepoRoles` mock call. Without them, the test only verifies the happy-path return value but wouldn&#39;t catch a regression in the role-resolution code path (e.g., if `listCustomRepoRoles` were accidentally skipped).

&lt;details&gt;
&lt;summary&gt;💡 Suggested additions&lt;/summary&gt;

```js
expect(mockGithub.rest.orgs.listCustomRepoRoles).toHaveBeenCalledWith({ org: &quot;example-org&quot; });
expect(

</details>

<details><summary>actions/setup/js/check_permissions_utils.cjs:252</summary>

**[/tdd]** There is no test for the case where `response.data.custom_roles` is `null` or absent (only the API-error path is tested). The `?? []` guard handles it silently, but a missing test means a regression (e.g., if the API changes its response shape) would go undetected.

&lt;details&gt;
&lt;summary&gt;💡 Suggested test&lt;/summary&gt;

```js
it(&quot;should fail closed when custom_roles list is absent from org response&quot;, async () =&gt; {
  mockGithub.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue({

</details>

@pelikhan
pelikhan enabled auto-merge (squash) August 4, 2026 12:12
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

[!CAUTION]
agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation....

Refreshed and re-ran the pr-finisher pass.

  • Branch — merged latest origin/main (was 1 commit behind); no conflicts.
  • Reviews — the one blocking thread (org-scoped listCustomRepoRoles unreachable with the repository-scoped GITHUB_TOKEN) is addressed: custom org roles now resolve from the standard permission field already returned by getCollaboratorPermissionLevel, failing closed when it is not a standard role. No other unresolved threads.
  • Checks (local) ✅ — make agent-report-progress passed (lint clean, 271/271 lock files in sync, unit tests pass); 102 JS tests in check_permissions_utils.test.cjs pass.
  • Checks (CI) ❓ — stale after the agent's push; agent pushes do not trigger workflows.
  • Mergeable ❓ — gh has no token in this sandbox, so PR mergeability could not be queried.

Still preventing merge: CI must be re-triggered by a maintainer (close/reopen the PR, workflow_dispatch, or a maintainer push), plus human review approval. Nothing else is actionable from my side.

Copilot AI requested a review from gh-aw-bot August 4, 2026 12:50
@pelikhan

pelikhan commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@copilot add more logging using core.info

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection results could not be parsed.

Review the workflow run logs for details.

Triage update: bug (security) / medium risk

  • Category: bug
  • Risk: medium (unchanged — permission-check fix, Test Quality Sentinel flagged 78/100 acceptable-with-gaps, PR Code Quality Reviewer raised concerns)
  • Score: 68 (impact 28 + urgency 22 + quality 18)
  • Recommended action: batch_review — group with other permission/security fixes for a single maintainer pass; CI green but quality flags need a second look.

Generated by 🔧 PR Triage Agent · auto · 29.9 AIC · ⌖ 2.62 AIC · ⊞ 8K ·

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add more logging using core.info

Done in 1a3a6dc — the permission-resolution diagnostics now use core.info instead of core.debug, so they show up in normal workflow logs:

  • API fields: permission='…', role='…'
  • Computed roles: effective='…', custom_role=…, base_role='…'
  • Fallback-unavailable notice for custom roles without a standard permission level
  • Which required role matched and via which path (effective-role / base-role), or that nothing matched

Tests updated accordingly; 102 tests pass and make agent-report-progress is green.

@pelikhan

pelikhan commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

/matt

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot This PR needs a quick maintainer-ready refresh.

  • Please address the remaining reviewer concern that custom role base_role must never authorize admin; tighten the guard and add a regression test for the unexpected admin case.
  • Refresh the branch with the latest base changes.
  • Then run the pr-finisher skill and hand back with a concise summary of remaining blockers.

Run: https://github.com/github/gh-aw/actions/runs/30912825871

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 18.4 AIC · ⌖ 7.69 AIC · ⊞ 5.9K ·
Comment /souschef to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on two correctness/clarity issues before merge.

📋 Key Themes & Highlights

Key Themes

  • debuginfo promotion: All diagnostic logging was bulk-promoted to core.info. This loses the ACTIONS_RUNNER_DEBUG gating and will clutter workflow logs for every permission check, not just failures or custom-role paths.
  • Log message clarity: The fail-closed message for resolvedBaseRole === '' doesn't distinguish between permission: 'none' (GitHub's explicit no-access sentinel) and a genuinely unknown value — the wording could mislead operators reading logs.
  • Real-world test coverage: The new Project Lead test is a good addition but leaves the code path under-specified — it only asserts authorized: true, not the resolved role name logged.

Positive Highlights

  • ✅ Root cause fix is clean and correct: permission is always present in the API response, whereas inherited_role was not.
  • ✅ Fail-closed semantics are preserved: non-standard permission values correctly deny access.
  • ✅ Test suite is well-structured and the rename from inherited_rolebase_role in variable names reads clearly.
  • ✅ Removing the resolveCustomRoleBaseRole / listCustomRepoRoles path (mentioned in the PR checklist but not needed) keeps the change minimal.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 41 AIC · ⌖ 14 AIC · ⊞ 7.1K
Comment /matt to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot This PR needs a quick maintainer-ready refresh.

  • Please address the remaining reviewer concern that custom role base_role must never authorize admin; tighten the guard and add a regress...

Done in 79bb1c3.

  • Guard tightened: custom-role base roles are now drawn from a dedicated CUSTOM_ROLE_BASE_ROLES set (read/triage/write/maintain) that deliberately excludes admin. If the API unexpectedly reports admin for a custom role, no base role is resolved and a core.warning is emitted.
  • Regression tests added: an admin-reporting custom role is denied for a required admin role and cannot satisfy a lesser required role either.
  • Branch — already current with origin/main (0 commits behind).
  • Checks (local) ✅ — make agent-report-progress green (lint clean, 271/271 lock files in sync); 104 JS tests pass.
  • Checks (CI) ❓ — stale after the agent's push; agent pushes do not trigger workflows.
  • Mergeable ❓ — gh has no token in this sandbox.

Remaining blockers: a maintainer must re-trigger CI (close/reopen, workflow_dispatch, or a maintainer push) and approve the review. Nothing else is actionable from my side.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot This PR needs a quick maintainer-ready refresh.

  • The latest reviewer concern appears to be the most recent Matt review about bulk-promoting diagnostics to core.info; please address that feedback (or explain why the logging level should remain as-is) and tighten the handoff around log signal/noise.
  • Re-check whether any review threads or blocking reviews still need follow-up, then summarize the exact remaining blockers.
  • Refresh the branch from the base branch if needed.
  • Run the pr-finisher skill and hand back with a concise maintainer-focused summary.

Run: https://github.com/github/gh-aw/actions/runs/30917238977

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.3 AIC · ⌖ 9.44 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@pelikhan
pelikhan merged commit 752161d into main Aug 4, 2026
7 of 8 checks passed
@pelikhan
pelikhan deleted the copilot/fix-check-permissions-inherited-role branch August 4, 2026 15:04
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check_permissions: inherited_role does not exist in the GitHub API, so custom repository roles are always denied

4 participants