-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix(config): prevent .pr_agent.toml state leak between repositories #2346
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
Open
tharatynowicz
wants to merge
2
commits into
The-PR-Agent:main
Choose a base branch
from
tharatynowicz:fix/apply-repo-settings-state-leak
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+147
−1
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,13 +6,16 @@ | |
| from dynaconf import Dynaconf | ||
| from starlette_context import context | ||
|
|
||
| from pr_agent.config_loader import get_settings | ||
| from pr_agent.config_loader import get_settings, reset_to_startup_defaults | ||
| from pr_agent.git_providers import get_git_provider_with_context | ||
| from pr_agent.log import get_logger | ||
|
|
||
|
|
||
| def apply_repo_settings(pr_url): | ||
| os.environ["AUTO_CAST_FOR_DYNACONF"] = "false" | ||
| # Reset first so that .pr_agent.toml from a previously-reviewed repo | ||
| # cannot leak into this call via the module-level global_settings singleton. | ||
| reset_to_startup_defaults() | ||
| git_provider = get_git_provider_with_context(pr_url) | ||
|
Comment on lines
14
to
19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Auto flag wiped apply_repo_settings() now resets settings to a startup snapshot on every call, which clears config.is_auto_command that webhook servers set before invoking PRAgent.handle_request(). Auto-command flows can then behave like manual flows (e.g., publishing temporary/progress comments) because the flag is read later by tools but is removed by the second apply_repo_settings() call. Agent Prompt
|
||
| if get_settings().config.use_repo_settings_file: | ||
| repo_settings_file = None | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import copy | ||
|
|
||
| import pytest | ||
|
|
||
| from pr_agent.config_loader import get_settings, global_settings | ||
| from pr_agent.git_providers import utils as git_utils | ||
|
|
||
|
|
||
| REPO_A_TOML = b""" | ||
| [pr_reviewer] | ||
| extra_instructions = "MARKER-FROM-REPO-A" | ||
|
|
||
| [pr_code_suggestions] | ||
| extra_instructions = "MARKER-FROM-REPO-A" | ||
| """ | ||
|
|
||
|
|
||
| class FakeGitProvider: | ||
| """Minimal stand-in used only to feed ``get_repo_settings()`` into | ||
| ``apply_repo_settings()``.""" | ||
|
|
||
| def __init__(self, repo_settings: bytes): | ||
| self._repo_settings = repo_settings | ||
|
|
||
| def get_repo_settings(self): | ||
| return self._repo_settings | ||
|
|
||
| # Touched only by handle_configurations_errors() on invalid TOML. | ||
| def is_supported(self, feature): | ||
| return False | ||
|
|
||
| def publish_comment(self, body): | ||
| pass | ||
|
|
||
| def publish_persistent_comment(self, *args, **kwargs): | ||
| pass | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def fresh_global_settings(): | ||
| """Snapshot and restore ``global_settings`` around each test so the | ||
| module-level Dynaconf singleton doesn't carry state across tests.""" | ||
| snapshot = copy.deepcopy(global_settings.as_dict()) | ||
| yield | ||
| for section in set(global_settings.as_dict().keys()) - set(snapshot.keys()): | ||
| global_settings.unset(section) | ||
| for section, contents in snapshot.items(): | ||
| global_settings.unset(section) | ||
| global_settings.set(section, copy.deepcopy(contents), merge=False) | ||
|
|
||
|
|
||
| class TestApplyRepoSettings: | ||
| def _extra_instructions(self, section: str) -> str: | ||
| return get_settings().get(f"{section}.extra_instructions", "") or "" | ||
|
|
||
| def test_repo_settings_from_toml_are_applied( | ||
| self, fresh_global_settings, monkeypatch | ||
| ): | ||
| """Sanity: ``apply_repo_settings()`` loads ``extra_instructions`` from a | ||
| repo's ``.pr_agent.toml``.""" | ||
| monkeypatch.setattr( | ||
| "pr_agent.git_providers.utils.get_git_provider_with_context", | ||
| lambda url: FakeGitProvider(REPO_A_TOML), | ||
| ) | ||
| git_utils.apply_repo_settings( | ||
| "https://git.example/projects/A/repos/a/pull-requests/1" | ||
| ) | ||
|
|
||
| assert "MARKER-FROM-REPO-A" in self._extra_instructions("pr_reviewer") | ||
| assert "MARKER-FROM-REPO-A" in self._extra_instructions("pr_code_suggestions") | ||
|
|
||
| def test_repo_without_toml_does_not_inherit_previous_repo_settings( | ||
| self, fresh_global_settings, monkeypatch | ||
| ): | ||
| """Regression: after ``apply_repo_settings()`` loads a repo with a | ||
| ``.pr_agent.toml``, a subsequent call for a repo WITHOUT | ||
| ``.pr_agent.toml`` must not still carry the previous repo's | ||
| ``extra_instructions``. | ||
|
|
||
| Before the fix this failed — the per-section merge in | ||
| ``apply_repo_settings()`` never cleared state from a prior repo, and | ||
| when the next repo returned an empty settings blob, the whole load | ||
| block was skipped and the prior state persisted. | ||
| """ | ||
| # 1) first repo loads custom instructions | ||
| monkeypatch.setattr( | ||
| "pr_agent.git_providers.utils.get_git_provider_with_context", | ||
| lambda url: FakeGitProvider(REPO_A_TOML), | ||
| ) | ||
| git_utils.apply_repo_settings( | ||
| "https://git.example/projects/A/repos/a/pull-requests/1" | ||
| ) | ||
| assert "MARKER-FROM-REPO-A" in self._extra_instructions("pr_reviewer"), ( | ||
| "precondition: repo A's marker should be present after its load" | ||
| ) | ||
|
|
||
| # 2) second repo has no .pr_agent.toml (get_repo_settings() → b"") | ||
| monkeypatch.setattr( | ||
| "pr_agent.git_providers.utils.get_git_provider_with_context", | ||
| lambda url: FakeGitProvider(b""), | ||
| ) | ||
| git_utils.apply_repo_settings( | ||
| "https://git.example/projects/B/repos/b/pull-requests/1" | ||
| ) | ||
|
|
||
| assert "MARKER-FROM-REPO-A" not in self._extra_instructions("pr_reviewer"), ( | ||
| "repo A's [pr_reviewer].extra_instructions leaked into repo B" | ||
| ) | ||
| assert "MARKER-FROM-REPO-A" not in self._extra_instructions( | ||
| "pr_code_suggestions" | ||
| ), "repo A's [pr_code_suggestions].extra_instructions leaked into repo B" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
2. Reset misses context settings
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools