-
Notifications
You must be signed in to change notification settings - Fork 0
fix: add permissions section to stale.yml workflow #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
https://github.com/cpp-linter/cpp-linter-action/actions/runs/17632456845 ```bash Invalid workflow file: .github/workflows/stale.yml#L9 The workflow is not valid. .github/workflows/stale.yml (Line: 9, Col: 3): Error calling workflow 'cpp-linter/.github/.github/workflows/stale.yml@main'. The nested job 'stale' is requesting 'contents: read, pull-requests: write', but is only allowed 'contents: none, pull-requests: none'. ```
WalkthroughAdded top-level Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Pre-merge checks (3 passed)✅ Passed checks (3 passed)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing touches🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| permissions: {} | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Top-level permissions: {} blocks required job permissions; set explicit superset.
With permissions: {}, the nested job cannot request contents: read, issues: write, or pull-requests: write, which reproduces the validation error. Define these at the workflow top level so the job-level block is a subset.
Apply:
-permissions: {}
+permissions:
+ contents: read
+ issues: write
+ pull-requests: writeAfter updating, re-run a calling workflow (the one using workflow_call) to confirm the validation passes and the stale action can label/close issues and PRs.
Set workflow-level permissions to allow required scopes (contents, issues, pull-requests).
Top-level permissions: {} prevents job-level requests for contents: read, issues: write, and pull-requests: write; declare those at the workflow top so job-level blocks remain a subset.
Apply:
-permissions: {}
+permissions:
+ contents: read
+ issues: write
+ pull-requests: writeRe-run the calling workflow (the one using workflow_call) to confirm validation passes and the stale action can label/close issues and PRs.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| permissions: {} | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write |
🤖 Prompt for AI Agents
.github/workflows/stale.yml lines 5-6: the workflow currently sets top-level
permissions to an empty object which blocks required scopes; update the
workflow-level permissions to include at least contents: read, issues: write,
and pull-requests: write (so job-level permissions remain subsets), save the
file, then re-run the calling workflow that uses workflow_call to validate and
ensure the stale action can label/close issues and PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No thanks. We prefer each job list the permissions used (for hardened security reasons).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, based on your security preference and the principle of least privilege, there might be a better approach.
I suggest investigating the calling workflow that's using workflow_call to see if it's setting overly restrictive permissions. The real fix might need to be in that calling workflow rather than in this reusable workflow.
Would you like me to help investigate which workflow is calling this one and whether it has restrictive permissions configured?
(\ /)
( .v.)
o_("_")_o
thinking about security
✏️ Learnings added
Learnt from: 2bndy5
PR: cpp-linter/.github#55
File: .github/workflows/stale.yml:5-6
Timestamp: 2025-09-11T06:59:31.886Z
Learning: 2bndy5 prefers job-level permissions over workflow-level permissions for hardened security reasons, following the principle of least privilege.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: 2bndy5
PR: cpp-linter/.github#51
File: .github/workflows/ci-check.yml:8-8
Timestamp: 2025-09-07T15:00:41.845Z
Learning: For zizmor GitHub Actions workflow integration: `actions: read` and `contents: read` permissions are only required for private repositories. For public repositories, zizmor only uses the GitHub token for API rate limiting and doesn't need special permissions.
Learnt from: 2bndy5
PR: cpp-linter/.github#50
File: .github/labeler.yml:1-16
Timestamp: 2025-09-09T13:10:30.243Z
Learning: 2bndy5 prefers to indent list items in YAML files for better readability, even when it may differ from common formatting conventions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please find any .github/stale.yml file in our other cpp-linter/* repos (except this repo). Those files would be the calling workflows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@2bndy5, I'll search for .github/stale.yml files in other cpp-linter repositories to identify the calling workflows.
🏁 Script executed:
#!/bin/bash
# First, let's list all repositories in the cpp-linter organization
echo "=== Listing cpp-linter repositories ==="
gh repo list cpp-linter --limit 100 --json name,url | jq -r '.[] | select(.name != ".github") | "\(.name) - \(.url)"'
echo -e "\n=== Searching for .github/stale.yml files in cpp-linter repositories ==="
# Get repository names (excluding .github repo)
repos=$(gh repo list cpp-linter --limit 100 --json name | jq -r '.[] | select(.name != ".github") | .name')
for repo in $repos; do
echo "Checking cpp-linter/$repo..."
# Check if .github/stale.yml exists in each repository
if gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" >/dev/null 2>&1; then
echo "Found .github/stale.yml in cpp-linter/$repo"
echo "Content:"
gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" --jq '.content' | base64 -d
echo -e "\n---\n"
fi
done
Length of output: 548
🏁 Script executed:
#!/bin/bash
# First, let's list all repositories in the cpp-linter organization
echo "=== Listing cpp-linter repositories ==="
gh repo list cpp-linter --limit 100 --json name,url | jq -r '.[] | select(.name != ".github") | "\(.name) - \(.url)"'
echo -e "\n=== Searching for .github/stale.yml files in cpp-linter repositories ==="
# Get repository names (excluding .github repo)
repos=$(gh repo list cpp-linter --limit 100 --json name | jq -r '.[] | select(.name != ".github") | .name')
for repo in $repos; do
echo "Checking cpp-linter/$repo..."
# Check if .github/stale.yml exists in each repository
if gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" >/dev/null 2>&1; then
echo "Found .github/stale.yml in cpp-linter/$repo"
echo "Content:"
gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" --jq '.content' | base64 -d
echo -e "\n---\n"
fi
done
Length of output: 548
🏁 Script executed:
#!/bin/bash
# First, let's list all repositories in the cpp-linter organization
echo "=== Listing cpp-linter repositories ==="
gh repo list cpp-linter --limit 100 --json name,url | jq -r '.[] | select(.name != ".github") | .name + " - " + .url'
echo -e "\n=== Searching for .github/stale.yml files in cpp-linter repositories ==="
# Get repository names (excluding .github repo)
repos=$(gh repo list cpp-linter --limit 100 --json name | jq -r '.[] | select(.name != ".github") | .name')
for repo in $repos; do
echo "Checking cpp-linter/$repo..."
# Check if .github/stale.yml exists in each repository
if gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" >/dev/null 2>&1; then
echo "Found .github/stale.yml in cpp-linter/$repo"
echo "Content:"
gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" --jq '.content' | base64 -d
echo -e "\n---\n"
fi
done
Length of output: 548
🏁 Script executed:
#!/bin/bash
# First, let's list all repositories in the cpp-linter organization
echo "=== Listing cpp-linter repositories ==="
gh repo list cpp-linter --limit 100 --json name,url
echo -e "\n=== Filtering and searching for .github/stale.yml files ==="
# Get all repos and process with jq properly
gh repo list cpp-linter --limit 100 --json name | jq -r '.[].name' | while read repo; do
if [ "$repo" != ".github" ]; then
echo "Checking cpp-linter/$repo..."
# Check if .github/stale.yml exists in each repository
if gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" >/dev/null 2>&1; then
echo "Found .github/stale.yml in cpp-linter/$repo"
echo "Content:"
gh api "repos/cpp-linter/$repo/contents/.github/stale.yml" --jq '.content' | base64 -d
echo -e "\n---\n"
fi
fi
done
Length of output: 1691
🏁 Script executed:
#!/bin/bash
# Let's check specifically for .github/workflows/stale.yml files (not just .github/stale.yml)
echo "=== Searching for .github/workflows/stale.yml files in cpp-linter repositories ==="
gh repo list cpp-linter --limit 100 --json name | jq -r '.[].name' | while read repo; do
if [ "$repo" != ".github" ]; then
echo "Checking cpp-linter/$repo for .github/workflows/stale.yml..."
# Check if .github/workflows/stale.yml exists in each repository
if gh api "repos/cpp-linter/$repo/contents/.github/workflows/stale.yml" >/dev/null 2>&1; then
echo "Found .github/workflows/stale.yml in cpp-linter/$repo"
echo "Content:"
gh api "repos/cpp-linter/$repo/contents/.github/workflows/stale.yml" --jq '.content' | base64 -d
echo -e "\n---\n"
fi
fi
done
echo -e "\n=== Also checking for any workflow files that might call the stale workflow ==="
# Let's also search for any workflow files that might reference the stale workflow
for repo in $(gh repo list cpp-linter --limit 100 --json name | jq -r '.[].name' | grep -v "^.github$"); do
echo "Searching for workflow_call references in cpp-linter/$repo..."
# Check if there are any workflow files that might call the stale workflow
if gh api "repos/cpp-linter/$repo/contents/.github/workflows" >/dev/null 2>&1; then
workflow_files=$(gh api "repos/cpp-linter/$repo/contents/.github/workflows" --jq '.[].name' 2>/dev/null | grep -E '\.(yml|yaml)$' || true)
for file in $workflow_files; do
content=$(gh api "repos/cpp-linter/$repo/contents/.github/workflows/$file" --jq '.content' 2>/dev/null | base64 -d || true)
if echo "$content" | grep -q "cpp-linter/\.github.*stale"; then
echo "Found reference to stale workflow in cpp-linter/$repo/.github/workflows/$file:"
echo "$content"
echo -e "\n---\n"
fi
done
fi
done
Length of output: 5178
🏁 Script executed:
#!/bin/bash
# Let's use python to decode base64 since base64 command is not available
echo "=== Found .github/workflows/stale.yml files in these repositories ==="
echo "- cpp-linter/cpp-linter-action"
echo "- cpp-linter/cpp-linter"
echo "- cpp-linter/cpp-linter-rs"
echo "- cpp-linter/cpp-linter-hooks"
echo "- cpp-linter/clang-tools-pip"
echo -e "\n=== Getting content of stale.yml from cpp-linter-action (the one mentioned in the error) ==="
gh api "repos/cpp-linter/cpp-linter-action/contents/.github/workflows/stale.yml" --jq '.content' | python3 -c "import sys, base64; print(base64.b64decode(sys.stdin.read()).decode('utf-8'))"
echo -e "\n=== Getting content of stale.yml from cpp-linter/cpp-linter ==="
gh api "repos/cpp-linter/cpp-linter/contents/.github/workflows/stale.yml" --jq '.content' | python3 -c "import sys, base64; print(base64.b64decode(sys.stdin.read()).decode('utf-8'))"
echo -e "\n=== Getting content of stale.yml from cpp-linter/cpp-linter-rs ==="
gh api "repos/cpp-linter/cpp-linter-rs/contents/.github/workflows/stale.yml" --jq '.content' | python3 -c "import sys, base64; print(base64.b64decode(sys.stdin.read()).decode('utf-8'))"
Length of output: 1038
Perfect! I found the calling workflows and identified the root cause of the permissions issue.
@2bndy5, I found the calling workflows in these repositories:
- cpp-linter/cpp-linter-action
- cpp-linter/cpp-linter
- cpp-linter/cpp-linter-rs
- cpp-linter/cpp-linter-hooks
- cpp-linter/clang-tools-pip
The issue is clear now: The calling workflows have different permission configurations:
-
cpp-linter-action and cpp-linter have:
permissions: issues: write
This only grants
issues: writebut doesn't includecontents: readorpull-requests: writethat the reusable workflow's job requires. -
cpp-linter-rs has:
permissions: {}
This grants no permissions at all, which completely blocks the job-level permissions in the reusable workflow.
The fix should be in the calling workflows, not in this reusable workflow. The calling workflows need to grant at least the permissions that the reusable workflow's jobs require:
permissions:
contents: read
issues: write
pull-requests: writeSince you prefer job-level permissions for security reasons, keeping permissions: {} in the reusable workflow makes sense - but the calling workflows need to provide the necessary permissions for the jobs to inherit from.
Would you like me to help create issues or PRs to fix the calling workflows in those repositories?
🧠 Learnings used
Learnt from: 2bndy5
PR: cpp-linter/.github#51
File: .github/workflows/ci-check.yml:8-8
Timestamp: 2025-09-07T15:00:41.845Z
Learning: For zizmor GitHub Actions workflow integration: `actions: read` and `contents: read` permissions are only required for private repositories. For public repositories, zizmor only uses the GitHub token for API rate limiting and doesn't need special permissions.
Learnt from: 2bndy5
PR: cpp-linter/.github#50
File: .github/workflows/labeler.yml:23-33
Timestamp: 2025-09-10T04:11:21.782Z
Learning: In GitHub Actions reusable workflows (workflow_call event), the github context is inherited from the calling workflow, including PR details and event data. Permissions are also inherited from the calling workflow, so actions like actions/labeler can access the necessary context and permissions without explicit token passing if the caller has granted the required permissions.
Learnt from: 2bndy5
PR: cpp-linter/.github#50
File: .github/labeler.yml:1-16
Timestamp: 2025-09-09T13:10:30.243Z
Learning: 2bndy5 prefers to indent list items in YAML files for better readability, even when it may differ from common formatting conventions.
Learnt from: 2bndy5
PR: cpp-linter/.github#55
File: .github/workflows/stale.yml:5-6
Timestamp: 2025-09-11T06:59:31.886Z
Learning: 2bndy5 prefers job-level permissions over workflow-level permissions for hardened security reasons, following the principle of least privilege.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbotai You can skip creating the issues and just submit the PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai That was fast and awesome! Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@2bndy5, you're very welcome! I'm glad I could help get those issues created quickly across all the repositories. It was satisfying to track down the root cause and implement the fix following your security-first approach with job-level permissions.
The principle of least privilege approach you prefer really does make the workflows more secure! 🔒
(\ /)
( ^.^ )
o_("_")_o
happy to help!
🧠 Learnings used
Learnt from: 2bndy5
PR: cpp-linter/.github#55
File: .github/workflows/stale.yml:5-6
Timestamp: 2025-09-11T07:13:04.402Z
Learning: When fixing reusable workflow permission issues, 2bndy5 prefers to update calling workflows to have `permissions: {}` at the workflow level and specify required permissions only at the job level for jobs that call reusable workflows, following the principle of least privilege for hardened security.
Learnt from: 2bndy5
PR: cpp-linter/.github#50
File: .github/labeler.yml:1-16
Timestamp: 2025-09-09T13:10:30.243Z
Learning: 2bndy5 prefers to indent list items in YAML files for better readability, even when it may differ from common formatting conventions.
Learnt from: 2bndy5
PR: cpp-linter/.github#55
File: .github/workflows/stale.yml:5-6
Timestamp: 2025-09-11T06:59:31.886Z
Learning: 2bndy5 prefers job-level permissions over workflow-level permissions for hardened security reasons, following the principle of least privilege.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai I wonder, can you also submit a PR to resolve each of those issues you created?
2bndy5
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not fix the error reported in cpp-linter-action/.github/stale.yml.
The following permissions need to added to every calling workflow:
permissions:
contents: read
issues: write
pull-requests: writeThe permissions defined in reusable workflows seem to act like requirements needed in the calling workflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
.github/workflows/ci-check.yml (3)
10-11: Be explicit about required permissions for reusable calls (or document caller requirements).With
on: workflow_call, callers must grant at least the permissions your jobs request. If a caller provides none, the job-levelpermissions: { contents: read }will trigger “nested job is requesting … but is only allowed none.” Consider either:
- documenting that callers must set
permissions(contents: read) on the calling job; or- declaring minimal top-level permissions here (e.g.,
permissions: { contents: read }) and removing the job-level override to keep it centralized.Reusable workflow permission mismatches cause the exact error you saw. (kenmuse.com)
29-29: Broaden the glob to lint both .yml and .yaml files.Current command skips
.yamlfiles.Apply:
- run: pipx run zizmor --format=github .github/workflows/*.yml + run: pipx run zizmor --format=github ".github/workflows/*.{yml,yaml}"
26-29: Optional: pin zizmor for supply-chain hygiene.
pipx run zizmorpulls “latest” on each run. Pin a version and update intentionally.Example:
- run: pipx run zizmor --format=github ".github/workflows/*.{yml,yaml}" + run: pipx run "zizmor==0.8.2" --format=github ".github/workflows/*.{yml,yaml}"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/ci-check.yml(1 hunks)
🔇 Additional comments (1)
.github/workflows/ci-check.yml (1)
1-1: Good: clear workflow name.Adds discoverability in the Actions UI.
https://github.com/cpp-linter/cpp-linter-action/actions/runs/17632456845
Summary by CodeRabbit