dont report abuse reports for unlisted versions to cinder (#22625)

* dont report abuse reports for unlisted versions to cinder

* fix test

* is_reportable -> is_individually_actionable

* change logic to include listed only, rather than exclude unlisted
This commit is contained in:
Andrew Williamson 2024-09-06 12:23:13 +01:00 коммит произвёл GitHub
Родитель 0ea334d43e
Коммит 7d2f18d896
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 99 добавлений и 8 удалений

Просмотреть файл

@ -20,7 +20,7 @@ from olympia.constants.abuse import (
) )
from olympia.ratings.models import Rating from olympia.ratings.models import Rating
from olympia.users.models import UserProfile from olympia.users.models import UserProfile
from olympia.versions.models import VersionReviewerFlags from olympia.versions.models import Version, VersionReviewerFlags
from .cinder import ( from .cinder import (
CinderAddon, CinderAddon,
@ -456,7 +456,7 @@ class AbuseReport(ModelBase):
) )
# Those reasons will be reported to Cinder. # Those reasons will be reported to Cinder.
REASONS.add_subset( REASONS.add_subset(
'REPORTABLE_REASONS', 'INDIVIDUALLY_ACTIONABLE_REASONS',
('HATEFUL_VIOLENT_DECEPTIVE', 'ILLEGAL', 'POLICY_VIOLATION', 'SOMETHING_ELSE'), ('HATEFUL_VIOLENT_DECEPTIVE', 'ILLEGAL', 'POLICY_VIOLATION', 'SOMETHING_ELSE'),
) )
# Abuse in these locations are handled by reviewers # Abuse in these locations are handled by reviewers
@ -779,6 +779,27 @@ class AbuseReport(ModelBase):
else: else:
return self.addon return self.addon
@property
def is_individually_actionable(self):
"""Is this abuse report reportable under DSA, so should be sent to Cinder"""
return bool(
self.reason in AbuseReport.REASONS.INDIVIDUALLY_ACTIONABLE_REASONS
and (
not self.guid
or (
Addon.unfiltered.filter(guid=self.guid).exists()
and (
not self.addon_version
or Version.unfiltered.filter(
addon__guid=self.guid,
version=self.addon_version,
channel=amo.CHANNEL_LISTED,
).exists()
)
)
)
)
@property @property
def is_handled_by_reviewers(self): def is_handled_by_reviewers(self):
return ( return (

Просмотреть файл

@ -155,7 +155,7 @@ class BaseAbuseReportSerializer(AMOModelSerializer):
instance = super().create(validated_data) instance = super().create(validated_data)
if ( if (
waffle.switch_is_active('dsa-job-technical-processing') waffle.switch_is_active('dsa-job-technical-processing')
and validated_data.get('reason') in AbuseReport.REASONS.REPORTABLE_REASONS and instance.is_individually_actionable
): ):
# call task to fire off cinder report # call task to fire off cinder report
report_to_cinder.delay(instance.id) report_to_cinder.delay(instance.id)

Просмотреть файл

@ -31,7 +31,7 @@ from olympia.constants.abuse import (
) )
from olympia.ratings.models import Rating from olympia.ratings.models import Rating
from olympia.reviewers.models import NeedsHumanReview from olympia.reviewers.models import NeedsHumanReview
from olympia.versions.models import VersionReviewerFlags from olympia.versions.models import Version, VersionReviewerFlags
from ..cinder import ( from ..cinder import (
CinderAddon, CinderAddon,
@ -66,7 +66,7 @@ from ..utils import (
) )
class TestAbuse(TestCase): class TestAbuseReport(TestCase):
fixtures = ['base/addon_3615', 'base/user_999'] fixtures = ['base/addon_3615', 'base/user_999']
def test_choices(self): def test_choices(self):
@ -482,6 +482,52 @@ class TestAbuse(TestCase):
report.update(rating=None, collection=collection) report.update(rating=None, collection=collection)
assert report.target == collection assert report.target == collection
def test_is_individually_actionable(self):
report = AbuseReport.objects.create(
guid='@lol', reason=AbuseReport.REASONS.HATEFUL_VIOLENT_DECEPTIVE
)
assert report.is_individually_actionable is False
addon = addon_factory(guid='@lol')
user = user_factory()
for target in (
{'guid': addon.guid},
{'user': user},
{'rating': Rating.objects.create(user=user, addon=addon, rating=5)},
{'collection': collection_factory()},
):
report.update(
reason=AbuseReport.REASONS.FEEDBACK_SPAM,
**{
'guid': None,
'user': None,
'rating': None,
'collection': None,
**target,
},
)
assert report.is_individually_actionable is False
report.update(reason=AbuseReport.REASONS.HATEFUL_VIOLENT_DECEPTIVE)
assert report.is_individually_actionable is True
report.update(
guid=addon.guid,
user=None,
rating=None,
collection=None,
addon_version=addon.current_version.version,
)
assert report.is_individually_actionable is True
self.make_addon_unlisted(addon)
assert report.is_individually_actionable is False
self.make_addon_listed(addon)
Version.objects.get(version=report.addon_version).delete()
assert report.is_individually_actionable is True
Version.unfiltered.get(version=report.addon_version).delete(hard=True)
assert report.is_individually_actionable is False
def test_is_handled_by_reviewers(self): def test_is_handled_by_reviewers(self):
addon = addon_factory() addon = addon_factory()
abuse_report = AbuseReport.objects.create( abuse_report = AbuseReport.objects.create(

Просмотреть файл

@ -539,11 +539,11 @@ class AddonAbuseViewSetTestBase:
assert response.status_code == 400 assert response.status_code == 400
assert json.loads(response.content) == {'addon_install_method': 'Invalid value'} assert json.loads(response.content) == {'addon_install_method': 'Invalid value'}
def _setup_reportable_reason(self, reason): def _setup_reportable_reason(self, reason, *, addon=None, extra_data=None):
addon = addon_factory(guid='@badman') addon = addon or addon_factory(guid='@badman')
response = self.client.post( response = self.client.post(
self.url, self.url,
data={'addon': addon.guid, 'reason': reason}, data={'addon': addon.guid, 'reason': reason, **(extra_data or {})},
REMOTE_ADDR='123.45.67.89', REMOTE_ADDR='123.45.67.89',
HTTP_X_FORWARDED_FOR=f'123.45.67.89, {get_random_ip()}', HTTP_X_FORWARDED_FOR=f'123.45.67.89, {get_random_ip()}',
) )
@ -555,6 +555,30 @@ class AddonAbuseViewSetTestBase:
self._setup_reportable_reason('hateful_violent_deceptive') self._setup_reportable_reason('hateful_violent_deceptive')
task_mock.assert_called() task_mock.assert_called()
@mock.patch('olympia.abuse.tasks.report_to_cinder.delay')
@override_switch('dsa-job-technical-processing', active=True)
def test_reportable_reason_does_call_if_version_listed(self, task_mock):
addon = addon_factory(guid='@badman')
self._setup_reportable_reason(
'hateful_violent_deceptive',
addon=addon,
extra_data={'addon_version': addon.current_version.version},
)
task_mock.assert_called()
@mock.patch('olympia.abuse.tasks.report_to_cinder.delay')
@override_switch('dsa-job-technical-processing', active=True)
def test_reportable_reason_does_not_call_if_version_unlisted(self, task_mock):
addon = addon_factory(guid='@badman')
version = addon.current_version
self.make_addon_unlisted(addon)
self._setup_reportable_reason(
'hateful_violent_deceptive',
addon=addon,
extra_data={'addon_version': version.version},
)
task_mock.assert_not_called()
@mock.patch('olympia.abuse.tasks.report_to_cinder.delay') @mock.patch('olympia.abuse.tasks.report_to_cinder.delay')
@override_switch('dsa-job-technical-processing', active=False) @override_switch('dsa-job-technical-processing', active=False)
def test_reportable_reason_does_not_call_cinder_with_waffle_off(self, task_mock): def test_reportable_reason_does_not_call_cinder_with_waffle_off(self, task_mock):