feat: add restricted mode for reduced GitHub permissions (#2279)#2491
Conversation
…ests - Cache key is now 'related_tickets_<md5(pr_url)>' instead of global 'related_tickets', preventing stale tickets leaking between unrelated PRs - Added cache_tickets=true config flag under [pr_reviewer] to allow disabling - Fixed Dynaconf merge_enabled=True list append bug in both production code (env_bridge.py uses case-insensitive pop before set) and test helpers (restore_settings now calls _remove_key before every set) - Added azure-identity dependency for Azure DevOps provider availability - 1043 tests pass, 0 failures
…aks in tests" This reverts commit 7f0d154.
…t#2279) Add a `config.restricted_mode` option that gates operations requiring elevated permissions (e.g. pushing code to the repository). When enabled, `is_supported("push_code")` returns `False` on GitHub, GitLab, and Bitbucket providers, and `/update_changelog --push_changelog_changes=true` gracefully skips with a clear message instead of failing. Users can set `restricted_mode = true` and use only `issues: write` and `pull-requests: write` permissions, without `contents: write`.
PR Summary by QodoAdd restricted_mode to disable code-push capabilities under reduced permissions
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
|
Hey @utsab345, Thanks! |
|
docs updated in additional_configurations.md (new Restricted Mode section) and github.md (note in Permission denied troubleshooting). @naorpeled let me know if you'd like any changes! |
|
Code review by qodo was updated up to the latest commit 37874ec |
…ider reads PRUpdateChangelog.__init__() was fetching languages, files, and CHANGELOG.md content before run() checked push_code support. In restricted mode, these reads were wasted and could fail if contents access is also restricted. Move the capability check into __init__() before the expensive provider calls, and store the result in self._skip_push so run() can return early.
|
Fixed both Qodo findings:
|
|
Code review by qodo was updated up to the latest commit 211a258 |
… test - Remove relevant_configs logging that dumps full settings dicts (potential secret leak in logs) - Pass ai_handler mock in test_run_without_push_support to avoid instantiating a real LiteLLMAIHandler
|
Code review by qodo was updated up to the latest commit 887289c |
- Use MagicMock(spec=[...]) in test_run_without_push_support so hasattr() correctly returns False for missing attributes instead of brittle delattr on bare MagicMock - Remove misleading 'contents: read (defaults to read)' comment from restricted mode docs; contents is simply not needed
|
Code review by qodo was updated up to the latest commit 3170375 |
|
Hey @utsab345, Thanks in adance! |
Move all provider-dependent initialization (vars dict, token handler, ai_handler binding, language/file/changelog reads) behind the _skip_push guard so the skip path avoids hitting the git provider at all during construction.
|
Code review by qodo was updated up to the latest commit 8b11aa2 |
naorpeled
left a comment
There was a problem hiding this comment.
LGTM — finalized. 🔥
I reviewed the logic end to end and it's clean and correctly scoped:
push_codeis queried in exactly one place (pr_update_changelog), so no other tool is affected by restricted mode.- Coverage is complete, not partial:
create_or_update_pr_fileis defined in precisely the three providers you added therestricted_modecheck to (GitHub, GitLab, Bitbucket). Any other provider lacks that method and already falls into the"not supported"path, so there's no silent gap. - The
__init__early-return skip path only sets attributes thatrun()'s skip branch touches, andrun()returns before reaching anything unset — noAttributeErrorrisk.
Qodo's three findings are already handled in the current commits: the config-dict debug logging was removed from run(), the brittle delattr test was rewritten with a proper MagicMock(spec=[...]), and the docs state the minimal permissions (issues: write + pull-requests: write).
Verified locally: test_pr_update_changelog.py — 14 passed. No Python lines exceed 120 chars (the long lines are docs/TOML prose, not linted).
Nice, minimal implementation of a genuinely useful capability. Thanks @utsab345!
Thanks for the thorough review and verification! Really appreciate the LGTM and the detailed feedback. 🚀 |
Retracting this — I approved prematurely. There are still-open Qodo findings (skip path drops changelog output, skip path over-initializes, and a misleading contents-permission note) that should be addressed first.
|
@utsab345 pushing some small fixes and then merging, thanks for this! |
…in restricted mode Address Qodo review on The-PR-Agent#2491: - The-PR-Agent#4 (skip path drops changelog output): when a changelog push is requested but not possible — provider lacks push support, or restricted_mode disables push_code — the tool no longer returns early with only an error. It now still generates the changelog and publishes it as a comment (which only needs pull-requests: write), with a note that the changes were not pushed. commit_changelog is set to False in that case. - The-PR-Agent#5 (skip path still initializes): removes the special early-return init path; the normal flow builds only what's needed to generate the changelog. - The-PR-Agent#6 (contents default note misleading): clarify that unlisted scopes default to none only within an explicit permissions: block; without one, defaults follow repo/org settings. Update the "no push support" test to assert the comment fallback, and add a restricted_mode test (push API present but is_supported('push_code') False) asserting it comments, not pushes.
…estricted When restricted_mode is active and push_changelog_changes=true, the tool now generates the changelog output via AI and publishes it as a comment with a '(push restricted by configuration)' label, instead of returning silently. This ensures users see the changelog even when they don't have contents: write permissions. For the 'not supported' case (provider lacks create_or_update_pr_file entirely), the existing early-return behavior is preserved.
|
Code review by qodo was updated up to the latest commit 0c99e1c |
|
Code review by qodo was updated up to the latest commit 7bf6093 |
Adds a
config.restricted_modeoption (defaultfalse) that lets users run PR-Agent with onlyissues: writeandpull-requests: writepermissions, without requiringcontents: write.What it does:
restricted_mode = true,GithubProvider.is_supported("push_code")returnsFalse(same for GitLab and Bitbucket)/update_changelog --push_changelog_changes=truechecksis_supported("push_code")and gracefully skips with a clear log/comment message instead of failing with a 403/review,/describe,/improve, etc.) continue to work normally since they only needpull-requests: writeWhy:
Only
/update_changelogwithpush_changelog_changes=trueactually callsrepo.update_file()which requirescontents: write. Every other tool works with PR comments, reviews, and edits. This change makes the permission requirement explicit and gives users a safe way to run with reduced scope.Closes #2279