fix(visual_review): allow tolerate via personal API keys#60749
Conversation
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
products/visual_review/backend/tests/test_presentation.py:233-260
Per the team's preference for parameterised tests, the two personal-API-key tests share an identical structure — the only differences are the scope string and the expected status code. They can be collapsed into a single parameterised test, which also makes it straightforward to add future scope variants without duplicating the body.
```suggestion
@pytest.mark.parametrize(
"scope,expected_status",
[
("visual_review:write", status.HTTP_200_OK),
("visual_review:read", status.HTTP_403_FORBIDDEN),
],
)
def test_mark_tolerated_via_personal_api_key(self, scope: str, expected_status: int):
"""Regression: tolerate must be reachable from personal API keys with write scope (MCP path)."""
run_id, snapshot_id = self._changed_snapshot_for_tolerate()
key = self.create_personal_api_key_with_scopes([scope])
self.client.logout()
response = self.client.post(
f"/api/projects/{self.team.id}/visual_review/runs/{run_id}/tolerate/",
{"snapshot_id": snapshot_id},
format="json",
HTTP_AUTHORIZATION=f"Bearer {key}",
)
assert response.status_code == expected_status, response.json()
```
Reviews (1): Last reviewed commit: "fix(visual_review): allow tolerate via p..." | Re-trigger Greptile |
pauldambra
left a comment
There was a problem hiding this comment.
QA Swarm review complete. See inline comments and the top-level summary.
|
Note 🤖 Automated comment by QA Swarm — not written by a human Multi-perspective review: qa-team (specialists + generalists), paul-reviewer, xp-reviewer, security-audit Verdict: 💬 APPROVE WITH NITSA correct, minimal, fail-closed-preserving authz fix. All four reviewers approve. Adding Key findings
ConvergenceThe parameterize nit was raised independently by qa-team, paul, and xp — highest-confidence finding, and it matches a hard repo convention. Reviewer summaries
Automated by QA Swarm — not a human review |
|
Retaining stamphog approval — delta since last review classified as |
The mark_tolerated action on RunViewSet was missing from scope_object_write_actions, so any request hitting /tolerate/ from a personal API key (or OAuth token) was rejected at the permission layer with "This action does not support personal API key access" — the MCP integration could approve and quarantine but not tolerate. Add it to the write actions list and cover the regression with three parameterised tests: session auth, personal API key with the write scope, and personal API key with the read scope (which should stay forbidden). Generated-By: PostHog Code Task-Id: 9c7e17df-cee3-4307-81cd-ffa8a4fcac6d
Address qa-swarm review nits (convergent across three reviewers): collapse the three near-identical tolerate tests into one parameterised case over (scope, expected_status), and drop the helper docstring per the repo's no-doc-comments-in-tests convention. The why-comment on the forced CHANGED result is kept — it explains a deliberate reach past the diff pipeline. Generated-By: PostHog Code Task-Id: 9c7e17df-cee3-4307-81cd-ffa8a4fcac6d
68e816e to
408ca30
Compare
New commits pushed (delta classified non_linear_history) — stamphog approval dismissed; re-review running automatically.
mypy rejected `**extra_headers` splatted into `APIClient.post` — the `dict[str, str]` could land on the `follow: bool` positional in the stub signature ([arg-type] at the post call). Use the DRF-idiomatic `self.client.credentials(HTTP_AUTHORIZATION=...)` to set the bearer token instead of passing it as a request kwarg, which is type-clean and reads better. Generated-By: PostHog Code Task-Id: 9c7e17df-cee3-4307-81cd-ffa8a4fcac6d

Problem
The
mark_toleratedaction onRunViewSet(POST /api/projects/:id/visual_review/runs/:run_id/tolerate/) was missing from the viewset'sscope_object_write_actions. As a result, any request authenticated with a personal API key (or OAuth token) was rejected at the API-scope permission layer withThis action does not support personal API key access.In practice this meant the visual-review MCP integration could approve and quarantine snapshots but could not tolerate them — the only one of the three write actions that hadn't been added to the scope list. It surfaced when trying to mark a couple of benign unrelated snapshot diffs as tolerated via the MCP.
Changes
mark_toleratedtoRunViewSet.scope_object_write_actions, so the action derives thevisual_review:writerequired scope and is reachable from personal API keys / OAuth tokens with that scope.visual_review:writecan tolerate (200) — the regression,visual_review:readis still forbidden (403).No behaviour change to the tolerate logic itself; this is purely the permission-scope wiring.
How did you test this code?
I'm an agent. I ran the new tests locally via
hogli test:products/visual_review/backend/tests/test_presentation.py::TestRunViewSet -k tolerate— 3 passed.The write-scope test fails before the one-line
scope_object_write_actionschange (403) and passes after (200), confirming it pins the regression.Automatic notifications
Docs update
No docs changes — internal API-scope fix.
🤖 Agent context
Authored by PostHog Code. Found while triaging a separate user-interviews PR's visual-review run: the MCP
toleratetool returned HTTP 403This action does not support personal API key access, whileapproveworked. Traced it toposthog/permissions.py:APIScopePermissionrequiring the action to appear in the viewset's read/write action lists to derive a required scope;mark_toleratedwas absent fromRunViewSet.scope_object_write_actionswhereas the analogousquarantine/unquarantineonRepoViewSetandapprove/auto_approveonRunViewSetwere present. One-line list addition plus regression coverage.Created with PostHog Code